diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 88f9048e..d25b2da3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: - id: check-toml - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.11.2 + rev: v0.11.9 hooks: - id: ruff args: @@ -20,7 +20,7 @@ repos: - id: ruff-format - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.6.10 + rev: 0.7.3 hooks: - id: uv-lock - id: uv-export diff --git a/backend/.env.example b/backend/.env.example index 64aecffc..3c9d73ef 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -15,18 +15,18 @@ REDIS_DATABASE=0 TOKEN_SECRET_KEY='1VkVF75nsNABBjK_7-qz7GtzNy3AMvktc9TCPwKczCk' # Opera Log OPERA_LOG_ENCRYPT_SECRET_KEY='d77b25790a804c2b4a339dd0207941e4cefa5751935a33735bc73bb7071a005b' -# Admin +# App Admin # OAuth2 OAUTH2_GITHUB_CLIENT_ID='test' OAUTH2_GITHUB_CLIENT_SECRET='test' OAUTH2_LINUX_DO_CLIENT_ID='test' OAUTH2_LINUX_DO_CLIENT_SECRET='test' -# Task +# App Task # Celery CELERY_BROKER_REDIS_DATABASE=1 CELERY_BACKEND_REDIS_DATABASE=2 # Rabbitmq -RABBITMQ_HOST='127.0.0.1' -RABBITMQ_PORT=5672 -RABBITMQ_USERNAME='guest' -RABBITMQ_PASSWORD='guest' +CELERY_RABBITMQ_HOST='127.0.0.1' +CELERY_RABBITMQ_PORT=5672 +CELERY_RABBITMQ_USERNAME='guest' +CELERY_RABBITMQ_PASSWORD='guest' diff --git a/backend/app/admin/api/v1/auth/captcha.py b/backend/app/admin/api/v1/auth/captcha.py index 6b5288dc..26abc9a8 100644 --- a/backend/app/admin/api/v1/auth/captcha.py +++ b/backend/app/admin/api/v1/auth/captcha.py @@ -5,9 +5,9 @@ from fastapi_limiter.depends import RateLimiter from starlette.concurrency import run_in_threadpool -from backend.app.admin.conf import admin_settings from backend.app.admin.schema.captcha import GetCaptchaDetail from backend.common.response.response_schema import ResponseSchemaModel, response_base +from backend.core.conf import settings from backend.database.redis import redis_client router = APIRouter() @@ -26,9 +26,9 @@ async def get_captcha(request: Request) -> ResponseSchemaModel[GetCaptchaDetail] img, code = await run_in_threadpool(img_captcha, img_byte=img_type) ip = request.state.ip await redis_client.set( - f'{admin_settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{ip}', + f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{ip}', code, - ex=admin_settings.CAPTCHA_LOGIN_EXPIRE_SECONDS, + ex=settings.CAPTCHA_LOGIN_EXPIRE_SECONDS, ) data = GetCaptchaDetail(image_type=img_type, image=img) return response_base.success(data=data) diff --git a/backend/app/admin/api/v1/oauth2/github.py b/backend/app/admin/api/v1/oauth2/github.py index 28c92037..ba7bf851 100644 --- a/backend/app/admin/api/v1/oauth2/github.py +++ b/backend/app/admin/api/v1/oauth2/github.py @@ -5,14 +5,14 @@ from fastapi_oauth20 import FastAPIOAuth20, GitHubOAuth20 from starlette.responses import RedirectResponse -from backend.app.admin.conf import admin_settings from backend.app.admin.service.oauth2_service import oauth2_service from backend.common.enums import UserSocialType from backend.common.response.response_schema import ResponseSchemaModel, response_base +from backend.core.conf import settings router = APIRouter() -_github_client = GitHubOAuth20(admin_settings.OAUTH2_GITHUB_CLIENT_ID, admin_settings.OAUTH2_GITHUB_CLIENT_SECRET) +_github_client = GitHubOAuth20(settings.OAUTH2_GITHUB_CLIENT_ID, settings.OAUTH2_GITHUB_CLIENT_SECRET) _github_oauth2 = FastAPIOAuth20(_github_client, redirect_route_name='github_login') @@ -44,4 +44,4 @@ async def github_login( user=user, social=UserSocialType.github, ) - return RedirectResponse(url=f'{admin_settings.OAUTH2_FRONTEND_REDIRECT_URI}?access_token={data.access_token}') + return RedirectResponse(url=f'{settings.OAUTH2_FRONTEND_REDIRECT_URI}?access_token={data.access_token}') diff --git a/backend/app/admin/api/v1/oauth2/linux_do.py b/backend/app/admin/api/v1/oauth2/linux_do.py index 6d43fcf4..9a2ae1ad 100644 --- a/backend/app/admin/api/v1/oauth2/linux_do.py +++ b/backend/app/admin/api/v1/oauth2/linux_do.py @@ -5,16 +5,16 @@ from fastapi_oauth20 import FastAPIOAuth20, LinuxDoOAuth20 from starlette.responses import RedirectResponse -from backend.app.admin.conf import admin_settings from backend.app.admin.service.oauth2_service import oauth2_service from backend.common.enums import UserSocialType from backend.common.response.response_schema import ResponseSchemaModel, response_base +from backend.core.conf import settings router = APIRouter() _linux_do_client = LinuxDoOAuth20( - admin_settings.OAUTH2_LINUX_DO_CLIENT_ID, - admin_settings.OAUTH2_LINUX_DO_CLIENT_SECRET, + settings.OAUTH2_LINUX_DO_CLIENT_ID, + settings.OAUTH2_LINUX_DO_CLIENT_SECRET, ) _linux_do_oauth2 = FastAPIOAuth20(_linux_do_client, redirect_route_name='linux_do_login') @@ -47,4 +47,4 @@ async def linux_do_login( user=user, social=UserSocialType.linux_do, ) - return RedirectResponse(url=f'{admin_settings.OAUTH2_FRONTEND_REDIRECT_URI}?access_token={data.access_token}') + return RedirectResponse(url=f'{settings.OAUTH2_FRONTEND_REDIRECT_URI}?access_token={data.access_token}') diff --git a/backend/app/admin/conf.py b/backend/app/admin/conf.py deleted file mode 100644 index cc41bd11..00000000 --- a/backend/app/admin/conf.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from functools import lru_cache - -from pydantic_settings import BaseSettings, SettingsConfigDict - -from backend.core.path_conf import BASE_PATH - - -class AdminSettings(BaseSettings): - """Admin 配置""" - - model_config = SettingsConfigDict(env_file=f'{BASE_PATH}/.env', env_file_encoding='utf-8', extra='ignore') - - # .env OAuth2 - OAUTH2_GITHUB_CLIENT_ID: str - OAUTH2_GITHUB_CLIENT_SECRET: str - OAUTH2_LINUX_DO_CLIENT_ID: str - OAUTH2_LINUX_DO_CLIENT_SECRET: str - - # OAuth2 - OAUTH2_FRONTEND_REDIRECT_URI: str = 'http://localhost:5173/oauth2/callback' - - # 验证码 - CAPTCHA_LOGIN_REDIS_PREFIX: str = 'fba:login:captcha' - CAPTCHA_LOGIN_EXPIRE_SECONDS: int = 60 * 5 # 3 分钟 - - -@lru_cache -def get_admin_settings() -> AdminSettings: - """获取 admin 配置""" - return AdminSettings() - - -admin_settings = get_admin_settings() diff --git a/backend/app/admin/service/auth_service.py b/backend/app/admin/service/auth_service.py index a6b31c58..0e298eff 100644 --- a/backend/app/admin/service/auth_service.py +++ b/backend/app/admin/service/auth_service.py @@ -5,7 +5,6 @@ from sqlalchemy.ext.asyncio import AsyncSession from starlette.background import BackgroundTask, BackgroundTasks -from backend.app.admin.conf import admin_settings from backend.app.admin.crud.crud_user import user_dao from backend.app.admin.model import User from backend.app.admin.schema.token import GetLoginToken, GetNewToken @@ -91,12 +90,12 @@ async def login( user = None try: user = await self.user_verify(db, obj.username, obj.password) - captcha_code = await redis_client.get(f'{admin_settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}') + captcha_code = await redis_client.get(f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}') if not captcha_code: raise errors.AuthorizationError(msg='验证码失效,请重新获取') if captcha_code.lower() != obj.captcha.lower(): raise errors.CustomError(error=CustomErrorCode.CAPTCHA_ERROR) - await redis_client.delete(f'{admin_settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}') + await redis_client.delete(f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}') await user_dao.update_login_time(db, obj.username) await db.refresh(user) a_token = await create_access_token( diff --git a/backend/app/admin/service/oauth2_service.py b/backend/app/admin/service/oauth2_service.py index 5adcb1ae..9ebf057a 100644 --- a/backend/app/admin/service/oauth2_service.py +++ b/backend/app/admin/service/oauth2_service.py @@ -5,7 +5,6 @@ from fast_captcha import text_captcha from fastapi import BackgroundTasks, Request, Response -from backend.app.admin.conf import admin_settings from backend.app.admin.crud.crud_user import user_dao from backend.app.admin.crud.crud_user_social import user_social_dao from backend.app.admin.schema.token import GetLoginToken @@ -102,7 +101,7 @@ async def create_with_login( msg='登录成功(OAuth2)', ) background_tasks.add_task(login_log_service.create, **login_log) - await redis_client.delete(f'{admin_settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}') + await redis_client.delete(f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{request.state.ip}') response.set_cookie( key=settings.COOKIE_REFRESH_TOKEN_KEY, value=refresh_token.refresh_token, diff --git a/backend/app/admin/service/user_service.py b/backend/app/admin/service/user_service.py index b8b6663f..97c8bcbd 100644 --- a/backend/app/admin/service/user_service.py +++ b/backend/app/admin/service/user_service.py @@ -191,7 +191,7 @@ async def update_avatar(*, request: Request, username: str, avatar: AvatarParam) """ async with async_db_session.begin() as db: if request.user.username != username: - raise errors.AuthorizationError + raise errors.AuthorizationError(msg='你只能修改自己的信息') user = await user_dao.get_by_username(db, username) if not user: raise errors.NotFoundError(msg='用户不存在') diff --git a/backend/app/task/celery.py b/backend/app/task/celery.py index 7ddcfd83..64c14de9 100644 --- a/backend/app/task/celery.py +++ b/backend/app/task/celery.py @@ -5,7 +5,6 @@ import celery import celery_aio_pool -from backend.app.task.conf import task_settings from backend.core.conf import settings __all__ = ['celery_app'] @@ -13,14 +12,14 @@ def get_broker_url() -> str: """获取消息代理 URL""" - if task_settings.CELERY_BROKER == 'redis': + if settings.CELERY_BROKER == 'redis': return ( f'redis://:{settings.REDIS_PASSWORD}@{settings.REDIS_HOST}:' - f'{settings.REDIS_PORT}/{task_settings.CELERY_BROKER_REDIS_DATABASE}' + f'{settings.REDIS_PORT}/{settings.CELERY_BROKER_REDIS_DATABASE}' ) return ( - f'amqp://{task_settings.RABBITMQ_USERNAME}:{task_settings.RABBITMQ_PASSWORD}@' - f'{task_settings.RABBITMQ_HOST}:{task_settings.RABBITMQ_PORT}' + f'amqp://{settings.CELERY_RABBITMQ_USERNAME}:{settings.CELERY_RABBITMQ_PASSWORD}@' + f'{settings.CELERY_RABBITMQ_HOST}:{settings.CELERY_RABBITMQ_PORT}' ) @@ -28,16 +27,16 @@ def get_result_backend() -> str: """获取结果后端 URL""" return ( f'redis://:{settings.REDIS_PASSWORD}@{settings.REDIS_HOST}:' - f'{settings.REDIS_PORT}/{task_settings.CELERY_BACKEND_REDIS_DATABASE}' + f'{settings.REDIS_PORT}/{settings.CELERY_BACKEND_REDIS_DATABASE}' ) def get_result_backend_transport_options() -> dict[str, Any]: """获取结果后端传输选项""" return { - 'global_keyprefix': task_settings.CELERY_BACKEND_REDIS_PREFIX, + 'global_keyprefix': settings.CELERY_BACKEND_REDIS_PREFIX, 'retry_policy': { - 'timeout': task_settings.CELERY_BACKEND_REDIS_TIMEOUT, + 'timeout': settings.CELERY_BACKEND_REDIS_TIMEOUT, }, } @@ -55,7 +54,7 @@ def init_celery() -> celery.Celery: 'fba_celery', enable_utc=False, timezone=settings.DATETIME_TIMEZONE, - beat_schedule=task_settings.CELERY_SCHEDULE, + beat_schedule=settings.CELERY_SCHEDULE, broker_url=get_broker_url(), broker_connection_retry_on_startup=True, result_backend=get_result_backend(), @@ -65,7 +64,7 @@ def init_celery() -> celery.Celery: ) # 自动发现任务 - app.autodiscover_tasks(task_settings.CELERY_TASK_PACKAGES) + app.autodiscover_tasks(settings.CELERY_TASK_PACKAGES) return app diff --git a/backend/app/task/celery_task/base.py b/backend/app/task/celery_task/base.py index 312ad16f..445f0185 100644 --- a/backend/app/task/celery_task/base.py +++ b/backend/app/task/celery_task/base.py @@ -7,15 +7,15 @@ from celery import Task from sqlalchemy.exc import SQLAlchemyError -from backend.app.task.conf import task_settings from backend.common.socketio.actions import task_notification +from backend.core.conf import settings class TaskBase(Task): """Celery 任务基类""" autoretry_for = (SQLAlchemyError,) - max_retries = task_settings.CELERY_TASK_MAX_RETRIES + max_retries = settings.CELERY_TASK_MAX_RETRIES async def before_start(self, task_id: str, args, kwargs) -> None: """ diff --git a/backend/app/task/conf.py b/backend/app/task/conf.py deleted file mode 100644 index 38e9e126..00000000 --- a/backend/app/task/conf.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from functools import lru_cache -from typing import Any, Literal - -from celery.schedules import crontab -from pydantic import model_validator -from pydantic_settings import BaseSettings, SettingsConfigDict - -from backend.core.path_conf import BASE_PATH - - -class TaskSettings(BaseSettings): - """Celery 任务配置""" - - model_config = SettingsConfigDict(env_file=f'{BASE_PATH}/.env', env_file_encoding='utf-8', extra='ignore') - - # .env 环境 - ENVIRONMENT: Literal['dev', 'pro'] - - # .env Redis 配置 - CELERY_BROKER_REDIS_DATABASE: int - CELERY_BACKEND_REDIS_DATABASE: int - - # .env RabbitMQ 配置 - # docker run -d --hostname fba-mq --name fba-mq -p 5672:5672 -p 15672:15672 rabbitmq:latest - RABBITMQ_HOST: str - RABBITMQ_PORT: int - RABBITMQ_USERNAME: str - RABBITMQ_PASSWORD: str - - # Celery 基础配置 - CELERY_BROKER: Literal['rabbitmq', 'redis'] = 'redis' - CELERY_BACKEND_REDIS_PREFIX: str = 'fba:celery:' - CELERY_BACKEND_REDIS_TIMEOUT: int = 5 - CELERY_TASK_PACKAGES: list[str] = [ - 'app.task.celery_task', - 'app.task.celery_task.db_log', - ] - CELERY_TASK_MAX_RETRIES: int = 5 - - # Celery 定时任务配置 - CELERY_SCHEDULE: dict[str, dict[str, Any]] = { - 'exec-every-10-seconds': { - 'task': 'task_demo_async', - 'schedule': 10, - }, - 'exec-every-sunday': { - 'task': 'delete_db_opera_log', - 'schedule': crontab('0', '0', day_of_week='6'), - }, - 'exec-every-15-of-month': { - 'task': 'delete_db_login_log', - 'schedule': crontab('0', '0', day_of_month='15'), - }, - } - - @model_validator(mode='before') - @classmethod - def validate_celery_broker(cls, values: Any) -> Any: - """生产环境强制使用 RabbitMQ 作为消息代理""" - if values['ENVIRONMENT'] == 'pro': - values['CELERY_BROKER'] = 'rabbitmq' - return values - - -@lru_cache -def get_task_settings() -> TaskSettings: - """获取 Celery 任务配置""" - return TaskSettings() - - -task_settings = get_task_settings() diff --git a/backend/common/socketio/server.py b/backend/common/socketio/server.py index 6bc91f15..a2d0a331 100644 --- a/backend/common/socketio/server.py +++ b/backend/common/socketio/server.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- import socketio -from backend.app.task.conf import task_settings from backend.common.log import log from backend.common.security.jwt import jwt_authentication from backend.core.conf import settings @@ -13,13 +12,13 @@ # 集成 Celery 实现消息订阅 client_manager=socketio.AsyncRedisManager( f'redis://:{settings.REDIS_PASSWORD}@{settings.REDIS_HOST}:' - f'{settings.REDIS_PORT}/{task_settings.CELERY_BROKER_REDIS_DATABASE}' + f'{settings.REDIS_PORT}/{settings.CELERY_BROKER_REDIS_DATABASE}' ) - if task_settings.CELERY_BROKER == 'redis' + if settings.CELERY_BROKER == 'redis' else socketio.AsyncAioPikaManager( ( - f'amqp://{task_settings.RABBITMQ_USERNAME}:{task_settings.RABBITMQ_PASSWORD}@' - f'{task_settings.RABBITMQ_HOST}:{task_settings.RABBITMQ_PORT}' + f'amqp://{settings.CELERY_RABBITMQ_USERNAME}:{settings.CELERY_RABBITMQ_PASSWORD}@' + f'{settings.CELERY_RABBITMQ_HOST}:{settings.CELERY_RABBITMQ_PORT}' ) ), async_mode='asgi', diff --git a/backend/core/conf.py b/backend/core/conf.py index 596ae6b9..f8ace006 100644 --- a/backend/core/conf.py +++ b/backend/core/conf.py @@ -3,6 +3,7 @@ from functools import lru_cache from typing import Any, Literal +from celery.schedules import crontab from pydantic import model_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -184,13 +185,77 @@ class Settings(BaseSettings): PLUGIN_PIP_CHINA: bool = True PLUGIN_PIP_INDEX_URL: str = 'https://mirrors.aliyun.com/pypi/simple/' + # App Admin + # .env OAuth2 + OAUTH2_GITHUB_CLIENT_ID: str + OAUTH2_GITHUB_CLIENT_SECRET: str + OAUTH2_LINUX_DO_CLIENT_ID: str + OAUTH2_LINUX_DO_CLIENT_SECRET: str + + # OAuth2 + OAUTH2_FRONTEND_REDIRECT_URI: str = 'http://localhost:5173/oauth2/callback' + + # 验证码 + CAPTCHA_LOGIN_REDIS_PREFIX: str = 'fba:login:captcha' + CAPTCHA_LOGIN_EXPIRE_SECONDS: int = 60 * 5 # 3 分钟 + + # App Task + # .env Redis 配置 + CELERY_BROKER_REDIS_DATABASE: int + CELERY_BACKEND_REDIS_DATABASE: int + + # .env RabbitMQ 配置 + # docker run -d --hostname fba-mq --name fba-mq -p 5672:5672 -p 15672:15672 rabbitmq:latest + CELERY_RABBITMQ_HOST: str + CELERY_RABBITMQ_PORT: int + CELERY_RABBITMQ_USERNAME: str + CELERY_RABBITMQ_PASSWORD: str + + # 基础配置 + CELERY_BROKER: Literal['rabbitmq', 'redis'] = 'redis' + CELERY_BACKEND_REDIS_PREFIX: str = 'fba:celery:' + CELERY_BACKEND_REDIS_TIMEOUT: int = 5 + CELERY_TASK_PACKAGES: list[str] = [ + 'app.task.celery_task', + 'app.task.celery_task.db_log', + ] + CELERY_TASK_MAX_RETRIES: int = 5 + + # 定时任务配置 + CELERY_SCHEDULE: dict[str, dict[str, Any]] = { + 'exec-every-10-seconds': { + 'task': 'task_demo_async', + 'schedule': 10, + }, + 'exec-every-sunday': { + 'task': 'delete_db_opera_log', + 'schedule': crontab('0', '0', day_of_week='6'), + }, + 'exec-every-15-of-month': { + 'task': 'delete_db_login_log', + 'schedule': crontab('0', '0', day_of_month='15'), + }, + } + + # Plugin Code Generator + # 代码下载 + CODE_GENERATOR_DOWNLOAD_ZIP_FILENAME: str = 'fba_generator' + + # Plugin Config + # 参数配置 + CONFIG_BUILT_IN_TYPES: list[str] = ['website', 'protocol', 'policy'] + @model_validator(mode='before') @classmethod def check_env(cls, values: Any) -> Any: - """生产环境下禁用 OpenAPI 文档和静态文件服务""" + """检查环境变量""" if values.get('ENVIRONMENT') == 'pro': + # FastAPI values['FASTAPI_OPENAPI_URL'] = None values['FASTAPI_STATIC_FILES'] = False + # Task + values['CELERY_BROKER'] = 'rabbitmq' + return values diff --git a/backend/plugin/code_generator/api/v1/gen.py b/backend/plugin/code_generator/api/v1/gen.py index e41f538a..0d30920d 100644 --- a/backend/plugin/code_generator/api/v1/gen.py +++ b/backend/plugin/code_generator/api/v1/gen.py @@ -9,7 +9,7 @@ from backend.common.security.jwt import DependsJwtAuth from backend.common.security.permission import RequestPermission from backend.common.security.rbac import DependsRBAC -from backend.plugin.code_generator.conf import generator_settings +from backend.core.conf import settings from backend.plugin.code_generator.schema.gen import ImportParam from backend.plugin.code_generator.service.gen_service import gen_service @@ -69,5 +69,5 @@ async def download_code(pk: Annotated[int, Path(description='业务 ID')]): return StreamingResponse( bio, media_type='application/x-zip-compressed', - headers={'Content-Disposition': f'attachment; filename={generator_settings.DOWNLOAD_ZIP_FILENAME}.zip'}, + headers={'Content-Disposition': f'attachment; filename={settings.CODE_GENERATOR_DOWNLOAD_ZIP_FILENAME}.zip'}, ) diff --git a/backend/plugin/code_generator/conf.py b/backend/plugin/code_generator/conf.py deleted file mode 100644 index 61ae8ea0..00000000 --- a/backend/plugin/code_generator/conf.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from functools import lru_cache - -from pydantic_settings import BaseSettings - - -class GeneratorSettings(BaseSettings): - """代码生成配置""" - - # 代码下载 - DOWNLOAD_ZIP_FILENAME: str = 'fba_generator' - - -@lru_cache -def get_generator_settings() -> GeneratorSettings: - """获取代码生成配置""" - return GeneratorSettings() - - -generator_settings = get_generator_settings() diff --git a/backend/plugin/config/conf.py b/backend/plugin/config/conf.py deleted file mode 100644 index eb1eae5d..00000000 --- a/backend/plugin/config/conf.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -from functools import lru_cache - -from pydantic_settings import BaseSettings - - -class ConfigSettings(BaseSettings): - """参数配置""" - - # 参数 - CONFIG_BUILT_IN_TYPES: list[str] = ['website', 'protocol', 'policy'] - - -@lru_cache -def get_config_settings() -> ConfigSettings: - """获取参数配置""" - return ConfigSettings() - - -config_settings = get_config_settings() diff --git a/backend/plugin/config/crud/crud_config.py b/backend/plugin/config/crud/crud_config.py index b0cf011c..65600898 100644 --- a/backend/plugin/config/crud/crud_config.py +++ b/backend/plugin/config/crud/crud_config.py @@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy_crud_plus import CRUDPlus -from backend.plugin.config.conf import config_settings +from backend.core.conf import settings from backend.plugin.config.model import Config from backend.plugin.config.schema.config import CreateConfigParam, UpdateConfigParam @@ -22,7 +22,7 @@ async def get(self, db: AsyncSession, pk: int) -> Config | None: :param pk: 参数配置 ID :return: """ - return await self.select_model_by_column(db, id=pk, type__not_in=config_settings.CONFIG_BUILT_IN_TYPES) + return await self.select_model_by_column(db, id=pk, type__not_in=settings.CONFIG_BUILT_IN_TYPES) async def get_by_type(self, db: AsyncSession, type: str) -> Sequence[Config]: """ @@ -63,7 +63,7 @@ async def get_list(self, name: str | None, type: str | None) -> Select: :param type: 参数配置类型 :return: """ - filters = {'type__not_in': config_settings.CONFIG_BUILT_IN_TYPES} + filters = {'type__not_in': settings.CONFIG_BUILT_IN_TYPES} if name is not None: filters.update(name__like=f'%{name}%') if type is not None: @@ -100,7 +100,7 @@ async def delete(self, db: AsyncSession, pk: list[int]) -> int: :return: """ return await self.delete_model_by_column( - db, allow_multiple=True, id__in=pk, type__not_in=config_settings.CONFIG_BUILT_IN_TYPES + db, allow_multiple=True, id__in=pk, type__not_in=settings.CONFIG_BUILT_IN_TYPES ) diff --git a/backend/plugin/config/service/config_service.py b/backend/plugin/config/service/config_service.py index 74318878..eb54d3d7 100644 --- a/backend/plugin/config/service/config_service.py +++ b/backend/plugin/config/service/config_service.py @@ -5,8 +5,8 @@ from sqlalchemy import Select from backend.common.exception import errors +from backend.core.conf import settings from backend.database.db import async_db_session -from backend.plugin.config.conf import config_settings from backend.plugin.config.crud.crud_config import config_dao from backend.plugin.config.model import Config from backend.plugin.config.schema.config import ( @@ -83,7 +83,7 @@ async def create(*, obj: CreateConfigParam) -> None: :return: """ async with async_db_session.begin() as db: - if obj.type in config_settings.CONFIG_BUILT_IN_TYPES: + if obj.type in settings.CONFIG_BUILT_IN_TYPES: raise errors.ForbiddenError(msg='非法类型参数') config = await config_dao.get_by_key(db, obj.key) if config: diff --git a/backend/scripts/init_data.py b/backend/scripts/init_data.py index 72b11267..ae65dba1 100644 --- a/backend/scripts/init_data.py +++ b/backend/scripts/init_data.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# ruff: noqa: I001 from anyio import run from backend.database.db import create_table diff --git a/backend/scripts/init_plugin.py b/backend/scripts/init_plugin.py index 70cb001a..27136a5b 100644 --- a/backend/scripts/init_plugin.py +++ b/backend/scripts/init_plugin.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# ruff: noqa: I001 from anyio import run from backend.plugin.tools import install_requirements_async diff --git a/deploy/backend/docker-compose/.env.server b/deploy/backend/docker-compose/.env.server index 75238b96..030e3736 100644 --- a/deploy/backend/docker-compose/.env.server +++ b/deploy/backend/docker-compose/.env.server @@ -26,7 +26,7 @@ OAUTH2_LINUX_DO_CLIENT_SECRET='test' CELERY_BROKER_REDIS_DATABASE=1 CELERY_BACKEND_REDIS_DATABASE=2 # Rabbitmq -RABBITMQ_HOST='fba_rabbitmq' -RABBITMQ_PORT=5672 -RABBITMQ_USERNAME='guest' -RABBITMQ_PASSWORD='guest' +CELERY_RABBITMQ_HOST='fba_rabbitmq' +CELERY_RABBITMQ_PORT=5672 +CELERY_RABBITMQ_USERNAME='guest' +CELERY_RABBITMQ_PASSWORD='guest' diff --git a/requirements.txt b/requirements.txt index 721a084d..317568eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,120 +1,323 @@ # This file was autogenerated by uv via the following command: # uv export -o requirements.txt --no-hashes aiofiles==24.1.0 + # via fastapi-best-architecture alembic==1.15.1 + # via fastapi-best-architecture amqp==5.3.1 + # via kombu annotated-types==0.7.0 + # via pydantic anyio==4.9.0 + # via + # httpx + # starlette + # watchfiles asgi-correlation-id==4.3.4 + # via fastapi-best-architecture asgiref==3.8.1 + # via fastapi-best-architecture async-timeout==5.0.1 ; python_full_version < '3.11.3' + # via + # asyncpg + # redis asyncmy==0.2.10 + # via fastapi-best-architecture asyncpg==0.30.0 + # via fastapi-best-architecture bcrypt==4.3.0 + # via fastapi-best-architecture bidict==0.23.1 + # via python-socketio billiard==4.2.1 + # via celery celery==5.3.6 + # via + # celery-aio-pool + # fastapi-best-architecture + # flower celery-aio-pool==0.1.0rc8 + # via fastapi-best-architecture certifi==2025.1.31 + # via + # httpcore + # httpx cffi==1.17.1 ; platform_python_implementation != 'PyPy' + # via + # cryptography + # gevent cfgv==3.4.0 + # via pre-commit click==8.1.8 + # via + # celery + # click-didyoumean + # click-plugins + # click-repl + # typer + # uvicorn click-didyoumean==0.3.1 + # via celery click-plugins==1.1.1 + # via celery click-repl==0.3.0 + # via celery colorama==0.4.6 ; sys_platform == 'win32' + # via + # click + # loguru + # pytest + # uvicorn cryptography==44.0.2 + # via fastapi-best-architecture distlib==0.3.9 + # via virtualenv dnspython==2.7.0 + # via email-validator ecdsa==0.19.1 + # via python-jose email-validator==2.2.0 + # via fastapi exceptiongroup==1.2.2 ; python_full_version < '3.11' + # via + # anyio + # pytest fast-captcha==0.3.2 + # via fastapi-best-architecture fastapi==0.115.11 + # via + # fastapi-best-architecture + # fastapi-limiter + # fastapi-pagination fastapi-cli==0.0.5 + # via + # fastapi + # fastapi-best-architecture fastapi-limiter==0.1.6 + # via fastapi-best-architecture fastapi-oauth20==0.0.1 + # via fastapi-best-architecture fastapi-pagination==0.13.0 + # via fastapi-best-architecture filelock==3.18.0 + # via virtualenv flower==2.0.1 + # via fastapi-best-architecture gevent==24.11.1 + # via fastapi-best-architecture greenlet==3.1.1 + # via + # gevent + # sqlalchemy h11==0.14.0 + # via + # httpcore + # uvicorn + # wsproto hiredis==3.1.0 + # via redis httpcore==1.0.7 + # via httpx httptools==0.6.4 + # via uvicorn httpx==0.28.1 + # via + # fastapi + # fastapi-oauth20 humanize==4.12.2 + # via flower identify==2.6.9 + # via pre-commit idna==3.10 + # via + # anyio + # email-validator + # httpx iniconfig==2.1.0 + # via pytest ip2loc==1.0.0 + # via fastapi-best-architecture itsdangerous==2.2.0 + # via fastapi-best-architecture jinja2==3.1.6 + # via + # fastapi + # fastapi-best-architecture kombu==5.5.1 + # via celery loguru==0.7.3 + # via fastapi-best-architecture mako==1.3.9 + # via alembic markdown-it-py==3.0.0 + # via rich markupsafe==3.0.2 + # via + # jinja2 + # mako mdurl==0.1.2 + # via markdown-it-py msgspec==0.19.0 + # via fastapi-best-architecture nodeenv==1.9.1 + # via pre-commit packaging==24.2 + # via + # asgi-correlation-id + # pytest + # pytest-sugar path==17.0.0 + # via fastapi-best-architecture pillow==11.1.0 + # via fast-captcha platformdirs==4.3.7 + # via virtualenv pluggy==1.5.0 + # via pytest pre-commit==4.2.0 prometheus-client==0.21.1 + # via flower prompt-toolkit==3.0.50 + # via click-repl psutil==7.0.0 + # via fastapi-best-architecture pwdlib==0.2.1 + # via fastapi-best-architecture pyasn1==0.4.8 + # via + # python-jose + # rsa pycparser==2.22 ; platform_python_implementation != 'PyPy' + # via cffi pydantic==2.11.0 + # via + # fastapi + # fastapi-best-architecture + # fastapi-pagination + # pydantic-settings + # sqlalchemy-crud-plus pydantic-core==2.33.0 + # via pydantic pydantic-settings==2.8.1 + # via fastapi-best-architecture pygments==2.19.1 + # via rich pytest==8.3.5 + # via pytest-sugar pytest-sugar==1.0.0 python-dateutil==2.9.0.post0 + # via celery python-dotenv==1.1.0 + # via + # pydantic-settings + # uvicorn python-engineio==4.11.2 + # via python-socketio python-jose==3.4.0 + # via fastapi-best-architecture python-multipart==0.0.20 + # via fastapi python-socketio==5.12.1 + # via fastapi-best-architecture pytz==2025.2 + # via flower pyyaml==6.0.2 + # via + # pre-commit + # uvicorn redis==5.2.1 + # via + # fastapi-best-architecture + # fastapi-limiter rich==13.9.4 + # via typer rsa==4.9 + # via python-jose rtoml==0.12.0 + # via fastapi-best-architecture setuptools==78.1.0 + # via + # zope-event + # zope-interface shellingham==1.5.4 + # via typer simple-websocket==1.1.0 + # via python-engineio six==1.17.0 + # via + # ecdsa + # python-dateutil sniffio==1.3.1 + # via anyio sqlalchemy==2.0.40 + # via + # alembic + # fastapi-best-architecture + # sqlalchemy-crud-plus sqlalchemy-crud-plus==1.8.0 + # via fastapi-best-architecture starlette==0.46.1 + # via + # asgi-correlation-id + # fastapi termcolor==2.5.0 + # via pytest-sugar tomli==2.2.1 ; python_full_version < '3.11' + # via pytest tornado==6.4.2 + # via flower typer==0.15.2 + # via fastapi-cli typing-extensions==4.13.0 + # via + # alembic + # anyio + # asgiref + # fastapi + # fastapi-pagination + # pydantic + # pydantic-core + # rich + # sqlalchemy + # typer + # typing-inspection + # uvicorn typing-inspection==0.4.0 + # via pydantic tzdata==2025.1 + # via + # celery + # kombu ua-parser==1.0.1 + # via user-agents ua-parser-builtins==0.18.0.post1 + # via ua-parser user-agents==2.2.0 + # via fastapi-best-architecture uvicorn==0.34.0 + # via + # fastapi + # fastapi-cli uvloop==0.21.0 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn vine==5.1.0 + # via + # amqp + # celery + # kombu virtualenv==20.29.3 + # via pre-commit watchfiles==1.0.4 + # via uvicorn wcwidth==0.2.13 + # via prompt-toolkit websockets==15.0.1 + # via uvicorn win32-setctime==1.2.0 ; sys_platform == 'win32' + # via loguru wsproto==1.2.0 + # via simple-websocket zope-event==5.0 + # via gevent zope-interface==7.2 + # via gevent