Spaces:
Sleeping
Sleeping
from fastapi import FastAPI, Request, HTTPException | |
from fastapi.staticfiles import StaticFiles | |
from fastapi.templating import Jinja2Templates | |
from .routes import invoices | |
from app.db.database import init_db, rate_limiter | |
import os | |
from fastapi.middleware.cors import CORSMiddleware | |
from starlette.middleware.base import BaseHTTPMiddleware | |
from dotenv import load_dotenv | |
import logging | |
from typing import Callable | |
import time | |
# Set up logging | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
# Load environment variables | |
load_dotenv() | |
# Get the absolute path to the app directory | |
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
class RateLimitMiddleware(BaseHTTPMiddleware): | |
async def dispatch(self, request: Request, call_next: Callable): | |
# Get client IP | |
client_ip = request.client.host | |
# Check rate limit | |
if not rate_limiter.is_allowed(client_ip): | |
logger.warning(f"Rate limit exceeded for IP: {client_ip}") | |
raise HTTPException( | |
status_code=429, | |
detail="Too many requests. Please try again later." | |
) | |
# Process request | |
start_time = time.time() | |
response = await call_next(request) | |
process_time = time.time() - start_time | |
# Log request details | |
logger.info( | |
f"Request: {request.method} {request.url.path} " | |
f"Client: {client_ip} " | |
f"Process time: {process_time:.2f}s" | |
) | |
return response | |
app = FastAPI( | |
title="Invoice Generator", | |
description="API for generating invoices", | |
version="1.0.0" | |
) | |
# Add rate limiting middleware | |
app.add_middleware(RateLimitMiddleware) | |
# Configure CORS with more specific settings | |
app.add_middleware( | |
CORSMiddleware, | |
allow_origins=["*"], # In production, replace with specific domains | |
allow_credentials=True, | |
allow_methods=["*"], | |
allow_headers=["*"], | |
max_age=3600, # Cache preflight requests for 1 hour | |
) | |
# Mount static files with absolute path | |
app.mount("/static", StaticFiles(directory=os.path.join(BASE_DIR, "app/static")), name="static") | |
# Templates with absolute path | |
templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "app/templates")) | |
# Include routers | |
app.include_router(invoices.router) | |
async def startup_event(): | |
logger.info("Starting application...") | |
await init_db() | |
logger.info("Application started successfully") | |
async def shutdown_event(): | |
logger.info("Shutting down application...") | |
# Root endpoint to serve the HTML page | |
async def root(request: Request): | |
return templates.TemplateResponse("index.html", {"request": request}) | |
# Health check endpoint | |
async def health_check(): | |
return {"status": "healthy", "timestamp": time.time()} | |
async def history_page(request: Request): | |
return templates.TemplateResponse("history.html", {"request": request}) | |