modify: config sqlite 配置规范

This commit is contained in:
2026-01-31 19:26:28 +08:00
parent ee899b2eb3
commit f7dbe571cc
5 changed files with 2158 additions and 147 deletions
+1953
View File
File diff suppressed because it is too large Load Diff
+19 -23
View File
@@ -1,22 +1,30 @@
[default]
app.name = "{{ cookiecutter.project_name }}"
app.version = "{{ cookiecutter.author_name }}"
app.version = "{{ cookiecutter.project_name }}"
app.description = "基于 FastAPI 和 Tortoise-ORM 的 chongming 打包脚手架"
app.debug = false
# env = "production"
[server]
host = "0.0.0.0"
port = 8000
reload = false
workers = 4
[database]
url = "sqlite://db.sqlite3"
# PostgreSQL 示例: "postgres://user:pass@localhost:5432/dbname"
# MySQL 示例: "mysql://user:pass@localhost:3306/dbname"
type = "sqlite"
generate_schemas = true # 生产环境使用迁移工具
[database.sqlite]
engine = "tortoise.backends.sqlite"
file_path = "db.sqlite3"
journal_mode = "WAL"
busy_timeout = 10000
maxsize = 10
minsize = 1
cache_size = -2000
synchronous = "NORMAL"
temp_store = "MEMORY"
mmap_size = 1073741824
foreign_keys = true
auto_vacuum = "INCREMENTAL"
max_cached_statements = 100
readonly = false
[security]
secret_key = "your-secret-key-change-in-production"
algorithm = "HS256"
@@ -32,10 +40,6 @@ port = 8000
reload = true
# workers = 4
[development.database]
url = "sqlite://dev.db"
generate_schemas = true
[development.logging]
level = "DEBUG"
file = "logs/dev.log"
@@ -56,16 +60,12 @@ port = 8000
reload = false
workers = 1
[production.database]
url = "sqlite://prod.db"
generate_schemas = true
[production.logging]
level = "INFO"
file = "logs/prod.log"
[production.cors]
allow_origins = ["https://yourdomain.com"]
allow_origins = ["https://yourdomain.com", "http://localhos:8000", "http://127.0.0.1:8000"]
allow_credentials = true
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_headers = ["Authorization", "Content-Type"]
@@ -78,9 +78,5 @@ path = ["resources/app.mbank", "resources/plugins.mbank"]
type = "svfs"
path = "resources/application.vfs"
[redis]
enabled = false
url = "redis://localhost:6379"
[cache]
default_ttl = 300
@@ -1,22 +1,30 @@
[default]
app.name = "{{ cookiecutter.project_name }}"
app.version = "{{ cookiecutter.author_name }}"
app.version = "{{ cookiecutter.project_name }}"
app.description = "基于 FastAPI 和 Tortoise-ORM 的 chongming 打包脚手架"
app.debug = false
env = "production"
[server]
host = "0.0.0.0"
port = 8000
reload = false
workers = 4
[database]
url = "sqlite://db.sqlite3"
# PostgreSQL 示例: "postgres://user:pass@localhost:5432/dbname"
# MySQL 示例: "mysql://user:pass@localhost:3306/dbname"
type = "sqlite"
generate_schemas = true # 生产环境使用迁移工具
[database.sqlite]
engine = "tortoise.backends.sqlite"
file_path = "db.sqlite3"
journal_mode = "WAL"
busy_timeout = 10000
maxsize = 10
minsize = 1
cache_size = -2000
synchronous = "NORMAL"
temp_store = "MEMORY"
mmap_size = 1073741824
foreign_keys = true
auto_vacuum = "INCREMENTAL"
max_cached_statements = 100
readonly = false
[security]
secret_key = "your-secret-key-change-in-production"
algorithm = "HS256"
@@ -32,10 +40,6 @@ port = 8000
reload = true
# workers = 4
[development.database]
url = "sqlite://dev.db"
generate_schemas = true
[development.logging]
level = "DEBUG"
file = "logs/dev.log"
@@ -56,16 +60,12 @@ port = 8000
reload = false
workers = 1
[production.database]
url = "sqlite://prod.db"
generate_schemas = true
[production.logging]
level = "INFO"
file = "logs/prod.log"
[production.cors]
allow_origins = ["https://yourdomain.com"]
allow_origins = ["https://yourdomain.com", "http://localhos:8000", "http://127.0.0.1:8000"]
allow_credentials = true
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_headers = ["Authorization", "Content-Type"]
@@ -78,9 +78,5 @@ path = ["resources/app.mbank", "resources/plugins.mbank"]
type = "svfs"
path = "resources/application.vfs"
[redis]
enabled = false
url = "redis://localhost:6379"
[cache]
default_ttl = 300
@@ -1,19 +1,62 @@
"""
配置管理模块
配置管理模块 - 简化版本
基于 Pydantic Settings 和 TOML 文件
"""
from typing import Optional, Dict, Any
from typing import Optional, List, Dict, Any
from pydantic_settings import BaseSettings
from pydantic import Field
from pydantic import model_validator
import toml
from pathlib import Path
import sys
import os
try:
from ...utils.launch import args
except:
from utils.launch import args # type: ignore
class SQLiteConfig(BaseSettings):
"""SQLite 配置"""
engine: str = "tortoise.backends.sqlite"
file_path: str = "db.sqlite3"
journal_mode: str = "WAL"
busy_timeout: int = 10000
maxsize: int = 10
minsize: int = 1
cache_size: int = -2000
synchronous: str = "NORMAL"
temp_store: str = "MEMORY"
mmap_size: int = 1073741824
foreign_keys: bool = True
auto_vacuum: str = "INCREMENTAL"
max_cached_statements: int = 100
readonly: bool = False
@property
def tortoise_config(self) -> Dict[str, Any]:
return {
"default": {
"engine": self.engine,
"credentials": {
"file_path": self.file_path,
"journal_mode": self.journal_mode,
"busy_timeout": self.busy_timeout,
"maxsize": self.maxsize,
"minsize": self.minsize,
"cache_size": self.cache_size,
"synchronous": self.synchronous,
"temp_store": self.temp_store,
"mmap_size": self.mmap_size,
"foreign_keys": self.foreign_keys,
"auto_vacuum": self.auto_vacuum,
"max_cached_statements": self.max_cached_statements,
"readonly": self.readonly,
},
}
}
class Settings(BaseSettings):
"""应用配置"""
@@ -26,25 +69,29 @@ class Settings(BaseSettings):
host: str = "0.0.0.0"
port: int = 8000
reload: bool = False
workers: int = 4
workers: int = 1 # 开发环境默认1,避免与 reload 冲突
# 数据库配置
database_url: str = "sqlite://db.sqlite3"
generate_schemas: bool = False
database_type: str = "sqlite"
# SQLite 配置
sqlite_config: SQLiteConfig = SQLiteConfig()
# 环境配置
env: str = "development"
debug: bool = False
# 数据库配置
generate_schemas: bool = False
# 日志配置
logging_level: str = "INFO"
logging_file: Optional[str] = None
# CORS 配置
cors_allow_origins: list = ["*"]
cors_allow_origins: List[str] = ["*"]
cors_allow_credentials: bool = True
cors_allow_methods: list = ["*"]
cors_allow_headers: list = ["*"]
cors_allow_methods: List[str] = ["*"]
cors_allow_headers: List[str] = ["*"]
# 安全配置
secret_key: str = "your-secret-key-change-in-production"
@@ -53,80 +100,96 @@ class Settings(BaseSettings):
# 模块系统
module_system_type: str = "local"
module_system_path: list = ["plugins", "app"]
module_system_path: List[str] = ["plugins", "app"]
class Config:
env_file = ".env"
extra = "ignore"
@model_validator(mode="after")
def validate_settings(self):
"""验证和调整设置"""
# 如果启用 reload,workers 必须为 1
if self.reload and self.workers != 1:
print(f"⚠️ reload 启用,将 workers 从 {self.workers} 调整为 1")
self.workers = 1
return self
@classmethod
def from_toml(cls, config_path: Optional[str] = None) -> "Settings":
"""从 TOML 文件加载配置"""
"""从 TOML 文件加载配置 - 简化版本"""
# 1. 确定配置文件路径
if config_path is None:
# 尝试多个可能的配置路径
possible_paths = [
"config.toml",
"./config.toml",
"../config.toml",
Path(__file__).parent.parent.parent / "config.toml",
]
config_path = "config.toml"
for path in possible_paths:
if Path(path).exists():
config_path = str(path)
print(f"📄 加载配置文件: {config_path}")
break
if config_path is None:
print("⚠️ 未找到配置文件,使用默认配置")
config_file = Path(config_path)
if not config_file.exists():
print(f"⚠️ 配置文件不存在: {config_path},使用默认配置")
return cls()
try:
config_file = Path(config_path)
if not config_file.exists():
print(f"⚠️ 配置文件不存在: {config_path}")
return cls()
# 2. 加载 TOML 文件
config_data = toml.load(config_file)
print(f"📄 加载配置文件: {config_path}")
# 获取默认配置
# 3. 确定环境
default_config = config_data.get("default", {})
# 确定环境
env = default_config.get("env", "development")
print(f"🌍 环境: {env}")
# 获取环境特定配置
# 4. 获取环境特定配置
env_config = config_data.get(env, {})
# 合并配置
merged_config = {**default_config, **env_config}
# 5. 构建配置字典
config_dict = {}
# 处理嵌套配置
for section in ["server", "database", "security", "logging", "cors", "module_system"]:
if section in config_data:
if isinstance(config_data[section], dict):
merged_config.update(config_data[section])
# 5.1 处理默认配置
for key, value in default_config.items():
if key.startswith("app."):
config_dict[key.replace("app.", "app_")] = value
elif key.startswith("server."):
config_dict[key.replace("server.", "")] = value
elif key == "env":
config_dict["env"] = value
# 从环境特定配置中覆盖
for section in ["server", "database", "security", "logging", "cors", "module_system"]:
env_section_key = f"{env}.{section}"
if env_section_key in config_data:
if isinstance(config_data[env_section_key], dict):
merged_config.update(config_data[env_section_key])
# 5.2 处理环境特定配置
for key, value in env_config.items():
if key.startswith("server."):
config_dict[key.replace("server.", "")] = value
elif key == "debug":
config_dict["debug"] = value
print(f"🌍 环境: {env}")
return cls(**merged_config)
# 5.3 处理 database 配置
database_config = {}
if "database" in env_config:
database_type = env_config["database"].get("type", "sqlite")
database_config = env_config["database"].get(database_type, {})
config_dict["database_type"] = database_type
# 5.4 处理 generate_schemas
if "database" in env_config:
db_config = env_config["database"]
if isinstance(db_config, dict) and "generate_schemas" in db_config:
config_dict["generate_schemas"] = db_config["generate_schemas"]
if database_type == "sqlite":
if database_config:
config_dict["sqlite_config"] = SQLiteConfig(**database_config)
return cls(**config_dict)
except Exception as e:
print(f"❌ 加载配置失败: {e}")
import traceback
traceback.print_exc()
return cls()
# 全局配置实例
_settings = None
def get_settings() -> Settings:
"""获取配置实例(单例模式)"""
global _settings
@@ -1,19 +1,21 @@
"""
Tortoise-ORM 数据库配置
"""
from typing import Optional
from tortoise import Tortoise
from tortoise.contrib.fastapi import register_tortoise
from .config import get_settings
settings = get_settings()
async def init_db(run_mode: str):
"""初始化数据库连接"""
settings = get_settings()
db_config = getattr(settings, f"{settings.database_type}_config").tortoise_config
if run_mode == "production":
db_config = {
"connections": {
"default": settings.database_url
},
"connections": db_config,
"apps": {
"models": {
"models": ["app.models", "aerich.models"],
@@ -23,11 +25,10 @@ async def init_db(run_mode: str):
"use_tz": False,
"timezone": "UTC",
}
elif run_mode == "development":
db_config = {
"connections": {
"default": settings.database_url
},
"connections": db_config,
"apps": {
"models": {
"models": ["{{ cookiecutter.project_name }}.app.models", "aerich.models"],
@@ -50,22 +51,23 @@ async def init_db(run_mode: str):
else:
print("⏭️ 跳过数据库表结构生成(建议使用迁移工具)")
print(f"✅ 数据库连接成功: {settings.database_url}")
print(f"✅ 数据库连接成功: {db_config}")
async def close_db():
"""关闭数据库连接"""
await Tortoise.close_connections()
print("✅ 数据库连接已关闭")
def register_db(app, run_mode: str):
"""将数据库注册到 FastAPI 应用"""
settings = get_settings()
modules_path = "app.models" if run_mode == "production" else "{{ cookiecutter.project_name }}.app.models"
file_path = getattr(settings, f"{settings.database_type}_config").file_path
register_tortoise(
app,
db_url=settings.database_url,
db_url=f"sqlite://{file_path}",
modules={"models": [modules_path]},
generate_schemas=settings.generate_schemas,
add_exception_handlers=True,
@@ -73,17 +75,19 @@ def register_db(app, run_mode: str):
return app
# 在 app/core/database.py 中添加
from aerich import Command
async def run_migrations():
"""运行数据库迁移"""
try:
command = Command(
tortoise_config={
"connections": {
"default": get_settings().database_url
},
"connections": getattr(
settings, f"{settings.database_type}_config"
).tortoise_config,
"apps": {
"models": {
"models": ["app.models", "aerich.models"],
@@ -91,7 +95,7 @@ async def run_migrations():
}
},
},
app="models"
app="models",
)
# 检查并应用迁移
@@ -100,4 +104,3 @@ async def run_migrations():
print("✅ 数据库迁移完成")
except Exception as e:
print(f"⚠️ 数据库迁移失败: {e}")