Files

133 lines
4.1 KiB
Python

import asyncio
import os
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from app.api.v1 import api_v1_router
from app.core.config import get_settings
from app.core.database import async_session_maker, engine
from app.core.limiter import limiter
from app.core.logging_middleware import DebugLoggingMiddleware
from app.core.middleware import SecurityHeadersMiddleware
from app.core.seed import seed_database
from app.services.debug_service import DebugService
settings = get_settings()
@asynccontextmanager
async def lifespan(app: FastAPI):
# Seed database on startup (idempotent — skips if data exists)
try:
async with async_session_maker() as db:
await seed_database(db)
except Exception:
pass # DB may not be ready yet, seed will run on next restart
# Auto-cleanup logs on startup
try:
async with async_session_maker() as db:
svc = DebugService(db)
await svc.auto_cleanup_if_needed()
except Exception:
pass
# Sync bot commands on startup
try:
async with async_session_maker() as db:
from app.integrations.telegram.sync_service import TelegramBotSyncService
from app.integrations.telegram.client import TelegramBotClient
bot_client = TelegramBotClient(token=settings.telegram_bot_token)
svc = TelegramBotSyncService(db, bot_client)
await svc.full_sync()
except Exception:
pass
# Auto-run QATesterAgent after fresh deploy (within 5 min)
async def _auto_qa():
try:
deploy_stamp = "/opt/voidea/.last_deploy"
if not os.path.exists(deploy_stamp):
return
with open(deploy_stamp) as f:
ts = int(f.read().strip())
if (datetime.now().timestamp() - ts) > 300:
return # older than 5 min
await asyncio.sleep(3)
async with async_session_maker() as db:
from app.agents.qa_tester_agent import QATesterAgent
agent = QATesterAgent(session=db)
result = await agent.run({"action": "api", "test_count": 2})
if not result.success:
logger = __import__("logging").getLogger("voidea.qa_auto")
logger.warning("Auto-QA after deploy: %s", result.message)
except Exception:
pass
asyncio.create_task(_auto_qa())
yield
await engine.dispose()
app = FastAPI(
title=settings.project_name,
description="VoIdeaAI — идеи рождаются вслух, решения приходят мгновенно!",
version=settings.project_version,
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
lifespan=lifespan,
)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
app.include_router(api_v1_router)
app.mount("/assets", StaticFiles(directory="webui/dist/assets"), name="assets")
app.mount("/icons", StaticFiles(directory="webui/dist/icons"), name="icons")
app.mount("/", StaticFiles(directory="webui/dist", html=True), name="static")
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(DebugLoggingMiddleware)
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health")
@limiter.limit("30/minute")
async def health_check(request: Request):
return {
"status": "healthy",
"version": settings.project_version,
"environment": settings.project_env,
}
@app.get("/api/v1/health")
@limiter.limit("30/minute")
async def api_health(request: Request):
return {
"status": "healthy",
"api_version": "v1",
"database": "connected",
"timestamp": datetime.now(timezone.utc).isoformat(),
}