modify: 前端集成

This commit is contained in:
2026-04-14 19:44:25 +08:00
parent 3eee2b240e
commit c6919fd8e9
38 changed files with 3995 additions and 7 deletions
+6
View File
@@ -1,3 +1,4 @@
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, Depends
@@ -12,6 +13,7 @@ from .core.config import get_config
from .core.cache import get_cache
from .core.logger import get_logger
from .core.scheduler import get_task_service, get_task_service_instance, TaskService
from .core.static_files import static_files_handler
from .api import api_router
from .task.execute_background import execute_background_task
from .task import init_tasks_callback
@@ -126,6 +128,10 @@ async def add_task(
return {"status": "success", "job_id": job.id}
if os.path.exists("static.svfs"):
app.mount("/static", static_files_handler, name="static")
# 全局异常处理器
@app.exception_handler(Exception)
async def global_exception_handler(_: Request, exc: Exception):
+72
View File
@@ -0,0 +1,72 @@
import os
import mimetypes
from fastapi import HTTPException
from fastapi.responses import Response
from fastapi.staticfiles import StaticFiles
from typing import Optional
from sqlite_vfs.core import SQLiteVFS
class SVFSStaticFiles(StaticFiles):
def __init__(self, directory, vfs_db_path="static.svfs"):
if not os.path.exists(vfs_db_path):
raise RuntimeError(
f"虚拟文件系统数据库 {vfs_db_path} 不存在,请先运行打包脚本"
)
self.vfs_db_path = vfs_db_path
self.vfs = SQLiteVFS(vfs_db_path, compress=False)
super().__init__(directory=directory)
async def get_response(self, path: str, scope):
# 统一路径格式:以 / 开头,并将 Windows 反斜杠转为正斜杠
normalized_path = "/" + path.replace("\\", "/")
# 获取文件信息
file_info = self.vfs.get_file_info(normalized_path)
if file_info is None:
# SPA 回退:如果不是静态资源扩展名,尝试返回 index.html
if not any(
normalized_path.endswith(ext)
for ext in [
".js",
".css",
".png",
".jpg",
".ico",
".svg",
".webp",
".json",
]
):
index_info = self.vfs.get_file_info("/index.html")
if index_info:
content = self.vfs.read_file("/index.html")
return Response(content=content, media_type="text/html")
raise HTTPException(status_code=404, detail="File not found")
# 如果是目录,也返回 index.html(某些情况)
if file_info["is_directory"]:
index_info = self.vfs.get_file_info(f"{normalized_path}/index.html")
if index_info:
content = self.vfs.read_file(f"{normalized_path}/index.html")
return Response(content=content, media_type="text/html")
raise HTTPException(status_code=404, detail="Directory index not found")
# 读取文件内容
content = self.vfs.read_file(normalized_path)
content_type = file_info.get("content_type") or "application/octet-stream"
# 简单根据扩展名补充 MIME 类型(可选)
if normalized_path.endswith(".js"):
content_type = "application/javascript"
elif normalized_path.endswith(".css"):
content_type = "text/css"
elif normalized_path.endswith(".html"):
content_type = "text/html"
elif normalized_path.endswith(".ico"):
content_type = "image/x-icon"
return Response(content=content, media_type=content_type)
static_files_handler = SVFSStaticFiles(directory="/")
+49 -2
View File
@@ -6,6 +6,46 @@ import shutil
import subprocess
import sys
import asyncio
from sqlite_vfs.core import SQLiteVFS
from sqlite_vfs.folder_packer import FolderPacker
def build_vue_web():
"""打包 Vue Web"""
# 构建 Vue Web 命令
dist_dir = "./src/chongming-web/dist"
output_db = "./build/static.svfs"
fs_name = "frontend"
cmd = [
"npm",
"run",
"build",
]
result = subprocess.run(
cmd,
cwd="./src/chongming-web",
capture_output=True,
text=True,
shell=True,
encoding="utf-8",
errors="replace",
)
if result.returncode != 0:
print("打包 Vue Web 失败")
print(result.stderr)
else:
vfs = SQLiteVFS(output_db, compress=False)
packer = FolderPacker(
sqlite_vfs=vfs,
exclude_patterns=[],
fs_name=fs_name,
)
try:
packer.pack_folder(dist_dir)
print(f"✅ 打包完成,文件保存为 {output_db}")
finally:
vfs.close()
def run_pyarmor_obfuscate():
@@ -65,6 +105,8 @@ def run_pyinstaller():
"--hidden-import",
"fastapi",
"--hidden-import",
"fastapi.staticfiles",
"--hidden-import",
"fastapi.middleware.cors",
"--hidden-import",
"uvicorn",
@@ -75,8 +117,6 @@ def run_pyinstaller():
"--hidden-import",
"apscheduler",
"--hidden-import",
"pyjwt",
"--hidden-import",
"diskcache",
"--hidden-import",
"passlib",
@@ -85,6 +125,10 @@ def run_pyinstaller():
"--hidden-import",
"passlib.handlers.bcrypt",
"--hidden-import",
"sqlite_vfs",
"--hidden-import",
"sqlite_vfs.core",
"--hidden-import",
"jwt",
# 其他选项
"--icon",
@@ -210,6 +254,9 @@ async def main():
# 初始化数据库
await init_database()
# 构建 vue 项目
build_vue_web()
def build():
asyncio.run(main())
+9 -3
View File
@@ -8,20 +8,26 @@ def generate_markdown_from_py_files(directory, output_file):
# 排除 venv 目录
dirs[:] = [d for d in dirs if d != ".venv"]
dirs[:] = [d for d in dirs if d != ".vscode"]
# dirs[:] = [d for d in dirs if d != "scripts"]
dirs[:] = [d for d in dirs if d != "node_modules"]
dirs[:] = [d for d in dirs if d != "build"]
dirs[:] = [d for d in dirs if d != "dist"]
for file in files:
if (
file.endswith(".py")
or file.endswith(".rs")
or file.endswith(".toml")
or file.endswith(".md")
or file.endswith(".css")
or file.endswith(".vue")
or file.endswith(".ts")
or file.endswith(".json")
or file.endswith(".html")
):
file_path = os.path.join(root, file)
if file == "output.md":
if file == "output.md" or file == "package-lock.json":
continue
md_file.write(f"`{file_path}`\n")
md_file.write("```python\n")
md_file.write("```\n")
with open(file_path, "r", encoding="utf-8") as py_file:
md_file.write(py_file.read())
md_file.write("\n```\n\n")