- Auto pull latest code before deployment - Clean old Docker images before building new ones - Add comprehensive cleanup commands documentation - Add production deployment guide - Fix frontend serving from /var/www/html for production - Preserve build cache for faster deployments Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
136 lines
3.9 KiB
Python
136 lines
3.9 KiB
Python
"""
|
|
Oliver Metadata Tool - FastAPI Backend
|
|
Main application entry point with CORS, middleware, and routers.
|
|
"""
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse, FileResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from contextlib import asynccontextmanager
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from app.api import auth, files, metadata, templates
|
|
from app.api import import_api
|
|
from app.core.redis_client import RedisSessionStore
|
|
from app.core.database import init_db
|
|
|
|
|
|
# Lifespan context manager for startup/shutdown events
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Application lifespan: startup and shutdown logic"""
|
|
# Startup
|
|
print("🚀 Starting Oliver Metadata Tool API...")
|
|
|
|
# Initialize database
|
|
await init_db()
|
|
print("✅ Database initialized")
|
|
|
|
# Initialize Redis
|
|
redis_url = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
|
app.state.redis = RedisSessionStore(redis_url)
|
|
print(f"✅ Redis connected: {redis_url}")
|
|
|
|
yield
|
|
|
|
# Shutdown
|
|
print("👋 Shutting down Oliver Metadata Tool API...")
|
|
await app.state.redis.close()
|
|
|
|
|
|
# Create FastAPI app
|
|
app = FastAPI(
|
|
title="Oliver Metadata Tool API",
|
|
description="Universal metadata creation and management API for files",
|
|
version="4.0.0",
|
|
lifespan=lifespan
|
|
)
|
|
|
|
|
|
# CORS Configuration
|
|
# Allow React frontend to make requests from different origin
|
|
origins = [
|
|
"http://localhost:3000", # React dev server
|
|
"http://localhost:5173", # Vite dev server
|
|
"http://localhost:80", # Production frontend
|
|
os.getenv("FRONTEND_URL", ""), # Custom frontend URL from env
|
|
]
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
# Include routers
|
|
app.include_router(auth.router, prefix="/auth", tags=["auth"])
|
|
app.include_router(files.router, prefix="/files", tags=["files"])
|
|
app.include_router(metadata.router, prefix="/metadata", tags=["metadata"])
|
|
app.include_router(templates.router, prefix="/templates", tags=["templates"])
|
|
app.include_router(import_api.router, prefix="/import", tags=["import"])
|
|
|
|
|
|
# Serve React frontend
|
|
# Try production path first, fallback to local frontend/dist
|
|
FRONTEND_DIR = os.getenv("FRONTEND_DIR", "/var/www/html/solventum-image-metadata")
|
|
STATIC_DIR = Path(FRONTEND_DIR) if Path(FRONTEND_DIR).exists() else Path(__file__).parent.parent.parent / "frontend" / "dist"
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
"""Serve React index.html or API info"""
|
|
if STATIC_DIR.exists() and (STATIC_DIR / "index.html").exists():
|
|
return FileResponse(str(STATIC_DIR / "index.html"))
|
|
else:
|
|
return {
|
|
"name": "Oliver Metadata Tool API",
|
|
"version": "4.0.0",
|
|
"status": "running",
|
|
"docs": "/docs",
|
|
"redoc": "/redoc"
|
|
}
|
|
|
|
# Mount static files for React assets
|
|
if STATIC_DIR.exists():
|
|
app.mount("/assets", StaticFiles(directory=str(STATIC_DIR / "assets")), name="assets")
|
|
|
|
|
|
# Health check endpoint
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""Health check endpoint for Docker/K8s"""
|
|
return {
|
|
"status": "healthy",
|
|
"database": "connected", # Will check actual DB later
|
|
"redis": "connected" # Will check actual Redis later
|
|
}
|
|
|
|
|
|
# Global exception handler
|
|
@app.exception_handler(Exception)
|
|
async def global_exception_handler(request, exc):
|
|
"""Handle all uncaught exceptions"""
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={
|
|
"error": "Internal server error",
|
|
"detail": str(exc) if os.getenv("DEBUG") == "true" else "An error occurred"
|
|
}
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
# Run with: python -m app.main
|
|
uvicorn.run(
|
|
"app.main:app",
|
|
host="0.0.0.0",
|
|
port=8000,
|
|
reload=True, # Auto-reload on code changes
|
|
log_level="info"
|
|
)
|