Files
crypto_monitor/scripts/bootstrap_deploy_secrets.py
T
dekun b733e551a0 Normalize fullwidth punctuation to ASCII across codebase.
Add scripts/normalize_ambiguous_unicode.py; fix corrupted patch_instance_theme_templates.py. Preserves curly quotes in string literals; removes Git homoglyph warnings on .env.example.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 23:42:26 +08:00

111 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""首次部署:自动生成中控通信密钥,登录会话密钥,并写入初始登录账号.
- HUB_BRIDGE_TOKEN:中控 + 三实例(相同,仅空/占位时写入,不覆盖已有)
- FLASK_SECRET_KEY:三实例(相同)
- HUB_SESSION_SECRET:仅中控
- APP_USERNAME=admin,APP_PASSWORD=admin123:实例(仅空时)
- HUB_USERNAME=admin,HUB_PASSWORD=admin123:中控(仅空时)
已有非空且非占位符的值不会被覆盖(长期密钥一次生成,不轮换).
"""
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
INSTANCE_DIRS = (
("okx", os.path.join(_REPO, "crypto_monitor_okx")),
("binance", os.path.join(_REPO, "crypto_monitor_binance")),
("gate", os.path.join(_REPO, "crypto_monitor_gate")),
)
HUB_DIR = os.path.join(_REPO, "manual_trading_hub")
FLASK_PLACEHOLDERS = frozenset(
{"", "CHANGE_TO_LONG_RANDOM_SECRET", "crypto_monitor_2026_secret_key"}
)
HUB_PLACEHOLDERS = frozenset({"", "your-long-random-token"})
SESSION_PLACEHOLDERS = frozenset({"", "another-long-random-string", "hub-dev-insecure"})
def _env_path(base: str) -> str:
return os.path.join(base, ".env")
def _should_set(current: str | None, placeholders: frozenset[str]) -> bool:
val = (current or "").strip()
return val in placeholders
def main() -> int:
parser = argparse.ArgumentParser(description="Bootstrap deploy secrets")
parser.add_argument("--dry-run", action="store_true", help="只打印将写入的项,不改文件")
args = parser.parse_args()
hub_token = secrets.token_urlsafe(32)
flask_secret = secrets.token_urlsafe(48)
session_secret = secrets.token_urlsafe(48)
planned: list[tuple[str, dict[str, str]]] = []
hub_env = _env_path(HUB_DIR)
if os.path.isfile(hub_env):
hub_lines = read_env_lines(hub_env)
hub_updates: dict[str, str] = {}
if _should_set(env_get(hub_lines, "HUB_BRIDGE_TOKEN"), HUB_PLACEHOLDERS):
hub_updates["HUB_BRIDGE_TOKEN"] = hub_token
if _should_set(env_get(hub_lines, "HUB_SESSION_SECRET"), SESSION_PLACEHOLDERS):
hub_updates["HUB_SESSION_SECRET"] = session_secret
if not (env_get(hub_lines, "HUB_USERNAME") or "").strip():
hub_updates["HUB_USERNAME"] = "admin"
if _should_set(env_get(hub_lines, "HUB_PASSWORD"), frozenset({""})):
hub_updates["HUB_PASSWORD"] = "admin123"
if hub_updates:
planned.append((hub_env, hub_updates))
for _name, inst_dir in INSTANCE_DIRS:
path = _env_path(inst_dir)
if not os.path.isfile(path):
continue
lines = read_env_lines(path)
updates: dict[str, str] = {}
if _should_set(env_get(lines, "HUB_BRIDGE_TOKEN"), HUB_PLACEHOLDERS):
updates["HUB_BRIDGE_TOKEN"] = hub_token
if _should_set(env_get(lines, "FLASK_SECRET_KEY"), FLASK_PLACEHOLDERS):
updates["FLASK_SECRET_KEY"] = flask_secret
if not (env_get(lines, "APP_USERNAME") or "").strip():
updates["APP_USERNAME"] = "admin"
if _should_set(env_get(lines, "APP_PASSWORD"), frozenset({""})):
updates["APP_PASSWORD"] = "admin123"
if updates:
planned.append((path, updates))
if not planned:
print("无需写入:密钥与登录项均已配置.")
return 0
for path, updates in planned:
rel = os.path.relpath(path, _REPO)
keys = ", ".join(sorted(updates.keys()))
if args.dry_run:
print(f"[dry-run] {rel}: {keys}")
continue
apply_env_updates(path, updates)
print(f"已写入 {rel}: {keys}")
if not args.dry_run:
print("完成.初始登录:admin / admin123(若本次写入了密码项).")
print("请 pm2 restart 中控与三实例使密钥生效.")
return 0
if __name__ == "__main__":
raise SystemExit(main())