task-283(后台迁移收尾/发布): admin-vue 后台工程入库 + Flask 后台整体退役
- 新后台 admin-frontend-vue 整工程入库(base=/admin-vue/、History 路由、Nginx 静态托管方案与 verify-dist 校验) - Java AdminConsoleController 改为入口重定向: /admin|/admin.html、/login|/login.html -> /admin-vue/; 删除 classpath:static 旧 admin.html/login.html 单页与 admin.js 副本 - Flask 后台整体退役: 删除 admin_api/auth/main 蓝图、web_source 页面、static 脚本与 admin 相关测试; app.py 收敛为仅注册 version_bp(/api/version、/api/version/latest, 供桌面端更新检查) - AdminApiGuardFilterTest 豁免样例路径 /admin.html -> /admin-vue/
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,87 +0,0 @@
|
||||
"""
|
||||
认证蓝图:登录、登出、登录状态校验
|
||||
"""
|
||||
from flask import Blueprint, request, redirect, url_for, session, jsonify, make_response, current_app
|
||||
from werkzeug.security import check_password_hash
|
||||
|
||||
from utils.db import get_db
|
||||
from utils.auth import login_required, is_session_user_valid
|
||||
from utils.render import render_html
|
||||
|
||||
auth = Blueprint('auth', __name__, url_prefix='')
|
||||
|
||||
|
||||
@auth.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
force_relogin = request.args.get('logout') == '1' or request.args.get('switch') == '1'
|
||||
if request.method == 'GET' and force_relogin:
|
||||
session.clear()
|
||||
response = make_response(render_html('login.html'))
|
||||
response.delete_cookie(current_app.config.get('SESSION_COOKIE_NAME', 'session'))
|
||||
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
|
||||
return response
|
||||
if request.method == 'GET' and session.get('user_id') and is_session_user_valid():
|
||||
return redirect(url_for('main.admin_page'))
|
||||
if request.method == 'POST':
|
||||
session.clear()
|
||||
wants_json = request.is_json or request.headers.get('X-Requested-With') == 'XMLHttpRequest'
|
||||
data = request.get_json() if request.is_json else request.form
|
||||
username = (data.get('username') or '').strip()
|
||||
password = data.get('password') or ''
|
||||
if not username or not password:
|
||||
if wants_json:
|
||||
return jsonify({'success': False, 'error': '请输入用户名和密码'})
|
||||
return render_html('login.html', error='请输入用户名和密码')
|
||||
try:
|
||||
conn = get_db()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT id, password_hash, machine, is_admin FROM users WHERE username = %s",
|
||||
(username,)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
conn.close()
|
||||
if row and check_password_hash(row['password_hash'], password):
|
||||
session.permanent = True
|
||||
session['user_id'] = row['id']
|
||||
session['username'] = username
|
||||
if wants_json:
|
||||
return jsonify({'success': True, 'redirect': url_for('main.admin_page')})
|
||||
return redirect(url_for('main.admin_page'))
|
||||
except Exception as exc:
|
||||
current_app.logger.error('[auth] login error: %s', exc, exc_info=True)
|
||||
if wants_json:
|
||||
return jsonify({'success': False, 'error': '登录失败,请稍后重试'})
|
||||
return render_html('login.html', error='登录失败,请稍后重试')
|
||||
if wants_json:
|
||||
return jsonify({'success': False, 'error': '用户名或密码错误'})
|
||||
return render_html('login.html', error='用户名或密码错误')
|
||||
return render_html('login.html')
|
||||
|
||||
|
||||
@auth.route('/api/auth/check')
|
||||
@login_required
|
||||
def api_auth_check():
|
||||
"""校验登录状态,用于页面加载时判断是否已登录。"""
|
||||
if not session.get('user_id'):
|
||||
return jsonify({'logged_in': False})
|
||||
try:
|
||||
conn = get_db()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT machine, is_admin FROM users WHERE id = %s", (session['user_id'],))
|
||||
row = cur.fetchone()
|
||||
conn.close()
|
||||
if not row:
|
||||
return jsonify({'logged_in': False})
|
||||
except Exception:
|
||||
return jsonify({'logged_in': False})
|
||||
return jsonify({'logged_in': True, 'redirect': url_for('main.admin_page')})
|
||||
|
||||
|
||||
@auth.route('/logout')
|
||||
def logout():
|
||||
session.clear()
|
||||
response = redirect(url_for('auth.login', logout='1'))
|
||||
response.delete_cookie(current_app.config.get('SESSION_COOKIE_NAME', 'session'))
|
||||
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
|
||||
return response
|
||||
@@ -1,54 +0,0 @@
|
||||
"""
|
||||
主页面蓝图:首页、管理后台页、静态文件
|
||||
"""
|
||||
import os
|
||||
from flask import Blueprint, current_app, redirect, url_for, send_file, session
|
||||
from werkzeug.utils import safe_join
|
||||
|
||||
from utils.auth import login_required, is_session_user_valid
|
||||
from utils.render import render_html
|
||||
|
||||
main = Blueprint('main', __name__, url_prefix='')
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
STATIC_DIR = os.path.join(BASE_DIR, 'static')
|
||||
|
||||
|
||||
@main.route('/')
|
||||
def index():
|
||||
if session.get('user_id') and is_session_user_valid():
|
||||
return redirect(url_for('main.admin_page'))
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
|
||||
@main.route('/admin')
|
||||
@login_required
|
||||
def admin_page():
|
||||
"""管理后台页:服务端直接渲染当前用户有权限的菜单,避免客户端二次渲染造成闪烁。"""
|
||||
context = {}
|
||||
try:
|
||||
# 复用权限菜单加载逻辑;Java 权限接口不可用时降级为无菜单(JS 侧会走 API 兜底)
|
||||
from blueprints.admin_api import _load_current_backend_menu_items
|
||||
_, _, items, denied = _load_current_backend_menu_items()
|
||||
if denied is None and items:
|
||||
context['admin_menu_items'] = items
|
||||
context['admin_menu_rendered'] = True
|
||||
else:
|
||||
# 权限接口失败:真实页面同样会失败,服务端不渲染菜单,避免展示越权菜单
|
||||
context['admin_menu_rendered'] = False
|
||||
current_app.logger.warning('[admin] 服务端菜单渲染失败,降级为客户端加载: %s',
|
||||
(denied[0].get_json() if denied and len(denied) > 0 else None))
|
||||
except Exception as exc:
|
||||
# 兜底:任何异常都不得阻断管理页打开,JS 会自行请求菜单接口
|
||||
current_app.logger.exception('[admin] 服务端渲染菜单异常: %s', exc)
|
||||
context['admin_menu_rendered'] = False
|
||||
return render_html('admin.html', **context)
|
||||
|
||||
|
||||
@main.route('/static/<path:filename>')
|
||||
def serve_static(filename):
|
||||
"""提供 static 目录及子目录下的静态文件访问"""
|
||||
filepath = safe_join(STATIC_DIR, filename)
|
||||
if filepath is None or not os.path.isfile(filepath):
|
||||
return '', 404
|
||||
return send_file(filepath, as_attachment=False)
|
||||
Reference in New Issue
Block a user