chore: sync GovOPlaN module split state
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ApiError, apiFetch, apiUrl, authHeaders, csrfToken, type ApiSettings, type FilesManagedFileLinkTarget } from "@govoplan/core-webui";
|
||||
import { ApiError, apiFetch, apiUrl, authHeaders, csrfToken, type ApiSettings, type DeltaDeletedItem, type FilesManagedFileLinkTarget } from "@govoplan/core-webui";
|
||||
|
||||
export type FileSpace = {
|
||||
id: string;
|
||||
@@ -6,6 +6,14 @@ export type FileSpace = {
|
||||
owner_type: "user" | "group";
|
||||
owner_id: string;
|
||||
description?: string | null;
|
||||
space_type?: "managed" | "connector";
|
||||
connector_space_id?: string | null;
|
||||
connector_profile_id?: string | null;
|
||||
provider?: string | null;
|
||||
library_id?: string | null;
|
||||
remote_path?: string | null;
|
||||
sync_mode?: string | null;
|
||||
read_only?: boolean;
|
||||
};
|
||||
|
||||
export type FileShare = {
|
||||
@@ -17,6 +25,250 @@ export type FileShare = {
|
||||
revoked_at?: string | null;
|
||||
};
|
||||
|
||||
export type FileSourceProvenance = {
|
||||
source_type?: string | null;
|
||||
connector_id?: string | null;
|
||||
provider?: string | null;
|
||||
external_id?: string | null;
|
||||
external_path?: string | null;
|
||||
external_url?: string | null;
|
||||
revision?: string | null;
|
||||
revision_label?: string | null;
|
||||
observed_at?: string | null;
|
||||
imported_at?: string | null;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorPolicySource = {
|
||||
scope_type: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
label?: string | null;
|
||||
policy: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorPolicyDecision = {
|
||||
allowed: boolean;
|
||||
reason?: string | null;
|
||||
source_path: unknown[];
|
||||
requirements: string[];
|
||||
details: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorPolicyStep = {
|
||||
scope_type: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
path: string;
|
||||
label: string;
|
||||
applied_fields: string[];
|
||||
policy: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorPolicyResponse = {
|
||||
scope_type: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
policy: Record<string, unknown>;
|
||||
effective_policy?: Record<string, unknown> | null;
|
||||
parent_policy?: Record<string, unknown> | null;
|
||||
effective_policy_sources?: FileConnectorPolicyStep[];
|
||||
parent_policy_sources?: FileConnectorPolicyStep[];
|
||||
};
|
||||
|
||||
export type FileConnectorProfile = {
|
||||
id: string;
|
||||
label: string;
|
||||
provider: string;
|
||||
endpoint_url?: string | null;
|
||||
base_path?: string | null;
|
||||
enabled: boolean;
|
||||
scope_type: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
source_path: string;
|
||||
credential_profile_id?: string | null;
|
||||
credential_profile_label?: string | null;
|
||||
credential_mode: string;
|
||||
credential_source?: string | null;
|
||||
credentials_configured: boolean;
|
||||
username?: string | null;
|
||||
capabilities: string[];
|
||||
policy_sources: FileConnectorPolicySource[];
|
||||
metadata: Record<string, unknown>;
|
||||
source_kind?: string;
|
||||
};
|
||||
|
||||
export type FileConnectorCredential = {
|
||||
id: string;
|
||||
label: string;
|
||||
provider?: string | null;
|
||||
enabled: boolean;
|
||||
scope_type: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
source_path: string;
|
||||
credential_mode: string;
|
||||
credential_secret_source?: string | null;
|
||||
credentials_configured: boolean;
|
||||
username?: string | null;
|
||||
policy_sources: FileConnectorPolicySource[];
|
||||
metadata: Record<string, unknown>;
|
||||
source_kind?: string;
|
||||
};
|
||||
|
||||
export type FileConnectorCredentialsPayload = {
|
||||
username?: string | null;
|
||||
password?: string | null;
|
||||
token?: string | null;
|
||||
password_env?: string | null;
|
||||
token_env?: string | null;
|
||||
secret_ref?: string | null;
|
||||
};
|
||||
|
||||
export type FileConnectorDiscoveryCandidate = {
|
||||
endpoint_url: string;
|
||||
status: "failed" | "usable" | "found" | "credentials_rejected";
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type FileConnectorDiscoveryPayload = {
|
||||
provider: FileConnectorProfilePayload["provider"];
|
||||
endpoint_url: string;
|
||||
base_path?: string | null;
|
||||
credential_mode?: FileConnectorProfilePayload["credential_mode"];
|
||||
credentials?: FileConnectorCredentialsPayload;
|
||||
metadata?: Record<string, unknown>;
|
||||
require_valid_credentials?: boolean;
|
||||
};
|
||||
|
||||
export type FileConnectorDiscoveryResponse = {
|
||||
provider: string;
|
||||
endpoint_url?: string | null;
|
||||
base_path?: string | null;
|
||||
status: "usable" | "found" | "credentials_rejected" | "not_found" | "unsupported";
|
||||
message: string;
|
||||
candidates: FileConnectorDiscoveryCandidate[];
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorProfilePayload = {
|
||||
id: string;
|
||||
label: string;
|
||||
provider: "seafile" | "nextcloud" | "webdav" | "smb" | "nfs" | "dms" | "generic";
|
||||
endpoint_url?: string | null;
|
||||
base_path?: string | null;
|
||||
enabled?: boolean;
|
||||
scope_type?: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
credential_profile_id?: string | null;
|
||||
credential_mode?: "none" | "anonymous" | "basic" | "token" | "secret_ref";
|
||||
credentials?: FileConnectorCredentialsPayload;
|
||||
capabilities?: string[];
|
||||
policy?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorProfileUpdatePayload = Partial<Omit<FileConnectorProfilePayload, "id" | "scope_type" | "scope_id">> & {
|
||||
clear_password?: boolean;
|
||||
clear_token?: boolean;
|
||||
};
|
||||
|
||||
export type FileConnectorCredentialPayload = {
|
||||
id: string;
|
||||
label: string;
|
||||
provider?: "seafile" | "nextcloud" | "webdav" | "smb" | "nfs" | "dms" | "generic" | null;
|
||||
enabled?: boolean;
|
||||
scope_type?: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
credential_mode?: "none" | "anonymous" | "basic" | "token" | "secret_ref";
|
||||
credentials?: FileConnectorCredentialsPayload;
|
||||
policy?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorCredentialUpdatePayload = Partial<Omit<FileConnectorCredentialPayload, "id" | "scope_type" | "scope_id">> & {
|
||||
clear_password?: boolean;
|
||||
clear_token?: boolean;
|
||||
};
|
||||
|
||||
export type FileConnectorBrowseItem = {
|
||||
kind: "library" | "folder" | "file";
|
||||
name: string;
|
||||
path: string;
|
||||
external_id?: string | null;
|
||||
external_url?: string | null;
|
||||
size_bytes?: number | null;
|
||||
content_type?: string | null;
|
||||
modified_at?: string | null;
|
||||
etag?: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorBrowseResponse = {
|
||||
profile_id: string;
|
||||
provider: string;
|
||||
path: string;
|
||||
library_id?: string | null;
|
||||
read_only: boolean;
|
||||
decision: FileConnectorPolicyDecision;
|
||||
items: FileConnectorBrowseItem[];
|
||||
};
|
||||
|
||||
export type FileConnectorProvider = {
|
||||
provider: string;
|
||||
label: string;
|
||||
protocol: string;
|
||||
implemented: boolean;
|
||||
installed: boolean;
|
||||
browse_supported: boolean;
|
||||
import_supported: boolean;
|
||||
optional_dependency?: string | null;
|
||||
permission_model: string;
|
||||
sync_strategy: string;
|
||||
conflict_strategy: string;
|
||||
preview_strategy: string;
|
||||
audit_events: string[];
|
||||
notes?: string | null;
|
||||
};
|
||||
|
||||
export type FileConnectorSpace = {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
owner_type: "user" | "group";
|
||||
owner_id: string;
|
||||
label: string;
|
||||
connector_profile_id: string;
|
||||
provider: string;
|
||||
library_id?: string | null;
|
||||
remote_path: string;
|
||||
sync_mode: string;
|
||||
read_only: boolean;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at?: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type FileConnectorSettingsDeltaResponse = {
|
||||
profiles: FileConnectorProfile[];
|
||||
credentials: FileConnectorCredential[];
|
||||
spaces: FileConnectorSpace[];
|
||||
policy?: FileConnectorPolicyResponse | null;
|
||||
changed_sections?: string[];
|
||||
deleted: DeltaDeletedItem[];
|
||||
watermark?: string | null;
|
||||
has_more: boolean;
|
||||
full: boolean;
|
||||
};
|
||||
|
||||
export type FileConnectorSpacePayload = {
|
||||
owner_type?: "user" | "group";
|
||||
owner_id?: string | null;
|
||||
label: string;
|
||||
connector_profile_id: string;
|
||||
library_id?: string | null;
|
||||
remote_path?: string;
|
||||
sync_mode?: "manual";
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type ManagedFile = {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
@@ -34,13 +286,43 @@ export type ManagedFile = {
|
||||
deleted_at?: string | null;
|
||||
audit_relevant: boolean;
|
||||
metadata?: Record<string, unknown> | null;
|
||||
source_provenance?: FileSourceProvenance | null;
|
||||
source_revision?: string | null;
|
||||
shares?: FileShare[];
|
||||
};
|
||||
|
||||
export type FileListResponse = { files: ManagedFile[] };
|
||||
export type FileSpacesResponse = { spaces: FileSpace[] };
|
||||
export type FileUploadResponse = { files: ManagedFile[] };
|
||||
export type FileUploadProgress = { loaded: number; total?: number; percentage: number | null };
|
||||
export type FileListResponse = {files: ManagedFile[];cursor?: string | null;next_cursor?: string | null;watermark?: string | null;};
|
||||
export type FileDeltaDeletedItem = {id: string;resource_type?: string | null;revision?: string | null;deleted_at?: string | null;};
|
||||
export type FileDeltaResponse = {
|
||||
files: ManagedFile[];
|
||||
folders: FileFolder[];
|
||||
deleted: FileDeltaDeletedItem[];
|
||||
watermark?: string | null;
|
||||
has_more: boolean;
|
||||
full: boolean;
|
||||
};
|
||||
export type ManagedFileSnapshotResponse = {files: ManagedFile[];folders: FileFolder[];watermark?: string | null;};
|
||||
export type FileSpacesResponse = {spaces: FileSpace[];};
|
||||
export type FileUploadResponse = {files: ManagedFile[];};
|
||||
export type FileUploadProgress = {loaded: number;total?: number;percentage: number | null;};
|
||||
export type FileConnectorFilePayload = {
|
||||
library_id: string;
|
||||
path: string;
|
||||
owner_type: "user" | "group";
|
||||
owner_id?: string | null;
|
||||
target_folder?: string | null;
|
||||
target_path?: string | null;
|
||||
campaign_id?: string | null;
|
||||
conflict_strategy?: ConflictStrategy;
|
||||
source_revision?: string | null;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
export type FileConnectorSyncResponse = {
|
||||
file: ManagedFile;
|
||||
action: "created" | "updated" | "unchanged";
|
||||
previous_version_id?: string | null;
|
||||
current_version_id: string;
|
||||
};
|
||||
export type FileFolder = {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
@@ -51,16 +333,18 @@ export type FileFolder = {
|
||||
updated_at: string;
|
||||
deleted_at?: string | null;
|
||||
};
|
||||
export type FileFoldersResponse = { folders: FileFolder[] };
|
||||
export type FolderDeleteResponse = { deleted_folders: number; deleted_files: number };
|
||||
export type BulkDeleteResponse = { deleted_count: number };
|
||||
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 FileFoldersResponse = {folders: FileFolder[];cursor?: string | null;next_cursor?: string | null;watermark?: string | null;};
|
||||
|
||||
const DEFAULT_MANAGED_FILE_WINDOW_SIZE = 500;
|
||||
export type FolderDeleteResponse = {deleted_folders: number;deleted_files: number;};
|
||||
export type BulkDeleteResponse = {deleted_count: number;};
|
||||
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";
|
||||
export type ConflictStrategy = "reject" | "overwrite" | "rename";
|
||||
export type ConflictResolution = { target_path: string; action: ConflictAction; new_path?: string };
|
||||
export type ConflictResolution = {target_path: string;action: ConflictAction;new_path?: string;};
|
||||
export type PatternResolveResponse = {
|
||||
patterns: { pattern: string; matches: ManagedFile[] }[];
|
||||
patterns: {pattern: string;matches: ManagedFile[];}[];
|
||||
unmatched: ManagedFile[];
|
||||
};
|
||||
|
||||
@@ -70,50 +354,141 @@ export function listFileSpaces(settings: ApiSettings): Promise<FileSpacesRespons
|
||||
}
|
||||
|
||||
|
||||
export function listFolders(settings: ApiSettings, params: { owner_type: "user" | "group"; owner_id: string }): Promise<FileFoldersResponse> {
|
||||
export function listFolders(settings: ApiSettings, params: {owner_type: "user" | "group";owner_id: string;page_size?: number;cursor?: string | null;}): Promise<FileFoldersResponse> {
|
||||
const search = new URLSearchParams();
|
||||
search.set("owner_type", params.owner_type);
|
||||
search.set("owner_id", params.owner_id);
|
||||
if (params.page_size) search.set("page_size", String(params.page_size));
|
||||
if (params.cursor) search.set("cursor", params.cursor);
|
||||
return apiFetch<FileFoldersResponse>(settings, `/api/v1/files/folders?${search.toString()}`);
|
||||
}
|
||||
|
||||
export function createFolder(
|
||||
settings: ApiSettings,
|
||||
payload: { owner_type: "user" | "group"; owner_id: string; path: string }
|
||||
): Promise<FileFolder> {
|
||||
settings: ApiSettings,
|
||||
payload: {owner_type: "user" | "group";owner_id: string;path: string;})
|
||||
: Promise<FileFolder> {
|
||||
return apiFetch<FileFolder>(settings, "/api/v1/files/folders", { method: "POST", body: JSON.stringify(payload) });
|
||||
}
|
||||
|
||||
export function deleteFolder(
|
||||
settings: ApiSettings,
|
||||
payload: { owner_type: "user" | "group"; owner_id: string; path: string; recursive?: boolean }
|
||||
): Promise<FolderDeleteResponse> {
|
||||
settings: ApiSettings,
|
||||
payload: {owner_type: "user" | "group";owner_id: string;path: string;recursive?: boolean;})
|
||||
: Promise<FolderDeleteResponse> {
|
||||
return apiFetch<FolderDeleteResponse>(settings, "/api/v1/files/folders/delete", { method: "POST", body: JSON.stringify({ recursive: true, ...payload }) });
|
||||
}
|
||||
|
||||
export function listFiles(settings: ApiSettings, params: { owner_type?: string; owner_id?: string; campaign_id?: string; path_prefix?: string } = {}): Promise<FileListResponse> {
|
||||
export function listFiles(settings: ApiSettings, params: {owner_type?: string;owner_id?: string;campaign_id?: string;path_prefix?: string;page_size?: number;cursor?: string | null;} = {}): Promise<FileListResponse> {
|
||||
const search = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value) search.set(key, value);
|
||||
if (value) search.set(key, String(value));
|
||||
}
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileListResponse>(settings, `/api/v1/files${suffix}`);
|
||||
}
|
||||
|
||||
export async function uploadFiles(
|
||||
settings: ApiSettings,
|
||||
files: File[],
|
||||
options: {
|
||||
owner_type: "user" | "group";
|
||||
owner_id: string;
|
||||
path?: string;
|
||||
campaign_id?: string;
|
||||
unpack_zip?: boolean;
|
||||
conflict_strategy?: ConflictStrategy;
|
||||
conflict_resolutions?: ConflictResolution[];
|
||||
onProgress?: (progress: FileUploadProgress) => void;
|
||||
export function listFilesDelta(
|
||||
settings: ApiSettings,
|
||||
params: {owner_type?: string;owner_id?: string;campaign_id?: string;path_prefix?: string;since?: string;limit?: number;} = {})
|
||||
: Promise<FileDeltaResponse> {
|
||||
const search = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value !== undefined && value !== null && value !== "") search.set(key, String(value));
|
||||
}
|
||||
): Promise<FileUploadResponse> {
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileDeltaResponse>(settings, `/api/v1/files/delta${suffix}`);
|
||||
}
|
||||
|
||||
function applyDeltaToSnapshot(
|
||||
files: ManagedFile[],
|
||||
folders: FileFolder[],
|
||||
response: FileDeltaResponse)
|
||||
: {files: ManagedFile[];folders: FileFolder[];} {
|
||||
if (response.full) return { files: response.files, folders: response.folders };
|
||||
const deletedFileIds = new Set(response.deleted.filter((item) => !item.resource_type || item.resource_type === "file").map((item) => item.id));
|
||||
const deletedFolderIds = new Set(response.deleted.filter((item) => item.resource_type === "folder").map((item) => item.id));
|
||||
const filesById = new Map(files.map((file) => [file.id, file]));
|
||||
const foldersById = new Map(folders.map((folder) => [folder.id, folder]));
|
||||
deletedFileIds.forEach((id) => filesById.delete(id));
|
||||
deletedFolderIds.forEach((id) => foldersById.delete(id));
|
||||
response.files.forEach((file) => filesById.set(file.id, file));
|
||||
response.folders.forEach((folder) => foldersById.set(folder.id, folder));
|
||||
return { files: Array.from(filesById.values()), folders: Array.from(foldersById.values()) };
|
||||
}
|
||||
|
||||
export async function listManagedFileSnapshot(
|
||||
settings: ApiSettings,
|
||||
params: {owner_type: "user" | "group";owner_id: string;path_prefix?: string;page_size?: number;})
|
||||
: Promise<ManagedFileSnapshotResponse> {
|
||||
const pageSize = params.page_size ?? DEFAULT_MANAGED_FILE_WINDOW_SIZE;
|
||||
let watermark: string | null | undefined = null;
|
||||
let folderCursor: string | null | undefined = null;
|
||||
let folders: FileFolder[] = [];
|
||||
do {
|
||||
const response = await listFolders(settings, {
|
||||
owner_type: params.owner_type,
|
||||
owner_id: params.owner_id,
|
||||
page_size: pageSize,
|
||||
cursor: folderCursor
|
||||
});
|
||||
watermark = watermark || response.watermark;
|
||||
folders = folders.concat(response.folders);
|
||||
folderCursor = response.next_cursor;
|
||||
} while (folderCursor);
|
||||
|
||||
let fileCursor: string | null | undefined = null;
|
||||
let files: ManagedFile[] = [];
|
||||
do {
|
||||
const response = await listFiles(settings, {
|
||||
owner_type: params.owner_type,
|
||||
owner_id: params.owner_id,
|
||||
path_prefix: params.path_prefix,
|
||||
page_size: pageSize,
|
||||
cursor: fileCursor
|
||||
});
|
||||
watermark = watermark || response.watermark;
|
||||
files = files.concat(response.files);
|
||||
fileCursor = response.next_cursor;
|
||||
} while (fileCursor);
|
||||
|
||||
if (watermark) {
|
||||
let since = watermark;
|
||||
let response: FileDeltaResponse | null = null;
|
||||
do {
|
||||
response = await listFilesDelta(settings, {
|
||||
owner_type: params.owner_type,
|
||||
owner_id: params.owner_id,
|
||||
path_prefix: params.path_prefix,
|
||||
since,
|
||||
limit: pageSize
|
||||
});
|
||||
const snapshot = applyDeltaToSnapshot(files, folders, response);
|
||||
files = snapshot.files;
|
||||
folders = snapshot.folders;
|
||||
since = response.watermark || "";
|
||||
} while (response.has_more && response.watermark);
|
||||
watermark = since || watermark;
|
||||
}
|
||||
|
||||
return { files, folders, watermark };
|
||||
}
|
||||
|
||||
export async function uploadFiles(
|
||||
settings: ApiSettings,
|
||||
files: File[],
|
||||
options: {
|
||||
owner_type: "user" | "group";
|
||||
owner_id: string;
|
||||
path?: string;
|
||||
campaign_id?: string;
|
||||
unpack_zip?: boolean;
|
||||
conflict_strategy?: ConflictStrategy;
|
||||
conflict_resolutions?: ConflictResolution[];
|
||||
source_provenance?: FileSourceProvenance;
|
||||
source_revision?: string;
|
||||
connector_policy_sources?: FileConnectorPolicySource[];
|
||||
onProgress?: (progress: FileUploadProgress) => void;
|
||||
})
|
||||
: Promise<FileUploadResponse> {
|
||||
const form = new FormData();
|
||||
files.forEach((file) => form.append("files", file));
|
||||
form.append("owner_type", options.owner_type);
|
||||
@@ -123,15 +498,18 @@ export async function uploadFiles(
|
||||
if (options.unpack_zip) form.append("unpack_zip", "true");
|
||||
if (options.conflict_strategy) form.append("conflict_strategy", options.conflict_strategy);
|
||||
if (options.conflict_resolutions?.length) form.append("conflict_resolutions_json", JSON.stringify(options.conflict_resolutions));
|
||||
if (options.source_provenance) form.append("source_provenance_json", JSON.stringify(options.source_provenance));
|
||||
if (options.source_revision) form.append("source_revision", options.source_revision);
|
||||
if (options.connector_policy_sources?.length) form.append("connector_policy_json", JSON.stringify({ sources: options.connector_policy_sources }));
|
||||
if (options.onProgress) return uploadFilesWithProgress(settings, form, options.onProgress);
|
||||
return apiFetch<FileUploadResponse>(settings, "/api/v1/files/upload", { method: "POST", body: form });
|
||||
}
|
||||
|
||||
function uploadFilesWithProgress(
|
||||
settings: ApiSettings,
|
||||
form: FormData,
|
||||
onProgress: (progress: FileUploadProgress) => void
|
||||
): Promise<FileUploadResponse> {
|
||||
settings: ApiSettings,
|
||||
form: FormData,
|
||||
onProgress: (progress: FileUploadProgress) => void)
|
||||
: Promise<FileUploadResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", apiUrl(settings, "/api/v1/files/upload"));
|
||||
@@ -145,12 +523,12 @@ function uploadFilesWithProgress(
|
||||
onProgress({
|
||||
loaded: event.loaded,
|
||||
total,
|
||||
percentage: total && total > 0 ? Math.round((event.loaded / total) * 100) : null
|
||||
percentage: total && total > 0 ? Math.round(event.loaded / total * 100) : null
|
||||
});
|
||||
};
|
||||
|
||||
xhr.onerror = () => reject(new Error("Upload failed because the network request could not be completed."));
|
||||
xhr.onabort = () => reject(new Error("Upload was cancelled."));
|
||||
xhr.onerror = () => reject(new Error("i18n:govoplan-files.upload_failed_because_the_network_request_could_.360a5ab3"));
|
||||
xhr.onabort = () => reject(new Error("i18n:govoplan-files.upload_was_cancelled.5bab0f9b"));
|
||||
xhr.onload = () => {
|
||||
const responseText = xhr.responseText || "";
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
@@ -179,7 +557,7 @@ 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 type FileBulkShareResponse = { shares: FileShare[]; shared_count: number };
|
||||
export type FileBulkShareResponse = {shares: FileShare[];shared_count: number;};
|
||||
|
||||
export function shareFilesWithTarget(settings: ApiSettings, fileIds: string[], target: FilesManagedFileLinkTarget): Promise<FileBulkShareResponse> {
|
||||
return apiFetch<FileBulkShareResponse>(settings, "/api/v1/files/bulk-shares", {
|
||||
@@ -188,6 +566,231 @@ export function shareFilesWithTarget(settings: ApiSettings, fileIds: string[], t
|
||||
});
|
||||
}
|
||||
|
||||
export function evaluateFileConnectorPolicy(
|
||||
settings: ApiSettings,
|
||||
payload: {source_provenance: FileSourceProvenance;operation?: string;policy_sources?: FileConnectorPolicySource[];})
|
||||
: Promise<{decision: FileConnectorPolicyDecision;}> {
|
||||
return apiFetch<{decision: FileConnectorPolicyDecision;}>(settings, "/api/v1/files/connector-policy/evaluate", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ operation: "access", policy_sources: [], ...payload })
|
||||
});
|
||||
}
|
||||
|
||||
export function getFileConnectorPolicy(
|
||||
settings: ApiSettings,
|
||||
scopeType: "system" | "tenant" | "user" | "group" | "campaign",
|
||||
scopeId?: string | null)
|
||||
: Promise<FileConnectorPolicyResponse> {
|
||||
const search = new URLSearchParams();
|
||||
if (scopeId) search.set("scope_id", scopeId);
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileConnectorPolicyResponse>(settings, `/api/v1/files/connectors/policies/${encodeURIComponent(scopeType)}${suffix}`);
|
||||
}
|
||||
|
||||
export function fetchFileConnectorSettingsDelta(
|
||||
settings: ApiSettings,
|
||||
params: {
|
||||
scope_type?: "system" | "tenant" | "user" | "group" | "campaign";
|
||||
scope_id?: string | null;
|
||||
provider?: string;
|
||||
campaign_id?: string | null;
|
||||
include_disabled?: boolean;
|
||||
include_inactive?: boolean;
|
||||
owner_type?: "user" | "group";
|
||||
owner_id?: string;
|
||||
since?: string | null;
|
||||
limit?: number;
|
||||
} = {})
|
||||
: Promise<FileConnectorSettingsDeltaResponse> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.scope_type) search.set("scope_type", params.scope_type);
|
||||
if (params.scope_id) search.set("scope_id", params.scope_id);
|
||||
if (params.provider) search.set("provider", params.provider);
|
||||
if (params.campaign_id) search.set("campaign_id", params.campaign_id);
|
||||
if (params.include_disabled) search.set("include_disabled", "true");
|
||||
if (params.include_inactive) search.set("include_inactive", "true");
|
||||
if (params.owner_type) search.set("owner_type", params.owner_type);
|
||||
if (params.owner_id) search.set("owner_id", params.owner_id);
|
||||
if (params.since) search.set("since", params.since);
|
||||
if (params.limit) search.set("limit", String(params.limit));
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileConnectorSettingsDeltaResponse>(settings, `/api/v1/files/connectors/settings/delta${suffix}`);
|
||||
}
|
||||
|
||||
export function updateFileConnectorPolicy(
|
||||
settings: ApiSettings,
|
||||
scopeType: "system" | "tenant" | "user" | "group" | "campaign",
|
||||
policy: Record<string, unknown>,
|
||||
scopeId?: string | null)
|
||||
: Promise<FileConnectorPolicyResponse> {
|
||||
const search = new URLSearchParams();
|
||||
if (scopeId) search.set("scope_id", scopeId);
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileConnectorPolicyResponse>(settings, `/api/v1/files/connectors/policies/${encodeURIComponent(scopeType)}${suffix}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ policy })
|
||||
});
|
||||
}
|
||||
|
||||
export function listFileConnectorProfiles(
|
||||
settings: ApiSettings,
|
||||
params: {provider?: string;campaign_id?: string;include_disabled?: boolean;} = {})
|
||||
: Promise<{profiles: FileConnectorProfile[];}> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.provider) search.set("provider", params.provider);
|
||||
if (params.campaign_id) search.set("campaign_id", params.campaign_id);
|
||||
if (params.include_disabled) search.set("include_disabled", "true");
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<{profiles: FileConnectorProfile[];}>(settings, `/api/v1/files/connectors/profiles${suffix}`);
|
||||
}
|
||||
|
||||
export function listFileConnectorProviders(settings: ApiSettings): Promise<{providers: FileConnectorProvider[];}> {
|
||||
return apiFetch<{providers: FileConnectorProvider[];}>(settings, "/api/v1/files/connectors/providers");
|
||||
}
|
||||
|
||||
export function discoverFileConnectorEndpoint(settings: ApiSettings, payload: FileConnectorDiscoveryPayload): Promise<FileConnectorDiscoveryResponse> {
|
||||
return apiFetch<FileConnectorDiscoveryResponse>(settings, "/api/v1/files/connectors/discover", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function listFileConnectorCredentials(
|
||||
settings: ApiSettings,
|
||||
params: {provider?: string;include_disabled?: boolean;} = {})
|
||||
: Promise<{credentials: FileConnectorCredential[];}> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.provider) search.set("provider", params.provider);
|
||||
if (params.include_disabled) search.set("include_disabled", "true");
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<{credentials: FileConnectorCredential[];}>(settings, `/api/v1/files/connectors/credentials${suffix}`);
|
||||
}
|
||||
|
||||
export function createFileConnectorCredential(settings: ApiSettings, payload: FileConnectorCredentialPayload): Promise<FileConnectorCredential> {
|
||||
return apiFetch<FileConnectorCredential>(settings, "/api/v1/files/connectors/credentials", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateFileConnectorCredential(
|
||||
settings: ApiSettings,
|
||||
credentialId: string,
|
||||
payload: FileConnectorCredentialUpdatePayload)
|
||||
: Promise<FileConnectorCredential> {
|
||||
return apiFetch<FileConnectorCredential>(settings, `/api/v1/files/connectors/credentials/${encodeURIComponent(credentialId)}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivateFileConnectorCredential(settings: ApiSettings, credentialId: string): Promise<FileConnectorCredential> {
|
||||
return apiFetch<FileConnectorCredential>(settings, `/api/v1/files/connectors/credentials/${encodeURIComponent(credentialId)}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function listFileConnectorSpaces(
|
||||
settings: ApiSettings,
|
||||
params: {owner_type?: "user" | "group";owner_id?: string;include_inactive?: boolean;} = {})
|
||||
: Promise<{spaces: FileConnectorSpace[];}> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.owner_type) search.set("owner_type", params.owner_type);
|
||||
if (params.owner_id) search.set("owner_id", params.owner_id);
|
||||
if (params.include_inactive) search.set("include_inactive", "true");
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<{spaces: FileConnectorSpace[];}>(settings, `/api/v1/files/connector-spaces${suffix}`);
|
||||
}
|
||||
|
||||
export function createFileConnectorSpace(settings: ApiSettings, payload: FileConnectorSpacePayload): Promise<FileConnectorSpace> {
|
||||
return apiFetch<FileConnectorSpace>(settings, "/api/v1/files/connector-spaces", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ owner_type: "user", remote_path: "", sync_mode: "manual", metadata: {}, ...payload })
|
||||
});
|
||||
}
|
||||
|
||||
export function updateFileConnectorSpace(
|
||||
settings: ApiSettings,
|
||||
spaceId: string,
|
||||
payload: Partial<Omit<FileConnectorSpacePayload, "connector_profile_id" | "owner_type" | "owner_id">> & {is_active?: boolean;})
|
||||
: Promise<FileConnectorSpace> {
|
||||
return apiFetch<FileConnectorSpace>(settings, `/api/v1/files/connector-spaces/${encodeURIComponent(spaceId)}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteFileConnectorSpace(settings: ApiSettings, spaceId: string): Promise<FileConnectorSpace> {
|
||||
return apiFetch<FileConnectorSpace>(settings, `/api/v1/files/connector-spaces/${encodeURIComponent(spaceId)}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function getFileConnectorProfile(
|
||||
settings: ApiSettings,
|
||||
profileId: string,
|
||||
params: {campaign_id?: string;include_disabled?: boolean;} = {})
|
||||
: Promise<FileConnectorProfile> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.campaign_id) search.set("campaign_id", params.campaign_id);
|
||||
if (params.include_disabled) search.set("include_disabled", "true");
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileConnectorProfile>(settings, `/api/v1/files/connectors/profiles/${encodeURIComponent(profileId)}${suffix}`);
|
||||
}
|
||||
|
||||
export function createFileConnectorProfile(settings: ApiSettings, payload: FileConnectorProfilePayload): Promise<FileConnectorProfile> {
|
||||
return apiFetch<FileConnectorProfile>(settings, "/api/v1/files/connectors/profiles", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateFileConnectorProfile(
|
||||
settings: ApiSettings,
|
||||
profileId: string,
|
||||
payload: FileConnectorProfileUpdatePayload)
|
||||
: Promise<FileConnectorProfile> {
|
||||
return apiFetch<FileConnectorProfile>(settings, `/api/v1/files/connectors/profiles/${encodeURIComponent(profileId)}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivateFileConnectorProfile(settings: ApiSettings, profileId: string): Promise<FileConnectorProfile> {
|
||||
return apiFetch<FileConnectorProfile>(settings, `/api/v1/files/connectors/profiles/${encodeURIComponent(profileId)}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function browseFileConnectorProfile(
|
||||
settings: ApiSettings,
|
||||
profileId: string,
|
||||
params: {path?: string;library_id?: string;campaign_id?: string;} = {})
|
||||
: Promise<FileConnectorBrowseResponse> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.path) search.set("path", params.path);
|
||||
if (params.library_id) search.set("library_id", params.library_id);
|
||||
if (params.campaign_id) search.set("campaign_id", params.campaign_id);
|
||||
const suffix = search.toString() ? `?${search.toString()}` : "";
|
||||
return apiFetch<FileConnectorBrowseResponse>(settings, `/api/v1/files/connectors/profiles/${encodeURIComponent(profileId)}/browse${suffix}`);
|
||||
}
|
||||
|
||||
export function importFileConnectorFile(
|
||||
settings: ApiSettings,
|
||||
profileId: string,
|
||||
payload: FileConnectorFilePayload)
|
||||
: Promise<FileUploadResponse> {
|
||||
return apiFetch<FileUploadResponse>(settings, `/api/v1/files/connectors/profiles/${encodeURIComponent(profileId)}/import`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ conflict_strategy: "reject", metadata: {}, ...payload })
|
||||
});
|
||||
}
|
||||
|
||||
export function syncFileConnectorFile(
|
||||
settings: ApiSettings,
|
||||
profileId: string,
|
||||
payload: FileConnectorFilePayload)
|
||||
: Promise<FileConnectorSyncResponse> {
|
||||
return apiFetch<FileConnectorSyncResponse>(settings, `/api/v1/files/connectors/profiles/${encodeURIComponent(profileId)}/sync`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ conflict_strategy: "rename", metadata: {}, ...payload })
|
||||
});
|
||||
}
|
||||
|
||||
export function shareFilesWithCampaign(settings: ApiSettings, fileIds: string[], campaignId: string): Promise<FileBulkShareResponse> {
|
||||
return shareFilesWithTarget(settings, fileIds, { type: "campaign", id: campaignId, label: "campaign" });
|
||||
}
|
||||
@@ -195,14 +798,14 @@ export function shareFilesWithCampaign(settings: ApiSettings, fileIds: string[],
|
||||
export async function shareFileWithCampaign(settings: ApiSettings, fileId: string, campaignId: string): Promise<FileShare> {
|
||||
const response = await shareFilesWithCampaign(settings, [fileId], campaignId);
|
||||
const share = response.shares[0];
|
||||
if (!share) throw new Error("File share was not created.");
|
||||
if (!share) throw new Error("i18n:govoplan-files.file_share_was_not_created.033ac476");
|
||||
return share;
|
||||
}
|
||||
|
||||
export function bulkRenameFiles(
|
||||
settings: ApiSettings,
|
||||
payload: { file_ids: string[]; folder_paths?: string[]; owner_type?: "user" | "group"; owner_id?: string; mode: "direct" | "prefix" | "suffix" | "replace"; new_name?: string; find?: string; replacement?: string; prefix?: string; suffix?: string; recursive?: boolean; dry_run?: boolean }
|
||||
): Promise<RenameResponse> {
|
||||
settings: ApiSettings,
|
||||
payload: {file_ids: string[];folder_paths?: string[];owner_type: "user" | "group";owner_id: string;mode: "direct" | "prefix" | "suffix" | "replace";new_name?: string;find?: string;replacement?: string;prefix?: string;suffix?: string;recursive?: boolean;dry_run?: boolean;})
|
||||
: Promise<RenameResponse> {
|
||||
return apiFetch<RenameResponse>(settings, "/api/v1/files/bulk-rename", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ replacement: "", prefix: "", suffix: "", dry_run: true, ...payload })
|
||||
@@ -210,27 +813,27 @@ export function bulkRenameFiles(
|
||||
}
|
||||
|
||||
export function resolveFilePatterns(
|
||||
settings: ApiSettings,
|
||||
payload: { patterns: string[]; owner_type?: "user" | "group"; owner_id?: string; campaign_id?: string; path_prefix?: string; include_unmatched?: boolean; case_sensitive?: boolean }
|
||||
): Promise<PatternResolveResponse> {
|
||||
settings: ApiSettings,
|
||||
payload: {patterns: string[];owner_type?: "user" | "group";owner_id?: string;campaign_id?: string;path_prefix?: string;include_unmatched?: boolean;case_sensitive?: boolean;})
|
||||
: Promise<PatternResolveResponse> {
|
||||
return apiFetch<PatternResolveResponse>(settings, "/api/v1/files/resolve-patterns", { method: "POST", body: JSON.stringify(payload) });
|
||||
}
|
||||
|
||||
export function transferFiles(
|
||||
settings: ApiSettings,
|
||||
payload: {
|
||||
operation: "move" | "copy";
|
||||
file_ids: string[];
|
||||
folder_paths: string[];
|
||||
source_owner_type: "user" | "group";
|
||||
source_owner_id: string;
|
||||
target_owner_type: "user" | "group";
|
||||
target_owner_id: string;
|
||||
target_folder: string;
|
||||
conflict_strategy?: ConflictStrategy;
|
||||
conflict_resolutions?: ConflictResolution[];
|
||||
}
|
||||
): Promise<TransferResponse> {
|
||||
settings: ApiSettings,
|
||||
payload: {
|
||||
operation: "move" | "copy";
|
||||
file_ids: string[];
|
||||
folder_paths: string[];
|
||||
source_owner_type: "user" | "group";
|
||||
source_owner_id: string;
|
||||
target_owner_type: "user" | "group";
|
||||
target_owner_id: string;
|
||||
target_folder: string;
|
||||
conflict_strategy?: ConflictStrategy;
|
||||
conflict_resolutions?: ConflictResolution[];
|
||||
})
|
||||
: Promise<TransferResponse> {
|
||||
return apiFetch<TransferResponse>(settings, "/api/v1/files/transfer", { method: "POST", body: JSON.stringify(payload) });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user