baton/backend/main.py
2026-03-20 20:50:31 +02:00

122 lines
3.4 KiB
Python

from __future__ import annotations
import asyncio
import logging
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from typing import Any
from fastapi import Depends, FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from backend import config, db, telegram
from backend.middleware import verify_webhook_secret
from backend.models import (
RegisterRequest,
RegisterResponse,
SignalRequest,
SignalResponse,
)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# aggregator = telegram.SignalAggregator(interval=10) # v2.0 feature — отключено в v1 (ADR-004)
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
await db.init_db()
logger.info("Database initialized")
await telegram.set_webhook(url=config.WEBHOOK_URL, secret=config.WEBHOOK_SECRET)
logger.info("Webhook registered")
# v2.0 feature — агрегатор отключён в v1 (ADR-004)
# task = asyncio.create_task(aggregator.run())
# logger.info("Aggregator started")
yield
# Shutdown
# aggregator.stop()
# await aggregator.flush()
# task.cancel()
# try:
# await task
# except asyncio.CancelledError:
# pass
# logger.info("Aggregator stopped, final flush done")
app = FastAPI(lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=[config.FRONTEND_ORIGIN],
allow_methods=["POST"],
allow_headers=["Content-Type"],
)
@app.post("/api/register", response_model=RegisterResponse)
async def register(body: RegisterRequest) -> RegisterResponse:
result = await db.register_user(uuid=body.uuid, name=body.name)
return RegisterResponse(user_id=result["user_id"], uuid=result["uuid"])
@app.post("/api/signal", response_model=SignalResponse)
async def signal(body: SignalRequest) -> SignalResponse:
geo = body.geo
lat = geo.lat if geo else None
lon = geo.lon if geo else None
accuracy = geo.accuracy if geo else None
signal_id = await db.save_signal(
user_uuid=body.user_id,
timestamp=body.timestamp,
lat=lat,
lon=lon,
accuracy=accuracy,
)
user_name = await db.get_user_name(body.user_id)
ts = datetime.fromtimestamp(body.timestamp / 1000, tz=timezone.utc)
name = user_name or body.user_id[:8]
geo_info = (
f"📍 {lat}, {lon}{accuracy}м)"
if geo
else "Без геолокации"
)
text = (
f"🚨 Сигнал от {name}\n"
f"{ts.strftime('%H:%M:%S')} UTC\n"
f"{geo_info}"
)
await telegram.send_message(text)
return SignalResponse(status="ok", signal_id=signal_id)
@app.post("/api/webhook/telegram")
async def webhook_telegram(
request: Request,
_: None = Depends(verify_webhook_secret),
) -> dict[str, Any]:
update = await request.json()
message = update.get("message", {})
text = message.get("text", "")
if text.startswith("/start"):
tg_user = message.get("from", {})
tg_user_id = str(tg_user.get("id", ""))
first_name = tg_user.get("first_name", "")
last_name = tg_user.get("last_name", "")
name = (first_name + " " + last_name).strip() or tg_user_id
if tg_user_id:
await db.register_user(uuid=tg_user_id, name=name)
logger.info("Telegram /start: registered user %s", tg_user_id)
return {"ok": True}