diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c1616b0 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# ===== 环境变量配置 ===== +# 复制此文件为 .env 并修改相应值 +# Docker 部署时 docker-compose.yml 会自动读取 .env 文件 + +# JWT 密钥(生产环境必须设置!) +# 生成方式: python -c "import secrets; print(secrets.token_urlsafe(32))" +JWT_SECRET_KEY= + +# 默认管理员账号(仅首次启动建库时生效) +ADMIN_USERNAME=admin +ADMIN_PASSWORD=admin123 diff --git a/.gitignore b/.gitignore index 747d4a3..02677dc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ __pycache__/ */__pycache__ *.pyc +# 环境变量(含敏感信息,勿提交) +.env + # 运行时数据 data/ logs/ diff --git a/data/accounts.json b/data/accounts.json deleted file mode 100644 index 61d34d5..0000000 --- a/data/accounts.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "accounts": [ - { - "username": "your_username", - "password": "your_password", - "email": "your_email@bdhg.xyz", - "email_password": "your_email_password", - "email_imap_server": "mail.bdhg.xyz", - "email_imap_port": 993 - } - ] -} diff --git a/data/cookies/用户9006787894.json b/data/cookies/用户9006787894.json deleted file mode 100644 index 729b2a5..0000000 --- a/data/cookies/用户9006787894.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "username": "用户9006787894", - "cookie": "dy_accounts_main=; dy_auth=e105BJzhxVWQ0tJKUXiiqN8ky11wgfj7ClW%2F8qo%2B1MPv8%2BDGHBq8VnuaJ1MWnm4EZqgVfLkM6PETWtGpLAHvadcMxCn9HWvTfT8%2BSKZR2dmsvReubJmeqBo; wan_auth37wan=4dc825c71d00LrMvodf%2B7ZrhnyMcsREgwQlgd7Ec06k5%2Bm%2FM0tdv0AJJ8gpoNoinSoPHS3vbJO8iK1vqiXfF2hTOLQqJOW3Wzs19DWLwpJpQgV8J%2BF0; LTP0=eyJhbGciOiJtZDUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOlsicGFzc3BvcnQiXSwiY3QiOjAsImN0aW1lIjoxNzgyMDU0MDA0LCJleHAiOjE3OTc4MjIwMTYsImtleSI6ImR5LWp3dC1tZDUiLCJsdGsiOiJhZDMxOTliZGRhYmM3ZWRmIiwibHRraWQiOjgwNTYxMzgyLCJzdWIiOiJsdCIsInVpZCI6OTg2Mzg1Mjk2fQ.Y2M0YTQ4YjQwZGYwZWI4YWZiZTllNmVjODNiZmJkMGE; last_login_way=nickname; PHPSESSID=ccr09aa2letju5662gdh5aih62; acf_auth=1a34kGp6rHjazzRy4fVzKXX11qCjC9RuQASDGOEfkRbLjsI3A5PzSCXawo5hZvO4g47LF9ruapNiMpf%2BE3u4TplWPMkW4c4eI0pyG05a%2F9dKr%2BOGM0hAEXk; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM4NTI5NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MDU2MTM4Miwic3RrIjoiNjFhODczZjUwZTg3M2Y5MCIsImV4cCI6MTc4MjY1ODgwNCwiaWF0IjoxNzgyMDU0MDA0LCJrZXkiOiJkeS1qd3QtbWQ1In0.OTFlMjIxOGU4MjZkMjcwMWM4NjQ1NTcwNjNmZWIzMDA; acf_dmjwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjk4NjM4NTI5NiwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImRtIiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo4MDU2MTM4Miwic3RrIjoiNjFhODczZjUwZTg3M2Y5MCIsImV4cCI6MTc4MjY1ODgwNCwiaWF0IjoxNzgyMDU0MDA0LCJrZXkiOiJkeS1qd3QtbWQ1In0.N2E0NmEyOWVlZDE1NTE4NDg0MTFlMmM5OGY1ZGQzZTk; acf_uid=986385296; acf_username=986385296; acf_nickname=%E7%94%A8%E6%88%B79006787894; acf_own_room=0; acf_groupid=1; acf_phonestatus=1; acf_avatar=https%3A%2F%2Fapic.douyucdn.cn%2Fupload%2Favatar%2Fdefault%2F25_; acf_ct=0; acf_ltkid=80561382; acf_biz=1; acf_stk=61a873f50e873f90; acf_devid=d53498d383f449f4c4ea28c4165aa2b4", - "timestamp": 1782054016 -} \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index 19e94eb..110a874 100755 --- a/deploy.sh +++ b/deploy.sh @@ -54,7 +54,7 @@ for i in $(seq 1 10); do echo " ✅ 部署成功!" echo "==============================" echo " 访问地址: http://localhost:8000" - echo " 默认账号: admin / admin123" + echo " 默认账号见环境变量 ADMIN_USERNAME/ADMIN_PASSWORD(未设置则为 admin/admin123)" echo "" echo " 查看日志: $COMPOSE logs -f" echo " 停止服务: $COMPOSE down" diff --git a/docker-compose.yml b/docker-compose.yml index b13aeb8..4ecdbbe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,11 @@ services: - ./logs:/app/logs environment: - TZ=Asia/Shanghai + # JWT 密钥(生产环境务必修改,可用 python -c "import secrets; print(secrets.token_urlsafe(32))" 生成) + - JWT_SECRET_KEY=${JWT_SECRET_KEY:-} + # 默认管理员账号密码(仅首次启动建库时生效) + - ADMIN_USERNAME=${ADMIN_USERNAME:-admin} + - ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123} healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"] interval: 30s diff --git a/start_web.sh b/start_web.sh index 5789caf..3a66f6c 100755 --- a/start_web.sh +++ b/start_web.sh @@ -28,7 +28,7 @@ echo " 斗鱼批量登录 - Web 后台" echo "==============================" echo " 后端: http://localhost:$BACKEND_PORT" echo " 前端: http://localhost:$FRONTEND_PORT" -echo " 默认账号: admin / admin123" +echo " 默认账号见环境变量 ADMIN_USERNAME/ADMIN_PASSWORD(未设置则为 admin/admin123)" echo " 按 Ctrl+C 停止所有服务" echo "==============================" echo "" diff --git a/web/backend/database.py b/web/backend/database.py index ba1faee..5348caf 100644 --- a/web/backend/database.py +++ b/web/backend/database.py @@ -1,5 +1,6 @@ """数据库引擎与会话管理""" +import os from pathlib import Path from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base @@ -73,16 +74,19 @@ def _migrate(): def _seed(): - """写入默认超管账号和角色。""" + """写入默认超管账号和角色(账号密码通过环境变量配置)。""" from .models import User from .security import hash_password + admin_username = os.getenv("ADMIN_USERNAME", "admin") + admin_password = os.getenv("ADMIN_PASSWORD", "admin123") + db = SessionLocal() try: if not db.query(User).first(): admin = User( - username="admin", - password_hash=hash_password("admin123"), + username=admin_username, + password_hash=hash_password(admin_password), role="super_admin", is_active=True, remark="默认超级管理员", diff --git a/web/backend/security.py b/web/backend/security.py index 820ae54..428cea7 100644 --- a/web/backend/security.py +++ b/web/backend/security.py @@ -1,13 +1,24 @@ """安全模块:密码哈希 + JWT""" +import os +import secrets +import warnings from datetime import datetime, timedelta, timezone from typing import Optional import bcrypt from jose import jwt, JWTError -SECRET_KEY = "douyu-web-secret-change-in-production" +# JWT 密钥:生产环境必须通过环境变量 JWT_SECRET_KEY 设置 +SECRET_KEY = os.getenv("JWT_SECRET_KEY") +if not SECRET_KEY: + warnings.warn( + "未设置 JWT_SECRET_KEY 环境变量,使用临时随机密钥(重启后所有 Token 将失效)。" + "生产环境请务必设置 JWT_SECRET_KEY 环境变量。", + stacklevel=2, + ) + SECRET_KEY = secrets.token_urlsafe(32) ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_HOURS = 24 +ACCESS_TOKEN_EXPIRE_HOURS = int(os.getenv("ACCESS_TOKEN_EXPIRE_HOURS", "24")) def hash_password(password: str) -> str: diff --git a/web/frontend/src/pages/LoginPage.tsx b/web/frontend/src/pages/LoginPage.tsx index c4245c4..efa7c99 100644 --- a/web/frontend/src/pages/LoginPage.tsx +++ b/web/frontend/src/pages/LoginPage.tsx @@ -61,9 +61,6 @@ export default function LoginPage({ onLogin }: { onLogin?: () => void }) { -
- 默认管理员: admin / admin123 -
);