网页框架部署成功
This commit is contained in:
59
README.md
59
README.md
@@ -6,6 +6,11 @@
|
||||
|
||||
InfoGenie 是一个前后端分离的多功能聚合应用,提供实时数据接口、休闲游戏、AI工具等丰富功能。
|
||||
|
||||
### 🌐 部署环境
|
||||
|
||||
- **前端部署地址**: https://infogenie.shumengya.top
|
||||
- **后端部署地址**: https://infogenie.api.shumengya.top
|
||||
|
||||
### 🏗️ 技术架构
|
||||
|
||||
- **前端**: React + Styled Components + React Router
|
||||
@@ -48,6 +53,60 @@ cd backend
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 🚢 部署指南
|
||||
|
||||
### 🖥️ 前端部署
|
||||
|
||||
1. 进入前端目录:`cd frontend/react-app`
|
||||
2. 安装依赖:`npm install`
|
||||
3. 构建生产环境应用:`npm run build`
|
||||
4. 将 `build` 目录下的所有文件上传到前端服务器的网站根目录
|
||||
|
||||
也可以直接运行 `frontend/react-app/deploy.bat` 脚本进行构建。
|
||||
|
||||
### ⚙️ 后端部署
|
||||
|
||||
1. 进入后端目录:`cd backend`
|
||||
2. 安装依赖:`pip install -r requirements.txt`
|
||||
3. 配置环境变量或创建 `.env` 文件,包含以下内容:
|
||||
```
|
||||
MONGO_URI=你的MongoDB连接字符串
|
||||
MAIL_USERNAME=你的邮箱地址
|
||||
MAIL_PASSWORD=你的邮箱授权码
|
||||
SECRET_KEY=你的应用密钥
|
||||
SESSION_COOKIE_SECURE=True
|
||||
```
|
||||
4. 使用 Gunicorn 或 uWSGI 作为 WSGI 服务器启动应用:
|
||||
```
|
||||
gunicorn -w 4 -b 0.0.0.0:5000 "app:create_app()"
|
||||
```
|
||||
5. 配置反向代理,将 `https://infogenie.api.shumengya.top` 反向代理到后端服务
|
||||
|
||||
也可以参考 `backend/deploy.bat` 脚本中的部署说明。
|
||||
|
||||
### ⚙️ 配置说明
|
||||
|
||||
#### 前端配置
|
||||
|
||||
前端通过环境变量配置API基础URL:
|
||||
|
||||
- 开发环境:`.env.development` 文件中设置 `REACT_APP_API_URL=http://localhost:5000`
|
||||
- 生产环境:`.env.production` 文件中设置 `REACT_APP_API_URL=https://infogenie.api.shumengya.top`
|
||||
|
||||
#### 后端配置
|
||||
|
||||
后端通过 `config.py` 和环境变量进行配置:
|
||||
|
||||
- MongoDB连接:通过环境变量 `MONGO_URI` 设置
|
||||
- 邮件服务:通过环境变量 `MAIL_USERNAME` 和 `MAIL_PASSWORD` 设置
|
||||
- CORS配置:在 `app.py` 中配置允许的前端域名
|
||||
|
||||
#### 60sAPI配置
|
||||
|
||||
60sAPI模块的静态文件位于 `frontend/60sapi` 目录,通过后端的静态文件服务提供访问。
|
||||
|
||||
各API模块的接口地址已配置为 `https://infogenie.api.shumengya.top/api/60s`。
|
||||
|
||||
#### 前端依赖
|
||||
```bash
|
||||
cd frontend/react-app
|
||||
|
||||
14
backend/.env.production
Normal file
14
backend/.env.production
Normal file
@@ -0,0 +1,14 @@
|
||||
# 生产环境配置
|
||||
|
||||
# MongoDB配置
|
||||
MONGO_URI=mongodb://用户名:密码@主机地址:端口/InfoGenie?authSource=admin
|
||||
|
||||
# 邮件配置
|
||||
MAIL_USERNAME=your-email@qq.com
|
||||
MAIL_PASSWORD=your-app-password
|
||||
|
||||
# 应用密钥
|
||||
SECRET_KEY=infogenie-production-secret-key-2025
|
||||
|
||||
# 会话安全配置
|
||||
SESSION_COOKIE_SECURE=True
|
||||
@@ -31,7 +31,7 @@ def create_app():
|
||||
# 加载配置
|
||||
app.config.from_object(Config)
|
||||
|
||||
# 启用CORS跨域支持
|
||||
# 启用CORS跨域支持(允许所有源)
|
||||
CORS(app, supports_credentials=True)
|
||||
|
||||
# 初始化MongoDB
|
||||
@@ -182,6 +182,4 @@ def create_app():
|
||||
if __name__ == '__main__':
|
||||
app = create_app()
|
||||
print("🚀 启动 InfoGenie 后端服务...")
|
||||
print("📡 API地址: http://localhost:5000")
|
||||
print("📚 文档地址: http://localhost:5000/api/health")
|
||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||
app.run(debug=True, host='0.0.0.0', port=5002)
|
||||
|
||||
27
backend/deploy.bat
Normal file
27
backend/deploy.bat
Normal file
@@ -0,0 +1,27 @@
|
||||
@echo off
|
||||
echo ===== 开始部署后端应用到生产环境 =====
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo 1. 安装依赖...
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo 2. 部署说明:
|
||||
echo.
|
||||
echo 请确保以下配置已完成:
|
||||
echo 1. 在服务器上配置环境变量或创建 .env 文件,包含以下内容:
|
||||
echo MONGO_URI=你的MongoDB连接字符串
|
||||
echo MAIL_USERNAME=你的邮箱地址
|
||||
echo MAIL_PASSWORD=你的邮箱授权码
|
||||
echo SECRET_KEY=你的应用密钥
|
||||
echo.
|
||||
echo 3. 启动后端服务:
|
||||
echo 在生产环境中,建议使用 Gunicorn 或 uWSGI 作为 WSGI 服务器
|
||||
echo 示例命令:gunicorn -w 4 -b 0.0.0.0:5000 "app:create_app()"
|
||||
echo.
|
||||
echo 4. 配置反向代理:
|
||||
echo 将 https://infogenie.api.shumengya.top 反向代理到后端服务
|
||||
echo.
|
||||
echo ===== 后端应用部署准备完成 =====
|
||||
|
||||
pause
|
||||
@@ -73,10 +73,13 @@ def scan_directories():
|
||||
except:
|
||||
title = module_name
|
||||
|
||||
# 根据环境获取基础URL
|
||||
base_url = 'https://infogenie.api.shumengya.top'
|
||||
|
||||
apis.append({
|
||||
'title': title,
|
||||
'description': f'{module_name}相关功能',
|
||||
'link': f'http://localhost:5000/60sapi/{category_name}/{module_name}/index.html',
|
||||
'link': f'{base_url}/60sapi/{category_name}/{module_name}/index.html',
|
||||
'status': 'active',
|
||||
'color': gradient_colors[i % len(gradient_colors)]
|
||||
})
|
||||
|
||||
6
build_frontend.bat
Normal file
6
build_frontend.bat
Normal file
@@ -0,0 +1,6 @@
|
||||
@echo off
|
||||
cd /d "e:\Python\InfoGenie\frontend\react-app"
|
||||
npm run build
|
||||
|
||||
npx serve -s build
|
||||
pause
|
||||
@@ -1,5 +1,5 @@
|
||||
// 本地后端API接口
|
||||
const LOCAL_API_BASE = 'http://localhost:5000/api/60s';
|
||||
const LOCAL_API_BASE = 'https://infogenie.api.shumengya.top/api/60s';
|
||||
|
||||
// API接口列表(备用)
|
||||
const API_ENDPOINTS = [
|
||||
|
||||
2
frontend/react-app/.env.production
Normal file
2
frontend/react-app/.env.production
Normal file
@@ -0,0 +1,2 @@
|
||||
# 生产环境API配置
|
||||
REACT_APP_API_URL=https://infogenie.api.shumengya.top
|
||||
@@ -44,6 +44,5 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"proxy": "http://localhost:5000"
|
||||
}
|
||||
}
|
||||
|
||||
48
frontend/react-app/public/sw.js
vendored
Normal file
48
frontend/react-app/public/sw.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Service Worker for InfoGenie App
|
||||
const CACHE_NAME = 'infogenie-cache-v1';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json'
|
||||
];
|
||||
|
||||
// 安装Service Worker
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
console.log('Opened cache');
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 拦截请求并从缓存中响应
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(response => {
|
||||
// 如果找到缓存的响应,则返回缓存
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// 更新Service Worker
|
||||
self.addEventListener('activate', event => {
|
||||
const cacheWhitelist = [CACHE_NAME];
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
if (cacheWhitelist.indexOf(cacheName) === -1) {
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
13
frontend/react-app/src/index.js
vendored
13
frontend/react-app/src/index.js
vendored
@@ -9,3 +9,16 @@ root.render(
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// 注册Service Worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.then(registration => {
|
||||
console.log('ServiceWorker registration successful with scope: ', registration.scope);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('ServiceWorker registration failed: ', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
7
frontend/react-app/src/pages/AiModelPage.js
vendored
7
frontend/react-app/src/pages/AiModelPage.js
vendored
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { FiCpu, FiUser, FiExternalLink, FiArrowLeft } from 'react-icons/fi';
|
||||
import { useUser } from '../contexts/UserContext';
|
||||
import axios from 'axios';
|
||||
import api from '../utils/api';
|
||||
|
||||
const AiContainer = styled.div`
|
||||
min-height: calc(100vh - 140px);
|
||||
@@ -262,7 +262,7 @@ const AiModelPage = () => {
|
||||
const fetchApps = async () => {
|
||||
try {
|
||||
setLoadingApps(true);
|
||||
const response = await axios.get('/api/aimodelapp/scan-directories');
|
||||
const response = await api.get('/api/aimodelapp/scan-directories');
|
||||
if (response.data.success) {
|
||||
setApps(response.data.apps);
|
||||
} else {
|
||||
@@ -278,7 +278,8 @@ const AiModelPage = () => {
|
||||
|
||||
const handleLaunchApp = (app) => {
|
||||
// 将相对路径转换为完整的服务器地址
|
||||
const fullLink = `http://localhost:5000${app.link}`;
|
||||
const baseUrl = process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top';
|
||||
const fullLink = `${baseUrl}${app.link}`;
|
||||
setEmbeddedApp({ ...app, link: fullLink });
|
||||
};
|
||||
|
||||
|
||||
73
frontend/react-app/src/pages/Api60sPage.js
vendored
73
frontend/react-app/src/pages/Api60sPage.js
vendored
@@ -238,81 +238,26 @@ const Api60sPage = () => {
|
||||
// 从后端API获取目录结构
|
||||
const scanDirectories = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:5000/api/60s/scan-directories');
|
||||
// 使用环境变量中配置的API URL
|
||||
const baseUrl = process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top';
|
||||
const apiUrl = `${baseUrl}/api/60s/scan-directories`;
|
||||
console.log('正在请求API目录结构:', apiUrl);
|
||||
const response = await fetch(apiUrl);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('无法从后端获取目录结构,使用前端扫描方式');
|
||||
console.warn('无法从后端获取目录结构:', error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// 前端扫描方式(备用)
|
||||
const frontendScan = async () => {
|
||||
const categories = [];
|
||||
// 前端扫描方式已移除
|
||||
|
||||
for (const [categoryName, config] of Object.entries(categoryConfig)) {
|
||||
const apis = [];
|
||||
|
||||
// 尝试访问已知的模块列表(只包含实际存在的模块)
|
||||
const knownModules = {
|
||||
'热搜榜单': ['抖音热搜榜'],
|
||||
'日更资讯': [],
|
||||
'实用功能': [],
|
||||
'娱乐消遣': []
|
||||
};
|
||||
|
||||
const moduleNames = knownModules[categoryName] || [];
|
||||
|
||||
for (let i = 0; i < moduleNames.length; i++) {
|
||||
const moduleName = moduleNames[i];
|
||||
try {
|
||||
const indexPath = `/60sapi/${categoryName}/${moduleName}/index.html`;
|
||||
const fullUrl = `http://localhost:5000${indexPath}`;
|
||||
const response = await fetch(fullUrl, { method: 'HEAD' });
|
||||
|
||||
if (response.ok) {
|
||||
// 获取页面标题
|
||||
const htmlResponse = await fetch(fullUrl);
|
||||
const html = await htmlResponse.text();
|
||||
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
||||
const title = titleMatch ? titleMatch[1].trim() : moduleName;
|
||||
|
||||
apis.push({
|
||||
title,
|
||||
description: `${moduleName}相关功能`,
|
||||
link: fullUrl,
|
||||
status: 'active',
|
||||
color: gradientColors[i % gradientColors.length]
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略访问失败的模块
|
||||
}
|
||||
}
|
||||
|
||||
if (apis.length > 0) {
|
||||
categories.push({
|
||||
title: categoryName,
|
||||
icon: config.icon,
|
||||
color: config.color,
|
||||
apis
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return categories;
|
||||
};
|
||||
|
||||
// 首先尝试后端扫描,失败则使用前端扫描
|
||||
// 只使用后端扫描
|
||||
const backendResult = await scanDirectories();
|
||||
if (backendResult && backendResult.success) {
|
||||
return backendResult.categories || [];
|
||||
} else {
|
||||
return await frontendScan();
|
||||
}
|
||||
return backendResult && backendResult.success ? backendResult.categories || [] : [];
|
||||
|
||||
} catch (error) {
|
||||
console.error('扫描API模块时出错:', error);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { FiGrid, FiPlay, FiExternalLink, FiArrowLeft } from 'react-icons/fi';
|
||||
import axios from 'axios';
|
||||
import api from '../utils/api';
|
||||
|
||||
const GameContainer = styled.div`
|
||||
min-height: calc(100vh - 140px);
|
||||
@@ -233,7 +233,7 @@ const SmallGamePage = () => {
|
||||
const fetchGames = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await axios.get('/api/smallgame/scan-directories');
|
||||
const response = await api.get('/api/smallgame/scan-directories');
|
||||
if (response.data.success) {
|
||||
setGames(response.data.games);
|
||||
} else {
|
||||
@@ -249,7 +249,8 @@ const SmallGamePage = () => {
|
||||
|
||||
const handlePlayGame = (game) => {
|
||||
// 将相对路径转换为完整的服务器地址
|
||||
const fullLink = `http://localhost:5000${game.link}`;
|
||||
const baseUrl = process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top';
|
||||
const fullLink = `${baseUrl}${game.link}`;
|
||||
setEmbeddedGame({ ...game, link: fullLink });
|
||||
};
|
||||
|
||||
|
||||
39
frontend/react-app/src/utils/api.js
vendored
39
frontend/react-app/src/utils/api.js
vendored
@@ -3,7 +3,7 @@ import toast from 'react-hot-toast';
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
baseURL: process.env.REACT_APP_API_URL || '/api',
|
||||
baseURL: process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top',
|
||||
timeout: 10000,
|
||||
withCredentials: true, // 支持携带cookie
|
||||
headers: {
|
||||
@@ -11,6 +11,9 @@ const api = axios.create({
|
||||
}
|
||||
});
|
||||
|
||||
// 打印当前使用的API URL,便于调试
|
||||
console.log('API Base URL:', process.env.REACT_APP_API_URL || 'https://infogenie.api.shumengya.top');
|
||||
|
||||
// 请求拦截器
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
@@ -44,63 +47,63 @@ api.interceptors.response.use(
|
||||
// 认证相关API
|
||||
export const authAPI = {
|
||||
// 发送验证码
|
||||
sendVerification: (data) => api.post('/auth/send-verification', data),
|
||||
sendVerification: (data) => api.post('/api/auth/send-verification', data),
|
||||
|
||||
// 验证验证码
|
||||
verifyCode: (data) => api.post('/auth/verify-code', data),
|
||||
verifyCode: (data) => api.post('/api/auth/verify-code', data),
|
||||
|
||||
// 登录
|
||||
login: (credentials) => api.post('/auth/login', credentials),
|
||||
login: (credentials) => api.post('/api/auth/login', credentials),
|
||||
|
||||
// 注册
|
||||
register: (userData) => api.post('/auth/register', userData),
|
||||
register: (userData) => api.post('/api/auth/register', userData),
|
||||
|
||||
// 登出
|
||||
logout: () => api.post('/auth/logout'),
|
||||
logout: () => api.post('/api/auth/logout'),
|
||||
|
||||
// 检查登录状态
|
||||
checkLogin: () => api.get('/auth/check'),
|
||||
checkLogin: () => api.get('/api/auth/check'),
|
||||
};
|
||||
|
||||
// 用户相关API
|
||||
export const userAPI = {
|
||||
// 获取用户资料
|
||||
getProfile: () => api.get('/user/profile'),
|
||||
getProfile: () => api.get('/api/user/profile'),
|
||||
|
||||
// 修改密码
|
||||
changePassword: (passwordData) => api.post('/user/change-password', passwordData),
|
||||
changePassword: (passwordData) => api.post('/api/user/change-password', passwordData),
|
||||
|
||||
// 获取用户统计
|
||||
getStats: () => api.get('/user/stats'),
|
||||
getStats: () => api.get('/api/user/stats'),
|
||||
|
||||
// 删除账户
|
||||
deleteAccount: (password) => api.post('/user/delete', { password }),
|
||||
deleteAccount: (password) => api.post('/api/user/delete', { password }),
|
||||
};
|
||||
|
||||
// 60s API相关接口
|
||||
export const api60s = {
|
||||
// 抖音热搜
|
||||
getDouyinHot: () => api.get('/60s/douyin'),
|
||||
getDouyinHot: () => api.get('/api/60s/douyin'),
|
||||
|
||||
// 微博热搜
|
||||
getWeiboHot: () => api.get('/60s/weibo'),
|
||||
getWeiboHot: () => api.get('/api/60s/weibo'),
|
||||
|
||||
// 猫眼票房
|
||||
getMaoyanBoxOffice: () => api.get('/60s/maoyan'),
|
||||
getMaoyanBoxOffice: () => api.get('/api/60s/maoyan'),
|
||||
|
||||
// 60秒读懂世界
|
||||
get60sNews: () => api.get('/60s/60s'),
|
||||
get60sNews: () => api.get('/api/60s/60s'),
|
||||
|
||||
// 必应壁纸
|
||||
getBingWallpaper: () => api.get('/60s/bing-wallpaper'),
|
||||
getBingWallpaper: () => api.get('/api/60s/bing-wallpaper'),
|
||||
|
||||
// 天气信息
|
||||
getWeather: (city = '北京') => api.get(`/60s/weather?city=${encodeURIComponent(city)}`),
|
||||
getWeather: (city = '北京') => api.get(`/api/60s/weather?city=${encodeURIComponent(city)}`),
|
||||
};
|
||||
|
||||
// 健康检查
|
||||
export const healthAPI = {
|
||||
check: () => api.get('/health'),
|
||||
check: () => api.get('/api/health'),
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
||||
72
nginx-config-example.conf
Normal file
72
nginx-config-example.conf
Normal file
@@ -0,0 +1,72 @@
|
||||
# Nginx配置示例 - InfoGenie部署
|
||||
|
||||
# 前端配置 - infogenie.shumengya.top
|
||||
server {
|
||||
listen 80;
|
||||
server_name infogenie.shumengya.top;
|
||||
|
||||
# 重定向HTTP到HTTPS
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name infogenie.shumengya.top;
|
||||
|
||||
# SSL证书配置
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
# 前端静态文件目录
|
||||
root /var/www/infogenie;
|
||||
index index.html;
|
||||
|
||||
# 处理React路由
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 安全相关配置
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
}
|
||||
|
||||
# 后端配置 - infogenie.api.shumengya.top
|
||||
server {
|
||||
listen 80;
|
||||
server_name infogenie.api.shumengya.top;
|
||||
|
||||
# 重定向HTTP到HTTPS
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name infogenie.api.shumengya.top;
|
||||
|
||||
# SSL证书配置
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
# 反向代理到后端服务
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# 安全相关配置
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
|
||||
# 允许较大的上传文件
|
||||
client_max_body_size 10M;
|
||||
}
|
||||
Reference in New Issue
Block a user