from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware from starlette.middleware.base import BaseHTTPMiddleware from app.database import engine, Base from app.routers import models from app.migrate import run_migrations import os # Create tables and run migrations Base.metadata.create_all(bind=engine) run_migrations() app = FastAPI(title="PrintForge", version="2.2.0") # CORS — allow production domain and local development app.add_middleware( CORSMiddleware, allow_origins=[ "https://3d.consultoria-as.com", "http://localhost:8000", "http://127.0.0.1:8000", ], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Security headers middleware class SecurityHeadersMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): response = await call_next(request) response.headers["X-Content-Type-Options"] = "nosniff" response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" # Permissive CSP: allow same-origin, CDN scripts, inline styles, images from anywhere response.headers["Content-Security-Policy"] = ( "default-src * 'self' data: blob: 'unsafe-inline' 'unsafe-eval'; " "script-src * 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net; " "style-src * 'self' 'unsafe-inline'; " "img-src * 'self' data: blob:; " "font-src * 'self' data:; " "connect-src * 'self';" ) return response app.add_middleware(SecurityHeadersMiddleware) app.include_router(models.router) # Serve static files app.mount("/static", StaticFiles(directory="static"), name="static") # Serve uploads, thumbnails and images directly app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads") app.mount("/thumbnails", StaticFiles(directory="thumbnails"), name="thumbnails") app.mount("/images", StaticFiles(directory="images"), name="images") @app.get("/") def root(): return FileResponse("static/index.html") @app.get("/upload") def upload_page(): return FileResponse("static/upload.html") @app.get("/model/{model_id}") def detail_page(model_id: int): return FileResponse("static/detail.html")