Initial commit: VoIdeaAI - voice-first AI idea assistant

This commit is contained in:
2026-05-13 12:51:42 +03:00
commit 688d043dad
421 changed files with 47915 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
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(),
}