first commit

This commit is contained in:
2026-03-06 17:57:44 +08:00
commit 6685bdb9a8
41 changed files with 3891 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

+78
View File
@@ -0,0 +1,78 @@
[default]
app.name = "chongming"
app.version = "0.1.0"
app.description = "基于 FastAPI 的 chongming 打包脚手架"
app.debug = false
env = "production"
[database]
type = "sqlite"
url = "sqlite+aiosqlite:///./database.db"
generate_schemas = true # 生产环境使用迁移工具
[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
[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"
max_sessions_per_user = 5
[development]
env = "development"
debug = true
[development.server]
host = "127.0.0.1"
port = 8001
reload = true
[development.logging]
level = "DEBUG"
file = "logs/dev.log"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
[development.cors]
allow_origins = ["*"]
allow_credentials = true
allow_methods = ["*"]
allow_headers = ["*"]
[production]
env = "production"
debug = false
[production.server]
host = "0.0.0.0"
port = 8000
reload = false
workers = 4
[production.logging]
level = "INFO"
file = "logs/prod.log"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
[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"]
[production.module_system]
type = "module-bank"
path = ["resources/app.mbank", "resources/plugins.mbank"]
[production.file_system]
type = "svfs"
path = "resources/application.vfs"
+67
View File
@@ -0,0 +1,67 @@
# main.py - 修复 reload 问题
#!/usr/bin/env python3
"""
FastAPI + Tortoise-ORM + chongming 示例
主入口文件,适配 chongming 打包系统
"""
import sys
from utils.launch import launch
from multiprocessing import freeze_support
import server
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
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)
# 打包环境强制禁用重载
is_frozen = getattr(sys, "frozen", False)
reload = False if is_frozen else server_config.get("reload", False)
workers = server_config.get("workers", 4)
# 显示启动信息
env = app_config.get("env", "development")
logger.info("=" * 50)
logger.info(f"🚀 启动 {default_config.get('app.name', 'FastAPI 应用')}")
logger.info(f"📦 版本: {default_config.get('app.version', '1.0.0')}")
logger.info(f"🌍 环境: {env}")
logger.info(f"📍 地址: http://{host}:{port}")
logger.info(f"📚 文档: http://{host}:{port}/docs")
logger.info(f"🔧 调试: {'启用' if app_config.get('debug', False) else '禁用'}")
logger.info(f"🔄 重载: {'启用' if reload else '禁用'}")
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,
)
def main():
freeze_support()
launch(app_run, "production")
if __name__ == "__main__":
main()
+19
View File
@@ -0,0 +1,19 @@
# server.py
import sys
from utils.launch import init_module_bank
from utils.config import ConfigLoader
config = ConfigLoader("config.toml")
init_module_bank(config)
# 从数据库导入真正的 app 模块
try:
import app # type: ignore # 此时会从 SQLite 数据库加载
app_instance = app.app # 获取 FastAPI 实例
except ImportError as e:
print(f"无法从数据库导入 app 模块: {e}")
sys.exit(1)
# 暴露 ASGI 应用变量,供 uvicorn 使用
app = app_instance
View File
+88
View File
@@ -0,0 +1,88 @@
# config_loader.py - 读取config.toml的示例代码
import os
import sys
from pathlib import Path
from typing import Any, Dict, Optional
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib
class ConfigLoader:
"""TOML配置文件加载器"""
def __init__(self, config_path: Optional[str] = None):
"""
初始化配置加载器
Args:
config_path: 配置文件路径,如果为None则自动查找
"""
if config_path is None:
# 自动查找配置文件
config_path = self._find_config_file()
self.config_path = Path(config_path)
self._config: Dict[str, Any] = {}
self.load()
def _find_config_file(self) -> str:
"""自动查找配置文件"""
possible_paths = [
"config.toml",
"config/config.toml",
"~/.config/myapp/config.toml",
"/etc/myapp/config.toml",
]
for path in possible_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
return expanded_path
raise FileNotFoundError("未找到配置文件")
def load(self) -> None:
"""加载配置文件"""
if not self.config_path.exists():
raise FileNotFoundError(f"配置文件不存在: {self.config_path}")
with open(self.config_path, "r", encoding="utf-8") as f:
self._config = tomllib.loads(f.read())
def get(self, key: str, default: Any = None) -> Any:
"""获取配置值,支持点号分隔的路径"""
keys = key.split(".")
value = self._config
try:
for k in keys:
value = value[k]
return value
except (KeyError, TypeError):
return default
def get_section(self, section: str) -> Dict[str, Any]:
"""获取整个配置节"""
return self._config.get(section, {})
def reload(self) -> None:
"""重新加载配置文件"""
self.load()
def __getitem__(self, key: str) -> Any:
"""支持字典式访问"""
return self.get(key)
def __contains__(self, key: str) -> bool:
"""检查配置是否存在"""
return self.get(key) is not None
@property
def all(self) -> Dict[str, Any]:
"""获取所有配置"""
return self._config
+32
View File
@@ -0,0 +1,32 @@
from .config import ConfigLoader
import os
from module_bank import PythonToSQLite
from typing import Callable
key = None
def init_module_bank(config):
MODULE_BANK_PATH_LIST = config["production"]["module_system"]["path"]
for module_path in MODULE_BANK_PATH_LIST:
if os.path.exists(module_path) == False:
raise ValueError(f"模块路径不存在: {module_path}")
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:
print(f"{module['module_name']} {'[包]' if module['is_package'] else ''}")
packer.install_importer(key=key)
def launch(run_fun: Callable[[dict, dict], None], run_mode: str):
config = ConfigLoader("config.toml")
# 获取对应运行模式的配置
default_config = config.get("default", {})
app_config = config.get(run_mode, {})
run_fun(app_config, default_config)