154 lines
4.9 KiB
Python
154 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import os
|
||
from typing import Optional
|
||
|
||
from flask import Blueprint, current_app, redirect, render_template, session, url_for
|
||
from flask_login import current_user, login_required
|
||
|
||
from routes.decorators import admin_required
|
||
from services.runtime import get_logger
|
||
|
||
pages_bp = Blueprint("pages", __name__)
|
||
|
||
|
||
def render_app_spa_or_legacy(
|
||
legacy_template_name: str,
|
||
legacy_context: Optional[dict] = None,
|
||
spa_initial_state: Optional[dict] = None,
|
||
):
|
||
"""渲染前台 Vue SPA(构建产物位于 static/app),失败则回退旧模板。"""
|
||
logger = get_logger()
|
||
legacy_context = legacy_context or {}
|
||
manifest_path = os.path.join(current_app.root_path, "static", "app", ".vite", "manifest.json")
|
||
try:
|
||
with open(manifest_path, "r", encoding="utf-8") as f:
|
||
manifest = json.load(f)
|
||
|
||
entry = manifest.get("index.html") or {}
|
||
js_file = entry.get("file")
|
||
css_files = entry.get("css") or []
|
||
|
||
if not js_file:
|
||
logger.warning(f"[app_spa] manifest缺少入口文件: {manifest_path}")
|
||
return render_template(legacy_template_name, **legacy_context)
|
||
|
||
app_spa_js_file = f"app/{js_file}"
|
||
app_spa_css_files = [f"app/{p}" for p in css_files]
|
||
app_spa_build_id = _get_asset_build_id(
|
||
os.path.join(current_app.root_path, "static"),
|
||
[app_spa_js_file, *app_spa_css_files],
|
||
)
|
||
|
||
return render_template(
|
||
"app.html",
|
||
app_spa_js_file=app_spa_js_file,
|
||
app_spa_css_files=app_spa_css_files,
|
||
app_spa_build_id=app_spa_build_id,
|
||
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, **legacy_context)
|
||
except Exception as e:
|
||
logger.error(f"[app_spa] 加载manifest失败: {e}")
|
||
return render_template(legacy_template_name, **legacy_context)
|
||
|
||
|
||
def _get_asset_build_id(static_root: str, rel_paths: list[str]) -> Optional[str]:
|
||
mtimes = []
|
||
for rel_path in rel_paths:
|
||
if not rel_path:
|
||
continue
|
||
try:
|
||
mtimes.append(os.path.getmtime(os.path.join(static_root, rel_path)))
|
||
except OSError:
|
||
continue
|
||
if not mtimes:
|
||
return None
|
||
return str(int(max(mtimes)))
|
||
|
||
|
||
@pages_bp.route("/")
|
||
def index():
|
||
"""主页 - 重定向到登录或应用"""
|
||
if current_user.is_authenticated:
|
||
return redirect(url_for("pages.app_page"))
|
||
return redirect(url_for("pages.login_page"))
|
||
|
||
|
||
@pages_bp.route("/login")
|
||
def login_page():
|
||
"""登录页面"""
|
||
return render_app_spa_or_legacy("login.html")
|
||
|
||
|
||
@pages_bp.route("/register")
|
||
def register_page():
|
||
"""注册页面"""
|
||
return render_app_spa_or_legacy("register.html")
|
||
|
||
|
||
@pages_bp.route("/app")
|
||
@login_required
|
||
def app_page():
|
||
"""主应用页面"""
|
||
return render_app_spa_or_legacy("index.html")
|
||
|
||
|
||
@pages_bp.route("/app/<path:subpath>")
|
||
@login_required
|
||
def app_page_subpath(subpath):
|
||
"""SPA 子路由刷新支持(History 模式)"""
|
||
return render_app_spa_or_legacy("index.html")
|
||
|
||
|
||
@pages_bp.route("/yuyx")
|
||
def admin_login_page():
|
||
"""后台登录页面"""
|
||
if "admin_id" in session:
|
||
return redirect(url_for("pages.admin_page"))
|
||
return render_template("admin_login.html")
|
||
|
||
|
||
@pages_bp.route("/yuyx/admin")
|
||
@admin_required
|
||
def admin_page():
|
||
"""后台管理页面"""
|
||
logger = get_logger()
|
||
manifest_path = os.path.join(current_app.root_path, "static", "admin", ".vite", "manifest.json")
|
||
try:
|
||
with open(manifest_path, "r", encoding="utf-8") as f:
|
||
manifest = json.load(f)
|
||
|
||
entry = manifest.get("index.html") or {}
|
||
js_file = entry.get("file")
|
||
css_files = entry.get("css") or []
|
||
|
||
if not js_file:
|
||
logger.warning(f"[admin_spa] manifest缺少入口文件: {manifest_path}")
|
||
return render_template("admin_legacy.html")
|
||
|
||
admin_spa_js_file = f"admin/{js_file}"
|
||
admin_spa_css_files = [f"admin/{p}" for p in css_files]
|
||
admin_spa_build_id = _get_asset_build_id(
|
||
os.path.join(current_app.root_path, "static"),
|
||
[admin_spa_js_file, *admin_spa_css_files],
|
||
)
|
||
|
||
return render_template(
|
||
"admin.html",
|
||
admin_spa_js_file=admin_spa_js_file,
|
||
admin_spa_css_files=admin_spa_css_files,
|
||
admin_spa_build_id=admin_spa_build_id,
|
||
)
|
||
except FileNotFoundError:
|
||
logger.warning(f"[admin_spa] 未找到manifest: {manifest_path},回退旧版后台模板")
|
||
return render_template("admin_legacy.html")
|
||
except Exception as e:
|
||
logger.error(f"[admin_spa] 加载manifest失败: {e}")
|
||
return render_template("admin_legacy.html")
|