ultisuite-client/lib/admin-settings/org-settings-store.ts
R3D347HR4Y 7ee1a66942
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(ai-assistant): enhance AI assistant configuration and integration
- Added support for managing AI models within the AI assistant settings.
- Introduced new hosted mail setup component for streamlined email configuration.
- Updated environment variables for local development and proxy settings.
- Enhanced error handling and user feedback in the chat page for API connectivity issues.
- Improved routing for AI-related API calls in the Next.js configuration.
- Added documentation for local development and agent management in CLAUDE.md.
2026-06-13 20:38:15 +02:00

406 lines
12 KiB
TypeScript

"use client"
import { create } from "zustand"
import type { OrgSettingsMeta } from "@/lib/admin-settings/map-api-org-settings"
import type {
Administrator,
AuthentikSettings,
FilePolicySettings,
IntegrationEntry,
MailingSettings,
NextcloudSettings,
OnlyOfficeSettings,
RichTextSettings,
AiAssistantSettings,
AgendaOrgPolicySettings,
MeetOrgPolicySettings,
OrgLLMSettings,
OrgSearchSettings,
OrgStorageQuotas,
PluginEntry,
TwoFactorPolicy,
UsageQuotaDefaults,
IdentityProvidersPolicy,
} from "@/lib/admin-settings/org-settings-types"
import { DEFAULT_MEET_POLICY } from "@/lib/meet/meet-settings-types"
const DEFAULT_AUTHENTIK: AuthentikSettings = {
enabled: true,
api_url: "",
slug: "ulti-suite",
client_id: "",
enforce_sso: true,
allow_password_fallback: false,
default_groups: "ulti-users",
}
const DEFAULT_IDENTITY_PROVIDERS: IdentityProvidersPolicy = {
allow_self_enrollment: true,
default_login_source: "",
providers: [],
}
const DEFAULT_TWO_FACTOR: TwoFactorPolicy = {
required_for_all: false,
required_for_admins: true,
allowed_methods: ["totp", "webauthn"],
grace_period_days: 7,
remember_device_days: 30,
}
const DEFAULT_STORAGE_QUOTAS: OrgStorageQuotas = {
default_mail_gib: 5,
default_drive_gib: 5,
default_photos_gib: 5,
warn_threshold_pct: 90,
}
const DEFAULT_USAGE_QUOTAS: UsageQuotaDefaults = {
llm_requests_per_day: 100,
llm_tokens_per_month: 500_000,
search_requests_per_day: 50,
max_api_tokens_per_user: 10,
max_webhooks_per_user: 20,
}
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: {
redirect_uri: "",
google: { enabled: false, client_id: "", client_secret: "" },
dropbox: { enabled: false, client_id: "", client_secret: "" },
microsoft: { enabled: false, client_id: "", client_secret: "" },
},
}
const DEFAULT_LLM: OrgLLMSettings = {
default_provider_id: "",
providers: [],
enforce_org_providers: false,
allow_user_override: true,
}
const DEFAULT_SEARCH: OrgSearchSettings = {
suite_engine: "postgres",
meilisearch_url: "",
meilisearch_api_key: "",
typesense_url: "",
typesense_api_key: "",
web_search: { default_provider_id: "brave-default", providers: [] },
enforce_org_search: false,
}
const DEFAULT_NEXTCLOUD: NextcloudSettings = {
enabled: false,
base_url: "",
admin_user: "",
admin_password: "",
drive_enabled: true,
calendar_enabled: true,
contacts_enabled: true,
talk_enabled: false,
}
const DEFAULT_MAILING: MailingSettings = {
enabled: false,
smtp_host: "",
smtp_port: 587,
smtp_user: "",
smtp_password: "",
from_email: "noreply@example.com",
from_name: "Ulti Suite",
tls_mode: "starttls",
}
const DEFAULT_ONLYOFFICE: OnlyOfficeSettings = {
enabled: false,
document_server_url: "",
jwt_secret: "",
jwt_header: "Authorization",
}
const DEFAULT_RICHTEXT: RichTextSettings = {
enabled: true,
storage_mode: "sidecar",
export_mirror_format: "",
hocuspocus_url: "",
}
const DEFAULT_AI_ASSISTANT: AiAssistantSettings = {
enabled: false,
openwebui_internal_url: "",
public_path: "/ai",
embed_default_temporary: true,
default_model: "",
enabled_tools: ["mail", "drive", "contacts", "search"],
chat_sync_enabled: true,
chat_nc_path: "/.ultimail/ai/chats",
models: [],
}
const DEFAULT_AGENDA: AgendaOrgPolicySettings = {
default_theme_mode: "system",
enforce_org_theme: false,
default_video_provider: "ultimeet",
enforce_org_video_provider: false,
video_provider_api_keys: {},
}
const DEFAULT_MEET: MeetOrgPolicySettings = DEFAULT_MEET_POLICY
const DEFAULT_PLUGINS: PluginEntry[] = [
{
id: "mail-automation",
name: "Automatisations mail",
description: "Règles, webhooks et tri IA sur la réception.",
enabled: true,
version: "1.0.0",
},
{
id: "contact-discovery",
name: "Découverte contacts",
description: "Enrichissement IA et signatures détectées.",
enabled: true,
version: "1.0.0",
},
{
id: "public-share",
name: "Partage public Drive",
description: "Liens publics et partages externes.",
enabled: true,
version: "1.0.0",
},
{
id: "office-editor",
name: "Édition OnlyOffice",
description: "Édition collaborative de documents.",
enabled: false,
version: "1.0.0",
},
{
id: "richtext-editor",
name: "Édition rich text TipTap",
description: "Édition rich text TipTap pour documents Word.",
enabled: true,
version: "1.0.0",
},
{
id: "ai-assistant",
name: "UltiAI",
description: "Assistant IA intégré avec tools mail, drive et contacts.",
enabled: false,
version: "1.0.0",
},
]
const DEFAULT_INTEGRATIONS: IntegrationEntry[] = [
{
id: "authentik",
name: "Authentik",
description: "SSO, groupes et provisionnement des comptes.",
enabled: true,
configured: false,
href: "/admin/settings/authentication",
},
{
id: "nextcloud",
name: "Nextcloud",
description: "Drive, agenda, contacts et Talk.",
enabled: false,
configured: false,
href: "/admin/settings/nextcloud",
},
{
id: "onlyoffice",
name: "OnlyOffice",
description: "Édition de documents dans le navigateur.",
enabled: false,
configured: false,
href: "/admin/settings/onlyoffice",
},
{
id: "smtp",
name: "Mailing unifié",
description: "SMTP pour notifications suite (partages, mentions).",
enabled: false,
configured: false,
href: "/admin/settings/mailing",
},
]
type OrgSettingsActions = {
setAuthentik: (patch: Partial<AuthentikSettings>) => void
setIdentityProviders: (patch: Partial<IdentityProvidersPolicy>) => void
setTwoFactor: (patch: Partial<TwoFactorPolicy>) => void
setStorageQuotas: (patch: Partial<OrgStorageQuotas>) => void
setUsageQuotas: (patch: Partial<UsageQuotaDefaults>) => void
setFilePolicies: (patch: Partial<FilePolicySettings>) => void
setLlm: (patch: Partial<OrgLLMSettings>) => void
setSearch: (patch: Partial<OrgSearchSettings>) => void
setNextcloud: (patch: Partial<NextcloudSettings>) => void
setMailing: (patch: Partial<MailingSettings>) => void
setOnlyoffice: (patch: Partial<OnlyOfficeSettings>) => void
setRichtext: (patch: Partial<RichTextSettings>) => void
setAiAssistant: (patch: Partial<AiAssistantSettings>) => void
setAgenda: (patch: Partial<AgendaOrgPolicySettings>) => void
setAdministrators: (admins: Administrator[]) => void
addAdministrator: (admin: Administrator) => void
removeAdministrator: (id: string) => void
updateAdministrator: (id: string, patch: Partial<Administrator>) => void
setPlugins: (plugins: PluginEntry[]) => void
togglePlugin: (id: string, enabled: boolean) => void
setIntegrations: (integrations: IntegrationEntry[]) => void
toggleIntegration: (id: string, enabled: boolean) => void
hydrateFromApi: (patch: Partial<{
authentik: AuthentikSettings
identityProviders: IdentityProvidersPolicy
twoFactor: TwoFactorPolicy
storageQuotas: OrgStorageQuotas
usageQuotas: UsageQuotaDefaults
filePolicies: FilePolicySettings
llm: OrgLLMSettings
search: OrgSearchSettings
administrators: Administrator[]
nextcloud: NextcloudSettings
mailing: MailingSettings
onlyoffice: OnlyOfficeSettings
richtext: RichTextSettings
aiAssistant: AiAssistantSettings
agenda: AgendaOrgPolicySettings
meet: MeetOrgPolicySettings
plugins: PluginEntry[]
integrations: IntegrationEntry[]
}>, meta?: OrgSettingsMeta) => void
setMeet: (patch: Partial<MeetOrgPolicySettings>) => void
}
export const useOrgSettingsStore = create<
{
authentik: AuthentikSettings
identityProviders: IdentityProvidersPolicy
twoFactor: TwoFactorPolicy
storageQuotas: OrgStorageQuotas
usageQuotas: UsageQuotaDefaults
filePolicies: FilePolicySettings
llm: OrgLLMSettings
search: OrgSearchSettings
administrators: Administrator[]
nextcloud: NextcloudSettings
mailing: MailingSettings
onlyoffice: OnlyOfficeSettings
richtext: RichTextSettings
aiAssistant: AiAssistantSettings
agenda: AgendaOrgPolicySettings
meet: MeetOrgPolicySettings
plugins: PluginEntry[]
integrations: IntegrationEntry[]
meta: OrgSettingsMeta | null
apiSynced: boolean
} & OrgSettingsActions
>()((set) => ({
authentik: DEFAULT_AUTHENTIK,
identityProviders: DEFAULT_IDENTITY_PROVIDERS,
twoFactor: DEFAULT_TWO_FACTOR,
storageQuotas: DEFAULT_STORAGE_QUOTAS,
usageQuotas: DEFAULT_USAGE_QUOTAS,
filePolicies: DEFAULT_FILE_POLICIES,
llm: DEFAULT_LLM,
search: DEFAULT_SEARCH,
administrators: [],
nextcloud: DEFAULT_NEXTCLOUD,
mailing: DEFAULT_MAILING,
onlyoffice: DEFAULT_ONLYOFFICE,
richtext: DEFAULT_RICHTEXT,
aiAssistant: DEFAULT_AI_ASSISTANT,
agenda: DEFAULT_AGENDA,
meet: DEFAULT_MEET,
plugins: DEFAULT_PLUGINS,
integrations: DEFAULT_INTEGRATIONS,
meta: null,
apiSynced: false,
setAuthentik: (patch) =>
set((s) => ({ authentik: { ...s.authentik, ...patch } })),
setIdentityProviders: (patch) =>
set((s) => ({
identityProviders: { ...s.identityProviders, ...patch },
})),
setTwoFactor: (patch) =>
set((s) => ({ twoFactor: { ...s.twoFactor, ...patch } })),
setStorageQuotas: (patch) =>
set((s) => ({ storageQuotas: { ...s.storageQuotas, ...patch } })),
setUsageQuotas: (patch) =>
set((s) => ({ usageQuotas: { ...s.usageQuotas, ...patch } })),
setFilePolicies: (patch) =>
set((s) => ({ filePolicies: { ...s.filePolicies, ...patch } })),
setLlm: (patch) => set((s) => ({ llm: { ...s.llm, ...patch } })),
setSearch: (patch) => set((s) => ({ search: { ...s.search, ...patch } })),
setNextcloud: (patch) =>
set((s) => ({ nextcloud: { ...s.nextcloud, ...patch } })),
setMailing: (patch) =>
set((s) => ({ mailing: { ...s.mailing, ...patch } })),
setOnlyoffice: (patch) =>
set((s) => ({ onlyoffice: { ...s.onlyoffice, ...patch } })),
setRichtext: (patch) =>
set((s) => ({ richtext: { ...s.richtext, ...patch } })),
setAiAssistant: (patch) =>
set((s) => ({ aiAssistant: { ...s.aiAssistant, ...patch } })),
setAgenda: (patch) => set((s) => ({ agenda: { ...s.agenda, ...patch } })),
setMeet: (patch) =>
set((s) => ({
meet: {
...s.meet,
...patch,
post_actions: patch.post_actions
? { ...s.meet.post_actions, ...patch.post_actions }
: s.meet.post_actions,
},
})),
setAdministrators: (administrators) => set({ administrators }),
addAdministrator: (admin) =>
set((s) => ({ administrators: [...s.administrators, admin] })),
removeAdministrator: (id) =>
set((s) => ({
administrators: s.administrators.filter((a) => a.id !== id),
})),
updateAdministrator: (id, patch) =>
set((s) => ({
administrators: s.administrators.map((a) =>
a.id === id ? { ...a, ...patch } : a
),
})),
setPlugins: (plugins) => set({ plugins }),
togglePlugin: (id, enabled) =>
set((s) => {
const plugins = s.plugins.map((p) => (p.id === id ? { ...p, enabled } : p))
if (id === "ai-assistant") {
return {
plugins,
aiAssistant: { ...s.aiAssistant, enabled },
}
}
return { plugins }
}),
setIntegrations: (integrations) => set({ integrations }),
toggleIntegration: (id, enabled) =>
set((s) => ({
integrations: s.integrations.map((i) =>
i.id === id ? { ...i, enabled } : i
),
})),
hydrateFromApi: (patch, meta) =>
set((s) => ({
...s,
...patch,
meta: meta ?? s.meta,
apiSynced: true,
})),
}))