assistant/backend/main.py

113 lines
3.7 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.

"""本地后端入口:FastAPI + PostgreSQL + Browser Use。
源码运行:python backend/main.py(项目根目录)
冻结运行:native/release/school-assistant-backend/school-assistant-backend.exe(PyInstaller onedir,由 Electron 拉起)
自检: … --selfcheck 只导入全部依赖与本项目模块并打印版本,不连数据库、不监听端口,供打包脚本验收
"""
import json
import sys
from pathlib import Path
# 支持从项目根目录运行: python backend/main.py
ROOT = Path(__file__).resolve().parent.parent
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from dotenv import load_dotenv # noqa: E402
from backend.config import ENV_FILE, FROZEN, HOME_DIR, KNOWLEDGE_DIR, settings # noqa: E402
load_dotenv(ENV_FILE)
def selfcheck() -> int:
"""打包验收:把运行时会碰到的模块都导入一遍,缺什么在这里就会暴露。"""
import importlib.metadata as metadata
import browser_use # noqa: F401
import bubus # noqa: F401
import cdp_use # noqa: F401
import cryptography # noqa: F401
import fastapi # noqa: F401
import httpx # noqa: F401
import psycopg # noqa: F401
import uvicorn # noqa: F401
import yaml # noqa: F401
import backend.prospecting.routes # noqa: F401
from backend.db.database import SCHEMA_PATH
def version_of(dist: str, module) -> str:
# PyInstaller 只为 browser-use 复制了 dist-info(它运行时要读),其余包冻结后没有 metadata,退回 __version__
try:
return metadata.version(dist)
except metadata.PackageNotFoundError:
return str(getattr(module, "__version__", "n/a"))
report = {
"ok": True,
"frozen": FROZEN,
"python": sys.version.split()[0],
"home": str(HOME_DIR),
"env_file": str(ENV_FILE),
"knowledge_dir": str(KNOWLEDGE_DIR),
"knowledge_files": sorted(p.name for p in KNOWLEDGE_DIR.glob("*.yaml")) if KNOWLEDGE_DIR.exists() else [],
"schema_sql": SCHEMA_PATH.exists(),
"versions": {
"browser-use": metadata.version("browser-use"),
"bubus": version_of("bubus", bubus),
"cdp-use": version_of("cdp-use", cdp_use),
"fastapi": version_of("fastapi", fastapi),
"uvicorn": version_of("uvicorn", uvicorn),
"psycopg": version_of("psycopg", psycopg),
"cryptography": version_of("cryptography", cryptography),
},
}
print(json.dumps(report, ensure_ascii=False))
return 0
if "--selfcheck" in sys.argv:
sys.exit(selfcheck())
import uvicorn # noqa: E402
from fastapi import FastAPI # noqa: E402
from fastapi.middleware.cors import CORSMiddleware # noqa: E402
from backend.prospecting.routes import orchestrator, router # noqa: E402
from backend.db.database import init_db # noqa: E402
init_db()
app = FastAPI(title="橘子拓客 API", version="0.4.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["app://ui", "http://127.0.0.1:8098", "http://localhost:8098"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(router, prefix="/api")
@app.on_event("startup")
async def _start_background_loops() -> None:
"""恢复被应用重启中断的拓客任务;不启用历史学校审批/中心同步循环。"""
orchestrator.recover_interrupted()
@app.get("/")
async def root():
return {"name": "ai-prospecting-assistant", "docs": "/docs"}
if __name__ == "__main__":
# 传 app 对象而不是 "backend.main:app" 字符串:冻结后本文件是 __main__,按字符串再导入会把模块跑两遍
uvicorn.run(
app,
host="127.0.0.1",
port=settings.api_port,
reload=False,
)