chore: sync GovOPlaN module split state

This commit is contained in:
2026-07-10 12:51:21 +02:00
parent 18e6c3eb9b
commit 13fd7fc3bd
50 changed files with 12678 additions and 1109 deletions

View File

@@ -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) });
}