"""数据库初始化脚本。 创建所有表结构 → 生成默认管理员账号 → 载入初始策略版本。 部署时 ``docker compose run --rm api python -m scripts.init_db`` 执行一次。 """ from __future__ import annotations import base64 import hashlib import json import logging import sys from datetime import datetime, timezone from pathlib import Path # 确保项目根目录在 sys.path 中 ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) import os os.environ.setdefault("MESHSAFE_MASTER_KEY", os.environ.get("MESHSAFE_MASTER_KEY", "Dt0ktRs8SjEH8VxWZJDX125NV1qiII88HoiXZPhWers=")) os.environ.setdefault("MESHSAFE_ADMIN_PASSWORD", os.environ.get("MESHSAFE_ADMIN_PASSWORD", "admin123")) from sqlalchemy import select from sqlalchemy.orm import Session from app.db import Base, engine from app.models import ConsoleUser, PolicyVersion from app.security.crypto import hash_password from app.services.policy import PolicyStore logger = logging.getLogger("init_db") def main() -> None: logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") logger.info("=== MeshSafe 数据库初始化 ===") # 1) 创建所有表 logger.info("步骤 1: 创建表结构 ...") Base.metadata.create_all(bind=engine) logger.info(" ✓ 所有表已创建 / 校验存在") with Session(engine) as db: # 2) 默认管理员(首次部署创建,已存在则跳过) logger.info("步骤 2: 管理员账号 ...") existing = db.execute(select(ConsoleUser).where(ConsoleUser.username == "admin")).scalar_one_or_none() if existing: logger.info(" ✓ 默认管理员已存在(username=admin),跳过") else: admin_pw = os.environ["MESHSAFE_ADMIN_PASSWORD"] pw_hash = hash_password(admin_pw) user = ConsoleUser( username="admin", password_hash=pw_hash, role="admin", is_active=True, must_change_password=True, ) db.add(user) db.commit() logger.info(" ✓ 默认管理员已创建(username=admin, 密码见环境变量 MESHSAFE_ADMIN_PASSWORD)") # 3) 载入初始策略版本(从 policy.yaml) logger.info("步骤 3: 载入初始策略版本 ...") existing_pv = db.execute( select(PolicyVersion).order_by(PolicyVersion.created_at.desc()) ).scalar_one_or_none() policy_path = os.environ.get("MESHSAFE_POLICY_FILE", "config/policy.yaml") policy_file = ROOT / policy_path if not policy_file.exists(): logger.warning(" 策略文件不存在:%s,跳过", policy_file) return store = PolicyStore(policy_file=str(policy_file)) policy = store.current() # 生成 version 和 sha256 content_json = json.dumps(policy.raw, sort_keys=True, ensure_ascii=False) content_hash = hashlib.sha256(content_json.encode("utf-8")).hexdigest() version_name = f"init-{policy.version}-{content_hash[:8]}" if existing_pv and existing_pv.sha256 == content_hash: logger.info(" ✓ 当前策略已是最新版本,跳过(sha256=%s)", content_hash[:12]) else: pv = PolicyVersion( version=version_name, content=policy.raw, sha256=content_hash, ) db.add(pv) db.commit() logger.info(" ✓ 初始策略版本已载入(version=%s, rules=%d, sha256=%s)", version_name, len(policy.zero_tolerance_rules), content_hash[:12]) logger.info("=== 初始化完成 ===") if __name__ == "__main__": main()