From f346d2da5b3ac370b5761e747496aa37903e3692 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 14 Apr 2026 10:18:20 +0200 Subject: [PATCH 1/9] move cert wizard pages --- .../SettingsCoreCertificateWizardPage.tsx | 6 ++-- ...rtificateWizardInternalUrlSettingsStep.tsx | 33 ++++++++++--------- ...tificateWizardInternalUrlSslConfigStep.tsx | 22 ++++++------- ...ttingsCoreCertificateWizardSummaryStep.tsx | 12 +++---- .../style.scss | 0 .../types.ts | 0 .../useSettingsCoreCertificateWizardStore.ts | 2 +- .../SettingsEdgeCertificateWizardPage.tsx | 6 ++-- ...rtificateWizardExternalUrlSettingsStep.tsx | 33 ++++++++++--------- ...tificateWizardExternalUrlSslConfigStep.tsx | 28 ++++++++-------- ...ttingsEdgeCertificateWizardSummaryStep.tsx | 12 +++---- .../style.scss | 0 .../types.ts | 0 .../useSettingsEdgeCertificateWizardStore.ts | 2 +- .../SettingsCertificatesPage/utils.ts | 10 ++++++ .../_wizard/settings-core-certificate.tsx | 2 +- .../_wizard/settings-edge-certificate.tsx | 2 +- 17 files changed, 91 insertions(+), 79 deletions(-) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx (92%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx (81%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx (68%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx (75%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/style.scss (100%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/types.ts (100%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts (95%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx (92%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx (83%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx (90%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx (75%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/style.scss (100%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/types.ts (100%) rename web/src/pages/{ => settings/SettingsCertificatesPage}/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts (95%) create mode 100644 web/src/pages/settings/SettingsCertificatesPage/utils.ts diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx similarity index 92% rename from web/src/pages/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx index f52d78efe..910b69fe6 100644 --- a/web/src/pages/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage.tsx @@ -1,8 +1,8 @@ import { useNavigate } from '@tanstack/react-router'; import { type ReactNode, useEffect, useMemo } from 'react'; -import { m } from '../../paraglide/messages'; -import type { WizardPageStep } from '../../shared/components/wizard/types'; -import { WizardPage } from '../../shared/components/wizard/WizardPage/WizardPage'; +import { m } from '../../../../paraglide/messages'; +import type { WizardPageStep } from '../../../../shared/components/wizard/types'; +import { WizardPage } from '../../../../shared/components/wizard/WizardPage/WizardPage'; import { SettingsCoreCertificateWizardInternalUrlSettingsStep } from './steps/SettingsCoreCertificateWizardInternalUrlSettingsStep'; import { SettingsCoreCertificateWizardInternalUrlSslConfigStep } from './steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep'; import { SettingsCoreCertificateWizardSummaryStep } from './steps/SettingsCoreCertificateWizardSummaryStep'; diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx similarity index 81% rename from web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx index 967bc26a3..7d94ff3c9 100644 --- a/web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSettingsStep.tsx @@ -1,20 +1,21 @@ import { useMutation } from '@tanstack/react-query'; import z from 'zod'; -import { m } from '../../../paraglide/messages'; -import api from '../../../shared/api/api'; -import type { InternalSslType } from '../../../shared/api/types'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { Divider } from '../../../shared/defguard-ui/components/Divider/Divider'; -import { Helper } from '../../../shared/defguard-ui/components/Helper/Helper'; -import { Radio } from '../../../shared/defguard-ui/components/Radio/Radio'; -import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { Snackbar } from '../../../shared/defguard-ui/providers/snackbar/snackbar'; -import { ThemeSpacing } from '../../../shared/defguard-ui/types'; -import { useAppForm } from '../../../shared/form'; -import { formChangeLogic } from '../../../shared/formLogic'; -import '../../SetupPage/autoAdoption/steps/style.scss'; +import { m } from '../../../../../paraglide/messages'; +import api from '../../../../../shared/api/api'; +import type { InternalSslType } from '../../../../../shared/api/types'; +import { Controls } from '../../../../../shared/components/Controls/Controls'; +import { WizardCard } from '../../../../../shared/components/wizard/WizardCard/WizardCard'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { Divider } from '../../../../../shared/defguard-ui/components/Divider/Divider'; +import { Helper } from '../../../../../shared/defguard-ui/components/Helper/Helper'; +import { Radio } from '../../../../../shared/defguard-ui/components/Radio/Radio'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { Snackbar } from '../../../../../shared/defguard-ui/providers/snackbar/snackbar'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; +import { useAppForm } from '../../../../../shared/form'; +import { formChangeLogic } from '../../../../../shared/formLogic'; +import '../../../../SetupPage/autoAdoption/steps/style.scss'; +import { getApiErrorMessage } from '../../utils'; import { SettingsCoreCertificateWizardStep } from '../types'; import { useSettingsCoreCertificateWizardStore } from '../useSettingsCoreCertificateWizardStore'; @@ -43,7 +44,7 @@ export const SettingsCoreCertificateWizardInternalUrlSettingsStep = () => { }); }, onError: (error) => { - Snackbar.error(m.settings_msg_save_failed()); + Snackbar.error(getApiErrorMessage(error) ?? m.settings_msg_save_failed()); console.error('Failed to save core internal URL settings:', error); }, }); diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx similarity index 68% rename from web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx index 68615ed19..5a6105376 100644 --- a/web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx @@ -1,16 +1,16 @@ import { useQuery } from '@tanstack/react-query'; import { useEffect } from 'react'; -import { m } from '../../../paraglide/messages'; -import api from '../../../shared/api/api'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { InternalSslResult } from '../../../shared/components/certificates/InternalSslResult/InternalSslResult'; -import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { ThemeSpacing } from '../../../shared/defguard-ui/types'; -import { downloadFile } from '../../../shared/utils/download'; -import caIcon from '../../SetupPage/assets/ca.png'; -import '../../SetupPage/autoAdoption/steps/style.scss'; +import { m } from '../../../../../paraglide/messages'; +import api from '../../../../../shared/api/api'; +import { Controls } from '../../../../../shared/components/Controls/Controls'; +import { InternalSslResult } from '../../../../../shared/components/certificates/InternalSslResult/InternalSslResult'; +import { WizardCard } from '../../../../../shared/components/wizard/WizardCard/WizardCard'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; +import { downloadFile } from '../../../../../shared/utils/download'; +import caIcon from '../../../../SetupPage/assets/ca.png'; +import '../../../../SetupPage/autoAdoption/steps/style.scss'; import { SettingsCoreCertificateWizardStep } from '../types'; import { useSettingsCoreCertificateWizardStore } from '../useSettingsCoreCertificateWizardStore'; diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx similarity index 75% rename from web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx index 347883393..d0484929b 100644 --- a/web/src/pages/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardSummaryStep.tsx @@ -1,11 +1,11 @@ import { useQueryClient } from '@tanstack/react-query'; import { useNavigate } from '@tanstack/react-router'; -import { m } from '../../../paraglide/messages'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { ThemeSpacing } from '../../../shared/defguard-ui/types'; +import { m } from '../../../../../paraglide/messages'; +import { Controls } from '../../../../../shared/components/Controls/Controls'; +import { WizardCard } from '../../../../../shared/components/wizard/WizardCard/WizardCard'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; import { useSettingsCoreCertificateWizardStore } from '../useSettingsCoreCertificateWizardStore'; import '../style.scss'; diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/style.scss b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/style.scss similarity index 100% rename from web/src/pages/SettingsCoreCertificateWizardPage/style.scss rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/style.scss diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/types.ts b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/types.ts similarity index 100% rename from web/src/pages/SettingsCoreCertificateWizardPage/types.ts rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/types.ts diff --git a/web/src/pages/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts similarity index 95% rename from web/src/pages/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts rename to web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts index 44148f12b..3cec0c1f6 100644 --- a/web/src/pages/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/useSettingsCoreCertificateWizardStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import type { CertInfo, InternalSslType } from '../../shared/api/types'; +import type { CertInfo, InternalSslType } from '../../../../shared/api/types'; import { SettingsCoreCertificateWizardStep, type SettingsCoreCertificateWizardStepValue, diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx similarity index 92% rename from web/src/pages/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx index 349110d0b..a53362b18 100644 --- a/web/src/pages/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage.tsx @@ -1,8 +1,8 @@ import { useNavigate } from '@tanstack/react-router'; import { type ReactNode, useEffect, useMemo } from 'react'; -import { m } from '../../paraglide/messages'; -import type { WizardPageStep } from '../../shared/components/wizard/types'; -import { WizardPage } from '../../shared/components/wizard/WizardPage/WizardPage'; +import { m } from '../../../../paraglide/messages'; +import type { WizardPageStep } from '../../../../shared/components/wizard/types'; +import { WizardPage } from '../../../../shared/components/wizard/WizardPage/WizardPage'; import { SettingsEdgeCertificateWizardExternalUrlSettingsStep } from './steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep'; import { SettingsEdgeCertificateWizardExternalUrlSslConfigStep } from './steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep'; import { SettingsEdgeCertificateWizardSummaryStep } from './steps/SettingsEdgeCertificateWizardSummaryStep'; diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx similarity index 83% rename from web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx index f6928dc63..9ae62d3b2 100644 --- a/web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSettingsStep.tsx @@ -1,20 +1,21 @@ import { useMutation } from '@tanstack/react-query'; import z from 'zod'; -import { m } from '../../../paraglide/messages'; -import api from '../../../shared/api/api'; -import type { ExternalSslType } from '../../../shared/api/types'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { Divider } from '../../../shared/defguard-ui/components/Divider/Divider'; -import { Helper } from '../../../shared/defguard-ui/components/Helper/Helper'; -import { Radio } from '../../../shared/defguard-ui/components/Radio/Radio'; -import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { Snackbar } from '../../../shared/defguard-ui/providers/snackbar/snackbar'; -import { ThemeSpacing } from '../../../shared/defguard-ui/types'; -import { useAppForm } from '../../../shared/form'; -import { formChangeLogic } from '../../../shared/formLogic'; -import '../../SetupPage/autoAdoption/steps/style.scss'; +import { m } from '../../../../../paraglide/messages'; +import api from '../../../../../shared/api/api'; +import type { ExternalSslType } from '../../../../../shared/api/types'; +import { Controls } from '../../../../../shared/components/Controls/Controls'; +import { WizardCard } from '../../../../../shared/components/wizard/WizardCard/WizardCard'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { Divider } from '../../../../../shared/defguard-ui/components/Divider/Divider'; +import { Helper } from '../../../../../shared/defguard-ui/components/Helper/Helper'; +import { Radio } from '../../../../../shared/defguard-ui/components/Radio/Radio'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { Snackbar } from '../../../../../shared/defguard-ui/providers/snackbar/snackbar'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; +import { useAppForm } from '../../../../../shared/form'; +import { formChangeLogic } from '../../../../../shared/formLogic'; +import '../../../../SetupPage/autoAdoption/steps/style.scss'; +import { getApiErrorMessage } from '../../utils'; import { SettingsEdgeCertificateWizardStep } from '../types'; import { useSettingsEdgeCertificateWizardStore } from '../useSettingsEdgeCertificateWizardStore'; @@ -43,7 +44,7 @@ export const SettingsEdgeCertificateWizardExternalUrlSettingsStep = () => { }); }, onError: (error) => { - Snackbar.error(m.settings_msg_save_failed()); + Snackbar.error(getApiErrorMessage(error) ?? m.settings_msg_save_failed()); console.error('Failed to save edge external URL settings:', error); }, }); diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx similarity index 90% rename from web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx index ea03ce288..a871a0a5e 100644 --- a/web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx @@ -1,19 +1,19 @@ import { useQuery } from '@tanstack/react-query'; import { useCallback, useEffect, useState } from 'react'; -import { m } from '../../../paraglide/messages'; -import api from '../../../shared/api/api'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { LoadingStep } from '../../../shared/components/LoadingStep/LoadingStep'; -import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { CodeCard } from '../../../shared/defguard-ui/components/CodeCard/CodeCard'; -import { Divider } from '../../../shared/defguard-ui/components/Divider/Divider'; -import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { ThemeSpacing } from '../../../shared/defguard-ui/types'; -import { useSSEController } from '../../../shared/hooks/useSSEController'; -import { downloadFile } from '../../../shared/utils/download'; -import caIcon from '../../SetupPage/assets/ca.png'; -import '../../SetupPage/autoAdoption/steps/style.scss'; +import { m } from '../../../../../paraglide/messages'; +import api from '../../../../../shared/api/api'; +import { Controls } from '../../../../../shared/components/Controls/Controls'; +import { LoadingStep } from '../../../../../shared/components/LoadingStep/LoadingStep'; +import { WizardCard } from '../../../../../shared/components/wizard/WizardCard/WizardCard'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { CodeCard } from '../../../../../shared/defguard-ui/components/CodeCard/CodeCard'; +import { Divider } from '../../../../../shared/defguard-ui/components/Divider/Divider'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; +import { useSSEController } from '../../../../../shared/hooks/useSSEController'; +import { downloadFile } from '../../../../../shared/utils/download'; +import caIcon from '../../../../SetupPage/assets/ca.png'; +import '../../../../SetupPage/autoAdoption/steps/style.scss'; import { SettingsEdgeCertificateWizardStep } from '../types'; import { useSettingsEdgeCertificateWizardStore } from '../useSettingsEdgeCertificateWizardStore'; diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx similarity index 75% rename from web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx index 1c6c4c811..afe566208 100644 --- a/web/src/pages/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/steps/SettingsEdgeCertificateWizardSummaryStep.tsx @@ -1,11 +1,11 @@ import { useQueryClient } from '@tanstack/react-query'; import { useNavigate } from '@tanstack/react-router'; -import { m } from '../../../paraglide/messages'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { ThemeSpacing } from '../../../shared/defguard-ui/types'; +import { m } from '../../../../../paraglide/messages'; +import { Controls } from '../../../../../shared/components/Controls/Controls'; +import { WizardCard } from '../../../../../shared/components/wizard/WizardCard/WizardCard'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; import { useSettingsEdgeCertificateWizardStore } from '../useSettingsEdgeCertificateWizardStore'; import '../style.scss'; diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/style.scss b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/style.scss similarity index 100% rename from web/src/pages/SettingsEdgeCertificateWizardPage/style.scss rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/style.scss diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/types.ts b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/types.ts similarity index 100% rename from web/src/pages/SettingsEdgeCertificateWizardPage/types.ts rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/types.ts diff --git a/web/src/pages/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts similarity index 95% rename from web/src/pages/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts rename to web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts index 8dc123b11..1a36eca44 100644 --- a/web/src/pages/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/useSettingsEdgeCertificateWizardStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import type { CertInfo, ExternalSslType } from '../../shared/api/types'; +import type { CertInfo, ExternalSslType } from '../../../../shared/api/types'; import { SettingsEdgeCertificateWizardStep, type SettingsEdgeCertificateWizardStepValue, diff --git a/web/src/pages/settings/SettingsCertificatesPage/utils.ts b/web/src/pages/settings/SettingsCertificatesPage/utils.ts new file mode 100644 index 000000000..40b11b6b7 --- /dev/null +++ b/web/src/pages/settings/SettingsCertificatesPage/utils.ts @@ -0,0 +1,10 @@ +import axios from 'axios'; + +export const getApiErrorMessage = (error: unknown): string | null => { + if (axios.isAxiosError(error)) { + const message = error.response?.data?.msg; + return typeof message === 'string' ? message : null; + } + + return null; +}; diff --git a/web/src/routes/_authorized/_wizard/settings-core-certificate.tsx b/web/src/routes/_authorized/_wizard/settings-core-certificate.tsx index 2aa4e8066..ec2d11e7c 100644 --- a/web/src/routes/_authorized/_wizard/settings-core-certificate.tsx +++ b/web/src/routes/_authorized/_wizard/settings-core-certificate.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from '@tanstack/react-router'; -import { SettingsCoreCertificateWizardPage } from '../../../pages/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage'; +import { SettingsCoreCertificateWizardPage } from '../../../pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/SettingsCoreCertificateWizardPage'; export const Route = createFileRoute('/_authorized/_wizard/settings-core-certificate')({ component: SettingsCoreCertificateWizardPage, diff --git a/web/src/routes/_authorized/_wizard/settings-edge-certificate.tsx b/web/src/routes/_authorized/_wizard/settings-edge-certificate.tsx index a05af6bc1..c7d43e1e4 100644 --- a/web/src/routes/_authorized/_wizard/settings-edge-certificate.tsx +++ b/web/src/routes/_authorized/_wizard/settings-edge-certificate.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from '@tanstack/react-router'; -import { SettingsEdgeCertificateWizardPage } from '../../../pages/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage'; +import { SettingsEdgeCertificateWizardPage } from '../../../pages/settings/SettingsCertificatesPage/SettingsEdgeCertificateWizardPage/SettingsEdgeCertificateWizardPage'; export const Route = createFileRoute('/_authorized/_wizard/settings-edge-certificate')({ component: SettingsEdgeCertificateWizardPage, From b4d35621529f9e7ebf3834cb9d5c5dbdcf2d8191 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 14 Apr 2026 10:43:23 +0200 Subject: [PATCH 2/9] validate dates when uploading a certificate --- crates/defguard_core/src/cert_settings.rs | 97 ++++++++++++------- .../tests/integration/api/common/mod.rs | 12 +++ .../tests/integration/api/core_certs.rs | 35 +++++-- .../tests/integration/api/proxy_certs.rs | 33 +++++-- 4 files changed, 122 insertions(+), 55 deletions(-) diff --git a/crates/defguard_core/src/cert_settings.rs b/crates/defguard_core/src/cert_settings.rs index 586468e68..b418c9c9f 100644 --- a/crates/defguard_core/src/cert_settings.rs +++ b/crates/defguard_core/src/cert_settings.rs @@ -1,7 +1,7 @@ use axum_server::tls_rustls::RustlsConfig; use defguard_certs::{ - CertificateAuthority, CertificateInfo, Csr, DnType, PemLabel, der_to_pem, generate_key_pair, - parse_pem_certificate, + CertificateAuthority, CertificateInfo, Csr, DnType, PemLabel, der_to_pem, generate_key_pair, + parse_pem_certificate, }; use defguard_common::db::models::{ Certificates, CoreCertSource, ProxyCertSource, settings::update_current_settings, @@ -12,14 +12,43 @@ use utoipa::ToSchema; use crate::error::WebError; -/// Ensures cert & key pair are valid to avoid bricking the web server after restart. -async fn validate_uploaded_cert_pair(cert_pem: &str, key_pem: &str) -> Result<(), WebError> { - let _ = rustls::crypto::ring::default_provider().install_default(); +fn validate_cert_validity(info: &CertificateInfo) -> Result<(), WebError> { + let now = chrono::Utc::now().naive_utc(); - RustlsConfig::from_pem(cert_pem.as_bytes().to_vec(), key_pem.as_bytes().to_vec()) - .await - .map(|_| ()) - .map_err(|_| WebError::BadRequest("Invalid certificate or private key PEM".to_string())) + if info.not_after <= info.not_before { + return Err(WebError::BadRequest( + "Certificate validity period is invalid".to_string(), + )); + } + + if info.not_after <= now { + return Err(WebError::BadRequest( + "Certificate has expired".to_string(), + )); + } + + if info.not_before > now { + return Err(WebError::BadRequest( + "Certificate is not valid yet".to_string(), + )); + } + + Ok(()) +} + +/// Parses an uploaded certificate, validates its key pair, and rejects invalid validity windows. +async fn parse_cert(cert_pem: &str, key_pem: &str) -> Result { + let _ = rustls::crypto::ring::default_provider().install_default(); + + RustlsConfig::from_pem(cert_pem.as_bytes().to_vec(), key_pem.as_bytes().to_vec()) + .await + .map_err(|_| WebError::BadRequest("Invalid certificate or private key PEM".to_string()))?; + + let cert_der = parse_pem_certificate(cert_pem)?; + let info = CertificateInfo::from_der(cert_der.as_ref())?; + validate_cert_validity(&info)?; + + Ok(info) } /// SSL configuration type for Defguard's internal (core) web server. @@ -142,20 +171,17 @@ pub async fn apply_internal_url_settings( not_after: info.not_after.to_string(), }) } - InternalSslType::OwnCert => { - let cert_pem_str = config.cert_pem.ok_or_else(|| { - WebError::BadRequest("cert_pem is required for own_cert".to_string()) - })?; - let key_pem_str = config.key_pem.ok_or_else(|| { - WebError::BadRequest("key_pem is required for own_cert".to_string()) - })?; - - validate_uploaded_cert_pair(&cert_pem_str, &key_pem_str).await?; - - let cert_der = parse_pem_certificate(&cert_pem_str)?; - let info = CertificateInfo::from_der(cert_der.as_ref())?; - let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); - let expiry = info.not_after; + InternalSslType::OwnCert => { + let cert_pem_str = config.cert_pem.ok_or_else(|| { + WebError::BadRequest("cert_pem is required for own_cert".to_string()) + })?; + let key_pem_str = config.key_pem.ok_or_else(|| { + WebError::BadRequest("key_pem is required for own_cert".to_string()) + })?; + + let info = parse_cert(&cert_pem_str, &key_pem_str).await?; + let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); + let expiry = info.not_after; certs.core_http_cert_source = CoreCertSource::Custom; certs.core_http_cert_pem = Some(cert_pem_str); @@ -267,20 +293,17 @@ pub async fn apply_external_url_settings( not_after: info.not_after.to_string(), }) } - ExternalSslType::OwnCert => { - let cert_pem_str = config.cert_pem.ok_or_else(|| { - WebError::BadRequest("cert_pem is required for own_cert".to_string()) - })?; - let key_pem_str = config.key_pem.ok_or_else(|| { - WebError::BadRequest("key_pem is required for own_cert".to_string()) - })?; - - validate_uploaded_cert_pair(&cert_pem_str, &key_pem_str).await?; - - let cert_der = parse_pem_certificate(&cert_pem_str)?; - let info = CertificateInfo::from_der(cert_der.as_ref())?; - let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); - let expiry = info.not_after; + ExternalSslType::OwnCert => { + let cert_pem_str = config.cert_pem.ok_or_else(|| { + WebError::BadRequest("cert_pem is required for own_cert".to_string()) + })?; + let key_pem_str = config.key_pem.ok_or_else(|| { + WebError::BadRequest("key_pem is required for own_cert".to_string()) + })?; + + let info = parse_cert(&cert_pem_str, &key_pem_str).await?; + let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); + let expiry = info.not_after; certs.proxy_http_cert_source = ProxyCertSource::Custom; certs.acme_domain = None; diff --git a/crates/defguard_core/tests/integration/api/common/mod.rs b/crates/defguard_core/tests/integration/api/common/mod.rs index 700924dc2..495f193bb 100644 --- a/crates/defguard_core/tests/integration/api/common/mod.rs +++ b/crates/defguard_core/tests/integration/api/common/mod.rs @@ -261,6 +261,18 @@ pub(crate) fn generate_test_cert_pem(common_name: &str) -> (String, String) { (cert_pem, key_pem) } +pub(crate) fn generate_expired_test_cert_pem(common_name: &str) -> (String, String) { + let ca = CertificateAuthority::new("Test CA", "test@example.com", 365).unwrap(); + let key_pair = generate_key_pair().unwrap(); + let san = vec![common_name.to_string()]; + let dn = vec![(DnType::CommonName, common_name)]; + let csr = Csr::new(&key_pair, &san, dn).unwrap(); + let cert = ca.sign_csr_with_validity(&csr, 0).unwrap(); + let cert_pem = der_to_pem(cert.der(), PemLabel::Certificate).unwrap(); + let key_pem = der_to_pem(key_pair.serialize_der().as_slice(), PemLabel::PrivateKey).unwrap(); + (cert_pem, key_pem) +} + /// Set minimal SMTP fields on a [`Settings`] so that `smtp_configured()` returns `true`. pub(crate) fn configure_smtp(settings: &mut Settings) { settings.smtp_server = Some("smtp.example.com".into()); diff --git a/crates/defguard_core/tests/integration/api/core_certs.rs b/crates/defguard_core/tests/integration/api/core_certs.rs index a46aae51e..d9b537c63 100644 --- a/crates/defguard_core/tests/integration/api/core_certs.rs +++ b/crates/defguard_core/tests/integration/api/core_certs.rs @@ -7,7 +7,9 @@ use reqwest::StatusCode; use serde_json::json; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; -use super::common::{generate_test_cert_pem, make_test_client, setup_pool}; +use super::common::{ + generate_expired_test_cert_pem, generate_test_cert_pem, make_test_client, setup_pool, +}; async fn seed_ca(pool: &sqlx::PgPool) { let ca = CertificateAuthority::new("Test CA", "test@example.com", 365).unwrap(); @@ -78,10 +80,10 @@ async fn test_internal_url_settings_endpoint(_: PgPoolOptions, options: PgConnec ); let (cert_pem, key_pem) = generate_test_cert_pem("uploaded.example.com"); - let response = client - .post("/api/v1/core/cert/internal_url_settings") - .json(&json!({ - "ssl_type": "own_cert", + let response = client + .post("/api/v1/core/cert/internal_url_settings") + .json(&json!({ + "ssl_type": "own_cert", "cert_pem": cert_pem, "key_pem": key_pem })) @@ -113,8 +115,23 @@ async fn test_internal_url_settings_endpoint(_: PgPoolOptions, options: PgConnec .json(&json!({ "ssl_type": "own_cert", "cert_pem": "-----BEGIN CERTIFICATE-----\nfake\n-----END CERTIFICATE-----\n" - })) - .send() - .await; - assert_eq!(response.status(), StatusCode::BAD_REQUEST); + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + + let (expired_cert_pem, expired_key_pem) = + generate_expired_test_cert_pem("expired.example.com"); + let response = client + .post("/api/v1/core/cert/internal_url_settings") + .json(&json!({ + "ssl_type": "own_cert", + "cert_pem": expired_cert_pem, + "key_pem": expired_key_pem + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let body: serde_json::Value = response.json().await; + assert_eq!(body["msg"], "Certificate has expired"); } diff --git a/crates/defguard_core/tests/integration/api/proxy_certs.rs b/crates/defguard_core/tests/integration/api/proxy_certs.rs index 619906077..d4038c00d 100644 --- a/crates/defguard_core/tests/integration/api/proxy_certs.rs +++ b/crates/defguard_core/tests/integration/api/proxy_certs.rs @@ -48,7 +48,7 @@ use tokio::{ }, }; -use super::common::{client::TestClient, generate_test_cert_pem}; +use super::common::{client::TestClient, generate_expired_test_cert_pem, generate_test_cert_pem}; use crate::common::{init_config, initialize_users}; // Mock: captures messages sent to the proxy manager channel. @@ -276,10 +276,10 @@ async fn test_external_url_settings_endpoint(_: PgPoolOptions, opts: PgConnectOp let (cert_pem, key_pem) = generate_test_cert_pem("uploaded-edge.example.com"); let expected_cert_pem = cert_pem.clone(); let expected_key_pem = key_pem.clone(); - let response = client - .post("/api/v1/proxy/cert/external_url_settings") - .json(&json!({ - "ssl_type": "own_cert", + let response = client + .post("/api/v1/proxy/cert/external_url_settings") + .json(&json!({ + "ssl_type": "own_cert", "cert_pem": cert_pem, "key_pem": key_pem })) @@ -320,8 +320,23 @@ async fn test_external_url_settings_endpoint(_: PgPoolOptions, opts: PgConnectOp .json(&json!({ "ssl_type": "own_cert", "cert_pem": "-----BEGIN CERTIFICATE-----\nfake\n-----END CERTIFICATE-----\n" - })) - .send() - .await; - assert_eq!(response.status(), StatusCode::BAD_REQUEST); + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + + let (expired_cert_pem, expired_key_pem) = + generate_expired_test_cert_pem("expired-edge.example.com"); + let response = client + .post("/api/v1/proxy/cert/external_url_settings") + .json(&json!({ + "ssl_type": "own_cert", + "cert_pem": expired_cert_pem, + "key_pem": expired_key_pem + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let body: serde_json::Value = response.json().await; + assert_eq!(body["msg"], "Certificate has expired"); } From 5456b7277fb9eadc14dac95624e55e9e791f890e Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 14 Apr 2026 10:58:31 +0200 Subject: [PATCH 3/9] refactor cert validation --- crates/defguard_core/src/cert_settings.rs | 102 ++++++++++------------ 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/crates/defguard_core/src/cert_settings.rs b/crates/defguard_core/src/cert_settings.rs index b418c9c9f..00777d7f6 100644 --- a/crates/defguard_core/src/cert_settings.rs +++ b/crates/defguard_core/src/cert_settings.rs @@ -1,7 +1,7 @@ use axum_server::tls_rustls::RustlsConfig; use defguard_certs::{ - CertificateAuthority, CertificateInfo, Csr, DnType, PemLabel, der_to_pem, generate_key_pair, - parse_pem_certificate, + CertificateAuthority, CertificateInfo, Csr, DnType, PemLabel, der_to_pem, generate_key_pair, + parse_pem_certificate, }; use defguard_common::db::models::{ Certificates, CoreCertSource, ProxyCertSource, settings::update_current_settings, @@ -12,43 +12,37 @@ use utoipa::ToSchema; use crate::error::WebError; -fn validate_cert_validity(info: &CertificateInfo) -> Result<(), WebError> { - let now = chrono::Utc::now().naive_utc(); +/// Parses an uploaded certificate, validates its key pair, and rejects invalid validity windows. +async fn parse_cert(cert_pem: &str, key_pem: &str) -> Result { + let _ = rustls::crypto::ring::default_provider().install_default(); - if info.not_after <= info.not_before { - return Err(WebError::BadRequest( - "Certificate validity period is invalid".to_string(), - )); - } + RustlsConfig::from_pem(cert_pem.as_bytes().to_vec(), key_pem.as_bytes().to_vec()) + .await + .map_err(|_| WebError::BadRequest("Invalid certificate or private key PEM".to_string()))?; - if info.not_after <= now { - return Err(WebError::BadRequest( - "Certificate has expired".to_string(), - )); - } + let cert_der = parse_pem_certificate(cert_pem)?; + let info = CertificateInfo::from_der(cert_der.as_ref())?; - if info.not_before > now { - return Err(WebError::BadRequest( - "Certificate is not valid yet".to_string(), - )); - } + // Validate cert dates + let now = chrono::Utc::now().naive_utc(); - Ok(()) -} + if info.not_after <= info.not_before { + return Err(WebError::BadRequest( + "Certificate validity period is invalid".to_string(), + )); + } -/// Parses an uploaded certificate, validates its key pair, and rejects invalid validity windows. -async fn parse_cert(cert_pem: &str, key_pem: &str) -> Result { - let _ = rustls::crypto::ring::default_provider().install_default(); - - RustlsConfig::from_pem(cert_pem.as_bytes().to_vec(), key_pem.as_bytes().to_vec()) - .await - .map_err(|_| WebError::BadRequest("Invalid certificate or private key PEM".to_string()))?; + if info.not_after <= now { + return Err(WebError::BadRequest("Certificate has expired".to_string())); + } - let cert_der = parse_pem_certificate(cert_pem)?; - let info = CertificateInfo::from_der(cert_der.as_ref())?; - validate_cert_validity(&info)?; + if info.not_before > now { + return Err(WebError::BadRequest( + "Certificate is not valid yet".to_string(), + )); + } - Ok(info) + Ok(info) } /// SSL configuration type for Defguard's internal (core) web server. @@ -171,17 +165,17 @@ pub async fn apply_internal_url_settings( not_after: info.not_after.to_string(), }) } - InternalSslType::OwnCert => { - let cert_pem_str = config.cert_pem.ok_or_else(|| { - WebError::BadRequest("cert_pem is required for own_cert".to_string()) - })?; - let key_pem_str = config.key_pem.ok_or_else(|| { - WebError::BadRequest("key_pem is required for own_cert".to_string()) - })?; - - let info = parse_cert(&cert_pem_str, &key_pem_str).await?; - let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); - let expiry = info.not_after; + InternalSslType::OwnCert => { + let cert_pem_str = config.cert_pem.ok_or_else(|| { + WebError::BadRequest("cert_pem is required for own_cert".to_string()) + })?; + let key_pem_str = config.key_pem.ok_or_else(|| { + WebError::BadRequest("key_pem is required for own_cert".to_string()) + })?; + + let info = parse_cert(&cert_pem_str, &key_pem_str).await?; + let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); + let expiry = info.not_after; certs.core_http_cert_source = CoreCertSource::Custom; certs.core_http_cert_pem = Some(cert_pem_str); @@ -293,17 +287,17 @@ pub async fn apply_external_url_settings( not_after: info.not_after.to_string(), }) } - ExternalSslType::OwnCert => { - let cert_pem_str = config.cert_pem.ok_or_else(|| { - WebError::BadRequest("cert_pem is required for own_cert".to_string()) - })?; - let key_pem_str = config.key_pem.ok_or_else(|| { - WebError::BadRequest("key_pem is required for own_cert".to_string()) - })?; - - let info = parse_cert(&cert_pem_str, &key_pem_str).await?; - let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); - let expiry = info.not_after; + ExternalSslType::OwnCert => { + let cert_pem_str = config.cert_pem.ok_or_else(|| { + WebError::BadRequest("cert_pem is required for own_cert".to_string()) + })?; + let key_pem_str = config.key_pem.ok_or_else(|| { + WebError::BadRequest("key_pem is required for own_cert".to_string()) + })?; + + let info = parse_cert(&cert_pem_str, &key_pem_str).await?; + let valid_for_days = (info.not_after.and_utc() - chrono::Utc::now()).num_days(); + let expiry = info.not_after; certs.proxy_http_cert_source = ProxyCertSource::Custom; certs.acme_domain = None; From ab651bd1184fe01bf91083acb73b62f628cc2474 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 14 Apr 2026 11:04:49 +0200 Subject: [PATCH 4/9] remove back buttons --- ...ettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx | 5 ----- ...ettingsEdgeCertificateWizardExternalUrlSslConfigStep.tsx | 6 ------ 2 files changed, 11 deletions(-) diff --git a/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx index 5a6105376..684b3d65b 100644 --- a/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx +++ b/web/src/pages/settings/SettingsCertificatesPage/SettingsCoreCertificateWizardPage/steps/SettingsCoreCertificateWizardInternalUrlSslConfigStep.tsx @@ -53,11 +53,6 @@ export const SettingsCoreCertificateWizardInternalUrlSslConfigStep = () => { /> -