Initial upload for secondary development
This commit is contained in:
151
chatlog_fastAPI/main.py
Normal file
151
chatlog_fastAPI/main.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pydantic import BaseModel
|
||||
from contextlib import asynccontextmanager
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import httpx
|
||||
from database import get_active_db_path, get_current_wxid, init_db, reset_wxid_cache, update_db_path
|
||||
from scheduler import start_scheduler
|
||||
from config import settings
|
||||
from routers import search, groups, topics, knowledge, ai, sse, files, chatlog_proxy
|
||||
from routers import settings as settings_router
|
||||
from services.chatlog_context import get_chatlog_context, update_chatlog_context
|
||||
from services.media_resolver import diagnose_media
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ChatlogContextRequest(BaseModel):
|
||||
account: str = ""
|
||||
workDir: str = ""
|
||||
dataDir: str = ""
|
||||
platform: str = "windows"
|
||||
version: int = 4
|
||||
chatlogExe: str = ""
|
||||
chatlogVersion: str = ""
|
||||
|
||||
async def _account_watch_loop():
|
||||
"""每 60 秒检测一次当前微信账号,如账号切换则自动切换数据库。"""
|
||||
while True:
|
||||
await asyncio.sleep(60)
|
||||
try:
|
||||
await update_db_path()
|
||||
except Exception as e:
|
||||
log.warning(f"[account_watch] update_db_path error: {e}")
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
await init_db()
|
||||
await start_scheduler()
|
||||
# 启动后台账号监控任务
|
||||
task = asyncio.create_task(_account_watch_loop())
|
||||
yield
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
@app.exception_handler(RuntimeError)
|
||||
async def runtime_error_handler(request: Request, exc: RuntimeError):
|
||||
return JSONResponse(status_code=500, content={"detail": str(exc)})
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origins,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
app.include_router(search.router)
|
||||
app.include_router(groups.router)
|
||||
app.include_router(topics.router)
|
||||
app.include_router(knowledge.router)
|
||||
app.include_router(ai.router)
|
||||
app.include_router(sse.router)
|
||||
app.include_router(files.router)
|
||||
app.include_router(settings_router.router)
|
||||
app.include_router(chatlog_proxy.router)
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
chatlog_ok = False
|
||||
chatlog_error = ""
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=3.0, trust_env=False) as client:
|
||||
resp = await client.get(f"{settings.chatlog_base_url}/api/v1/session", params={"limit": 1, "format": "json"})
|
||||
chatlog_ok = resp.status_code == 200
|
||||
if not chatlog_ok:
|
||||
chatlog_error = f"HTTP {resp.status_code}"
|
||||
except Exception as e:
|
||||
chatlog_error = str(e)
|
||||
|
||||
wxid = await get_current_wxid() if chatlog_ok else "default"
|
||||
return {
|
||||
"ok": True,
|
||||
"chatlog_ok": chatlog_ok,
|
||||
"chatlog_error": chatlog_error,
|
||||
"wxid": wxid,
|
||||
"db_path": get_active_db_path(),
|
||||
"data_dir": settings.data_dir,
|
||||
}
|
||||
|
||||
|
||||
@app.post("/api/system/refresh-account")
|
||||
async def refresh_account():
|
||||
reset_wxid_cache()
|
||||
db_path = await update_db_path(force=True)
|
||||
wxid = await get_current_wxid()
|
||||
return {"ok": True, "wxid": wxid, "db_path": db_path}
|
||||
|
||||
|
||||
@app.post("/api/system/chatlog-context")
|
||||
async def set_chatlog_context(body: ChatlogContextRequest):
|
||||
return {"ok": True, "context": update_chatlog_context(body.model_dump())}
|
||||
|
||||
|
||||
@app.get("/api/system/chatlog-context")
|
||||
async def read_chatlog_context():
|
||||
return {"ok": True, "context": get_chatlog_context()}
|
||||
|
||||
|
||||
@app.get("/api/system/media-diagnostics")
|
||||
async def media_diagnostics(kind: str = "voice", key: str = ""):
|
||||
return await diagnose_media(kind, key)
|
||||
|
||||
|
||||
static_dir = Path(settings.static_dir)
|
||||
if static_dir.exists():
|
||||
assets_dir = static_dir / "assets"
|
||||
if assets_dir.exists():
|
||||
app.mount("/assets", StaticFiles(directory=str(assets_dir)), name="assets")
|
||||
for static_name in ("favicon.svg", "icons.svg"):
|
||||
static_file = static_dir / static_name
|
||||
|
||||
if static_file.exists():
|
||||
@app.get(f"/{static_name}", include_in_schema=False)
|
||||
async def _serve_static_file(name=static_name):
|
||||
return FileResponse(static_dir / name)
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def spa_index():
|
||||
return FileResponse(static_dir / "index.html")
|
||||
|
||||
@app.get("/{full_path:path}", include_in_schema=False)
|
||||
async def spa_fallback(full_path: str):
|
||||
path = static_dir / full_path
|
||||
if path.exists() and path.is_file():
|
||||
return FileResponse(path)
|
||||
return FileResponse(static_dir / "index.html")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
# 为了在使用 PyInstaller 打包时也能正常运行
|
||||
uvicorn.run(app, host="127.0.0.1", port=8000, reload=False)
|
||||
Reference in New Issue
Block a user