Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions web/messages/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
"settings_page_title": "Settings",
"settings_breadcrumb_general": "General",
"settings_breadcrumb_instance": "Instance settings",
"settings_breadcrumb_vpn_stats": "VPN stats",
"settings_breadcrumb_enrollment": "Enrollment",
"settings_breadcrumb_client_behavior": "Client behavior",
"settings_instance_title": "Instance settings",
"settings_instance_subtitle": "Here you can configure general instance parameters.",
"settings_instance_section_core": "General",
"settings_instance_section_core_description": "Configure the primary instance identity, URLs and session lifetime used across the platform.",
"settings_instance_section_data_retention": "VPN stats retention",
"settings_instance_section_data_retention_description": "Control if VPN statistics are purged automatically and how long they are kept.",
"settings_instance_label_name": "Instance name",
"settings_instance_label_public_proxy_url": "Public Edge Component URL",
"settings_instance_label_session_duration": "Session duration",
"settings_vpn_stats_title": "VPN stats",
"settings_vpn_stats_subtitle": "Configure statistics purge behavior for VPN data.",
"settings_vpn_stats_toggle_disable_title": "Disable stats purge",
"settings_vpn_stats_label_purge_frequency": "Stats purge frequency",
"settings_vpn_stats_label_purge_threshold": "Stats purge threshold",
Expand All @@ -24,8 +25,7 @@
"settings_enrollment_label_password_reset_session_expires_in": "Password reset session expires in",
"settings_general_section_instance_content": "Configure your instance name and branding settings. Add a logo to personalize the interface and make it easily recognizable to your users.",
"settings_general_section_client_behavior_content": "Manage how users interact with the Defguard client. Control device management permissions, configuration access, and traffic routing options.",
"settings_general_section_vpn_stats_content": "Configure VPN statistics cleanup behavior, including purge frequency and retention threshold.",
"settings_general_section_enrollment_content": "Set enrollment and password reset token and session timeout values.",
"settings_general_section_enrollment_content": "Configure enrollment settings to define how users are invited, registered, and onboarded into your instance.",
"settings_duration_one_day": "1 day",
"settings_duration_days": "{days} days",
"settings_duration_one_hour": "1 hour",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import { businessBadgeProps } from '../../../../shared/components/badges/Busines
import { SettingsLayout } from '../../../../shared/components/SettingsLayout/SettingsLayout';
import { SectionSelect } from '../../../../shared/defguard-ui/components/SectionSelect/SectionSelect';
import { SizedBox } from '../../../../shared/defguard-ui/components/SizedBox/SizedBox';
import { TooltipContent } from '../../../../shared/defguard-ui/providers/tooltip/TooltipContent';
import { TooltipProvider } from '../../../../shared/defguard-ui/providers/tooltip/TooltipContext';
import { TooltipTrigger } from '../../../../shared/defguard-ui/providers/tooltip/TooltipTrigger';
import { ThemeSpacing } from '../../../../shared/defguard-ui/types';
import { getLicenseInfoQueryOptions } from '../../../../shared/query';

Expand All @@ -26,31 +23,9 @@ export const SettingsGeneralTab = () => {
/>
</Link>
<SizedBox height={ThemeSpacing.Xl} />
<TooltipProvider>
<TooltipTrigger>
<SectionSelect
image="proxy-management"
title="Proxy management"
content="Configure your proxy settings and manage all proxy endpoints. Adjust connection rules and ensure your traffic is routed exactly as required."
disabled
/>
</TooltipTrigger>
<TooltipContent>
<p>{`Not implemented`}</p>
</TooltipContent>
</TooltipProvider>
<SizedBox height={ThemeSpacing.Xl} />
<Link to="/settings/vpn-stats">
<SectionSelect
image="vector"
title={m.settings_vpn_stats_title()}
content={m.settings_general_section_vpn_stats_content()}
/>
</Link>
<SizedBox height={ThemeSpacing.Xl} />
<Link to="/settings/enrollment">
<SectionSelect
image="self-enrollment"
image="enrollment"
title={m.settings_enrollment_title()}
content={m.settings_general_section_enrollment_content()}
/>
Expand Down
138 changes: 114 additions & 24 deletions web/src/pages/settings/SettingsInstancePage/SettingsInstancePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { SettingsCard } from '../../../shared/components/SettingsCard/SettingsCa
import { SettingsHeader } from '../../../shared/components/SettingsHeader/SettingsHeader';
import { SettingsLayout } from '../../../shared/components/SettingsLayout/SettingsLayout';
import { Button } from '../../../shared/defguard-ui/components/Button/Button';
import { Divider } from '../../../shared/defguard-ui/components/Divider/Divider';
import { MarkedSection } from '../../../shared/defguard-ui/components/MarkedSection/MarkedSection';
import { MarkedSectionHeader } from '../../../shared/defguard-ui/components/MarkedSectionHeader/MarkedSectionHeader';
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';
Expand Down Expand Up @@ -76,6 +79,9 @@ const formSchema = z.object({
.url(m.initial_setup_general_config_error_public_proxy_url_invalid())
.min(1, m.initial_setup_general_config_error_public_proxy_url_required()),
authentication_period_days: z.number().min(1, m.form_error_invalid()),
disable_stats_purge: z.boolean(),
stats_purge_frequency_hours: z.number(m.form_error_required()).int().min(1),
stats_purge_threshold_days: z.number(m.form_error_required()).int().min(1),
});

type FormFields = z.infer<typeof formSchema>;
Expand All @@ -92,6 +98,23 @@ const sessionDurationOptions = createNumericSelectOptions({

const sessionDurationFallbackUnit = 'days';

const statsPurgeFrequencyOptions = createNumericSelectOptions({
1: m.settings_duration_one_hour(),
12: m.settings_duration_hours({ hours: 12 }),
24: m.settings_duration_one_day(),
48: m.settings_duration_days({ days: 2 }),
168: m.settings_duration_one_week(),
720: m.settings_duration_one_month(),
});

const statsPurgeThresholdOptions = createNumericSelectOptions({
1: m.settings_duration_one_day(),
7: m.settings_duration_days({ days: 7 }),
14: m.settings_duration_days({ days: 14 }),
30: m.settings_duration_days({ days: 30 }),
90: m.settings_duration_days({ days: 90 }),
});

const Content = ({ settings }: { settings: Settings }) => {
const { mutateAsync } = useMutation({
mutationFn: api.settings.patchSettings,
Expand All @@ -111,11 +134,17 @@ const Content = ({ settings }: { settings: Settings }) => {
instance_name: settings.instance_name ?? '',
public_proxy_url: settings.public_proxy_url ?? '',
authentication_period_days: settings.authentication_period_days ?? 7,
disable_stats_purge: settings.disable_stats_purge ?? false,
stats_purge_frequency_hours: settings.stats_purge_frequency_hours ?? 24,
stats_purge_threshold_days: settings.stats_purge_threshold_days ?? 30,
}),
[
settings.instance_name,
settings.public_proxy_url,
settings.authentication_period_days,
settings.disable_stats_purge,
settings.stats_purge_frequency_hours,
settings.stats_purge_threshold_days,
],
);

Expand All @@ -129,6 +158,26 @@ const Content = ({ settings }: { settings: Settings }) => {
[defaultValues.authentication_period_days],
);

const statsPurgeFrequencySelectOptions = useMemo(
() =>
withNumericFallbackOption(
statsPurgeFrequencyOptions,
defaultValues.stats_purge_frequency_hours,
'hours',
),
[defaultValues.stats_purge_frequency_hours],
);

const statsPurgeThresholdSelectOptions = useMemo(
() =>
withNumericFallbackOption(
statsPurgeThresholdOptions,
defaultValues.stats_purge_threshold_days,
'days',
),
[defaultValues.stats_purge_threshold_days],
);

const form = useAppForm({
defaultValues,
validationLogic: formChangeLogic,
Expand All @@ -151,30 +200,71 @@ const Content = ({ settings }: { settings: Settings }) => {
}}
>
<form.AppForm>
<form.AppField name="instance_name">
{(field) => (
<field.FormInput required label={m.settings_instance_label_name()} />
)}
</form.AppField>
<SizedBox height={ThemeSpacing.Xl} />
<form.AppField name="public_proxy_url">
{(field) => (
<field.FormInput
required
label={m.settings_instance_label_public_proxy_url()}
/>
)}
</form.AppField>
<SizedBox height={ThemeSpacing.Xl} />
<form.AppField name="authentication_period_days">
{(field) => (
<field.FormSelect
required
label={m.settings_instance_label_session_duration()}
options={sessionDurationSelectOptions}
/>
)}
</form.AppField>
<MarkedSection icon="settings">
<MarkedSectionHeader
title={m.settings_instance_section_core()}
description={m.settings_instance_section_core_description()}
/>
<form.AppField name="instance_name">
{(field) => (
<field.FormInput required label={m.settings_instance_label_name()} />
)}
</form.AppField>
<SizedBox height={ThemeSpacing.Xl} />
<form.AppField name="public_proxy_url">
{(field) => (
<field.FormInput
required
label={m.settings_instance_label_public_proxy_url()}
/>
)}
</form.AppField>
<SizedBox height={ThemeSpacing.Xl} />
<form.AppField name="authentication_period_days">
{(field) => (
<field.FormSelect
required
label={m.settings_instance_label_session_duration()}
options={sessionDurationSelectOptions}
/>
)}
</form.AppField>
</MarkedSection>
<Divider spacing={ThemeSpacing.Xl2} />
<MarkedSection icon="activity">
<MarkedSectionHeader
title={m.settings_instance_section_data_retention()}
description={m.settings_instance_section_data_retention_description()}
/>
<form.AppField name="disable_stats_purge">
{(field) => (
<field.FormInteractiveBlock
variant="toggle"
title={m.settings_vpn_stats_toggle_disable_title()}
/>
)}
</form.AppField>
<SizedBox height={ThemeSpacing.Xl} />
<form.AppField name="stats_purge_frequency_hours">
{(field) => (
<field.FormSelect
required
label={m.settings_vpn_stats_label_purge_frequency()}
options={statsPurgeFrequencySelectOptions}
/>
)}
</form.AppField>
<SizedBox height={ThemeSpacing.Xl} />
<form.AppField name="stats_purge_threshold_days">
{(field) => (
<field.FormSelect
required
label={m.settings_vpn_stats_label_purge_threshold()}
options={statsPurgeThresholdSelectOptions}
/>
)}
</form.AppField>
</MarkedSection>
</form.AppForm>
<form.Subscribe
selector={(s) => ({
Expand Down
Loading
Loading