#!/usr/bin/env python3 """首次部署:自动生成登录会话密钥与初始账号(仅空/占位时写入,不覆盖已有). - FLASK_SECRET_KEY - APP_USERNAME=admin / APP_PASSWORD=admin123(仅空时) - HUB_BRIDGE_TOKEN(仅空时,供后期中控) - 云服务器测试默认:监听/鉴权/模拟盘(仅空或占位时) """ from __future__ import annotations import argparse import os import secrets import sys _REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if _REPO not in sys.path: sys.path.insert(0, _REPO) from lib.env.env_file_lib import apply_env_updates, env_get, read_env_lines FLASK_PLACEHOLDERS = frozenset( { "", "CHANGE_TO_LONG_RANDOM_SECRET", "crypto_monitor_2026_secret_key", "coinox_2026_secret_key", } ) PASSWORD_PLACEHOLDERS = frozenset({"", "CHANGE_ME_STRONG_PASSWORD"}) def main() -> int: parser = argparse.ArgumentParser(description="Bootstrap deploy secrets for CoinOX") parser.add_argument("--dry-run", action="store_true", help="只打印将写入的项,不改文件") args = parser.parse_args() env_path = os.path.join(_REPO, ".env") if not os.path.isfile(env_path): print(f" 跳过: 未找到 {env_path}") return 0 lines = read_env_lines(env_path) updates: dict[str, str] = {} cur_secret = env_get(lines, "FLASK_SECRET_KEY") if (cur_secret or "").strip() in FLASK_PLACEHOLDERS: updates["FLASK_SECRET_KEY"] = secrets.token_urlsafe(48) if not (env_get(lines, "APP_USERNAME") or "").strip(): updates["APP_USERNAME"] = "admin" cur_pass = env_get(lines, "APP_PASSWORD") if (cur_pass or "").strip() in PASSWORD_PLACEHOLDERS: updates["APP_PASSWORD"] = "admin123" if not (env_get(lines, "HUB_BRIDGE_TOKEN") or "").strip(): updates["HUB_BRIDGE_TOKEN"] = secrets.token_urlsafe(32) # 云服务器测试默认(仅空时写入;不覆盖用户已改配置) if not (env_get(lines, "APP_HOST") or "").strip(): updates["APP_HOST"] = "0.0.0.0" if not (env_get(lines, "APP_PORT") or "").strip(): updates["APP_PORT"] = "5010" if not (env_get(lines, "APP_DEBUG") or "").strip(): updates["APP_DEBUG"] = "false" if not (env_get(lines, "APP_AUTH_DISABLED") or "").strip(): updates["APP_AUTH_DISABLED"] = "false" if not (env_get(lines, "SIM_DEFAULT_MODE") or "").strip(): updates["SIM_DEFAULT_MODE"] = "sim" if not (env_get(lines, "OKX_OPTIONS_MARGIN_MODE") or "").strip(): updates["OKX_OPTIONS_MARGIN_MODE"] = "coin" if not (env_get(lines, "EXCHANGE_DISPLAY_NAME") or "").strip(): updates["EXCHANGE_DISPLAY_NAME"] = "CoinOX" if not (env_get(lines, "OKX_OPTIONS_ENABLED") or "").strip(): updates["OKX_OPTIONS_ENABLED"] = "true" if not (env_get(lines, "OKX_TRADE_MODE") or "").strip(): updates["OKX_TRADE_MODE"] = "perp_options" live = (env_get(lines, "LIVE_TRADING_ENABLED") or "").strip().lower() if live in ("",): updates["LIVE_TRADING_ENABLED"] = "false" if not updates: print(" 密钥已存在,无需写入") return 0 print(f" 将更新 {env_path}: {', '.join(updates.keys())}") if args.dry_run: for k, v in updates.items(): shown = ( "***" if k in ("APP_PASSWORD", "HUB_BRIDGE_TOKEN") or "SECRET" in k else v ) print(f" {k}={shown}") return 0 apply_env_updates(env_path, updates) print(" 已写入云服务器测试默认(请尽快修改 APP_PASSWORD;公网保持 APP_AUTH_DISABLED=false)") return 0 if __name__ == "__main__": sys.exit(main())