113 lines
3.7 KiB
Python
113 lines
3.7 KiB
Python
"""本地后端入口: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,
|
||
)
|