Add curated env config UI with Chinese labels and deploy secret bootstrap
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""首次部署:自动生成中控通信密钥、登录会话密钥,并写入初始登录账号。
|
||||
|
||||
- HUB_BRIDGE_TOKEN:中控 + 三实例(相同)
|
||||
- FLASK_SECRET_KEY:三实例(相同)
|
||||
- APP_USERNAME=admin、APP_PASSWORD=admin123:仅当 .env 中尚未设置密码时写入
|
||||
|
||||
已有非空且非占位符的值不会被覆盖。
|
||||
"""
|
||||
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"})
|
||||
|
||||
|
||||
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)
|
||||
planned: list[tuple[str, dict[str, str]]] = []
|
||||
|
||||
# 中控 HUB_BRIDGE_TOKEN
|
||||
hub_env = _env_path(HUB_DIR)
|
||||
if os.path.isfile(hub_env):
|
||||
hub_lines = read_env_lines(hub_env)
|
||||
if _should_set(env_get(hub_lines, "HUB_BRIDGE_TOKEN"), HUB_PLACEHOLDERS):
|
||||
planned.append((hub_env, {"HUB_BRIDGE_TOKEN": hub_token}))
|
||||
|
||||
# 三实例
|
||||
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(若本次写入了 APP_PASSWORD)。")
|
||||
print("请 pm2 restart 中控与三实例使 FLASK_SECRET_KEY / HUB_BRIDGE_TOKEN 生效。")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user