diff --git a/app/core/session_middleware.py b/app/core/session_middleware.py index 63f18dc..b272e77 100644 --- a/app/core/session_middleware.py +++ b/app/core/session_middleware.py @@ -5,9 +5,8 @@ from fastapi.responses import JSONResponse from starlette.middleware.base import BaseHTTPMiddleware from starlette.types import ASGIApp -from app.core.database import get_session +from app.core.config import settings from app.models.session import Session -from app.repositories.session_repository import SessionRepository logger = logging.getLogger(__name__) @@ -20,6 +19,10 @@ class SessionMiddleware(BaseHTTPMiddleware): self.cookie_name = cookie_name async def dispatch(self, request: Request, call_next): + # В режиме landing пропускаем работу с БД + if settings.app_mode == "landing": + return await call_next(request) + # Пропускаем статические файлы, служебные эндпоинты и OPTIONS запросы if ( request.url.path.startswith( @@ -37,6 +40,10 @@ class SessionMiddleware(BaseHTTPMiddleware): session_obj = None try: + # Ленивый импорт для избежания загрузки database.py в landing режиме + from app.core.database import get_session + from app.repositories.session_repository import SessionRepository + # Работаем с БД в рамках одной async сессии async for db_session in get_session(): session_repo = SessionRepository(db_session) diff --git a/docker-compose.landing.yml b/docker-compose.landing.yml new file mode 100644 index 0000000..eb8b81a --- /dev/null +++ b/docker-compose.landing.yml @@ -0,0 +1,56 @@ +# Docker Compose for Landing Mode +# Usage: docker-compose -f docker-compose.landing.yml up -d +# Requires APP_MODE=landing in .env + +services: + # HR AI Backend (landing mode - no DB required) + backend: + image: cr.yandex/crpnbn7b142t058unrn0/hr-ai-backend:latest + expose: + - "8000" + env_file: + - .env + environment: + - APP_MODE=landing + - APP_ENV=production + - DEBUG=false + command: ["uv", "run", "fastapi", "run", "main.py", "--host", "0.0.0.0", "--port", "8000"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + restart: unless-stopped + + # Frontend + frontend: + image: cr.yandex/crpnbn7b142t058unrn0/hr-ai-frontend:latest + platform: linux/amd64 + expose: + - "3000" + environment: + - NODE_ENV=production + restart: unless-stopped + volumes: + - ./.env.local:/app/.env.local:ro + + # Caddy reverse proxy with automatic HTTPS + caddy: + image: caddy:2-alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + depends_on: + - backend + - frontend + environment: + - DOMAIN=${DOMAIN:-localhost} + restart: unless-stopped + +volumes: + caddy_data: + caddy_config: diff --git a/main.py b/main.py index ed17b8d..fc65d47 100644 --- a/main.py +++ b/main.py @@ -7,18 +7,28 @@ from fastapi.middleware.cors import CORSMiddleware from app.core.config import settings from app.core.mode_middleware import LandingModeMiddleware -from app.core.session_middleware import SessionMiddleware -from app.routers import resume_router, vacancy_router -from app.routers.admin_router import router as admin_router -from app.routers.analysis_router import router as analysis_router from app.routers.contact_router import router as contact_router -from app.routers.interview_reports_router import router as interview_report_router -from app.routers.interview_router import router as interview_router -from app.routers.session_router import router as session_router + +# В режиме landing не загружаем роутеры, требующие БД +if settings.app_mode != "landing": + from app.core.session_middleware import SessionMiddleware + from app.routers import resume_router, vacancy_router + from app.routers.admin_router import router as admin_router + from app.routers.analysis_router import router as analysis_router + from app.routers.interview_reports_router import router as interview_report_router + from app.routers.interview_router import router as interview_router + from app.routers.session_router import router as session_router @asynccontextmanager async def lifespan(app: FastAPI): + # В режиме landing агент не нужен + if settings.app_mode == "landing": + print("[STARTUP] Landing mode - skipping AI Agent startup") + yield + print("[SHUTDOWN] Landing mode - no AI Agent to stop") + return + # Запускаем AI агента при старте приложения from app.services.agent_manager import agent_manager @@ -56,21 +66,23 @@ app.add_middleware( allow_headers=["*"], ) -# Добавляем middleware для управления сессиями (после CORS) -app.add_middleware(SessionMiddleware, cookie_name="session_id") - # Добавляем middleware для режима лендинга (блокирует API в режиме landing) app.add_middleware(LandingModeMiddleware) -app.include_router(vacancy_router, prefix="/api/v1") -app.include_router(resume_router, prefix="/api/v1") -app.include_router(session_router, prefix="/api/v1") -app.include_router(interview_router, prefix="/api/v1") -app.include_router(analysis_router, prefix="/api/v1") -app.include_router(admin_router, prefix="/api/v1") -app.include_router(interview_report_router, prefix="/api/v1") +# Contact router доступен всегда (не требует БД) app.include_router(contact_router, prefix="/api/v1") +# Остальные роутеры и session middleware только в app режиме +if settings.app_mode != "landing": + app.add_middleware(SessionMiddleware, cookie_name="session_id") + app.include_router(vacancy_router, prefix="/api/v1") + app.include_router(resume_router, prefix="/api/v1") + app.include_router(session_router, prefix="/api/v1") + app.include_router(interview_router, prefix="/api/v1") + app.include_router(analysis_router, prefix="/api/v1") + app.include_router(admin_router, prefix="/api/v1") + app.include_router(interview_report_router, prefix="/api/v1") + @app.get("/") async def root():