94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
from contextlib import asynccontextmanager
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import RedirectResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from starlette.types import Scope
|
|
|
|
from config import UPLOAD_DIR, ENVIRONMENT
|
|
from database.schema import create_tables
|
|
from routers import documents, review, search, upload, auth
|
|
from services.auth_service import get_session, cleanup_expired_sessions
|
|
|
|
|
|
Path(UPLOAD_DIR).mkdir(parents=True, exist_ok=True)
|
|
Path("data").mkdir(exist_ok=True)
|
|
|
|
|
|
class CachedStaticFiles(StaticFiles):
|
|
"""StaticFiles that sets a long Cache-Control header so browsers
|
|
don't re-download the same image on every refresh."""
|
|
|
|
def __init__(self, *args, max_age: int = 86400, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self._max_age = max_age
|
|
|
|
async def get_response(self, path: str, scope: Scope):
|
|
response = await super().get_response(path, scope)
|
|
if response.status_code == 200:
|
|
response.headers["Cache-Control"] = f"public, max-age={self._max_age}"
|
|
return response
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
create_tables()
|
|
try:
|
|
cleanup_expired_sessions()
|
|
except Exception:
|
|
pass
|
|
# Resume any documents that were left in 'pending' state from a previous run
|
|
# (e.g. server restart killed the background extraction task).
|
|
try:
|
|
import asyncio
|
|
from database.connection import get_db
|
|
from services.extractor import get_default_provider
|
|
from routers.upload import _extract_and_save
|
|
|
|
default_provider = get_default_provider()
|
|
with get_db() as conn:
|
|
stuck = conn.execute(
|
|
"SELECT id, image_path, provider FROM documents WHERE status='pending'"
|
|
).fetchall()
|
|
for row in stuck:
|
|
provider = row["provider"] or default_provider
|
|
asyncio.create_task(_extract_and_save(row["id"], row["image_path"], provider))
|
|
except Exception:
|
|
pass
|
|
yield
|
|
|
|
|
|
# Disable docs/openapi in production
|
|
if ENVIRONMENT == "production":
|
|
app = FastAPI(title="Lebanese Real Estate Registry", docs_url=None, redoc_url=None, openapi_url=None, lifespan=lifespan)
|
|
else:
|
|
app = FastAPI(title="Lebanese Real Estate Registry", lifespan=lifespan)
|
|
|
|
@app.middleware("http")
|
|
async def check_authentication(request: Request, call_next):
|
|
path = request.url.path
|
|
allowed_paths = ["/auth/login", "/static"]
|
|
is_allowed = any(path.startswith(p) for p in allowed_paths)
|
|
|
|
request.state.user = None
|
|
if not is_allowed:
|
|
session_id = request.cookies.get("session_id")
|
|
user = get_session(session_id) if session_id else None
|
|
if not user:
|
|
return RedirectResponse(url="/auth/login", status_code=303)
|
|
request.state.user = user
|
|
|
|
response = await call_next(request)
|
|
return response
|
|
|
|
|
|
app.mount("/uploads", CachedStaticFiles(directory=UPLOAD_DIR, max_age=604800), name="uploads")
|
|
app.mount("/static", CachedStaticFiles(directory="static", max_age=86400), name="static")
|
|
|
|
app.include_router(upload.router)
|
|
app.include_router(review.router)
|
|
app.include_router(search.router)
|
|
app.include_router(documents.router)
|
|
app.include_router(auth.router, prefix="/auth")
|