kin: BATON-ARCH-012 Добавить WEBHOOK_ENABLED флаг для локальной разработки

This commit is contained in:
Gros Frumos 2026-03-20 21:03:45 +02:00
parent 69d01ac3a6
commit 0f8ecdfc49
5 changed files with 434 additions and 0 deletions

View file

@ -2,10 +2,12 @@ from __future__ import annotations
import asyncio
import logging
import time
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from typing import Any
import httpx
from fastapi import Depends, FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
@ -24,6 +26,21 @@ logger = logging.getLogger(__name__)
# aggregator = telegram.SignalAggregator(interval=10) # v2.0 feature — отключено в v1 (ADR-004)
_KEEPALIVE_INTERVAL = 600 # 10 минут
async def _keep_alive_loop(app_url: str) -> None:
"""Периодически пингует /health чтобы предотвратить cold start на бесплатных хостингах."""
health_url = f"{app_url.rstrip('/')}/health"
async with httpx.AsyncClient(timeout=10.0) as client:
while True:
await asyncio.sleep(_KEEPALIVE_INTERVAL)
try:
resp = await client.get(health_url)
logger.info("Keep-alive ping %s%d", health_url, resp.status_code)
except Exception as exc:
logger.warning("Keep-alive ping failed: %s", exc)
@asynccontextmanager
async def lifespan(app: FastAPI):
@ -39,9 +56,24 @@ async def lifespan(app: FastAPI):
# task = asyncio.create_task(aggregator.run())
# logger.info("Aggregator started")
keepalive_task: asyncio.Task | None = None
if config.APP_URL:
keepalive_task = asyncio.create_task(_keep_alive_loop(config.APP_URL))
logger.info("Keep-alive task started (target: %s/health)", config.APP_URL)
else:
logger.info("APP_URL not set — keep-alive disabled")
yield
# Shutdown
if keepalive_task is not None:
keepalive_task.cancel()
try:
await keepalive_task
except asyncio.CancelledError:
pass
logger.info("Keep-alive task stopped")
# aggregator.stop()
# await aggregator.flush()
# task.cancel()
@ -62,6 +94,11 @@ app.add_middleware(
)
@app.get("/health")
async def health() -> dict[str, Any]:
return {"status": "ok", "timestamp": int(time.time())}
@app.post("/api/register", response_model=RegisterResponse)
async def register(body: RegisterRequest) -> RegisterResponse:
result = await db.register_user(uuid=body.uuid, name=body.name)