feat: govern file lifecycle and connector writes

This commit is contained in:
2026-08-20 22:06:57 +02:00
parent 95aef18955
commit 6c3cf1c55e
27 changed files with 2721 additions and 65 deletions
+128
View File
@@ -295,6 +295,7 @@ export type FileConnectorProvider = {
installed: boolean;
browse_supported: boolean;
import_supported: boolean;
write_supported: boolean;
optional_dependency?: string | null;
permission_model: string;
sync_strategy: string;
@@ -343,6 +344,7 @@ export type FileConnectorSpacePayload = {
library_id?: string | null;
remote_path?: string;
sync_mode?: "manual";
read_only?: boolean;
metadata?: Record<string, unknown>;
};
@@ -361,6 +363,10 @@ export type ManagedFile = {
created_at: string;
updated_at: string;
deleted_at?: string | null;
retained_until?: string | null;
legal_hold?: boolean;
lifecycle_revision?: number;
lifecycle_reason?: string | null;
audit_relevant: boolean;
metadata?: Record<string, unknown> | null;
source_provenance?: FileSourceProvenance | null;
@@ -420,6 +426,22 @@ export type FileConnectorSyncResponse = {
previous_version_id?: string | null;
current_version_id: string;
};
export type FileConnectorWritePayload = {
file_id: string;
remote_path: string;
idempotency_key: string;
expected_revision?: string | null;
};
export type FileConnectorWriteResponse = {
recovery_operation_id: string;
status: string;
replayed: boolean;
provider: string;
remote_path: string;
revision?: string | null;
checksum_sha256: string;
size_bytes: number;
};
export type FileFolder = {
id: string;
tenant_id: string;
@@ -434,7 +456,49 @@ export type FileFoldersResponse = {folders: FileFolder[];cursor?: string | null;
const DEFAULT_MANAGED_FILE_WINDOW_SIZE = 500;
export type FolderDeleteResponse = {deleted_folders: number;deleted_files: number;};
export type FolderRestoreResponse = {restored_folders: number;restored_files: number;};
export type BulkDeleteResponse = {deleted_count: number;};
export type FileRestoreResponse = {restored_count: number;};
export type FileLifecycleUpdatePayload = {
retained_until?: string | null;
legal_hold: boolean;
reason: string;
expected_revision: number;
};
export type FilePurgePreviewItem = {
file_id: string;
filename: string;
lifecycle_revision: number;
deleted_at?: string | null;
retained_until?: string | null;
legal_hold: boolean;
blockers: string[];
blob_ids: string[];
};
export type FilePurgePreviewResponse = {
preview_sha256: string;
eligible: boolean;
items: FilePurgePreviewItem[];
};
export type FilePurgeExecutePayload = {
file_ids: string[];
preview_sha256: string;
idempotency_key: string;
approval_reference: string;
confirmation: "PURGE";
};
export type FilePurgeResponse = {
recovery_operation_id: string;
status: string;
replayed: boolean;
purged_files: number;
released_blobs: number;
};
export type FileBlobGcResponse = {
inspected_blobs: number;
deleted_blobs: number;
unresolved_operation_ids: string[];
};
export type RenameResponse = {dry_run: boolean;items: {kind: "file" | "folder";id: string;file_id?: string | null;folder_path?: string | null;old_path: string;new_path: string;}[];};
export type TransferResponse = {operation: "move" | "copy";files: number;folders: number;};
export type ConflictAction = "overwrite" | "rename" | "skip";
@@ -748,6 +812,55 @@ export function bulkDeleteFiles(settings: ApiSettings, fileIds: string[]): Promi
return apiFetch<BulkDeleteResponse>(settings, "/api/v1/files/bulk-delete", { method: "POST", body: JSON.stringify({ file_ids: fileIds }) });
}
export function updateFileLifecycle(
settings: ApiSettings,
fileId: string,
payload: FileLifecycleUpdatePayload)
: Promise<ManagedFile> {
return apiFetch<ManagedFile>(settings, `/api/v1/files/${encodeURIComponent(fileId)}/lifecycle`, {
method: "PATCH",
body: JSON.stringify(payload)
});
}
export function restoreFile(settings: ApiSettings, fileId: string): Promise<FileRestoreResponse> {
return apiFetch<FileRestoreResponse>(settings, `/api/v1/files/assets/${encodeURIComponent(fileId)}/restore`, { method: "POST" });
}
export function restoreFolder(
settings: ApiSettings,
payload: {owner_type: "user" | "group";owner_id: string;path: string;recursive?: boolean;})
: Promise<FolderRestoreResponse> {
return apiFetch<FolderRestoreResponse>(settings, "/api/v1/files/folders/restore", {
method: "POST",
body: JSON.stringify({ recursive: true, ...payload })
});
}
export function previewFilePurge(settings: ApiSettings, fileIds: string[]): Promise<FilePurgePreviewResponse> {
return apiFetch<FilePurgePreviewResponse>(settings, "/api/v1/files/purge/preview", {
method: "POST",
body: JSON.stringify({ file_ids: fileIds })
});
}
export function executeFilePurge(settings: ApiSettings, payload: FilePurgeExecutePayload): Promise<FilePurgeResponse> {
return apiFetch<FilePurgeResponse>(settings, "/api/v1/files/purge/execute", {
method: "POST",
body: JSON.stringify(payload)
});
}
export function garbageCollectFileBlobs(
settings: ApiSettings,
payload: {limit?: number;approval_reference: string;})
: Promise<FileBlobGcResponse> {
return apiFetch<FileBlobGcResponse>(settings, "/api/v1/files/purge/blobs", {
method: "POST",
body: JSON.stringify(payload)
});
}
export type FileBulkShareResponse = {shares: FileShare[];shared_count: number;};
export type FileSharePayload = {
target_type: "user" | "group" | "tenant" | "campaign";
@@ -978,6 +1091,21 @@ export function deleteFileConnectorSpace(settings: ApiSettings, spaceId: string)
return apiFetch<FileConnectorSpace>(settings, `/api/v1/files/connector-spaces/${encodeURIComponent(spaceId)}`, { method: "DELETE" });
}
export function restoreFileConnectorSpace(settings: ApiSettings, spaceId: string): Promise<FileConnectorSpace> {
return apiFetch<FileConnectorSpace>(settings, `/api/v1/files/connector-spaces/${encodeURIComponent(spaceId)}/restore`, { method: "POST" });
}
export function writeBackFileConnectorSpace(
settings: ApiSettings,
spaceId: string,
payload: FileConnectorWritePayload)
: Promise<FileConnectorWriteResponse> {
return apiFetch<FileConnectorWriteResponse>(settings, `/api/v1/files/connector-spaces/${encodeURIComponent(spaceId)}/write-back`, {
method: "POST",
body: JSON.stringify(payload)
});
}
export function getFileConnectorProfile(
settings: ApiSettings,
profileId: string,