feat: 新增可配置的路径前缀以支持子路径部署
添加前后端环境变量配置示例,新增后端路径前缀配置项,更新后端路由与静态文件挂载逻辑,调整前端代理配置与 API 调用代码,适配子路径部署场景。
This commit is contained in:
@@ -1,29 +1,37 @@
|
||||
# MongoDB
|
||||
MONGODB_URL=mongodb://localhost:27017
|
||||
MONGODB_DB_NAME=pregnancy_report
|
||||
# Path Prefix
|
||||
API_PREFIX=/app_03/api
|
||||
UPLOADS_PREFIX=/uploads
|
||||
|
||||
# MongoDB (通过 SSH 隧道连接到服务器)
|
||||
# MONGODB_URL=mongodb+srv://shutiaozhao127_db_user:YEJzZUi0nbEuFYL3@cluster0.bkztgqx.mongodb.net/?appName=Cluster0
|
||||
# MONGODB_DB_NAME=pregnancy_report
|
||||
MONGODB_URL=mongodb://root:mgdb4link@localhost:27017/?authSource=admin
|
||||
MONGODB_DB_NAME=app_03_dev
|
||||
|
||||
# JWT
|
||||
JWT_SECRET_KEY=your-secret-key-change-this
|
||||
JWT_SECRET_KEY=pregnancy_ai_demo_666888
|
||||
JWT_ALGORITHM=HS256
|
||||
JWT_EXPIRE_MINUTES=10080
|
||||
|
||||
# WeChat Mini Program
|
||||
WECHAT_APP_ID=your-wechat-app-id
|
||||
WECHAT_APP_SECRET=your-wechat-app-secret
|
||||
WECHAT_APP_ID=
|
||||
WECHAT_APP_SECRET=
|
||||
|
||||
# Claude AI
|
||||
ANTHROPIC_API_KEY=your-anthropic-api-key
|
||||
ANTHROPIC_BASE_URL=
|
||||
AI_MODEL_NAME=claude-sonnet-4-20250514
|
||||
# AI API (硅基流动 - OpenAI 兼容接口)
|
||||
AI_API_KEY=sk-pnbkufcvwbnmzcwdhwzecdgpiqqsumtsnfojtozmmrfqiuil
|
||||
AI_BASE_URL=https://api.siliconflow.cn/v1
|
||||
AI_VISION_MODEL=Qwen/Qwen2.5-VL-72B-Instruct
|
||||
AI_TEXT_MODEL=Qwen/Qwen3-235B-A22B-Instruct-2507
|
||||
|
||||
# CORS(多个域名逗号分隔,* 表示允许所有)
|
||||
CORS_ORIGINS=*
|
||||
|
||||
|
||||
# File Upload
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
|
||||
# Server
|
||||
HOST=0.0.0.0
|
||||
PORT=8000
|
||||
PORT=8030
|
||||
DEBUG=true
|
||||
|
||||
@@ -2,6 +2,10 @@ from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Path Prefix
|
||||
api_prefix: str = "/api"
|
||||
uploads_prefix: str = "/uploads"
|
||||
|
||||
# MongoDB
|
||||
mongodb_url: str = "mongodb://localhost:27017"
|
||||
mongodb_db_name: str = "pregnancy_report"
|
||||
|
||||
@@ -48,6 +48,14 @@ app = FastAPI(
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
api_prefix = settings.api_prefix.rstrip("/") or ""
|
||||
if api_prefix and not api_prefix.startswith("/"):
|
||||
api_prefix = f"/{api_prefix}"
|
||||
|
||||
uploads_prefix = settings.uploads_prefix.rstrip("/") or ""
|
||||
if uploads_prefix and not uploads_prefix.startswith("/"):
|
||||
uploads_prefix = f"/{uploads_prefix}"
|
||||
|
||||
# CORS 配置
|
||||
cors_origins = (
|
||||
["*"]
|
||||
@@ -88,20 +96,20 @@ async def global_exception_handler(request: Request, exc: Exception):
|
||||
content={"detail": "服务器内部错误,请稍后重试"},
|
||||
)
|
||||
|
||||
app.mount("/uploads", StaticFiles(directory=settings.upload_dir), name="uploads")
|
||||
app.mount(uploads_prefix or "/uploads", StaticFiles(directory=settings.upload_dir), name="uploads")
|
||||
|
||||
app.include_router(auth.router, prefix="/api/auth", tags=["认证"])
|
||||
app.include_router(user.router, prefix="/api/user", tags=["用户"])
|
||||
app.include_router(report.router, prefix="/api/report", tags=["报告"])
|
||||
app.include_router(analysis.router, prefix="/api/analysis", tags=["AI解读"])
|
||||
app.include_router(chat.router, prefix="/api/chat", tags=["AI对话"])
|
||||
app.include_router(health_records.router, prefix="/api/health-records", tags=["健康档案"])
|
||||
app.include_router(calendar.router, prefix="/api/calendar", tags=["产检日历"])
|
||||
app.include_router(doctor.router, prefix="/api/doctor", tags=["专家咨询"])
|
||||
app.include_router(share.router, prefix="/api/share", tags=["分享"])
|
||||
app.include_router(auth.router, prefix=f"{api_prefix}/auth", tags=["认证"])
|
||||
app.include_router(user.router, prefix=f"{api_prefix}/user", tags=["用户"])
|
||||
app.include_router(report.router, prefix=f"{api_prefix}/report", tags=["报告"])
|
||||
app.include_router(analysis.router, prefix=f"{api_prefix}/analysis", tags=["AI解读"])
|
||||
app.include_router(chat.router, prefix=f"{api_prefix}/chat", tags=["AI对话"])
|
||||
app.include_router(health_records.router, prefix=f"{api_prefix}/health-records", tags=["健康档案"])
|
||||
app.include_router(calendar.router, prefix=f"{api_prefix}/calendar", tags=["产检日历"])
|
||||
app.include_router(doctor.router, prefix=f"{api_prefix}/doctor", tags=["专家咨询"])
|
||||
app.include_router(share.router, prefix=f"{api_prefix}/share", tags=["分享"])
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
@app.get(f"{api_prefix}/health")
|
||||
async def health_check():
|
||||
return {"status": "ok", "service": "孕育无忧 API"}
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ textarea{width:100%;padding:10px;border:1px solid #ddd;border-radius:8px;font-si
|
||||
|
||||
<script>
|
||||
const API = '';
|
||||
const API_PREFIX = window.location.pathname.startsWith('/app_03') ? '/app_03/api' : '/api';
|
||||
let TOKEN = '';
|
||||
let currentReportId = '';
|
||||
let ocrDataItems = [];
|
||||
@@ -237,6 +238,7 @@ function showLoading(text='加载中...'){
|
||||
function hideLoading(){document.getElementById('globalLoading').style.display='none'}
|
||||
|
||||
async function api(path,opts={}){
|
||||
if(path.startsWith('/api/')) path = API_PREFIX + path.slice(4);
|
||||
const headers = {'Authorization':'Bearer '+TOKEN, ...(opts.headers||{})};
|
||||
if(opts.body && !(opts.body instanceof FormData)){
|
||||
headers['Content-Type']='application/json';
|
||||
@@ -270,7 +272,7 @@ function badgeHtml(status){
|
||||
async function demoLogin(){
|
||||
showLoading('正在登录...');
|
||||
try{
|
||||
const data = await fetch(API+'/api/auth/demo-login',{method:'POST'}).then(r=>r.json());
|
||||
const data = await api('/api/auth/demo-login',{method:'POST'});
|
||||
TOKEN = data.access_token;
|
||||
hideLoading();
|
||||
}catch(e){
|
||||
|
||||
Reference in New Issue
Block a user