You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
8.1 KiB
260 lines
8.1 KiB
import shutil
|
|
|
|
from aerich import Command
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware import Middleware
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from tortoise.expressions import Q
|
|
|
|
from app.api import api_router
|
|
from app.controllers.api import api_controller
|
|
from app.controllers.user import UserCreate, user_controller
|
|
from app.core.exceptions import (
|
|
DoesNotExist,
|
|
DoesNotExistHandle,
|
|
HTTPException,
|
|
HttpExcHandle,
|
|
IntegrityError,
|
|
IntegrityHandle,
|
|
RequestValidationError,
|
|
RequestValidationHandle,
|
|
ResponseValidationError,
|
|
ResponseValidationHandle,
|
|
)
|
|
from app.log import logger
|
|
from app.models.admin import Api, Menu, Role
|
|
from app.schemas.menus import MenuType
|
|
from app.settings.config import settings
|
|
from tortoise.transactions import in_transaction
|
|
|
|
from .middlewares import BackGroundTaskMiddleware, HttpAuditLogMiddleware
|
|
|
|
|
|
def make_middlewares():
|
|
middleware = [
|
|
Middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.CORS_ORIGINS,
|
|
allow_credentials=settings.CORS_ALLOW_CREDENTIALS,
|
|
allow_methods=settings.CORS_ALLOW_METHODS,
|
|
allow_headers=settings.CORS_ALLOW_HEADERS,
|
|
),
|
|
Middleware(BackGroundTaskMiddleware),
|
|
Middleware(
|
|
HttpAuditLogMiddleware,
|
|
methods=["GET", "POST", "PUT", "DELETE"],
|
|
exclude_paths=[
|
|
"/api/v1/base/access_token",
|
|
"/docs",
|
|
"/openapi.json",
|
|
"/api/model/",
|
|
"/api/v1/upload/image",
|
|
"/api/v1/upload/video"
|
|
],
|
|
),
|
|
]
|
|
return middleware
|
|
|
|
|
|
def register_exceptions(app: FastAPI):
|
|
app.add_exception_handler(DoesNotExist, DoesNotExistHandle)
|
|
app.add_exception_handler(HTTPException, HttpExcHandle)
|
|
app.add_exception_handler(IntegrityError, IntegrityHandle)
|
|
app.add_exception_handler(RequestValidationError, RequestValidationHandle)
|
|
app.add_exception_handler(ResponseValidationError, ResponseValidationHandle)
|
|
|
|
|
|
def register_routers(app: FastAPI, prefix: str = "/api"):
|
|
app.include_router(api_router, prefix=prefix)
|
|
|
|
|
|
async def init_superuser():
|
|
user = await user_controller.model.exists()
|
|
if not user:
|
|
await user_controller.create_user(
|
|
UserCreate(
|
|
username="admin",
|
|
email="admin@admin.com",
|
|
password="123456",
|
|
is_active=True,
|
|
is_superuser=True,
|
|
)
|
|
)
|
|
|
|
|
|
async def init_menus():
|
|
menus = await Menu.exists()
|
|
if not menus:
|
|
parent_menu = await Menu.create(
|
|
menu_type=MenuType.CATALOG,
|
|
name="系统管理",
|
|
path="/system",
|
|
order=1,
|
|
parent_id=0,
|
|
icon="carbon:gui-management",
|
|
is_hidden=False,
|
|
component="Layout",
|
|
keepalive=False,
|
|
redirect="/system/user",
|
|
)
|
|
children_menu = [
|
|
Menu(
|
|
menu_type=MenuType.MENU,
|
|
name="用户管理",
|
|
path="user",
|
|
order=1,
|
|
parent_id=parent_menu.id,
|
|
icon="material-symbols:person-outline-rounded",
|
|
is_hidden=False,
|
|
component="/system/user",
|
|
keepalive=False,
|
|
),
|
|
Menu(
|
|
menu_type=MenuType.MENU,
|
|
name="角色管理",
|
|
path="role",
|
|
order=2,
|
|
parent_id=parent_menu.id,
|
|
icon="carbon:user-role",
|
|
is_hidden=False,
|
|
component="/system/role",
|
|
keepalive=False,
|
|
),
|
|
Menu(
|
|
menu_type=MenuType.MENU,
|
|
name="菜单管理",
|
|
path="menu",
|
|
order=3,
|
|
parent_id=parent_menu.id,
|
|
icon="material-symbols:list-alt-outline",
|
|
is_hidden=False,
|
|
component="/system/menu",
|
|
keepalive=False,
|
|
),
|
|
Menu(
|
|
menu_type=MenuType.MENU,
|
|
name="API管理",
|
|
path="api",
|
|
order=4,
|
|
parent_id=parent_menu.id,
|
|
icon="ant-design:api-outlined",
|
|
is_hidden=False,
|
|
component="/system/api",
|
|
keepalive=False,
|
|
),
|
|
Menu(
|
|
menu_type=MenuType.MENU,
|
|
name="部门管理",
|
|
path="dept",
|
|
order=5,
|
|
parent_id=parent_menu.id,
|
|
icon="mingcute:department-line",
|
|
is_hidden=False,
|
|
component="/system/dept",
|
|
keepalive=False,
|
|
),
|
|
Menu(
|
|
menu_type=MenuType.MENU,
|
|
name="审计日志",
|
|
path="auditlog",
|
|
order=6,
|
|
parent_id=parent_menu.id,
|
|
icon="ph:clipboard-text-bold",
|
|
is_hidden=False,
|
|
component="/system/auditlog",
|
|
keepalive=False,
|
|
),
|
|
]
|
|
await Menu.bulk_create(children_menu)
|
|
await Menu.create(
|
|
menu_type=MenuType.MENU,
|
|
name="一级菜单",
|
|
path="/top-menu",
|
|
order=2,
|
|
parent_id=0,
|
|
icon="material-symbols:featured-play-list-outline",
|
|
is_hidden=False,
|
|
component="/top-menu",
|
|
keepalive=False,
|
|
redirect="",
|
|
)
|
|
|
|
|
|
async def init_apis():
|
|
apis = await api_controller.model.exists()
|
|
if not apis:
|
|
await api_controller.refresh_api()
|
|
|
|
|
|
async def init_db():
|
|
command = Command(tortoise_config=settings.TORTOISE_ORM)
|
|
|
|
try:
|
|
await command.init_db(safe=True)
|
|
except FileExistsError:
|
|
pass
|
|
|
|
await command.init()
|
|
|
|
# ✅ 检查是否已经有迁移记录(只迁移一次)
|
|
async with in_transaction() as conn:
|
|
try:
|
|
result = await conn.execute_query("SELECT COUNT(*) FROM aerich")
|
|
count = result[1][0][0] # [ (123,) ] -> count = 123
|
|
except Exception as e:
|
|
count = 0 # 表不存在,视为未迁移
|
|
count = 1
|
|
if count == 0:
|
|
logger.info("📦 First-time migration, running migrate + upgrade...")
|
|
try:
|
|
await command.migrate()
|
|
except AttributeError:
|
|
logger.warning("unable to retrieve model history from database, model history will be created from scratch")
|
|
shutil.rmtree("migrations")
|
|
await command.init_db(safe=True)
|
|
|
|
try:
|
|
# 移除 run_in_transaction=True 参数
|
|
await command.upgrade()
|
|
except Exception as e:
|
|
logger.error(f"Migration upgrade failed: {e}")
|
|
# 如果升级失败,尝试清理并重新初始化
|
|
shutil.rmtree("migrations", ignore_errors=True)
|
|
await command.init_db(safe=True)
|
|
await command.migrate()
|
|
await command.upgrade()
|
|
else:
|
|
logger.info("✅ Migration already applied, skipping migrate + upgrade.")
|
|
|
|
|
|
async def init_roles():
|
|
roles = await Role.exists()
|
|
if not roles:
|
|
admin_role = await Role.create(
|
|
name="管理员",
|
|
desc="管理员角色",
|
|
)
|
|
user_role = await Role.create(
|
|
name="普通用户",
|
|
desc="普通用户角色",
|
|
)
|
|
|
|
# 分配所有API给管理员角色
|
|
all_apis = await Api.all()
|
|
await admin_role.apis.add(*all_apis)
|
|
# 分配所有菜单给管理员和普通用户
|
|
all_menus = await Menu.all()
|
|
await admin_role.menus.add(*all_menus)
|
|
await user_role.menus.add(*all_menus)
|
|
|
|
# 为普通用户分配基本API
|
|
basic_apis = await Api.filter(Q(method__in=["GET"]) | Q(tags="基础模块"))
|
|
await user_role.apis.add(*basic_apis)
|
|
|
|
|
|
async def init_data():
|
|
await init_db()
|
|
await init_superuser()
|
|
await init_menus()
|
|
await init_apis()
|
|
await init_roles()
|
|
|