152 lines
4.9 KiB
Python
152 lines
4.9 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Literal
|
|
from fastapi import APIRouter, Depends, Query
|
|
from sqlalchemy.orm import Session
|
|
|
|
from govoplan_core.auth import ApiPrincipal, require_scope
|
|
from govoplan_files.backend.schemas import (
|
|
FileFolderCreateRequest,
|
|
FileFolderDeleteRequest,
|
|
FileFolderDeleteResponse,
|
|
FileFolderResponse,
|
|
FileFoldersResponse,
|
|
)
|
|
from govoplan_core.db.session import get_session
|
|
from govoplan_files.backend.storage.paths import UnsafeFilePathError
|
|
from govoplan_files.backend.storage.common import FileStorageError
|
|
from govoplan_files.backend.storage.folders import (
|
|
create_folder,
|
|
list_folders_for_user,
|
|
list_folders_for_user_window,
|
|
soft_delete_folder,
|
|
)
|
|
|
|
|
|
from govoplan_files.backend.route_support import (
|
|
_folder_response,
|
|
_http_error,
|
|
_is_admin,
|
|
)
|
|
from govoplan_files.backend.services.list_queries import (
|
|
FOLDERS_LIST_CURSOR_SCOPE,
|
|
_cursor_page_size,
|
|
_files_delta_watermark,
|
|
_folder_cursor_values,
|
|
_folders_list_fingerprint,
|
|
_next_folder_list_cursor,
|
|
)
|
|
|
|
router = APIRouter(prefix="/files", tags=["files"])
|
|
|
|
|
|
@router.get("/folders", response_model=FileFoldersResponse)
|
|
def list_file_folders(
|
|
owner_type: Literal["user", "group"],
|
|
owner_id: str,
|
|
page_size: int | None = Query(default=None, ge=1, le=1000),
|
|
cursor: str | None = None,
|
|
session: Session = Depends(get_session),
|
|
principal: ApiPrincipal = Depends(require_scope("files:file:read")),
|
|
):
|
|
try:
|
|
watermark = _files_delta_watermark(session, principal.tenant_id)
|
|
effective_page_size = _cursor_page_size(
|
|
FOLDERS_LIST_CURSOR_SCOPE, cursor, page_size
|
|
)
|
|
if effective_page_size is None:
|
|
folders = list_folders_for_user(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
user_id=principal.user.id,
|
|
owner_type=owner_type,
|
|
owner_id=owner_id,
|
|
is_admin=_is_admin(principal),
|
|
)
|
|
return FileFoldersResponse(
|
|
folders=[_folder_response(folder) for folder in folders],
|
|
watermark=watermark,
|
|
)
|
|
fingerprint = _folders_list_fingerprint(
|
|
principal,
|
|
owner_type=owner_type,
|
|
owner_id=owner_id,
|
|
page_size=effective_page_size,
|
|
)
|
|
after_path, after_id = _folder_cursor_values(cursor, fingerprint=fingerprint)
|
|
folders, has_more = list_folders_for_user_window(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
user_id=principal.user.id,
|
|
owner_type=owner_type,
|
|
owner_id=owner_id,
|
|
is_admin=_is_admin(principal),
|
|
page_size=effective_page_size,
|
|
after_path=after_path,
|
|
after_id=after_id,
|
|
)
|
|
return FileFoldersResponse(
|
|
folders=[_folder_response(folder) for folder in folders],
|
|
cursor=cursor,
|
|
next_cursor=_next_folder_list_cursor(
|
|
principal,
|
|
folders,
|
|
owner_type=owner_type,
|
|
owner_id=owner_id,
|
|
page_size=effective_page_size,
|
|
has_more=has_more,
|
|
),
|
|
watermark=watermark,
|
|
)
|
|
except FileStorageError as exc:
|
|
raise _http_error(exc) from exc
|
|
|
|
|
|
@router.post("/folders", response_model=FileFolderResponse)
|
|
def create_file_folder(
|
|
payload: FileFolderCreateRequest,
|
|
session: Session = Depends(get_session),
|
|
principal: ApiPrincipal = Depends(require_scope("files:file:organize")),
|
|
):
|
|
try:
|
|
folder = create_folder(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
owner_type=payload.owner_type,
|
|
owner_id=payload.owner_id,
|
|
user_id=principal.user.id,
|
|
path=payload.path,
|
|
is_admin=_is_admin(principal),
|
|
)
|
|
session.commit()
|
|
return _folder_response(folder)
|
|
except (FileStorageError, UnsafeFilePathError, ValueError) as exc:
|
|
session.rollback()
|
|
raise _http_error(exc) from exc
|
|
|
|
|
|
@router.post("/folders/delete", response_model=FileFolderDeleteResponse)
|
|
def delete_file_folder(
|
|
payload: FileFolderDeleteRequest,
|
|
session: Session = Depends(get_session),
|
|
principal: ApiPrincipal = Depends(require_scope("files:file:delete")),
|
|
):
|
|
try:
|
|
deleted_folders, deleted_files = soft_delete_folder(
|
|
session,
|
|
tenant_id=principal.tenant_id,
|
|
owner_type=payload.owner_type,
|
|
owner_id=payload.owner_id,
|
|
user_id=principal.user.id,
|
|
path=payload.path,
|
|
recursive=payload.recursive,
|
|
is_admin=_is_admin(principal),
|
|
)
|
|
session.commit()
|
|
return FileFolderDeleteResponse(
|
|
deleted_folders=deleted_folders, deleted_files=deleted_files
|
|
)
|
|
except (FileStorageError, UnsafeFilePathError, ValueError) as exc:
|
|
session.rollback()
|
|
raise _http_error(exc) from exc
|