""" 数据库连接与初始化 """ import re import pymysql try: from config import mysql_host as config_mysql_host from config import mysql_user as config_mysql_user from config import mysql_password as config_mysql_password from config import mysql_database as config_mysql_database from config import mysql_host_source as config_mysql_host_source from config import mysql_user_source as config_mysql_user_source from config import mysql_database_source as config_mysql_database_source except ImportError: config_mysql_host = 'localhost' config_mysql_user = 'root' config_mysql_password = '' config_mysql_database = 'maixiang_ai' config_mysql_host_source = 'fallback.default' config_mysql_user_source = 'fallback.default' config_mysql_database_source = 'fallback.default' mysql_host = config_mysql_host mysql_user = config_mysql_user mysql_password = config_mysql_password mysql_database = config_mysql_database mysql_host_source = config_mysql_host_source mysql_user_source = config_mysql_user_source mysql_database_source = config_mysql_database_source def _safe_identifier(name): """仅允许字母、数字、下划线的数据库/表名片段,防止注入 DDL 片段。""" if not re.fullmatch(r'[A-Za-z0-9_]+', name or ''): raise ValueError(f'非法标识符: {name!r}') return name def describe_db_target(): return ( f"{mysql_user}@{mysql_host}/{mysql_database} " f"(host={mysql_host_source}, user={mysql_user_source}, database={mysql_database_source})" ) def get_db(): return pymysql.connect( host=mysql_host, user=mysql_user, password=mysql_password, database=mysql_database, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor ) def init_db(): """确保版本公开 API 依赖的最小表结构存在。 本进程(15124)只服务 /api/version、/api/version/latest,仅依赖 web_config 表。 历史遗留说明(2026-09 全维度审查后删除):这里原本还会创建 users 表、执行 role 角色 迁移、并在查不到 super_admin 时用 ADMIN_PASSWORD(默认 admin123)插入一个超管。 管理后台早已迁到 Java,users 表结构由 Flyway 迁移管理,本进程再写会与之冲突; 其中「无超管即用默认密码建超管」在生产等同于后门(删除超管或改 role 枚举后本进程 重启就会静默重建一个密码已知的超管),故整段移除。 """ conn = pymysql.connect( host=mysql_host, user=mysql_user, password=mysql_password, charset='utf8mb4' ) try: db_name = _safe_identifier(mysql_database) with conn.cursor() as cur: cur.execute(f"CREATE DATABASE IF NOT EXISTS `{db_name}` DEFAULT CHARSET utf8mb4") cur.execute(f"USE `{db_name}`") cur.execute(""" CREATE TABLE IF NOT EXISTS web_config ( id INT AUTO_INCREMENT PRIMARY KEY, version VARCHAR(64) NOT NULL, file_url VARCHAR(1024) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) """) conn.commit() finally: conn.close()