modify: 功能tongbu

This commit is contained in:
2026-04-29 11:24:19 +08:00
parent b1968f5ddc
commit 2dfa836acb
69 changed files with 4817 additions and 518 deletions
@@ -1,78 +1,91 @@
[default]
app.debug = false
app.description = "基于 FastAPI 的 {{ cookiecutter.project_name }} 打包脚手架"
app.name = "{{ cookiecutter.project_name }}"
app.version = "0.1.0"
app.description = "基于 FastAPI 的 chongming 打包脚手架"
app.debug = false
env = "production"
prefix = "/api/v1"
upload_path = "uploads"
[database]
type = "sqlite"
url = "sqlite+aiosqlite:///./database.db"
generate_schemas = true # 生产环境使用迁移工具
generate_schemas = true # 生产环境使用迁移工具
type = "sqlite"
url = "sqlite+aiosqlite:///./data/database.db"
[database.sqlite]
echo = true
pool_size = 20
max_overflow = 10
pool_timeout = 30
pool_recycle = 300
pool_pre_ping = true
connect_args.timeout = 15
connect_args.check_same_thread = false
connect_args.timeout = 15
echo = false
echo_pool = false
max_overflow = 10
pool_pre_ping = true
pool_recycle = 300
pool_size = 20
pool_timeout = 30
[scheduler]
job_store_path = "scheduler_jobs.db"
[cache]
cache_store_path = "./"
[security]
secret_key = "your-secret-key-change-in-production"
algorithm = "HS256"
access_token_expire_minutes = 30
refresh_token_expire_days = 7
jwt_cache_db_path = "jwt_cache"
algorithm = "HS256"
max_sessions_per_user = 5
refresh_token_expire_days = 7
secret_key = "your-secret-key-change-in-production"
[development]
env = "development"
debug = true
env = "development"
[development.server]
access_log = false
host = "127.0.0.1"
port = 8001
reload = true
[development.logging]
level = "DEBUG"
file = "logs/dev.log"
file = "logs"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
level = "DEBUG"
[development.cors]
allow_origins = ["*"]
allow_credentials = true
allow_methods = ["*"]
allow_headers = ["*"]
allow_methods = ["*"]
allow_origins = ["*"]
[production]
env = "production"
debug = false
env = "production"
[production.server]
access_log = false
host = "0.0.0.0"
port = 8000
reload = false
timeout_keep_alive = 65
workers = 4
[production.logging]
level = "INFO"
file = "logs/prod.log"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
backup_count = 5 # 最多保存 10 个日志文件
file = "logs"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
level = "INFO"
max_size = 10485760 # 10MB
[production.cors]
allow_origins = ["https://yourdomain.com", "http://localhost:8000", "http://127.0.0.1:8000"]
allow_credentials = true
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_headers = ["Authorization", "Content-Type"]
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_origins = ["https://yourdomain.com", "http://localhost:8000", "http://127.0.0.1:8000"]
[production.module_system]
type = "module-bank"
path = ["resources/app.mbank", "resources/plugins.mbank"]
type = "module-bank"
[production.file_system]
path = "static.svfs"
type = "svfs"
path = "resources/application.vfs"
+48 -17
View File
@@ -1,33 +1,39 @@
# main.py - 修复 reload 问题
#!/usr/bin/env python3
"""
FastAPI + Tortoise-ORM + {{ cookiecutter.project_name }} 示例
主入口文件,适配 {{ cookiecutter.project_name }} 打包系统
FastAPI chongming 示例
主入口文件,适配 chongming 打包系统
"""
import sys
import platform
from utils.launch import launch
from multiprocessing import freeze_support
import server
use_shell = platform.system() == "Windows"
def app_run(app_config: dict, default_config: dict):
"""
应用运行函数 - 适配模块银行环境
Args:
app_config: 应用配置
default_config: 默认配置
"""
import uvicorn
from app.core.logger import setup_logging, get_logger # type: ignore
app_config: 应用配置字典,包含服务器配置、环境设置等
default_config: 默认配置字典,包含应用名称、版本等基础信息
Returns:
None: 此函数不返回值,直接启动服务器并阻塞运行
"""
from app.core.logger import get_logger # type: ignore
setup_logging(app_config["logging"])
logger = get_logger("app")
# 获取服务器配置
server_config = app_config.get("server", {})
host = server_config.get("host", "0.0.0.0")
port = server_config.get("port", 8000)
timeout_keep_alive = server_config.get("timeout_keep_alive", 5)
# 打包环境强制禁用重载
is_frozen = getattr(sys, "frozen", False)
@@ -47,18 +53,43 @@ def app_run(app_config: dict, default_config: dict):
logger.info(f"👥 Workers: {workers}")
logger.info("=" * 50)
uvicorn.run(
"server:app",
host=host,
port=port,
reload=reload,
workers=workers,
log_level="debug" if app_config.get("debug", False) else "info",
access_log=True,
)
# 根据操作系统选择不同的服务器启动方式
if use_shell:
import uvicorn
uvicorn.run(
"server:app",
host=host,
port=port,
reload=reload,
workers=workers,
log_level="debug" if app_config.get("debug", False) else "info",
access_log=False,
timeout_keep_alive=timeout_keep_alive,
)
else:
from gunicorn.app.wsgiapp import run
sys.argv = [
"gunicorn",
"server:app",
"-k",
"uvicorn.workers.UvicornWorker",
"-w",
str(workers),
"-b",
f"{host}:{port}",
]
run()
def main():
"""
主函数入口 - 初始化多进程支持并启动应用
Returns:
None: 此函数不返回值,调用launch函数启动应用
"""
freeze_support()
launch(app_run, "production")
@@ -3,17 +3,19 @@ import sys
from utils.launch import init_module_bank
from utils.config import ConfigLoader
# 加载配置文件并初始化模块银行
config = ConfigLoader("config.toml")
init_module_bank(config)
# 从数据库导入真正的 app 模块
# 从数据库动态导入 app 模块
# 该模块存储在 SQLite 数据库中,通过自定义导入机制加载
try:
import app # type: ignore # 此时会从 SQLite 数据库加载
import app # type: ignore
app_instance = app.app # 获取 FastAPI 实例
app_instance = app.app
except ImportError as e:
print(f"无法从数据库导入 app 模块: {e}")
sys.exit(1)
# 暴露 ASGI 应用变量,供 uvicorn 使用
# 暴露 ASGI 应用实例,供 uvicorn 服务器使用
app = app_instance
@@ -6,6 +6,7 @@ import sys
from pathlib import Path
from typing import Any, Dict, Optional
# 根据Python版本选择合适的TOML解析库
if sys.version_info >= (3, 11):
import tomllib
else:
@@ -13,7 +14,15 @@ else:
class ConfigLoader:
"""TOML配置文件加载器"""
"""TOML配置文件加载器
提供配置文件的加载、访问和管理功能。支持自动查找配置文件、
点号分隔的嵌套键访问、配置节获取以及配置重载等功能。
Attributes:
config_path: 配置文件的路径
_config: 存储解析后的配置数据
"""
def __init__(self, config_path: Optional[str] = None):
"""
@@ -21,6 +30,9 @@ class ConfigLoader:
Args:
config_path: 配置文件路径,如果为None则自动查找
Raises:
FileNotFoundError: 当无法找到配置文件时抛出异常
"""
if config_path is None:
# 自动查找配置文件
@@ -31,7 +43,21 @@ class ConfigLoader:
self.load()
def _find_config_file(self) -> str:
"""自动查找配置文件"""
"""
自动查找配置文件
按照预定义的优先级顺序搜索配置文件:
1. 当前目录下的config.toml
2. config子目录下的config.toml
3. 用户配置目录~/.config/myapp/config.toml
4. 系统配置目录/etc/myapp/config.toml
Returns:
str: 找到的第一个配置文件路径
Raises:
FileNotFoundError: 当所有可能的位置都未找到配置文件时抛出异常
"""
possible_paths = [
"config.toml",
"config/config.toml",
@@ -47,7 +73,15 @@ class ConfigLoader:
raise FileNotFoundError("未找到配置文件")
def load(self) -> None:
"""加载配置文件"""
"""
加载配置文件
读取并解析TOML格式的配置文件,将配置数据存储到内部字典中。
Raises:
FileNotFoundError: 当配置文件不存在时抛出异常
tomllib.TOMLDecodeError: 当配置文件格式不正确时抛出异常
"""
if not self.config_path.exists():
raise FileNotFoundError(f"配置文件不存在: {self.config_path}")
@@ -55,7 +89,20 @@ class ConfigLoader:
self._config = tomllib.loads(f.read())
def get(self, key: str, default: Any = None) -> Any:
"""获取配置值,支持点号分隔的路径"""
"""
获取配置值,支持点号分隔的路径
通过点号分隔的键路径访问嵌套的配置值。例如:
- "database.host" 访问 {"database": {"host": "localhost"}}
- "server.port" 访问 {"server": {"port": 8080}}
Args:
key: 配置键,支持点号分隔的嵌套路径
default: 当键不存在时返回的默认值,默认为None
Returns:
Any: 配置值,如果键不存在则返回默认值
"""
keys = key.split(".")
value = self._config
@@ -67,22 +114,71 @@ class ConfigLoader:
return default
def get_section(self, section: str) -> Dict[str, Any]:
"""获取整个配置节"""
"""
获取整个配置节
返回指定配置节的完整字典内容,适用于需要访问某个配置节下
所有配置项的场景。
Args:
section: 配置节名称
Returns:
Dict[str, Any]: 配置节的内容字典,如果节不存在则返回空字典
"""
return self._config.get(section, {})
def reload(self) -> None:
"""重新加载配置文件"""
"""
重新加载配置文件
从磁盘重新读取配置文件并更新内部配置数据。
适用于配置文件在运行时被修改的场景。
Raises:
FileNotFoundError: 当配置文件不存在时抛出异常
"""
self.load()
def __getitem__(self, key: str) -> Any:
"""支持字典式访问"""
"""
支持字典式访问
允许使用方括号语法访问配置值,如:config["database.host"]
Args:
key: 配置键
Returns:
Any: 配置值
Raises:
KeyError: 当键不存在且无默认值时抛出异常(通过get方法处理)
"""
return self.get(key)
def __contains__(self, key: str) -> bool:
"""检查配置是否存在"""
"""
检查配置是否存在
支持使用in操作符检查配置键是否存在,如:"database.host" in config
Args:
key: 要检查的配置键
Returns:
bool: 如果配置键存在且值不为None则返回True,否则返回False
"""
return self.get(key) is not None
@property
def all(self) -> Dict[str, Any]:
"""获取所有配置"""
"""
获取所有配置
返回完整的配置字典,适用于需要遍历或导出所有配置的场景。
Returns:
Dict[str, Any]: 包含所有配置的字典
"""
return self._config
@@ -7,14 +7,29 @@ key = None
def init_module_bank(config):
"""
初始化模块银行系统,将指定路径下的Python模块打包并安装导入器。
该函数会验证所有模块路径的有效性,创建PythonToSQLite打包器实例,
列出所有可用模块,并为每个打包器安装导入器以支持模块的动态加载。
Args:
config (dict): 配置字典,必须包含 'production' -> 'module_system' -> 'path' 键,
其值为模块路径列表
Raises:
ValueError: 当配置的模块路径中有任何一个不存在时抛出异常
"""
MODULE_BANK_PATH_LIST = config["production"]["module_system"]["path"]
for module_path in MODULE_BANK_PATH_LIST:
if os.path.exists(module_path) == False:
if not os.path.exists(module_path):
raise ValueError(f"模块路径不存在: {module_path}")
# 为每个模块路径创建PythonToSQLite打包器实例
packer_list = [
PythonToSQLite(module_path, key=key) for module_path in MODULE_BANK_PATH_LIST
]
# 遍历所有打包器,列出模块信息并安装导入器
for packer in packer_list:
modules = packer.list_modules()
for module in modules:
@@ -23,6 +38,21 @@ def init_module_bank(config):
def launch(run_fun: Callable[[dict, dict], None], run_mode: str):
"""
启动应用程序,加载配置并执行指定的运行函数。
该函数从config.toml文件中加载配置,根据运行模式获取对应的应用配置和默认配置,
然后调用传入的运行函数执行应用程序逻辑。
Args:
run_fun (Callable[[dict, dict], None]): 运行函数,接收两个参数:
- app_config (dict): 当前运行模式的配置
- default_config (dict): 默认配置
run_mode (str): 运行模式,用于从配置文件中获取对应模式的配置项
Returns:
None
"""
config = ConfigLoader("config.toml")
# 获取对应运行模式的配置