ultisuite-client/lib/admin-settings/map-api-org-settings.ts
R3D347HR4Y ad1370ea7e
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat: enhance configuration and add new demo layouts
- Introduced turbopack alias for canvas in next.config.mjs.
- Updated package.json scripts for development and branding tasks.
- Added new dependencies for Tiptap extensions.
- Implemented new demo layouts for agenda, contacts, drive, and mail applications.
- Enhanced globals.css for improved theming and splash screen animations.
- Added OAuth callback handling for drive mounts.
- Updated layout components to integrate new demo shells and improve structure.
2026-06-12 19:10:24 +02:00

289 lines
10 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, DriveMountOAuthSettings, DriveMountOAuthProviderSettings, IdentityProvidersPolicy, IdentityProvider } from "@/lib/admin-settings/org-settings-types"
import { DEFAULT_MEET_POLICY } from "@/lib/meet/meet-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_IDENTITY_PROVIDERS: IdentityProvidersPolicy = {
allow_self_enrollment: true,
default_login_source: "",
providers: [],
}
function mergeIdentityProviders(
fromApi: Partial<IdentityProvidersPolicy> | undefined
): IdentityProvidersPolicy {
return {
...DEFAULT_IDENTITY_PROVIDERS,
...fromApi,
providers: (fromApi?.providers ?? []).map((provider) => ({
...provider,
allowed_email_domains: provider.allowed_email_domains ?? [],
allowed_identities: provider.allowed_identities ?? [],
allowed_organizations: provider.allowed_organizations ?? [],
default_groups: provider.default_groups ?? [],
sync_status: provider.sync_status ?? "pending",
oauth: provider.oauth
? { ...provider.oauth, client_secret: provider.oauth.client_secret ?? "" }
: undefined,
ldap: provider.ldap
? { ...provider.ldap, bind_password: provider.ldap.bind_password ?? "" }
: undefined,
saml: provider.saml
? { ...provider.saml, signing_cert: provider.saml.signing_cert ?? "" }
: undefined,
})),
}
}
function mapProviderToApi(provider: IdentityProvider) {
return {
id: provider.id,
name: provider.name,
slug: provider.slug,
type: provider.type,
enabled: provider.enabled,
authentik_pk: provider.authentik_pk,
sync_status: provider.sync_status,
sync_error: provider.sync_error,
last_synced_at: provider.last_synced_at,
allowed_email_domains: provider.allowed_email_domains,
allowed_identities: provider.allowed_identities,
allowed_organizations: provider.allowed_organizations,
default_groups: provider.default_groups,
oauth: provider.oauth,
saml: provider.saml,
ldap: provider.ldap,
}
}
const DEFAULT_MOUNT_OAUTH_PROVIDER: DriveMountOAuthProviderSettings = {
enabled: false,
client_id: "",
client_secret: "",
}
const DEFAULT_MOUNT_OAUTH: DriveMountOAuthSettings = {
redirect_uri: "",
google: { ...DEFAULT_MOUNT_OAUTH_PROVIDER },
dropbox: { ...DEFAULT_MOUNT_OAUTH_PROVIDER },
microsoft: { ...DEFAULT_MOUNT_OAUTH_PROVIDER },
}
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,
mount_oauth: DEFAULT_MOUNT_OAUTH,
}
function mergeFilePolicies(fromApi: Partial<FilePolicySettings> | undefined): FilePolicySettings {
const mountOAuth = fromApi?.mount_oauth
return {
...DEFAULT_FILE_POLICIES,
...fromApi,
virustotal_api_key: fromApi?.virustotal_api_key ?? "",
mount_oauth: {
...DEFAULT_MOUNT_OAUTH,
...mountOAuth,
google: { ...DEFAULT_MOUNT_OAUTH.google, ...mountOAuth?.google, client_secret: mountOAuth?.google?.client_secret ?? "" },
dropbox: { ...DEFAULT_MOUNT_OAUTH.dropbox, ...mountOAuth?.dropbox, client_secret: mountOAuth?.dropbox?.client_secret ?? "" },
microsoft: { ...DEFAULT_MOUNT_OAUTH.microsoft, ...mountOAuth?.microsoft, client_secret: mountOAuth?.microsoft?.client_secret ?? "" },
},
}
}
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,
},
identityProviders: mergeIdentityProviders(policy.identity_providers),
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 },
richtext: {
enabled: policy.richtext?.enabled ?? true,
storage_mode: policy.richtext?.storage_mode ?? "sidecar",
export_mirror_format: policy.richtext?.export_mirror_format ?? "",
hocuspocus_url: policy.richtext?.hocuspocus_url ?? "",
},
aiAssistant: {
enabled: policy.ai_assistant?.enabled ?? false,
openwebui_internal_url: policy.ai_assistant?.openwebui_internal_url ?? "",
public_path: policy.ai_assistant?.public_path ?? "/ai",
embed_default_temporary: policy.ai_assistant?.embed_default_temporary ?? true,
default_model: policy.ai_assistant?.default_model ?? "",
enabled_tools: policy.ai_assistant?.enabled_tools ?? ["mail", "drive", "contacts", "search"],
chat_sync_enabled: policy.ai_assistant?.chat_sync_enabled ?? true,
chat_nc_path: policy.ai_assistant?.chat_nc_path ?? "/.ultimail/ai/chats",
},
agenda: {
default_theme_mode: policy.agenda?.default_theme_mode ?? "system",
enforce_org_theme: policy.agenda?.enforce_org_theme ?? false,
default_video_provider: policy.agenda?.default_video_provider ?? "ultimeet",
enforce_org_video_provider: policy.agenda?.enforce_org_video_provider ?? false,
video_provider_api_keys: policy.agenda?.video_provider_api_keys ?? {},
},
meet: {
...DEFAULT_MEET_POLICY,
...policy.meet,
post_actions: {
...DEFAULT_MEET_POLICY.post_actions,
...policy.meet?.post_actions,
},
},
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,
},
identity_providers: {
allow_self_enrollment: state.identityProviders.allow_self_enrollment,
default_login_source: state.identityProviders.default_login_source,
providers: state.identityProviders.providers.map(mapProviderToApi),
},
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 },
richtext: { ...state.richtext },
ai_assistant: { ...state.aiAssistant },
agenda: { ...state.agenda },
meet: { ...state.meet },
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,
}
}