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
2 changes: 2 additions & 0 deletions web/messages/en/modal.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"modal_selection_title": "Select items",
"modal_delete_gateway_title": "Delete Gateway",
"modal_delete_gateway_body": "Deleting gateway {name} may cause location {locationName} to stop working if it is the only Gateway connected to this location or if other connected Gateways are currently unavailable. This action cannot be undone.",
"modal_delete_gateway_disabled_body": "Gateway **{name}** is currently **disabled**. Deleting it now will make it permanently unusable. Are you sure you want to proceed?",
"modal_delete_gateway_disconnected_body": "Gateway **{name}** is currently **disconnected**. Deleting it now will make it permanently unusable. Are you sure you want to proceed?",
"modal_delete_location_title": "Delete location",
"modal_delete_location_body": "Deleting location {name} will also delete related gateways. This action cannot be undone.",
"modal_add_location_title": "Select location type",
Expand Down
28 changes: 24 additions & 4 deletions web/src/pages/EditGatewayPage/EditGatewayPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ const EditGatewayForm = ({ gateway }: { gateway: Gateway }) => {
getLocationQueryOptions(gateway.location_id),
);

const isConnected = useMemo(() => {
if (gateway.connected_at == null) {
return false;
}
if (gateway.disconnected_at == null) {
return true;
}
return gateway.connected_at >= gateway.disconnected_at;
}, [gateway.connected_at, gateway.disconnected_at]);

const getDeleteModalContent = () => {
if (!gateway.enabled) {
return m.modal_delete_gateway_disabled_body({ name: gateway.name });
}
if (!isConnected) {
return m.modal_delete_gateway_disconnected_body({ name: gateway.name });
}
return m.modal_delete_gateway_body({
name: gateway.name,
locationName: location.name,
});
};

const { mutateAsync: editGateway } = useMutation({
mutationFn: api.gateway.editGateway,
meta: {
Expand Down Expand Up @@ -167,10 +190,7 @@ const EditGatewayForm = ({ gateway }: { gateway: Gateway }) => {
onClick: () => {
openModal(ModalName.ConfirmAction, {
title: m.modal_delete_gateway_title(),
contentMd: m.modal_delete_gateway_body({
name: gateway.name,
locationName: location.name,
}),
contentMd: getDeleteModalContent(),
actionPromise: () => api.gateway.deleteGateway(gateway.id),
invalidateKeys: [['gateway'], ['network']],
submitProps: { text: m.gateway_edit_delete(), variant: 'critical' },
Expand Down
19 changes: 15 additions & 4 deletions web/src/pages/LocationsPage/components/GatewaysTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,20 @@ export const GatewaysTable = () => {
enableResizing: false,
cell: (info) => {
const rowData = info.row.original;

const getDeleteModalContent = () => {
if (!rowData.enabled) {
return m.modal_delete_gateway_disabled_body({ name: rowData.name });
}
if (!rowData.connected) {
return m.modal_delete_gateway_disconnected_body({ name: rowData.name });
}
return m.modal_delete_gateway_body({
name: rowData.name,
locationName: rowData.location_name,
});
};

const menuItems: MenuItemsGroup[] = [
{
items: [
Expand Down Expand Up @@ -235,10 +249,7 @@ export const GatewaysTable = () => {
onClick: () => {
openModal(ModalName.ConfirmAction, {
title: m.modal_delete_gateway_title(),
contentMd: m.modal_delete_gateway_body({
name: rowData.name,
locationName: rowData.location_name,
}),
contentMd: getDeleteModalContent(),
actionPromise: () => api.gateway.deleteGateway(rowData.id),
invalidateKeys: [['gateway'], ['network']],
submitProps: { text: m.controls_delete(), variant: 'critical' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ export const MigrationWizardExternalUrlSslConfigStep = () => {
}, []);

const { data: sslInfoData } = useQuery({
queryKey: ['external_ssl_info'],
queryFn: () => api.initial_setup.getExternalSslInfo(),
queryKey: ['migration', 'external_ssl_info'],
queryFn: () => api.migration.getExternalSslInfo(),
enabled: sslType === 'defguard_ca',
select: (response) => response.data,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const MigrationWizardInternalUrlSslConfigStep = () => {
}, []);

const { data: sslInfoData } = useQuery({
queryKey: ['internal_ssl_info'],
queryFn: () => api.initial_setup.getInternalSslInfo(),
queryKey: ['migration', 'internal_ssl_info'],
queryFn: () => api.migration.getInternalSslInfo(),
enabled: sslType === 'defguard_ca',
select: (response) => response.data,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ const discriminatedSchema = z.discriminatedUnion('directory_sync_enabled', [
syncSchema,
]);

const validationSchema = syncSchema
.omit({ directory_sync_interval: true })
.extend({ directory_sync_interval: z.number() })
.superRefine((val, ctx) => {
if (
val.directory_sync_enabled &&
(typeof val.directory_sync_interval !== 'number' ||
val.directory_sync_interval < 60)
) {
ctx.addIssue({
path: ['directory_sync_interval'],
code: 'custom',
message: m.form_error_min({ value: 60 }),
});
}
});

type FormFields = z.infer<typeof discriminatedSchema>;

export const EditMicrosoftProviderForm = ({
Expand Down Expand Up @@ -77,8 +94,8 @@ export const EditMicrosoftProviderForm = ({
defaultValues,
validationLogic: formChangeLogic,
validators: {
onSubmit: syncSchema,
onChange: syncSchema,
onSubmit: validationSchema,
onChange: validationSchema,
},
onSubmit: async ({ value }) => {
const base_url = formatMicrosoftBaseUrl(value.microsoftTenantId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ const discriminatedSchema = z.discriminatedUnion('directory_sync_enabled', [
syncSchema,
]);

const validationSchema = syncSchema
.omit({ okta_dirsync_client_id: true, okta_private_jwk: true })
.extend({
okta_dirsync_client_id: z.string(),
okta_private_jwk: z.string(),
})
.superRefine((val, ctx) => {
if (val.directory_sync_enabled) {
if (val.okta_dirsync_client_id.trim().length === 0) {
ctx.addIssue({
path: ['okta_dirsync_client_id'],
code: 'custom',
message: m.form_error_required(),
});
}
if (val.okta_private_jwk.trim().length === 0) {
ctx.addIssue({
path: ['okta_private_jwk'],
code: 'custom',
message: m.form_error_required(),
});
}
}
});

type FormFields = z.infer<typeof discriminatedSchema>;

export const EditOktaProviderForm = ({
Expand Down Expand Up @@ -61,8 +86,8 @@ export const EditOktaProviderForm = ({
defaultValues,
validationLogic: formChangeLogic,
validators: {
onSubmit: syncSchema,
onChange: syncSchema,
onSubmit: validationSchema,
onChange: validationSchema,
},
onSubmit: async ({ value }) => {
await onSubmit(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
useFloating,
useInteractions,
} from '@floating-ui/react';
import { useMutation } from '@tanstack/react-query';
import { useNavigate } from '@tanstack/react-router';
import clsx from 'clsx';
import { type HTMLProps, useMemo, useState } from 'react';
Expand All @@ -26,7 +25,10 @@ import { Icon } from '../../defguard-ui/components/Icon';
import type { IconKindValue } from '../../defguard-ui/components/Icon/icon-types';
import { InteractionBox } from '../../defguard-ui/components/InteractionBox/InteractionBox';
import { SizedBox } from '../../defguard-ui/components/SizedBox/SizedBox';
import { Snackbar } from '../../defguard-ui/providers/snackbar/snackbar';
import { ThemeSpacing } from '../../defguard-ui/types';
import { openModal } from '../../hooks/modalControls/modalsSubjects';
import { ModalName } from '../../hooks/modalControls/modalTypes';
import './style.scss';

type Status = 'all' | 'none' | 'some';
Expand Down Expand Up @@ -151,13 +153,6 @@ const FloatingMenu = ({
const disconnected = useMemo(() => status.filter((gw) => !gw.connected), [status]);
const navigate = useNavigate();

const { mutate: removeGw } = useMutation({
mutationFn: api.gateway.deleteGateway,
meta: {
invalidate: [['gateway'], ['network']],
},
});

return (
<div className={clsx('gateways-status-floating', className)} {...rest}>
{connected.length > 0 && (
Expand Down Expand Up @@ -198,7 +193,29 @@ const FloatingMenu = ({
icon="close"
iconSize={20}
onClick={() => {
removeGw(gw.id);
const getDeleteModalContent = () => {
if (!gw.enabled) {
return m.modal_delete_gateway_disabled_body({ name: gw.name });
}
if (!gw.connected) {
return m.modal_delete_gateway_disconnected_body({
name: gw.name,
});
}
return m.modal_delete_gateway_body({
name: gw.name,
locationName: gw.location_name,
});
};
openModal(ModalName.ConfirmAction, {
title: m.modal_delete_gateway_title(),
contentMd: getDeleteModalContent(),
actionPromise: () => api.gateway.deleteGateway(gw.id),
invalidateKeys: [['gateway'], ['network']],
submitProps: { text: m.controls_delete(), variant: 'critical' },
onSuccess: () => Snackbar.default(m.gateway_delete_success()),
onError: () => Snackbar.error(m.gateway_delete_failed()),
});
}}
/>
</li>
Expand Down
Loading