modify: 前端集成
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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="/")
|
||||
@@ -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())
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user