197 lines
5.9 KiB
Python
197 lines
5.9 KiB
Python
from dotenv import load_dotenv
|
|
|
|
load_dotenv() # Carica le variabili dal file .env
|
|
"""
|
|
Parking Manager Application
|
|
FastAPI + SQLite + Vanilla JS
|
|
"""
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.responses import FileResponse, RedirectResponse, JSONResponse
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from contextlib import asynccontextmanager
|
|
from slowapi import Limiter, _rate_limit_exceeded_handler
|
|
from slowapi.util import get_remote_address
|
|
from slowapi.errors import RateLimitExceeded
|
|
from datetime import datetime
|
|
|
|
from app import config
|
|
from app.routes.auth import router as auth_router
|
|
from app.routes.users import router as users_router
|
|
from app.routes.offices import router as offices_router
|
|
from app.routes.presence import router as presence_router
|
|
from app.routes.parking import router as parking_router
|
|
from database.connection import init_db
|
|
|
|
# Rate limiter setup
|
|
limiter = Limiter(key_func=get_remote_address)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Initialize database on startup"""
|
|
def log(msg):
|
|
config.logger.info(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {msg}")
|
|
|
|
log("Starting Parking Manager application")
|
|
|
|
# Caddy Integration Logs
|
|
log("--- Caddy Integration & Handshake ---")
|
|
|
|
# Step 1: Auth / Forward Auth
|
|
log("1. Checking Caddy Forward Auth Configuration...")
|
|
status = "ENABLED (Authelia)" if config.AUTHELIA_ENABLED else "DISABLED (Internal Auth)"
|
|
log(f" - Auth Mode: {status}")
|
|
|
|
if config.AUTHELIA_ENABLED:
|
|
log(" - Configuring Trusted Headers from Caddy:")
|
|
log(f" * User: {config.AUTHELIA_HEADER_USER}")
|
|
log(f" * Name: {config.AUTHELIA_HEADER_NAME}")
|
|
log(f" * Email: {config.AUTHELIA_HEADER_EMAIL}")
|
|
log(f" * Groups: {config.AUTHELIA_HEADER_GROUPS}")
|
|
else:
|
|
log(" - No trusted headers configured (Standalone mode)")
|
|
|
|
# Step 2: CORS / Origins
|
|
log("2. Configuring Caddy CORS / Origins...")
|
|
for origin in config.ALLOWED_ORIGINS:
|
|
log(f" - Trusted Origin: {origin}")
|
|
|
|
init_db()
|
|
log("3. Database Connection: ESTABLISHED")
|
|
|
|
# Step 3: Network / Reachability
|
|
local_url = f"http://{config.HOST}:{config.PORT}"
|
|
# Try to find a public URL from allowed origins (excluding localhost/ips)
|
|
public_candidates = [o for o in config.ALLOWED_ORIGINS if "localhost" not in o and "127.0.0.1" not in o and not o.startswith("*")]
|
|
reachable_url = public_candidates[0] if public_candidates else local_url.replace("0.0.0.0", "localhost")
|
|
|
|
log("4. Finalizing Caddy Handshake...")
|
|
log(f" - Listening on: {config.HOST}:{config.PORT}")
|
|
log("--- Handshake Complete ---")
|
|
|
|
log(f"feedback: App reachable via Caddy at {reachable_url}")
|
|
|
|
yield
|
|
log("Shutting down Parking Manager application")
|
|
|
|
|
|
app = FastAPI(title="Parking Manager", version="1.0.0", lifespan=lifespan)
|
|
|
|
# Add rate limiter
|
|
app.state.limiter = limiter
|
|
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
|
|
|
# CORS
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=config.ALLOWED_ORIGINS,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# API Routes
|
|
app.include_router(auth_router)
|
|
app.include_router(users_router)
|
|
app.include_router(offices_router)
|
|
app.include_router(presence_router)
|
|
app.include_router(parking_router)
|
|
from app.routes.reports import router as reports_router
|
|
app.include_router(reports_router)
|
|
|
|
# Static Files
|
|
app.mount("/css", StaticFiles(directory=str(config.FRONTEND_DIR / "css")), name="css")
|
|
app.mount("/js", StaticFiles(directory=str(config.FRONTEND_DIR / "js")), name="js")
|
|
app.mount("/assets", StaticFiles(directory=str(config.FRONTEND_DIR / "assets")), name="assets")
|
|
|
|
|
|
# Page Routes
|
|
@app.get("/")
|
|
async def index():
|
|
"""Landing page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "landing.html")
|
|
|
|
|
|
@app.get("/login")
|
|
async def login_page():
|
|
"""Login page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "login.html")
|
|
|
|
|
|
@app.get("/register")
|
|
async def register_page():
|
|
"""Register page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "register.html")
|
|
|
|
|
|
@app.get("/dashboard")
|
|
async def dashboard():
|
|
"""Dashboard - redirect to team calendar"""
|
|
return RedirectResponse(url="/team-calendar", status_code=302)
|
|
|
|
|
|
@app.get("/presence")
|
|
async def presence_page():
|
|
"""My Presence page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "presence.html")
|
|
|
|
|
|
@app.get("/team-calendar")
|
|
async def team_calendar_page():
|
|
"""Team Calendar page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "team-calendar.html")
|
|
|
|
|
|
@app.get("/team-rules")
|
|
async def team_rules_page():
|
|
"""Team Rules page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "team-rules.html")
|
|
|
|
|
|
@app.get("/admin/users")
|
|
async def admin_users_page():
|
|
"""Admin Users page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "admin-users.html")
|
|
|
|
|
|
@app.get("/admin/offices")
|
|
async def admin_offices_page():
|
|
"""Admin Offices page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "admin-offices.html")
|
|
|
|
|
|
@app.get("/profile")
|
|
async def profile_page():
|
|
"""Profile page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "profile.html")
|
|
|
|
|
|
@app.get("/settings")
|
|
async def settings_page():
|
|
"""Settings page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "settings.html")
|
|
|
|
|
|
@app.get("/parking-settings")
|
|
async def parking_settings_page():
|
|
"""Parking Settings page"""
|
|
return FileResponse(config.FRONTEND_DIR / "pages" / "parking-settings.html")
|
|
|
|
|
|
@app.get("/favicon.svg")
|
|
async def favicon():
|
|
"""Favicon"""
|
|
return FileResponse(config.FRONTEND_DIR / "favicon.svg", media_type="image/svg+xml")
|
|
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
"""Health check"""
|
|
return {"status": "ok"}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run("main:app", host=config.HOST, port=config.PORT, reload=True)
|