import uuid from fastapi import APIRouter, Depends, File, Form, Query, Response, UploadFile from fastapi.responses import FileResponse from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_current_user, get_db_session, require_roles from app.schemas.collaboration import ( CollaborationEditorConfigRead, CollaborationExportRecord, CollaborationRevisionRead, CollaborationEditRequestRead, CollaborationEditRequestResolve, CollaborationOwnershipTransferRequest, CollaborationShareLinkRead, CollaborationShareLinkUpdate, CollaborationRevisionUpdate, ) from app.schemas.ledger import ( LedgerAccessSettings, LedgerCandidateRead, LedgerMemberRead, LedgerMemberUpsert, LedgerRead, LedgerRestore, LedgerSettings, ) from app.schemas.onlyoffice import OnlyOfficePreviewConfigRead from app.services import collaboration_service, collaboration_share_service, ledger_service, onlyoffice_collaboration_service, onlyoffice_service def no_store(response: Response): response.headers["Cache-Control"] = "no-store" router = APIRouter(dependencies=[Depends(no_store)]) @router.get("", response_model=list[LedgerRead]) async def list_ledgers(db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): return await ledger_service.list_ledgers(db, user) @router.post("/initialize", response_model=list[LedgerRead]) async def initialize(db: AsyncSession = Depends(get_db_session), user=Depends(require_roles(["ADMIN"]))): return await ledger_service.initialize_ledgers(db, user) @router.get("/{ledger_id}", response_model=LedgerRead) async def read(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user) return await ledger_service.read_ledger(db, item, user) @router.patch("/{ledger_id}", response_model=LedgerRead) async def update(ledger_id: uuid.UUID, payload: LedgerSettings, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await ledger_service.update_settings(db, item, payload, user) @router.post("/{ledger_id}/import", response_model=LedgerRead) async def import_template( ledger_id: uuid.UUID, file: UploadFile = File(...), generation: int = Form(..., ge=1), db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await ledger_service.import_content(db, item, file, generation, user) @router.get("/{ledger_id}/members", response_model=list[LedgerMemberRead]) async def members(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True) return await ledger_service.list_members(db, item) @router.get("/{ledger_id}/candidates", response_model=list[LedgerCandidateRead]) async def candidates( ledger_id: uuid.UUID, keyword: str = Query(default="", max_length=100), db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): await ledger_service.get_ledger(db, ledger_id, user, manage=True) return await ledger_service.candidates(db, keyword) @router.put("/{ledger_id}/members", status_code=204) async def grant( ledger_id: uuid.UUID, payload: LedgerMemberUpsert, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) await ledger_service.set_member(db, item, payload, user) @router.delete("/{ledger_id}/members/{user_id}", status_code=204) async def revoke( ledger_id: uuid.UUID, user_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) await ledger_service.remove_member(db, item, user_id, user) @router.get("/{ledger_id}/editor-config", response_model=CollaborationEditorConfigRead) async def editor_config(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, lock=True) return await onlyoffice_collaboration_service.build_editor_config(db, item, user) @router.patch("/{ledger_id}/access-settings", response_model=LedgerRead) async def access_settings(ledger_id: uuid.UUID, payload: LedgerAccessSettings, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await ledger_service.update_access_settings(db, item, payload, user) @router.get("/{ledger_id}/share-link", response_model=CollaborationShareLinkRead) async def share_link(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await collaboration_share_service.get_share_link(db, item, user) @router.put("/{ledger_id}/share-link", response_model=CollaborationShareLinkRead) async def update_share_link(ledger_id: uuid.UUID, payload: CollaborationShareLinkUpdate, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await collaboration_share_service.update_share_link(db, item, payload, user) @router.post("/{ledger_id}/edit-requests", response_model=CollaborationEditRequestRead) async def request_edit(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, lock=True) return await collaboration_service.create_edit_request(db, item, user) @router.get("/{ledger_id}/edit-requests", response_model=list[CollaborationEditRequestRead]) async def edit_requests(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True) return await collaboration_service.list_edit_requests(db, item, user) @router.post("/{ledger_id}/edit-requests/{request_id}/resolve", response_model=CollaborationEditRequestRead) async def resolve_request(ledger_id: uuid.UUID, request_id: uuid.UUID, payload: CollaborationEditRequestResolve, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await collaboration_service.resolve_edit_request(db, item, request_id, payload, user) @router.post("/{ledger_id}/transfer-ownership", response_model=LedgerRead) async def transfer_owner(ledger_id: uuid.UUID, payload: CollaborationOwnershipTransferRequest, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) item = await collaboration_service.transfer_ownership(db, item, payload, user) return await ledger_service.read_ledger(db, item, user) @router.post("/{ledger_id}/downloads", status_code=204) async def download_check( ledger_id: uuid.UUID, payload: CollaborationExportRecord, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user) await collaboration_service.record_download(db, item, user, payload.file_type) @router.get("/{ledger_id}/revisions", response_model=list[CollaborationRevisionRead]) async def revisions(ledger_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user)): item = await ledger_service.get_ledger(db, ledger_id, user) return await collaboration_service.list_revisions(db, item) @router.post("/{ledger_id}/revisions/{revision_id}/restore", response_model=CollaborationRevisionRead) async def restore( ledger_id: uuid.UUID, revision_id: uuid.UUID, payload: LedgerRestore, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user, manage=True, lock=True) return await ledger_service.restore_revision(db, item, revision_id, payload.generation, user) @router.patch("/{ledger_id}/revisions/{revision_id}", response_model=CollaborationRevisionRead) async def name_revision( ledger_id: uuid.UUID, revision_id: uuid.UUID, payload: CollaborationRevisionUpdate, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user, lock=True) return await collaboration_service.update_revision(db, item, revision_id, payload, user) @router.get("/{ledger_id}/revisions/{revision_id}/download") async def download_revision( ledger_id: uuid.UUID, revision_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user, lock=True) await collaboration_service.require_file_exporter(db, item, user) revision = await collaboration_service.prepare_revision_preview(db, item, revision_id, user) await collaboration_service.record_download(db, item, user, "xlsx") return FileResponse(revision.file_uri, filename=item.title, media_type=revision.mime_type, headers={"Cache-Control": "no-store"}) @router.get("/{ledger_id}/revisions/{revision_id}/preview-config", response_model=OnlyOfficePreviewConfigRead) async def preview( ledger_id: uuid.UUID, revision_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), user=Depends(get_current_user), ): item = await ledger_service.get_ledger(db, ledger_id, user) revision = await collaboration_service.prepare_revision_preview(db, item, revision_id, user) await onlyoffice_service.ensure_onlyoffice_available() return onlyoffice_service.build_preview_config( resource_type="collaboration_revision", resource_id=revision.id, file_name=item.title, file_hash=revision.file_hash, user_id=user.id, user_name=user.full_name, )