From 2e21a963d557030e27de91fccfb903a68be626a6 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Tue, 6 Feb 2024 18:38:08 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E8=BF=90=E8=A1=8C=E7=8E=AF=E5=A2=83=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E8=BF=90=E8=A1=8C=E5=BA=94?= =?UTF-8?q?=E7=94=A8=20#I91AE9=20#3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dash-fastapi-backend/.env.dev | 50 +++++++ dash-fastapi-backend/.env.prod | 50 +++++++ dash-fastapi-backend/app.py | 19 ++- dash-fastapi-backend/config/database.py | 4 +- dash-fastapi-backend/config/env.py | 131 +++++++++++++++--- dash-fastapi-backend/config/get_redis.py | 10 +- dash-fastapi-backend/config/get_scheduler.py | 10 +- .../controller/login_controller.py | 8 +- .../module_admin/service/login_service.py | 8 +- .../module_admin/service/online_service.py | 2 +- dash-fastapi-frontend/.env.dev | 22 +++ dash-fastapi-frontend/.env.prod | 22 +++ dash-fastapi-frontend/app.py | 3 +- dash-fastapi-frontend/config/env.py | 67 +++++++++ dash-fastapi-frontend/config/global_config.py | 3 +- dash-fastapi-frontend/server.py | 14 +- dash-fastapi-frontend/utils/request.py | 6 +- dash-fastapi-frontend/wsgi.py | 5 +- 18 files changed, 378 insertions(+), 56 deletions(-) create mode 100644 dash-fastapi-backend/.env.dev create mode 100644 dash-fastapi-backend/.env.prod create mode 100644 dash-fastapi-frontend/.env.dev create mode 100644 dash-fastapi-frontend/.env.prod create mode 100644 dash-fastapi-frontend/config/env.py diff --git a/dash-fastapi-backend/.env.dev b/dash-fastapi-backend/.env.dev new file mode 100644 index 0000000..1689376 --- /dev/null +++ b/dash-fastapi-backend/.env.dev @@ -0,0 +1,50 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'dev' +# 应用名称 +APP_NAME = 'Dash-FasAPI-Admin' +# 应用代理路径 +APP_ROOT_PATH = '' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 9099 +# 应用版本 +APP_VERSION= '1.2.0' +# 应用是否开启热重载 +APP_RELOAD = true + +# -------- Jwt配置 -------- +# Jwt秘钥 +JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' +# Jwt算法 +JWT_ALGORITHM = 'HS256' +# 令牌过期时间 +JWT_EXPIRE_MINUTES = 1440 +# redis中令牌过期时间 +JWT_REDIS_EXPIRE_MINUTES = 30 + + +# -------- 数据库配置 -------- +# 数据库主机 +DB_HOST = '127.0.0.1' +# 数据库端口 +DB_PORT = 3306 +# 数据库用户名 +DB_USERNAME = 'root' +# 数据库密码 +DB_PASSWORD = 'mysqlroot' +# 数据库名称 +DB_DATABASE = 'dash-fastapi' + +# -------- Redis配置 -------- +# Redis主机 +REDIS_HOST = '127.0.0.1' +# Redis端口 +REDIS_PORT = 6379 +# Redis用户名 +REDIS_USERNAME = '' +# Redis密码 +REDIS_PASSWORD = '' +# Redis数据库 +REDIS_DATABASE = 2 \ No newline at end of file diff --git a/dash-fastapi-backend/.env.prod b/dash-fastapi-backend/.env.prod new file mode 100644 index 0000000..bc743e9 --- /dev/null +++ b/dash-fastapi-backend/.env.prod @@ -0,0 +1,50 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'prod' +# 应用名称 +APP_NAME = 'Dash-FasAPI' +# 应用代理路径 +APP_ROOT_PATH = '/prod-api' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 9099 +# 应用版本 +APP_VERSION= '1.2.0' +# 应用是否开启热重载 +APP_RELOAD = false + +# -------- Jwt配置 -------- +# Jwt秘钥 +JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' +# Jwt算法 +JWT_ALGORITHM = 'HS256' +# 令牌过期时间 +JWT_EXPIRE_MINUTES = 1440 +# redis中令牌过期时间 +JWT_REDIS_EXPIRE_MINUTES = 30 + + +# -------- 数据库配置 -------- +# 数据库主机 +DB_HOST = '127.0.0.1' +# 数据库端口 +DB_PORT = 3306 +# 数据库用户名 +DB_USERNAME = 'root' +# 数据库密码 +DB_PASSWORD = 'mysqlroot' +# 数据库名称 +DB_DATABASE = 'dash-fastapi' + +# -------- Redis配置 -------- +# Redis主机 +REDIS_HOST = '127.0.0.1' +# Redis端口 +REDIS_PORT = 6379 +# Redis用户名 +REDIS_USERNAME = '' +# Redis密码 +REDIS_PASSWORD = '' +# Redis数据库 +REDIS_DATABASE = 2 \ No newline at end of file diff --git a/dash-fastapi-backend/app.py b/dash-fastapi-backend/app.py index 93a8c2a..81d0bce 100644 --- a/dash-fastapi-backend/app.py +++ b/dash-fastapi-backend/app.py @@ -18,6 +18,7 @@ from module_admin.controller.job_controller import jobController from module_admin.controller.server_controller import serverController from module_admin.controller.cache_controller import cacheController from module_admin.controller.common_controller import commonController +from config.env import AppConfig from config.get_redis import RedisUtil from config.get_db import init_create_table from config.get_scheduler import SchedulerUtil @@ -26,9 +27,10 @@ from utils.log_util import logger from utils.common_util import worship app = FastAPI( - title='Dash-FastAPI', - description='Dash-FastAPI接口文档', - version='1.0.0', + title=AppConfig.app_name, + description=f'{AppConfig.app_name}接口文档', + version=AppConfig.app_version, + root_path=AppConfig.app_root_path, ) # 前端页面url @@ -49,14 +51,14 @@ app.add_middleware( @app.on_event("startup") async def startup_event(): - logger.info("Dash-FastAPI开始启动") + logger.info(f"{AppConfig.app_name}开始启动") worship() await init_create_table() app.state.redis = await RedisUtil.create_redis_pool() await RedisUtil.init_sys_dict(app.state.redis) await RedisUtil.init_sys_config(app.state.redis) await SchedulerUtil.init_system_scheduler() - logger.info("Dash-FastAPI启动成功") + logger.info(f"{AppConfig.app_name}启动成功") @app.on_event("shutdown") @@ -103,4 +105,9 @@ app.include_router(cacheController, prefix="/monitor", tags=['系统监控-缓 app.include_router(commonController, prefix="/common", tags=['通用模块']) if __name__ == '__main__': - uvicorn.run(app='app:app', host="0.0.0.0", port=9099, reload=True) + uvicorn.run( + app='app:app', + host=AppConfig.app_host, + port=AppConfig.app_port, + reload=AppConfig.app_reload + ) diff --git a/dash-fastapi-backend/config/database.py b/dash-fastapi-backend/config/database.py index a9acc4c..8d2012f 100644 --- a/dash-fastapi-backend/config/database.py +++ b/dash-fastapi-backend/config/database.py @@ -4,8 +4,8 @@ from sqlalchemy.orm import sessionmaker from urllib.parse import quote_plus from config.env import DataBaseConfig -SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.USERNAME}:{quote_plus(DataBaseConfig.PASSWORD)}@" \ - f"{DataBaseConfig.HOST}:{DataBaseConfig.PORT}/{DataBaseConfig.DB}" +SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@" \ + f"{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}" engine = create_engine( SQLALCHEMY_DATABASE_URL, echo=True diff --git a/dash-fastapi-backend/config/env.py b/dash-fastapi-backend/config/env.py index be50c11..ce63d17 100644 --- a/dash-fastapi-backend/config/env.py +++ b/dash-fastapi-backend/config/env.py @@ -1,36 +1,54 @@ import os +import sys +import argparse +from pydantic import BaseSettings +from functools import lru_cache +from dotenv import load_dotenv -class JwtConfig: +class AppSettings(BaseSettings): + """ + 应用配置 + """ + app_env: str = 'dev' + app_name: str = 'RuoYi-FasAPI' + app_root_path: str = '/dev-api' + app_host: str = '0.0.0.0' + app_port: int = 9099 + app_version: str = '1.0.0' + app_reload: bool = True + + +class JwtSettings(BaseSettings): """ Jwt配置 """ - SECRET_KEY = "b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55" - ALGORITHM = "HS256" - ACCESS_TOKEN_EXPIRE_MINUTES = 1440 - REDIS_TOKEN_EXPIRE_MINUTES = 30 + jwt_secret_key: str = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55' + jwt_algorithm: str = 'HS256' + jwt_expire_minutes: int = 1440 + jwt_redis_expire_minutes: int = 30 -class DataBaseConfig: +class DataBaseSettings(BaseSettings): """ 数据库配置 """ - HOST = "127.0.0.1" - PORT = 3306 - USERNAME = 'root' - PASSWORD = 'mysqlroot' - DB = 'dash-fastapi' + db_host: str = '127.0.0.1' + db_port: int = 3306 + db_username: str = 'root' + db_password: str = 'mysqlroot' + db_database: str = 'ruoyi-fastapi' -class RedisConfig: +class RedisSettings(BaseSettings): """ Redis配置 """ - HOST = "127.0.0.1" - PORT = 6379 - USERNAME = '' - PASSWORD = '' - DB = 2 + redis_host: str = '127.0.0.1' + redis_port: int = 6379 + redis_username: str = '' + redis_password: str = '' + redis_database: int = 2 class CachePathConfig: @@ -52,3 +70,82 @@ class RedisInitKeyConfig: ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'} PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'} SMS_CODE = {'key': 'sms_code', 'remark': '短信验证码'} + + +class GetConfig: + """ + 获取配置 + """ + + def __init__(self): + self.parse_cli_args() + + @lru_cache() + def get_app_config(self): + """ + 获取应用配置 + """ + # 实例化应用配置模型 + return AppSettings() + + @lru_cache() + def get_jwt_config(self): + """ + 获取Jwt配置 + """ + # 实例化Jwt配置模型 + return JwtSettings() + + @lru_cache() + def get_database_config(self): + """ + 获取数据库配置 + """ + # 实例化数据库配置模型 + return DataBaseSettings() + + @lru_cache() + def get_redis_config(self): + """ + 获取Redis配置 + """ + # 实例化Redis配置模型 + return RedisSettings() + + @staticmethod + def parse_cli_args(): + """ + 解析命令行参数 + """ + if 'uvicorn' in sys.argv[0]: + # 使用uvicorn启动时,命令行参数需要按照uvicorn的文档进行配置,无法自定义参数 + pass + else: + # 使用argparse定义命令行参数 + parser = argparse.ArgumentParser(description='命令行参数') + parser.add_argument('--env', type=str, default='', help='运行环境') + # 解析命令行参数 + args = parser.parse_args() + # 设置环境变量,如果未设置命令行参数,默认APP_ENV为dev + os.environ['APP_ENV'] = args.env if args.env else 'dev' + # 读取运行环境 + run_env = os.environ.get('APP_ENV', '') + # 运行环境未指定时默认加载.env.dev + env_file = '.env.dev' + # 运行环境不为空时按命令行参数加载对应.env文件 + if run_env != '': + env_file = f'.env.{run_env}' + # 加载配置 + load_dotenv(env_file) + + +# 实例化获取配置类 +get_config = GetConfig() +# 应用配置 +AppConfig = get_config.get_app_config() +# Jwt配置 +JwtConfig = get_config.get_jwt_config() +# 数据库配置 +DataBaseConfig = get_config.get_database_config() +# Redis配置 +RedisConfig = get_config.get_redis_config() diff --git a/dash-fastapi-backend/config/get_redis.py b/dash-fastapi-backend/config/get_redis.py index 2890d63..4c3ef80 100644 --- a/dash-fastapi-backend/config/get_redis.py +++ b/dash-fastapi-backend/config/get_redis.py @@ -20,11 +20,11 @@ class RedisUtil: """ logger.info("开始连接redis...") redis = await aioredis.from_url( - url=f"redis://{RedisConfig.HOST}", - port=RedisConfig.PORT, - username=RedisConfig.USERNAME, - password=RedisConfig.PASSWORD, - db=RedisConfig.DB, + url=f"redis://{RedisConfig.redis_host}", + port=RedisConfig.redis_port, + username=RedisConfig.redis_username, + password=RedisConfig.redis_password, + db=RedisConfig.redis_database, encoding="utf-8", decode_responses=True ) diff --git a/dash-fastapi-backend/config/get_scheduler.py b/dash-fastapi-backend/config/get_scheduler.py index af1c633..2e51af4 100644 --- a/dash-fastapi-backend/config/get_scheduler.py +++ b/dash-fastapi-backend/config/get_scheduler.py @@ -70,11 +70,11 @@ job_stores = { 'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine), 'redis': RedisJobStore( **dict( - host=RedisConfig.HOST, - port=RedisConfig.PORT, - username=RedisConfig.USERNAME, - password=RedisConfig.PASSWORD, - db=RedisConfig.DB + host=RedisConfig.redis_host, + port=RedisConfig.redis_port, + username=RedisConfig.redis_username, + password=RedisConfig.redis_password, + db=RedisConfig.redis_database ) ) } diff --git a/dash-fastapi-backend/module_admin/controller/login_controller.py b/dash-fastapi-backend/module_admin/controller/login_controller.py index 19ff188..191d871 100644 --- a/dash-fastapi-backend/module_admin/controller/login_controller.py +++ b/dash-fastapi-backend/module_admin/controller/login_controller.py @@ -32,7 +32,7 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D except LoginException as e: return response_400(data="", message=e.message) try: - access_token_expires = timedelta(minutes=JwtConfig.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token_expires = timedelta(minutes=JwtConfig.jwt_expire_minutes) session_id = str(uuid.uuid4()) access_token = create_access_token( data={ @@ -45,10 +45,10 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D expires_delta=access_token_expires ) await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", access_token, - ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes)) # 此方法可实现同一账号同一时间只能登录一次 # await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{result[0].user_id}", access_token, - # ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + # ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes)) logger.info('登录成功') # 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False @@ -107,7 +107,7 @@ async def get_login_user_info(request: Request, current_user: CurrentUserInfoSer @loginController.post("/logout", dependencies=[Depends(get_current_user), Depends(CheckUserInterfaceAuth('common'))]) async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme), query_db: Session = Depends(get_db)): try: - payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM]) + payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm]) session_id: str = payload.get("session_id") await logout_services(request, session_id) logger.info('退出成功') diff --git a/dash-fastapi-backend/module_admin/service/login_service.py b/dash-fastapi-backend/module_admin/service/login_service.py index ca6f8db..53b133a 100644 --- a/dash-fastapi-backend/module_admin/service/login_service.py +++ b/dash-fastapi-backend/module_admin/service/login_service.py @@ -59,7 +59,7 @@ async def get_current_user(request: Request = Request, token: str = Depends(oaut try: if token.startswith('Bearer'): token = token.split(' ')[1] - payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM]) + payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm]) user_id: str = payload.get("user_id") session_id: str = payload.get("session_id") if user_id is None: @@ -78,9 +78,9 @@ async def get_current_user(request: Request = Request, token: str = Depends(oaut # redis_token = await request.app.state.redis.get(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}") if token == redis_token: await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", redis_token, - ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes)) # await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}", redis_token, - # ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES)) + # ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes)) return CurrentUserInfoServiceResponse( user=user.user_basic_info, @@ -227,7 +227,7 @@ def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None else: expire = datetime.utcnow() + timedelta(minutes=15) to_encode.update({"exp": expire}) - encoded_jwt = jwt.encode(to_encode, JwtConfig.SECRET_KEY, algorithm=JwtConfig.ALGORITHM) + encoded_jwt = jwt.encode(to_encode, JwtConfig.jwt_secret_key, algorithm=JwtConfig.jwt_algorithm) return encoded_jwt diff --git a/dash-fastapi-backend/module_admin/service/online_service.py b/dash-fastapi-backend/module_admin/service/online_service.py index 47277ea..6ef9080 100644 --- a/dash-fastapi-backend/module_admin/service/online_service.py +++ b/dash-fastapi-backend/module_admin/service/online_service.py @@ -23,7 +23,7 @@ class OnlineService: access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys] online_info_list = [] for item in access_token_values_list: - payload = jwt.decode(item, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM]) + payload = jwt.decode(item, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm]) online_dict = dict( session_id=payload.get('session_id'), user_name=payload.get('user_name'), diff --git a/dash-fastapi-frontend/.env.dev b/dash-fastapi-frontend/.env.dev new file mode 100644 index 0000000..6f36848 --- /dev/null +++ b/dash-fastapi-frontend/.env.dev @@ -0,0 +1,22 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'dev' +# 应用名称 +APP_NAME = '通用后台管理系统' +# 应用请求后端url +APP_BASE_URL = 'http://127.0.0.1:9099' +# 后端是否使用代理模式 +APP_IS_PROXY = false +# 应用代理路径 +APP_PROXY_PATH = '' +# 应用秘钥 +APP_SECRET_KEY = 'Dash-FastAPI-Admin' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 8088 +# 应用是否开启debug模式 +APP_DEBUG = true +# flask-compress压缩配置 +APP_COMPRESS_ALGORITHM = 'br' +APP_COMPRESS_BR_LEVEL = 11 \ No newline at end of file diff --git a/dash-fastapi-frontend/.env.prod b/dash-fastapi-frontend/.env.prod new file mode 100644 index 0000000..72685a3 --- /dev/null +++ b/dash-fastapi-frontend/.env.prod @@ -0,0 +1,22 @@ +# -------- 应用配置 -------- +# 应用运行环境 +APP_ENV = 'prod' +# 应用名称 +APP_NAME = '通用后台管理系统' +# 应用请求后端url +APP_BASE_URL = 'http://127.0.0.1:9099' +# 后端是否使用代理模式 +APP_IS_PROXY = true +# 应用代理路径 +APP_PROXY_PATH = '/prod-api' +# 应用秘钥 +APP_SECRET_KEY = 'Dash-FastAPI-Admin' +# 应用主机 +APP_HOST = '0.0.0.0' +# 应用端口 +APP_PORT = 8088 +# 应用是否开启debug模式 +APP_DEBUG = false +# flask-compress压缩配置 +APP_COMPRESS_ALGORITHM = 'br' +APP_COMPRESS_BR_LEVEL = 11 \ No newline at end of file diff --git a/dash-fastapi-frontend/app.py b/dash-fastapi-frontend/app.py index 029167c..e1d840d 100644 --- a/dash-fastapi-frontend/app.py +++ b/dash-fastapi-frontend/app.py @@ -8,6 +8,7 @@ from flask import session from operator import itemgetter from server import app +from config.env import AppConfig from config.global_config import RouterConfig from store.store import render_store_container @@ -256,4 +257,4 @@ def router(pathname, url_trigger, session_token): if __name__ == '__main__': - app.run(host='0.0.0.0', port=8088, debug=True) + app.run(host=AppConfig.app_host, port=AppConfig.app_port, debug=AppConfig.app_debug) diff --git a/dash-fastapi-frontend/config/env.py b/dash-fastapi-frontend/config/env.py new file mode 100644 index 0000000..afa30bb --- /dev/null +++ b/dash-fastapi-frontend/config/env.py @@ -0,0 +1,67 @@ +import os +import argparse +from pydantic import BaseSettings +from functools import lru_cache +from dotenv import load_dotenv + + +class AppSettings(BaseSettings): + """ + 应用配置 + """ + app_env: str = 'dev' + app_name: str = '通用后台管理系统' + app_base_url: str = 'http://127.0.0.1:9099' + app_proxy_path: str = '/dev-api' + app_is_proxy: bool = False + app_secret_key: str = 'Dash-FastAPI-Admin' + app_host: str = '0.0.0.0' + app_port: int = 8088 + app_debug: bool = True + app_compress_algorithm = 'br' + app_compress_br_level = 11 + + +class GetConfig: + """ + 获取配置 + """ + + def __init__(self): + self.parse_cli_args() + + @lru_cache() + def get_app_config(self): + """ + 获取应用配置 + """ + # 实例化应用配置模型 + return AppSettings() + + @staticmethod + def parse_cli_args(): + """ + 解析命令行参数 + """ + # 使用argparse定义命令行参数 + parser = argparse.ArgumentParser(description='命令行参数') + parser.add_argument('--env', type=str, default='', help='运行环境') + # 解析命令行参数 + args = parser.parse_args() + # 设置环境变量,如果未设置命令行参数,默认APP_ENV为dev + os.environ['APP_ENV'] = args.env if args.env else 'dev' + # 读取运行环境 + run_env = os.environ.get('APP_ENV', '') + # 运行环境未指定时默认加载.env.dev + env_file = '.env.dev' + # 运行环境不为空时按命令行参数加载对应.env文件 + if run_env != '': + env_file = f'.env.{run_env}' + # 加载配置 + load_dotenv(env_file) + + +# 实例化获取配置类 +get_config = GetConfig() +# 应用配置 +AppConfig = get_config.get_app_config() diff --git a/dash-fastapi-frontend/config/global_config.py b/dash-fastapi-frontend/config/global_config.py index 3103078..2485a4f 100644 --- a/dash-fastapi-frontend/config/global_config.py +++ b/dash-fastapi-frontend/config/global_config.py @@ -1,4 +1,5 @@ import os +from config.env import AppConfig class PathConfig: @@ -21,7 +22,7 @@ class RouterConfig: class ApiBaseUrlConfig: # api基本url - BaseUrl = 'http://127.0.0.1:9099' + BaseUrl = AppConfig.app_base_url + AppConfig.app_proxy_path if AppConfig.app_is_proxy else AppConfig.app_base_url class IconConfig: diff --git a/dash-fastapi-frontend/server.py b/dash-fastapi-frontend/server.py index d1fc8ea..ada6f7b 100644 --- a/dash-fastapi-frontend/server.py +++ b/dash-fastapi-frontend/server.py @@ -4,6 +4,7 @@ import time from loguru import logger from flask import request, session from user_agents import parse +from config.env import AppConfig from config.global_config import PathConfig app = dash.Dash( @@ -15,12 +16,12 @@ app = dash.Dash( server = app.server -app.title = '通用后台管理系统' +app.title = AppConfig.app_name # 配置密钥 -app.server.secret_key = 'Dash-FastAPI' -app.server.config['COMPRESS_ALGORITHM'] = 'br' -app.server.config['COMPRESS_BR_LEVEL'] = 8 +app.server.secret_key = AppConfig.app_secret_key +app.server.config['COMPRESS_ALGORITHM'] = AppConfig.app_compress_algorithm +app.server.config['COMPRESS_BR_LEVEL'] = AppConfig.app_compress_br_level log_time = time.strftime("%Y%m%d", time.localtime()) # sys_log_file_path = os.path.join(PathConfig.ABS_ROOT_PATH, 'log', 'sys_log', f'sys_request_log_{log_time}.log') @@ -34,17 +35,18 @@ logger.add(api_log_file_path, filter=lambda x: '[api]' in x['message'], # 获取用户浏览器信息 @server.before_request def get_user_agent_info(): + request_addr = request.headers.get("X-Forwarded-For") if AppConfig.app_env == 'prod' else request.remote_addr user_string = str(request.user_agent) user_agent = parse(user_string) bw = user_agent.browser.family bw_version = user_agent.browser.version[0] if bw == 'IE': logger.warning("[sys]请求人:{}||请求IP:{}||请求方法:{}||请求Data:{}", - session.get('name'), request.remote_addr, request.method, '用户使用IE内核') + session.get('name'), request_addr, request.method, '用户使用IE内核') return "

请不要使用IE浏览器或360浏览器兼容模式

" if bw_version < 71: logger.warning("[sys]请求人:{}||请求IP:{}||请求方法:{}||请求Data:{}", - session.get('name'), request.remote_addr, request.method, '用户Chrome内核版本太低') + session.get('name'), request_addr, request.method, '用户Chrome内核版本太低') return "

Chrome内核版本号太低,请升级浏览器

" \ "

点击此处可下载最新版Chrome浏览器

" diff --git a/dash-fastapi-frontend/utils/request.py b/dash-fastapi-frontend/utils/request.py index 08e31fc..a605dad 100644 --- a/dash-fastapi-frontend/utils/request.py +++ b/dash-fastapi-frontend/utils/request.py @@ -1,6 +1,7 @@ import requests from typing import Optional from flask import session, request +from config.env import AppConfig from config.global_config import ApiBaseUrlConfig from server import logger @@ -11,11 +12,12 @@ def api_request(method: str, url: str, is_headers: bool, params: Optional[dict] method = method.lower().strip() user_agent = request.headers.get('User-Agent') authorization = session.get('Authorization') if session.get('Authorization') else '' + remote_addr = request.headers.get("X-Forwarded-For") if AppConfig.app_env == 'prod' else request.remote_addr if is_headers: - api_headers = {'Authorization': 'Bearer ' + authorization, 'remote_addr': request.remote_addr, + api_headers = {'Authorization': 'Bearer ' + authorization, 'remote_addr': remote_addr, 'User-Agent': user_agent} else: - api_headers = {'remote_addr': request.remote_addr, 'User-Agent': user_agent} + api_headers = {'remote_addr': remote_addr, 'User-Agent': user_agent} try: if method == 'get': response = requests.get(url=api_url, params=params, data=data, json=json, headers=api_headers, diff --git a/dash-fastapi-frontend/wsgi.py b/dash-fastapi-frontend/wsgi.py index 3b653a0..4e50b77 100644 --- a/dash-fastapi-frontend/wsgi.py +++ b/dash-fastapi-frontend/wsgi.py @@ -1,8 +1,9 @@ from waitress import serve from app import app +from config.env import AppConfig serve( app.server, - host='0.0.0.0', - port=8088 + host=AppConfig.app_host, + port=AppConfig.app_port ) -- Gitee From 073ecee70c24aed5595bbe88022a250dcb545611 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Tue, 6 Feb 2024 23:06:36 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dprod=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95IP?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dash-fastapi-backend/module_admin/annotation/log_annotation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dash-fastapi-backend/module_admin/annotation/log_annotation.py b/dash-fastapi-backend/module_admin/annotation/log_annotation.py index c2d88a6..f1e0f70 100644 --- a/dash-fastapi-backend/module_admin/annotation/log_annotation.py +++ b/dash-fastapi-backend/module_admin/annotation/log_annotation.py @@ -12,6 +12,7 @@ from typing import Optional from module_admin.service.login_service import get_current_user from module_admin.service.log_service import OperationLogService, LoginLogService from module_admin.entity.vo.log_vo import OperLogModel, LogininforModel +from config.env import AppConfig def log_decorator(title: str, business_type: int, log_type: Optional[str] = 'operation'): @@ -48,7 +49,7 @@ def log_decorator(title: str, business_type: int, log_type: Optional[str] = 'ope # 获取请求的url oper_url = request.url.path # 获取请求的ip及ip归属区域 - oper_ip = request.headers.get('remote_addr') + oper_ip = request.headers.get('X-Forwarded-For') if AppConfig.app_env == 'prod' else request.headers.get('remote_addr') oper_location = '内网IP' try: if oper_ip != '127.0.0.1' and oper_ip != 'localhost': -- Gitee From 0c327a44774e5cd4852a12ebdc344a3d55c42e88 Mon Sep 17 00:00:00 2001 From: insistence <3055204202@qq.com> Date: Tue, 6 Feb 2024 23:30:44 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0README=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5aa73da..4542378 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@

logo

-

Dash-FastAPI-Admin v1.1.0

+

Dash-FastAPI-Admin v1.2.0

基于Dash+FastAPI前后端分离的纯Python快速开发框架

- + @@ -97,7 +97,7 @@ Dash-FastAPI-Admin是一套全部开源的快速开发平台,毫无保留给 - *密码:admin123* - 演示地址:dfadmin管理系统 -## 项目运行相关 +## 项目开发及发布 ```bash # 克隆项目 @@ -110,30 +110,94 @@ cd dash-fastapi-admin pip3 install -r requirements.txt ``` -### 前端 +### 开发 + +#### 前端 +```bash +# 进入前端目录 +cd dash-fastapi-frontend + +# 配置应用信息 +在.env.dev文件中配置应用开发模式的相关信息 + +# 运行前端 +python3 app.py --env=dev +``` + +#### 后端 +```bash +# 进入后端目录 +cd dash-fastapi-backend + +# 配置环境 +1.在.env.dev文件中配置开发模式的数据库环境 +2.在.env.dev文件中配置开发模式的redis环境 + +# 运行sql文件 +1.新建数据库dash-fastapi(默认,可修改) +2.使用命令或数据库连接工具运行sql文件夹下的dash-fastapi.sql + +# 运行后端 +python3 app.py --env=dev +``` + +### 发布 + +本应用发布建议使用nginx部署,nginx代理配置参考如下: + +```bash +server { + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://127.0.0.1:8088/; + } + + location /prod-api { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://127.0.0.1:9099/; + rewrite ^/prod-api/(.*)$ /$1 break; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } +} +``` + +#### 前端 ```bash # 进入前端目录 cd dash-fastapi-frontend +# 配置应用信息 +在.env.prod文件中配置应用发布的相关信息,注意:APP_BASE_URL需要配置为nginx代理的地址,例如上面的nginx代理监听的是8000端口,则APP_BASE_URL需要配置为http://127.0.0.1:8000 + # 运行前端 -python3 wsgi.py +python3 wsgi.py --env=prod ``` -### 后端 +#### 后端 ```bash # 进入后端目录 cd dash-fastapi-backend # 配置环境 -1.在config/env.py的DataBaseConfig类中配置数据库环境 -2.在config/env.py的RedisConfig类中配置redis环境 +1.在.env.prod文件中配置生产模式的数据库环境 +2.在.env.prod文件中配置生产模式的redis环境 # 运行sql文件 1.新建数据库dash-fastapi(默认,可修改) 2.使用命令或数据库连接工具运行sql文件夹下的dash-fastapi.sql # 运行后端 -python3 app.py +python3 app.py --env=prod ``` ### 访问 -- Gitee