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
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()
|
|
|