diff --git a/.env.example b/.env.example index cd03fde..08cc69b 100644 --- a/.env.example +++ b/.env.example @@ -6,9 +6,10 @@ DEBUG=false # Flask Session 密钥(部署时务必改为随机字符串,deploy.sh 首次会自动生成) SECRET_KEY=change-this-to-a-random-secret-key -# 初始管理员账号(仅首次初始化数据库时写入,之后请在「系统设置」修改密码) +# 初始管理员(首次建库自动写入;已建库后修改需设 ADMIN_SYNC_FROM_ENV=true 并重启) ADMIN_USERNAME=admin ADMIN_PASSWORD=change-me-on-first-login +ADMIN_SYNC_FROM_ENV=false # 企业微信 Webhook(也可在系统设置页面修改) WECHAT_WEBHOOK= diff --git a/README.md b/README.md index cb04f13..dbdb44a 100644 --- a/README.md +++ b/README.md @@ -91,14 +91,19 @@ HOST=0.0.0.0 PORT=6600 SECRET_KEY=随机长字符串 ADMIN_USERNAME=admin -ADMIN_PASSWORD=首次登录密码 +ADMIN_PASSWORD=你的密码 +ADMIN_SYNC_FROM_ENV=false WECHAT_WEBHOOK=企业微信机器人地址(可选) QUOTE_SOURCE=sina ``` -普通用户保持 `QUOTE_SOURCE=sina` 即可,无需配置同花顺 token。 +**改密码说明**:账号存在 `futures.db` 里,改 `.env` 后不会自动生效。 -> 管理员密码首次从 `.env` 写入数据库并哈希存储,之后请在「系统设置」中修改。 +- **首次部署**:写好 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 后启动即可。 +- **已部署后**:在 `.env` 设 `ADMIN_SYNC_FROM_ENV=true`,改密码后 `pm2 restart qihuo`;或在网页「系统设置」改密。 +- **忘记密码**:`source venv/bin/activate && python reset_admin.py` + +普通用户保持 `QUOTE_SOURCE=sina` 即可。 ### 5. PM2 启动 diff --git a/app.py b/app.py index 690e006..08e0c61 100644 --- a/app.py +++ b/app.py @@ -17,7 +17,7 @@ from werkzeug.security import check_password_hash, generate_password_hash from symbols import search_symbols, ths_to_codes from market import get_price as market_get_price, set_ths_refresh_token, get_quote_source_label -load_dotenv() +load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")) app = Flask(__name__) app.secret_key = os.getenv("SECRET_KEY", "futures_monitor_default_secret") @@ -96,11 +96,7 @@ def init_db(): conn.commit() conn.close() - if not get_setting("admin_username"): - username = os.getenv("ADMIN_USERNAME", "admin") - password = os.getenv("ADMIN_PASSWORD", "admin123") - set_setting("admin_username", username) - set_setting("admin_password_hash", generate_password_hash(password)) + sync_admin_from_env() if not get_setting("wechat_webhook") and os.getenv("WECHAT_WEBHOOK"): set_setting("wechat_webhook", os.getenv("WECHAT_WEBHOOK")) @@ -109,6 +105,33 @@ def init_db(): set_setting("ths_refresh_token", os.getenv("THS_REFRESH_TOKEN")) +def sync_admin_from_env(): + """ + 从 .env 同步管理员账号。 + - 首次建库:自动写入 ADMIN_USERNAME / ADMIN_PASSWORD + - 已建库后改 .env:需设 ADMIN_SYNC_FROM_ENV=true 并重启服务 + """ + sync = os.getenv("ADMIN_SYNC_FROM_ENV", "false").lower() in ("1", "true", "yes") + env_username = os.getenv("ADMIN_USERNAME", "").strip() + env_password = os.getenv("ADMIN_PASSWORD", "").strip() + placeholder_passwords = {"", "change-me-on-first-login", "admin123"} + + if not get_setting("admin_username"): + username = env_username or "admin" + password = env_password if env_password not in placeholder_passwords else "admin123" + set_setting("admin_username", username) + set_setting("admin_password_hash", generate_password_hash(password)) + return + + if not sync: + return + + if env_username: + set_setting("admin_username", env_username) + if env_password and env_password not in placeholder_passwords: + set_setting("admin_password_hash", generate_password_hash(env_password)) + + init_db() diff --git a/reset_admin.py b/reset_admin.py new file mode 100644 index 0000000..0960d61 --- /dev/null +++ b/reset_admin.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +"""从 .env 重置管理员账号(服务器上忘记密码时使用)""" +import os +import sys + +from dotenv import load_dotenv +from werkzeug.security import generate_password_hash + +BASE = os.path.dirname(os.path.abspath(__file__)) +load_dotenv(os.path.join(BASE, ".env")) + +sys.path.insert(0, BASE) +from app import set_setting, get_setting # noqa: E402 + +username = os.getenv("ADMIN_USERNAME", "admin").strip() or "admin" +password = os.getenv("ADMIN_PASSWORD", "").strip() +if not password or password == "change-me-on-first-login": + print("请在 .env 中设置 ADMIN_PASSWORD 后再运行此脚本") + sys.exit(1) + +old_username = get_setting("admin_username") +set_setting("admin_username", username) +set_setting("admin_password_hash", generate_password_hash(password)) +print(f"已重置管理员: {username}(原账号: {old_username or '无'})")