Files
srbhr--resume-matcher/apps/backend/app/main.py
T
wehub-resource-sync 5bdf4cc89a
Publish Docker Image / publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:36 +08:00

123 lines
3.4 KiB
Python

"""FastAPI application entry point."""
import asyncio
import logging
import sys
from contextlib import asynccontextmanager
from fastapi import FastAPI
# Fix for Windows: Use ProactorEventLoop for subprocess support (Playwright)
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
logger = logging.getLogger(__name__)
from fastapi.middleware.cors import CORSMiddleware
from app import __version__
from app.config import settings
from app.database import db
from app.pdf import close_pdf_renderer, init_pdf_renderer
from app.routers import (
applications_router,
config_router,
enrichment_router,
health_router,
jobs_router,
resume_wizard_router,
resumes_router,
)
def _configure_application_logging() -> None:
"""Set application log level from configuration."""
numeric_level = getattr(logging, settings.log_level, logging.INFO)
logging.getLogger("app").setLevel(numeric_level)
_configure_application_logging()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan manager."""
# Startup
settings.data_dir.mkdir(parents=True, exist_ok=True)
# Import a legacy TinyDB database into SQLite if present (idempotent).
# Fail-fast on error: starting with an empty DB would look like data loss.
from app.scripts.migrate_tinydb_to_sqlite import migrate as migrate_tinydb
result = await migrate_tinydb()
if result.get("status") == "migrated":
logger.info("Startup data migration: %s", result)
# Fold any legacy plaintext API keys into the encrypted store (idempotent,
# non-clobbering), then strip them from config.json.
from app.config import migrate_legacy_keys
migrate_legacy_keys()
# PDF renderer uses lazy initialization - will initialize on first use
# await init_pdf_renderer()
yield
# Shutdown - wrap each cleanup in try-except to ensure all resources are released
try:
await close_pdf_renderer()
except Exception as e:
logger.error(f"Error closing PDF renderer: {e}")
try:
await db.close()
except Exception as e:
logger.error(f"Error closing database: {e}")
app = FastAPI(
title="Resume Matcher API",
description="AI-powered resume tailoring for job descriptions",
version=__version__,
lifespan=lifespan,
)
# CORS middleware - origins configurable via CORS_ORIGINS env var
app.add_middleware(
CORSMiddleware,
allow_origins=settings.effective_cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include routers
app.include_router(health_router, prefix="/api/v1")
app.include_router(config_router, prefix="/api/v1")
app.include_router(resumes_router, prefix="/api/v1")
app.include_router(jobs_router, prefix="/api/v1")
app.include_router(enrichment_router, prefix="/api/v1")
app.include_router(applications_router, prefix="/api/v1")
app.include_router(resume_wizard_router, prefix="/api/v1")
@app.get("/")
async def root():
"""Root endpoint."""
return {
"name": "Resume Matcher API",
"version": __version__,
"docs": "/docs",
}
def main():
"""Entry point for the project.scripts console script."""
import uvicorn
uvicorn.run(
"app.main:app",
host=settings.host,
port=settings.port,
reload=settings.reload,
)
if __name__ == "__main__":
main()