2026-04-08 19:32:23 +08:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
2026-04-08 19:54:18 +08:00
|
|
|
|
import asyncio
|
2026-04-08 19:32:23 +08:00
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
|
|
|
|
|
|
|
|
from fastapi import FastAPI
|
|
|
|
|
|
|
2026-04-09 11:54:30 +08:00
|
|
|
|
from app.logging_config import setup_logging
|
2026-04-10 18:16:15 +08:00
|
|
|
|
from app.media_static import MediaStaticFiles
|
2026-04-09 11:54:30 +08:00
|
|
|
|
from app.db import init_db
|
2026-04-08 19:32:23 +08:00
|
|
|
|
from app.routers import biomass, ingest
|
2026-04-08 19:54:18 +08:00
|
|
|
|
from app.services.action_watch import run_action_watch_loop
|
2026-04-08 20:35:55 +08:00
|
|
|
|
from app.services.measure_watch import run_measure_watch_loop
|
2026-04-08 19:32:23 +08:00
|
|
|
|
from app.settings import get_settings
|
|
|
|
|
|
|
2026-04-09 11:54:30 +08:00
|
|
|
|
setup_logging()
|
|
|
|
|
|
|
2026-04-08 19:32:23 +08:00
|
|
|
|
|
|
|
|
|
|
@asynccontextmanager
|
|
|
|
|
|
async def lifespan(app: FastAPI):
|
2026-04-09 11:54:30 +08:00
|
|
|
|
setup_logging()
|
2026-04-08 19:32:23 +08:00
|
|
|
|
s = get_settings()
|
2026-04-09 11:54:30 +08:00
|
|
|
|
init_db(s)
|
2026-04-08 19:32:23 +08:00
|
|
|
|
s.media_root.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
s.stream_tmp_dir.mkdir(parents=True, exist_ok=True)
|
2026-04-08 20:35:55 +08:00
|
|
|
|
tasks: list[asyncio.Task[None]] = []
|
2026-04-08 19:54:18 +08:00
|
|
|
|
if s.action_watch_dir is not None:
|
2026-04-08 20:35:55 +08:00
|
|
|
|
tasks.append(asyncio.create_task(run_action_watch_loop(s)))
|
|
|
|
|
|
if s.measure_watch_dir is not None:
|
|
|
|
|
|
tasks.append(asyncio.create_task(run_measure_watch_loop(s)))
|
2026-04-08 19:32:23 +08:00
|
|
|
|
yield
|
2026-04-08 20:35:55 +08:00
|
|
|
|
for t in tasks:
|
|
|
|
|
|
t.cancel()
|
|
|
|
|
|
for t in tasks:
|
2026-04-08 19:54:18 +08:00
|
|
|
|
try:
|
2026-04-08 20:35:55 +08:00
|
|
|
|
await t
|
2026-04-08 19:54:18 +08:00
|
|
|
|
except asyncio.CancelledError:
|
|
|
|
|
|
pass
|
2026-04-08 19:32:23 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app = FastAPI(title="Fish API", lifespan=lifespan)
|
|
|
|
|
|
app.include_router(ingest.router)
|
|
|
|
|
|
app.include_router(biomass.router)
|
|
|
|
|
|
|
|
|
|
|
|
_settings = get_settings()
|
|
|
|
|
|
_settings.media_root.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
app.mount(
|
|
|
|
|
|
"/media",
|
2026-04-10 18:16:15 +08:00
|
|
|
|
MediaStaticFiles(directory=str(_settings.media_root)),
|
2026-04-08 19:32:23 +08:00
|
|
|
|
name="media",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
|
|
async def root():
|
|
|
|
|
|
return {
|
|
|
|
|
|
"service": "fish-api",
|
|
|
|
|
|
"docs": "/docs",
|
|
|
|
|
|
"ingest": "/api/v1/ingest/",
|
|
|
|
|
|
"biomass_camera": "/api/v1/biomass/real/camera/",
|
|
|
|
|
|
"biomass_health": "/api/v1/biomass/health/result/",
|
2026-04-08 20:35:55 +08:00
|
|
|
|
"note": "若配置了 ACTION_WATCH_DIR / MEASURE_WATCH_DIR,启动后会后台监控对应目录。",
|
2026-04-08 19:32:23 +08:00
|
|
|
|
}
|