Kamixitong/app/web/__init__.py
2025-11-22 20:32:49 +08:00

156 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 创建Web蓝图
from flask import Blueprint, render_template, request, redirect, url_for, session, flash, jsonify, current_app
from flask_login import login_user, logout_user, login_required, current_user
from app.models.admin import Admin
from app import db
web_bp = Blueprint('web', __name__)
user_bp = Blueprint('user', __name__, url_prefix='/index')
@web_bp.route('/')
def index():
"""首页 - 重定向到前端主页"""
return redirect(url_for('user.user_index'))
@web_bp.route('/login', methods=['GET', 'POST'])
def login():
"""登录页面"""
try:
if request.method == 'POST':
current_app.logger.info(f"收到登录请求 - IP: {request.remote_addr}, User-Agent: {request.headers.get('User-Agent')}")
# 检查CSRF令牌 - 生产环境部署时更宽松的验证
csrf_token = request.form.get('csrf_token')
if not csrf_token:
current_app.logger.warning(f"登录请求缺少CSRF令牌 - IP: {request.remote_addr}, User-Agent: {request.headers.get('User-Agent')}, Origin: {request.headers.get('Origin')}, Referer: {request.headers.get('Referer')}")
# 在生产环境中如果缺少CSRF令牌记录警告但继续处理
# 这有助于解决域名部署时的CSRF问题
if current_app.config.get('FLASK_ENV') == 'production':
current_app.logger.info("生产环境: 缺少CSRF令牌但继续处理登录请求")
else:
# 开发环境严格验证
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({
'success': False,
'message': '缺少CSRF令牌请刷新页面后重试'
}), 400
flash('缺少CSRF令牌请刷新页面后重试', 'error')
return render_template('login.html')
username = request.form.get('username', '').strip()
password = request.form.get('password', '').strip()
current_app.logger.info(f"登录尝试 - 用户名: {username}, 来源IP: {request.remote_addr}")
if not username or not password:
# 对于AJAX请求返回JSON错误
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({
'success': False,
'message': '请输入用户名和密码'
}), 400
# 对于普通表单提交使用flash消息
flash('请输入用户名和密码', 'error')
return render_template('login.html')
try:
# 查找用户(排除已软删除的用户)
admin = Admin.query.filter_by(username=username, is_deleted=0).first()
# 验证用户和密码
if not admin:
current_app.logger.warning(f"登录失败 - 用户不存在: {username}")
failure_message = '用户名或密码错误'
elif not admin.is_active:
current_app.logger.warning(f"登录失败 - 账号未激活: {username}")
failure_message = '账号未激活'
else:
# 验证密码
password_valid = admin.check_password(password)
if password_valid:
# 登录成功
current_app.logger.info(f"登录成功 - 用户名: {username}, IP: {request.remote_addr}")
login_user(admin, remember=True)
# 更新最后登录信息
try:
admin.update_last_login(request.remote_addr)
except Exception as e:
current_app.logger.error(f"更新最后登录信息失败: {str(e)}")
# 如果是AJAX请求返回JSON
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
import secrets
token = secrets.token_urlsafe(32)
return jsonify({
'success': True,
'token': token,
'user': {
'username': admin.username,
'role': admin.role,
'is_super_admin': admin.is_super_admin()
},
'redirect': url_for('web.dashboard')
})
# 普通请求,重定向
next_page = request.args.get('next')
if next_page:
return redirect(next_page)
return redirect(url_for('web.dashboard'))
else:
current_app.logger.warning(f"登录失败 - 密码错误: {username}")
failure_message = '用户名或密码错误'
# 登录失败,返回错误信息
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({
'success': False,
'message': failure_message
}), 401
flash(failure_message, 'error')
except Exception as e:
current_app.logger.error(f"登录过程中发生错误 - 用户名: {username}, 错误: {str(e)}")
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({
'success': False,
'message': '登录过程中发生错误,请稍后重试'
}), 500
flash('登录过程中发生错误,请稍后重试', 'error')
return render_template('login.html')
return render_template('login.html')
except Exception as e:
current_app.logger.error(f"登录路由发生严重错误: {str(e)}", exc_info=True)
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return jsonify({
'success': False,
'message': '服务器内部错误,请联系管理员'
}), 500
flash('服务器内部错误,请联系管理员', 'error')
return render_template('login.html')
@web_bp.route('/logout')
@login_required
def logout():
"""退出登录"""
logout_user()
flash('已退出登录', 'info')
return redirect(url_for('web.login'))
@web_bp.route('/dashboard')
@login_required
def dashboard():
"""仪表板"""
return render_template('dashboard.html')
@web_bp.route('/favicon.ico')
def favicon():
"""Favicon 处理 - 返回空响应避免404错误"""
from flask import Response
return Response(status=204) # No Content
# 导入视图函数
from . import views, user_views