You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

110 lines
3.7 KiB

from pathlib import Path
from contextlib import asynccontextmanager
import anyio
from fastapi import Depends, FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from app.api.router import api_router
from app.api.dependencies import require_current_user
from app.api.routes.tus_uploads import router as tus_upload_router
from app.web.routes import router as web_router
from app.core.config import get_settings
from app.core.exceptions import AppError
from app.db.session import create_session_factory
from app.services.directory_service import DirectoryService
from app.services.auth_service import AuthService
from app.services.job_service import JobService
from app.services.local_storage_service import LocalStorageService
from app.services.policy_service import PolicyService
from app.services.storage_service import StorageService
from app.services.upload_service import UploadService
WEB_DIST_DIR = Path(__file__).resolve().parent / "web" / "dist"
@asynccontextmanager
async def lifespan(_app: FastAPI):
session_factory = create_session_factory()
async with session_factory() as session:
service = DirectoryService(session)
await service.ensure_root_directory()
auth_service = AuthService(session)
await auth_service.ensure_bootstrap_user()
upload_service = UploadService(session)
await upload_service.ensure_temp_dir()
local_storage_service = LocalStorageService(session)
await local_storage_service.ensure_backend_ready()
policy_service = PolicyService(session)
await policy_service.ensure_default_policies()
storage_service = StorageService(session)
await storage_service.ensure_cache_dir()
settings = get_settings()
async def _job_loop() -> None:
while True:
async with session_factory() as session:
service = JobService(session)
await service.run_pending_jobs(limit=settings.job_batch_size)
await anyio.sleep(settings.job_poll_interval_seconds)
async with anyio.create_task_group() as task_group:
task_group.start_soon(_job_loop)
yield
task_group.cancel_scope.cancel()
def create_app() -> FastAPI:
settings = get_settings()
app = FastAPI(
title=settings.app_name,
debug=settings.debug,
version="0.1.0",
lifespan=lifespan,
)
@app.exception_handler(AppError)
async def app_error_handler(_request: Request, exc: AppError) -> JSONResponse:
status_code = 400
if exc.code.endswith("_not_found") or exc.code == "not_found":
status_code = 404
elif exc.code.endswith("_conflict") or exc.code == "conflict":
status_code = 409
elif exc.code == "unauthorized" or exc.code.startswith("auth_") or exc.code == "authentication_failed":
status_code = 401
elif exc.code == "range_not_satisfiable":
status_code = 416
return JSONResponse(
status_code=status_code,
content={
"error": {
"code": exc.code,
"message": exc.detail,
"details": None,
}
},
)
app.include_router(api_router, prefix=settings.api_prefix)
app.include_router(web_router)
app.include_router(
tus_upload_router,
prefix="/files",
tags=["tus"],
dependencies=[Depends(require_current_user)],
)
if (WEB_DIST_DIR / "assets").exists():
app.mount(
"/assets",
StaticFiles(directory=WEB_DIST_DIR / "assets"),
name="web-assets",
)
return app
app = create_app()