ultisuite-client/lib/admin-settings/map-api-org-settings.ts
R3D347HR4Y f44dadc453
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
feat(admin): add VirusTotal scan settings and mail UI
File policies API key field, conditional scan badge, safe policy merge.
2026-06-07 22:05:28 +02:00

172 lines
5.7 KiB
TypeScript

import type { ApiOrgPolicy, ApiOrgSettingsResponse } from "@/lib/api/admin-org-types"
import type { OrgPolicySectionKey } from "@/lib/api/admin-org-types"
import type { IntegrationEntry, OrgSettingsState, FilePolicySettings } from "@/lib/admin-settings/org-settings-types"
const INTEGRATION_HREFS: Record<string, string> = {
authentik: "/admin/settings/authentication",
nextcloud: "/admin/settings/nextcloud",
onlyoffice: "/admin/settings/onlyoffice",
smtp: "/admin/settings/mailing",
}
function mergeIntegrations(
fromApi: IntegrationEntry[] | undefined
): IntegrationEntry[] {
if (!fromApi?.length) return []
return fromApi.map((item) => ({
...item,
href: INTEGRATION_HREFS[item.id] ?? item.href,
}))
}
const DEFAULT_FILE_POLICIES: FilePolicySettings = {
max_upload_mib: 512,
allowed_extensions: "",
block_executable: true,
external_sharing: "authenticated",
default_link_expiry_days: 30,
virus_scan_enabled: false,
virustotal_api_key: "",
retention_trash_days: 30,
}
function mergeFilePolicies(fromApi: Partial<FilePolicySettings> | undefined): FilePolicySettings {
return {
...DEFAULT_FILE_POLICIES,
...fromApi,
virustotal_api_key: fromApi?.virustotal_api_key ?? "",
}
}
export function apiOrgPolicyToStore(policy: ApiOrgPolicy): Partial<OrgSettingsState> {
return {
authentik: {
enabled: policy.authentik.enabled,
api_url: policy.authentik.api_url,
slug: policy.authentik.slug,
client_id: policy.authentik.client_id,
enforce_sso: policy.authentik.enforce_sso,
allow_password_fallback: policy.authentik.allow_password_fallback,
default_groups: policy.authentik.default_groups,
},
twoFactor: {
required_for_all: policy.two_factor.required_for_all,
required_for_admins: policy.two_factor.required_for_admins,
allowed_methods: policy.two_factor.allowed_methods,
grace_period_days: policy.two_factor.grace_period_days,
remember_device_days: policy.two_factor.remember_device_days,
},
storageQuotas: { ...policy.storage_quotas },
usageQuotas: { ...policy.usage_quotas },
filePolicies: mergeFilePolicies(policy.file_policies),
llm: {
...policy.llm,
providers: policy.llm.providers ?? [],
},
search: {
...policy.search,
web_search: policy.search.web_search ?? {
default_provider_id: "brave-default",
providers: [],
},
},
administrators: policy.administrators ?? [],
nextcloud: { ...policy.nextcloud },
mailing: { ...policy.mailing },
onlyoffice: { ...policy.onlyoffice },
plugins: policy.plugins ?? [],
integrations: mergeIntegrations(policy.integrations as IntegrationEntry[]),
}
}
export function storeToApiOrgPolicy(state: OrgSettingsState): ApiOrgPolicy {
return {
authentik: {
enabled: state.authentik.enabled,
api_url: state.authentik.api_url,
slug: state.authentik.slug,
client_id: state.authentik.client_id,
enforce_sso: state.authentik.enforce_sso,
allow_password_fallback: state.authentik.allow_password_fallback,
default_groups: state.authentik.default_groups,
},
two_factor: {
required_for_all: state.twoFactor.required_for_all,
required_for_admins: state.twoFactor.required_for_admins,
allowed_methods: state.twoFactor.allowed_methods,
grace_period_days: state.twoFactor.grace_period_days,
remember_device_days: state.twoFactor.remember_device_days,
},
storage_quotas: { ...state.storageQuotas },
usage_quotas: { ...state.usageQuotas },
file_policies: { ...state.filePolicies },
llm: {
default_provider_id: state.llm.default_provider_id,
providers: state.llm.providers,
contact_discovery_model: state.llm.contact_discovery_model,
contact_discovery_provider_id: state.llm.contact_discovery_provider_id,
enforce_org_providers: state.llm.enforce_org_providers,
allow_user_override: state.llm.allow_user_override,
},
search: {
suite_engine: state.search.suite_engine,
meilisearch_url: state.search.meilisearch_url,
meilisearch_api_key: state.search.meilisearch_api_key,
typesense_url: state.search.typesense_url,
typesense_api_key: state.search.typesense_api_key,
web_search: state.search.web_search,
enforce_org_search: state.search.enforce_org_search,
},
administrators: state.administrators,
nextcloud: { ...state.nextcloud },
mailing: { ...state.mailing },
onlyoffice: { ...state.onlyoffice },
plugins: state.plugins.map(({ id, name, description, enabled, version }) => ({
id,
name,
description,
enabled,
version,
})),
integrations: state.integrations.map(({ id, name, description, enabled, configured }) => ({
id,
name,
description,
enabled,
configured,
})),
}
}
export function pickApiOrgPolicySections(
state: OrgSettingsState,
sections: OrgPolicySectionKey[]
): Partial<ApiOrgPolicy> {
const full = storeToApiOrgPolicy(state)
const patch: Partial<ApiOrgPolicy> = {}
for (const key of sections) {
patch[key] = full[key] as never
}
return patch
}
export type OrgSettingsMeta = {
effective: ApiOrgSettingsResponse["effective"]
secrets: ApiOrgSettingsResponse["secrets"]
envVars: ApiOrgSettingsResponse["env_vars"]
deployLocked: ApiOrgSettingsResponse["deploy_locked"]
updatedAt: string
updatedBy: string
}
export function apiOrgSettingsMeta(data: ApiOrgSettingsResponse): OrgSettingsMeta {
return {
effective: data.effective,
secrets: data.secrets,
envVars: data.env_vars ?? [],
deployLocked: data.deploy_locked ?? {},
updatedAt: data.updated_at,
updatedBy: data.updated_by,
}
}