feat(app): migrate auth pages to Vue SPA (stage 2)

This commit is contained in:
2025-12-13 23:30:51 +08:00
parent 34f44eed3e
commit 324e0d614a
35 changed files with 1175 additions and 85 deletions

121
app.py
View File

@@ -1189,13 +1189,13 @@ def index():
@app.route('/login')
def login_page():
"""登录页面"""
return render_template('login.html')
return render_app_spa_or_legacy('login.html')
@app.route('/register')
def register_page():
"""注册页面"""
return render_template('register.html')
return render_app_spa_or_legacy('register.html')
@app.route('/app')
@@ -1243,11 +1243,16 @@ def admin_page():
return render_template('admin_legacy.html')
def render_app_spa_or_legacy(legacy_template_name: str):
def render_app_spa_or_legacy(
legacy_template_name: str,
legacy_context: dict | None = None,
spa_initial_state: dict | None = None,
):
"""渲染前台 Vue SPA构建产物位于 static/app失败则回退旧模板。
说明:该函数仅负责“资源注入/回退逻辑”,不改变任何接口与业务流程。
"""
legacy_context = legacy_context or {}
manifest_path = os.path.join(app.root_path, 'static', 'app', '.vite', 'manifest.json')
try:
with open(manifest_path, 'r', encoding='utf-8') as f:
@@ -1259,19 +1264,20 @@ def render_app_spa_or_legacy(legacy_template_name: str):
if not js_file:
logger.warning(f"[app_spa] manifest缺少入口文件: {manifest_path}")
return render_template(legacy_template_name)
return render_template(legacy_template_name, **legacy_context)
return render_template(
'app.html',
app_spa_js_file=f'app/{js_file}',
app_spa_css_files=[f'app/{p}' for p in css_files],
app_spa_initial_state=spa_initial_state,
)
except FileNotFoundError:
logger.info(f"[app_spa] 未找到manifest: {manifest_path},回退旧模板: {legacy_template_name}")
return render_template(legacy_template_name)
return render_template(legacy_template_name, **legacy_context)
except Exception as e:
logger.error(f"[app_spa] 加载manifest失败: {e}")
return render_template(legacy_template_name)
return render_template(legacy_template_name, **legacy_context)
# ==================== 用户认证API ====================
@app.route('/api/register', methods=['POST'])
@@ -1387,10 +1393,35 @@ def verify_email(token):
database.set_user_vip(user_id, auto_approve_vip_days)
logger.info(f"用户邮箱验<EFBFBD><EFBFBD><EFBFBD>成功: user_id={user_id}, email={email}")
return render_template('verify_success.html')
spa_initial_state = {
'page': 'verify_result',
'success': True,
'title': '验证成功',
'message': '您的邮箱已验证成功!账号已激活,现在可以登录使用了。',
'primary_label': '立即登录',
'primary_url': '/login',
'redirect_url': '/login',
'redirect_seconds': 5,
}
return render_app_spa_or_legacy('verify_success.html', spa_initial_state=spa_initial_state)
else:
logger.warning(f"邮箱验证失败: token={token[:20]}...")
return render_template('verify_failed.html', error_message="验证链接无效或已过期,请重新注册或申请重发验证邮件")
error_message = "验证链接无效或已过期,请重新注册或申请重发验证邮件"
spa_initial_state = {
'page': 'verify_result',
'success': False,
'title': '验证失败',
'error_message': error_message,
'primary_label': '重新注册',
'primary_url': '/register',
'secondary_label': '返回登录',
'secondary_url': '/login',
}
return render_app_spa_or_legacy(
'verify_failed.html',
legacy_context={'error_message': error_message},
spa_initial_state=spa_initial_state,
)
@app.route('/api/resend-verify-email', methods=['POST'])
@@ -1517,11 +1548,26 @@ def forgot_password():
def reset_password_page(token):
"""密码重置页面"""
result = email_service.verify_password_reset_token(token)
if result:
return render_template('reset_password.html', token=token, valid=True, error_message='')
else:
return render_template('reset_password.html', token=token, valid=False,
error_message='重置链接无效或已过期,请重新申请密码重置')
valid = bool(result)
error_message = '' if valid else '重置链接无效或已过期,请重新申请密码重置'
legacy_context = {
'token': token,
'valid': valid,
'error_message': error_message,
}
spa_initial_state = {
'page': 'reset_password',
'token': token,
'valid': valid,
'error_message': error_message,
}
return render_app_spa_or_legacy(
'reset_password.html',
legacy_context=legacy_context,
spa_initial_state=spa_initial_state,
)
@app.route('/api/reset-password-confirm', methods=['POST'])
@@ -3697,21 +3743,46 @@ def verify_bind_email(token):
# 更新用户邮箱
if database.update_user_email(user_id, email, verified=True):
# 返回成功页面
return render_template('verify_success.html',
title='邮箱绑定成功',
message=f'邮箱 {email} 已成功绑定到您的账号!',
redirect_url='/'
)
spa_initial_state = {
'page': 'verify_result',
'success': True,
'title': '邮箱绑定成功',
'message': f'邮箱 {email} 已成功绑定到您的账号!',
'primary_label': '返回登录',
'primary_url': '/login',
'redirect_url': '/login',
'redirect_seconds': 5,
}
return render_app_spa_or_legacy('verify_success.html', spa_initial_state=spa_initial_state)
else:
return render_template('verify_failed.html',
title='绑定失败',
message='邮箱绑定失败,请重试'
error_message = '邮箱绑定失败,请重试'
spa_initial_state = {
'page': 'verify_result',
'success': False,
'title': '绑定失败',
'error_message': error_message,
'primary_label': '返回登录',
'primary_url': '/login',
}
return render_app_spa_or_legacy(
'verify_failed.html',
legacy_context={'error_message': error_message},
spa_initial_state=spa_initial_state,
)
else:
return render_template('verify_failed.html',
title='链接无效',
message='验证链接已过期或无效,请重新发送验证邮件'
error_message = '验证链接已过期或无效,请重新发送验证邮件'
spa_initial_state = {
'page': 'verify_result',
'success': False,
'title': '链接无效',
'error_message': error_message,
'primary_label': '返回登录',
'primary_url': '/login',
}
return render_app_spa_or_legacy(
'verify_failed.html',
legacy_context={'error_message': error_message},
spa_initial_state=spa_initial_state,
)