- 新增 SQLite 数据库层(db.py)持久化命令监听配置,支持热更新无需重启 - 命令过滤从白名单扩展为黑白名单双模式(COMMAND_LIST_MODE: allow/deny) - 新增后台管理页面 /admin/,侧边栏布局,支持在线修改所有命令监听配置 - 新增 REST API:GET/PUT /api/settings、POST /api/settings/reload - 新增 rebuild_pattern() 支持配置变更后正则动态重编译 - 中间件放行 /admin 和 /api 路径免鉴权 - 添加 aiosqlite 依赖
83 lines
2.7 KiB
Python
83 lines
2.7 KiB
Python
"""SQLite 数据库层:存储可动态修改的配置项。"""
|
|
|
|
import aiosqlite
|
|
from pathlib import Path
|
|
|
|
DB_PATH = Path(__file__).parent / "data" / "webhook.db"
|
|
|
|
# 需要持久化的配置项及其默认值(从 .env 加载时的回退)
|
|
SETTINGS_DEFAULTS: dict[str, str] = {
|
|
"command_prefix": "#",
|
|
"command_length_min": "2",
|
|
"command_length_max": "4",
|
|
"command_scope": "all",
|
|
"command_list_mode": "allow",
|
|
"command_allowed_groups": "",
|
|
"command_denied_groups": "",
|
|
"command_allowed_users": "",
|
|
"command_denied_users": "",
|
|
"command_at_sender": "true",
|
|
"command_callback_url": "",
|
|
"command_callback_timeout": "180",
|
|
}
|
|
|
|
|
|
async def init_db() -> None:
|
|
"""建表 + 首次启动时写入默认值。"""
|
|
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
await db.execute(
|
|
"""
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL DEFAULT ''
|
|
)
|
|
"""
|
|
)
|
|
await db.commit()
|
|
|
|
# 首次启动:插入不存在的默认值
|
|
for key, default in SETTINGS_DEFAULTS.items():
|
|
await db.execute(
|
|
"INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)",
|
|
(key, default),
|
|
)
|
|
await db.commit()
|
|
|
|
|
|
async def get_setting(key: str) -> str | None:
|
|
"""读取单个配置值,不存在返回 None。"""
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
cursor = await db.execute("SELECT value FROM settings WHERE key = ?", (key,))
|
|
row = await cursor.fetchone()
|
|
return row[0] if row else None
|
|
|
|
|
|
async def get_settings() -> dict[str, str]:
|
|
"""读取全部配置,返回 {key: value} 字典。"""
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
cursor = await db.execute("SELECT key, value FROM settings")
|
|
rows = await cursor.fetchall()
|
|
return {row[0]: row[1] for row in rows}
|
|
|
|
|
|
async def update_setting(key: str, value: str) -> None:
|
|
"""更新单个配置值。"""
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
await db.execute(
|
|
"INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?",
|
|
(key, value, value),
|
|
)
|
|
await db.commit()
|
|
|
|
|
|
async def update_settings(data: dict[str, str]) -> None:
|
|
"""批量更新配置值。"""
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
for key, value in data.items():
|
|
await db.execute(
|
|
"INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?",
|
|
(key, value, value),
|
|
)
|
|
await db.commit()
|