From ba80ca1f6792729b4042c128867e7f961c5d09d4 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:58:05 +0000 Subject: [PATCH] app URL settings --- .../src/main/resources/settings.yml.template | 1 + .../public/locales/en-GB/translation.json | 4 +- .../configSections/AdminMailSection.tsx | 59 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/app/core/src/main/resources/settings.yml.template b/app/core/src/main/resources/settings.yml.template index 70417f65b..78eb55b60 100644 --- a/app/core/src/main/resources/settings.yml.template +++ b/app/core/src/main/resources/settings.yml.template @@ -128,6 +128,7 @@ system: disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML) maxDPI: 500 # Maximum allowed DPI for PDF to image conversion corsAllowedOrigins: [] # List of allowed origins for CORS (e.g. ['http://localhost:5173', 'https://app.example.com']). Leave empty to disable CORS. + frontendUrl: '' # Base URL for frontend (e.g. 'https://pdf.example.com'). Used for generating invite links in emails. If empty, falls back to backend URL. serverCertificate: enabled: true # Enable server-side certificate for "Sign with Stirling-PDF" option organizationName: Stirling-PDF # Organization name for generated certificates diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index a67ade89d..a00ca634d 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -3801,7 +3801,9 @@ "from": "From Address", "from.description": "The email address to use as the sender", "enableInvites": "Enable Email Invites", - "enableInvites.description": "Allow admins to invite users via email with auto-generated passwords" + "enableInvites.description": "Allow admins to invite users via email with auto-generated passwords", + "frontendUrl": "Frontend URL", + "frontendUrl.description": "Base URL for frontend (e.g. https://pdf.example.com). Used for generating invite links in emails. Leave empty to use backend URL." }, "legal": { "title": "Legal Documents", diff --git a/frontend/src/core/components/shared/config/configSections/AdminMailSection.tsx b/frontend/src/core/components/shared/config/configSections/AdminMailSection.tsx index fbbfeebbf..360d3eade 100644 --- a/frontend/src/core/components/shared/config/configSections/AdminMailSection.tsx +++ b/frontend/src/core/components/shared/config/configSections/AdminMailSection.tsx @@ -6,15 +6,18 @@ import RestartConfirmationModal from '@app/components/shared/config/RestartConfi import { useRestartServer } from '@app/components/shared/config/useRestartServer'; import { useAdminSettings } from '@app/hooks/useAdminSettings'; import PendingBadge from '@app/components/shared/config/PendingBadge'; +import apiClient from '@app/services/apiClient'; interface MailSettingsData { enabled?: boolean; enableInvites?: boolean; + inviteLinkExpiryHours?: number; host?: string; port?: number; username?: string; password?: string; from?: string; + frontendUrl?: string; } export default function AdminMailSection() { @@ -31,6 +34,47 @@ export default function AdminMailSection() { isFieldPending, } = useAdminSettings({ sectionName: 'mail', + fetchTransformer: async () => { + const [mailResponse, systemResponse] = await Promise.all([ + apiClient.get('/api/v1/admin/settings/section/mail'), + apiClient.get('/api/v1/admin/settings/section/system') + ]); + + const mail = mailResponse.data || {}; + const system = systemResponse.data || {}; + + const result: any = { + ...mail, + frontendUrl: system.frontendUrl || '' + }; + + // Merge pending blocks from both endpoints + const pendingBlock: any = {}; + if (mail._pending) { + Object.assign(pendingBlock, mail._pending); + } + if (system._pending?.frontendUrl !== undefined) { + pendingBlock.frontendUrl = system._pending.frontendUrl; + } + + if (Object.keys(pendingBlock).length > 0) { + result._pending = pendingBlock; + } + + return result; + }, + saveTransformer: (settings) => { + const { frontendUrl, ...mailSettings } = settings; + + const deltaSettings: Record = { + 'system.frontendUrl': frontendUrl + }; + + return { + sectionData: mailSettings, + deltaSettings + }; + } }); useEffect(() => { @@ -175,6 +219,21 @@ export default function AdminMailSection() { placeholder="noreply@example.com" /> + +
+ + {t('admin.settings.mail.frontendUrl', 'Frontend URL')} + + + } + description={t('admin.settings.mail.frontendUrl.description', 'Base URL for frontend (e.g. https://pdf.example.com). Used for generating invite links in emails. Leave empty to use backend URL.')} + value={settings.frontendUrl || ''} + onChange={(e) => setSettings({ ...settings, frontendUrl: e.target.value })} + placeholder="https://pdf.example.com" + /> +