- Replace hardcoded JWT secret with randomly generated key persisted to file - Replace hardcoded default password with random password shown in logs - Migrate token storage from localStorage to HttpOnly SameSite=strict cookie - Add IP-based login rate limiter (5 attempts / 15 min, 429 on lockout) - Add token_version for JWT revocation on password change - Add password strength validation (min 6 chars, 3+ unique characters) - Inject decoded user payload into request.state.user in auth middleware - Add /api/auth/me and /api/auth/logout endpoints - Narrow auth middleware exception handling (JWTError only, not all Exception) - Update updated_at timestamp on password change - Remove localStorage token management from frontend (axios, router, store) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
1.2 KiB
Python
52 lines
1.2 KiB
Python
# 硬编码配置
|
|
import os
|
|
import secrets
|
|
import logging
|
|
|
|
_logger = logging.getLogger("app.config")
|
|
|
|
# api 目录的绝对路径(基于本文件位置计算,不依赖工作目录)
|
|
_BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
# 数据库配置
|
|
DATABASE_PATH = os.path.join(_BASE_DIR, "data", "todo.db")
|
|
DATABASE_URL = f"sqlite:///{DATABASE_PATH}"
|
|
|
|
# WebUI 配置
|
|
WEBUI_PATH = os.path.join(_BASE_DIR, "webui")
|
|
|
|
# CORS 配置
|
|
CORS_ORIGINS = [
|
|
"http://localhost:5173",
|
|
"http://localhost:23994",
|
|
]
|
|
|
|
# 日志配置
|
|
LOG_LEVEL = "INFO"
|
|
LOG_DIR = os.path.join(_BASE_DIR, "logs")
|
|
|
|
# 分页配置
|
|
DEFAULT_PAGE_SIZE = 20
|
|
|
|
# 服务配置
|
|
HOST = "0.0.0.0"
|
|
PORT = 23994
|
|
|
|
|
|
# JWT 密钥(首次启动随机生成,持久化到文件)
|
|
def _load_jwt_secret() -> str:
|
|
secret_file = os.path.join(_BASE_DIR, "data", ".jwt_secret")
|
|
if os.path.exists(secret_file):
|
|
with open(secret_file) as f:
|
|
return f.read().strip()
|
|
secret = secrets.token_hex(32)
|
|
os.makedirs(os.path.dirname(secret_file), exist_ok=True)
|
|
with open(secret_file, "w") as f:
|
|
f.write(secret)
|
|
_logger.warning("已生成新的 JWT 密钥")
|
|
return secret
|
|
|
|
|
|
JWT_SECRET = _load_jwt_secret()
|
|
ACCESS_TOKEN_EXPIRE_MINUTES = 1440 # 24小时
|