BookSystem/backend/auth/main.py
jayhgq 0545d68018 feat(settings):增加系统Logo系统设置项,并支持根据上传的图片显示系统logo
1. 系统设置中增加系统Logo设置项
2. 系统标题增加logo的显示,并可自动判断,是否上传logo,若上传则显示
3. 完善系统设置功能,支持上传图片,支持设置值为布尔值或列表
4. 增加上传和获取图片的接口
5. 完善nginx,支持图片的接口代理
2026-06-19 23:17:13 +08:00

173 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.exceptions import RequestValidationError
from contextlib import asynccontextmanager
from apps.urls import api_router
from apps.settings.urls import UPLOAD_DIR
from database import engine, get_db
from models import Base
from init_data import init_all_data
from pathlib import Path
from starlette.requests import Request
from starlette.responses import JSONResponse
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时初始化数据库
async with engine.begin() as conn:
# 创建所有表
await conn.run_sync(Base.metadata.create_all)
# 初始化数据
async for db in get_db():
await init_all_data(db)
break
yield
# 关闭时的清理工作
await engine.dispose()
# 实例化FastAPI
app = FastAPI(
title="图书系统授权服务API",
description="""图书系统授权服务API是一套用于用户认证和授权的服务提供用户注册、登录、权限校验等功能。
同时实现了基于角色的访问控制RBAC支持自定义角色和权限。还增加了日志记录功能方便监控和调试。""",
version="1.0.0",
lifespan=lifespan,
docs_url=None,
redoc_url="/redoc",
)
# 开发环境前后端分离时的跨域(生产环境请收窄 allow_origins
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173",
"http://127.0.0.1:5173",
"http://localhost:3000",
"http://127.0.0.1:3000",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["X-Captcha-ID"],
)
# 挂载swagger-ui静态文件目录
swagger_ui_path = Path(__file__).parent.parent / "swagger-ui"
app.mount("/static", StaticFiles(directory=str(swagger_ui_path)), name="static")
# 上传图片通过自定义端点提供(和 settings/urls.py 中的 UPLOAD_DIR 保持一致)
@app.get("/uploads/{filename}")
async def serve_upload(filename: str):
"""提供上传的图片文件"""
file_path = UPLOAD_DIR / filename
if not file_path.exists():
raise HTTPException(status_code=404, detail="文件不存在")
return FileResponse(str(file_path))
# 自定义Swagger UI页面
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Book System API - Swagger UI</title>
<link rel="stylesheet" type="text/css" href="/static/swagger-ui.css">
<link rel="icon" type="image/png" href="/static/favicon-32x32.png" sizes="32x32"/>
<style>
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin: 0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="/static/swagger-ui-bundle.js"></script>
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "/openapi.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",
persistAuthorization: true,
docExpansion: "list"
});
window.ui = ui;
};
</script>
</body>
</html>
"""
return HTMLResponse(content=html_content)
# 包含路由
app.include_router(api_router)
# 声明装饰器方法和路径
@app.get("/")
# 声明装饰器函数
async def home():
return {"message": "Hello World!!!"}
# 自定义验证错误处理器
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
errors = []
for err in exc.errors():
field = err.get("loc", ["unknown"])[-1]
msg = err.get("msg", "验证失败")
if "min_length" in msg.lower():
if field == "username":
msg = "用户名长度至少为3个字符"
elif field == "password":
msg = "密码长度至少为6个字符"
elif "max_length" in msg.lower():
if field == "username":
msg = "用户名长度不能超过50个字符"
elif field == "password":
msg = "密码长度不能超过50个字符"
errors.append({"field": field, "message": msg})
return JSONResponse(
status_code=422,
content={"code": 422, "message": "验证失败", "errors": errors}
)
# 如果使用命令行启动使用uvicorn 文件名:app --reload启动即可下面命令就不用写
# 如果写下面的命令就不需要命令行启动了直接用IDE运行即可
if __name__ == "__main__":
import uvicorn
import os
name = f"{os.path.splitext(os.path.basename(os.path.abspath(__file__)))[0]}:app"
uvicorn.run(name, host="0.0.0.0", port=8000, reload=True, reload_dirs=["_"])