Skip to content

Commit c2a4278

Browse files
scripted cli path for standalone device creation
1 parent 5d10592 commit c2a4278

File tree

9 files changed

+91
-21
lines changed

9 files changed

+91
-21
lines changed

web/src/i18n/en/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ const en: BaseTranslation = {
5454
},
5555
},
5656
addStandaloneDevice: {
57+
toasts: {
58+
deviceCreated: 'Device added',
59+
creationFailed: 'Device could not be added.',
60+
},
5761
infoBox: {
5862
setup:
5963
'Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.',

web/src/i18n/i18n-types.ts

+20
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ type RootTranslation = {
176176
}
177177
}
178178
addStandaloneDevice: {
179+
toasts: {
180+
/**
181+
* D​e​v​i​c​e​ ​a​d​d​e​d
182+
*/
183+
deviceCreated: string
184+
/**
185+
* D​e​v​i​c​e​ ​c​o​u​l​d​ ​n​o​t​ ​b​e​ ​a​d​d​e​d​.
186+
*/
187+
creationFailed: string
188+
}
179189
infoBox: {
180190
/**
181191
* H​e​r​e​ ​y​o​u​ ​c​a​n​ ​a​d​d​ ​d​e​f​i​n​i​t​i​o​n​s​ ​o​r​ ​g​e​n​e​r​a​t​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​s​ ​f​o​r​ ​d​e​v​i​c​e​s​ ​t​h​a​t​ ​c​a​n​ ​c​o​n​n​e​c​t​ ​t​o​ ​y​o​u​r​ ​V​P​N​.​ ​O​n​l​y​ ​l​o​c​a​t​i​o​n​s​ ​w​i​t​h​o​u​t​ ​M​u​l​t​i​-​F​a​c​t​o​r​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​a​r​e​ ​a​v​a​i​l​a​b​l​e​ ​h​e​r​e​,​ ​a​s​ ​M​F​A​ ​i​s​ ​o​n​l​y​ ​s​u​p​p​o​r​t​e​d​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​D​e​s​k​t​o​p​ ​C​l​i​e​n​t​ ​f​o​r​ ​n​o​w​.
@@ -4947,6 +4957,16 @@ export type TranslationFunctions = {
49474957
}
49484958
}
49494959
addStandaloneDevice: {
4960+
toasts: {
4961+
/**
4962+
* Device added
4963+
*/
4964+
deviceCreated: () => LocalizedString
4965+
/**
4966+
* Device could not be added.
4967+
*/
4968+
creationFailed: () => LocalizedString
4969+
}
49504970
infoBox: {
49514971
/**
49524972
* Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.

web/src/pages/overview/modals/AddStandaloneDeviceModal/steps/FinishCliStep/FinishCliStep.tsx

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import './style.scss';
22

3+
import { useMemo } from 'react';
34
import { shallow } from 'zustand/shallow';
45

56
import { useI18nContext } from '../../../../../../i18n/i18n-react';
@@ -13,12 +14,24 @@ import {
1314
import { ExpandableCard } from '../../../../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard';
1415
import { MessageBox } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox';
1516
import { MessageBoxType } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/types';
17+
import { useClipboard } from '../../../../../../shared/hooks/useClipboard';
1618
import { useAddStandaloneDeviceModal } from '../../store';
1719

1820
export const FinishCliStep = () => {
1921
const { LL } = useI18nContext();
2022
const localLL = LL.modals.addStandaloneDevice.steps.cli.finish;
2123
const [closeModal] = useAddStandaloneDeviceModal((s) => [s.close], shallow);
24+
const enroll = useAddStandaloneDeviceModal((s) => s.enrollResponse);
25+
const { writeToClipboard } = useClipboard();
26+
27+
const commandToCopy = useMemo(() => {
28+
if (enroll) {
29+
return `defguard -u ${enroll.enrollment_url} -t ${enroll.enrollment_token}`;
30+
}
31+
return '';
32+
}, [enroll]);
33+
34+
if (!enroll) return null;
2235
return (
2336
<div className="finish-cli-step">
2437
<MessageBox
@@ -32,18 +45,25 @@ export const FinishCliStep = () => {
3245
text={localLL.downloadButton()}
3346
size={ButtonSize.LARGE}
3447
styleVariant={ButtonStyleVariant.PRIMARY}
48+
onClick={() => {}}
3549
/>
3650
</a>
3751
</div>
3852
<ExpandableCard
3953
title={localLL.commandCopy()}
4054
actions={[
41-
<ActionButton variant={ActionButtonVariant.COPY} onClick={() => {}} key={0} />,
55+
<ActionButton
56+
variant={ActionButtonVariant.COPY}
57+
onClick={() => {
58+
writeToClipboard(commandToCopy);
59+
}}
60+
key={0}
61+
/>,
4262
]}
4363
expanded={true}
4464
disableExpand={true}
4565
>
46-
<p>{'defguard -u https://enrollment.defguard.net -t sdf$&9234&8dfsk345LSD3'}</p>
66+
<p className="config">{commandToCopy}</p>
4767
</ExpandableCard>
4868
<div className="controls solo">
4969
<Button

web/src/pages/overview/modals/AddStandaloneDeviceModal/steps/FinishCliStep/style.scss

+6
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@
1717
height: 47px;
1818
}
1919
}
20+
21+
.expanded-content {
22+
p {
23+
@include typography(app-input);
24+
}
25+
}
2026
}
2127
}

web/src/pages/overview/modals/AddStandaloneDeviceModal/steps/SetupCliStep/SetupCliStep.tsx

+18-13
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,60 @@ import {
1111
import { MessageBox } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox';
1212
import { MessageBoxType } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/types';
1313
import useApi from '../../../../../../shared/hooks/useApi';
14+
import { useToaster } from '../../../../../../shared/hooks/useToaster';
1415
import { QueryKeys } from '../../../../../../shared/queries';
1516
import { StandaloneDeviceModalForm } from '../../components/StandaloneDeviceModalForm';
1617
import { useAddStandaloneDeviceModal } from '../../store';
1718
import {
1819
AddStandaloneDeviceFormFields,
1920
AddStandaloneDeviceModalChoice,
21+
AddStandaloneDeviceModalStep,
2022
} from '../../types';
2123

2224
export const SetupCliStep = () => {
2325
const { LL } = useI18nContext();
2426
const localLL = LL.modals.addStandaloneDevice.steps.cli.setup;
2527
const [formLoading, setFormLoading] = useState(false);
2628
const queryClient = useQueryClient();
27-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2829
const [setState, close, next, submitSubject] = useAddStandaloneDeviceModal(
2930
(s) => [s.setStore, s.close, s.changeStep, s.submitSubject],
3031
shallow,
3132
);
3233

34+
const toast = useToaster();
35+
3336
const {
34-
standaloneDevice: { createDevice },
37+
standaloneDevice: { createCliDevice },
3538
} = useApi();
3639

3740
const { mutateAsync } = useMutation({
38-
mutationFn: createDevice,
39-
onSuccess: (resp) => {
40-
console.table(resp);
41+
mutationFn: createCliDevice,
42+
onSuccess: () => {
43+
toast.success(LL.modals.addStandaloneDevice.toasts.deviceCreated());
4144
queryClient.invalidateQueries({
4245
queryKey: [QueryKeys.FETCH_STANDALONE_DEVICE_LIST],
4346
});
44-
// next(AddStandaloneDeviceModalStep.FINISH_CLI);
47+
},
48+
onError: (e) => {
49+
toast.error(LL.modals.addStandaloneDevice.toasts.creationFailed());
50+
console.error(e);
4551
},
4652
});
4753

4854
const initIp = useAddStandaloneDeviceModal((s) => s.initAvailableIp);
4955

5056
const handleSubmit = useCallback(
5157
async (values: AddStandaloneDeviceFormFields) => {
52-
console.table(values);
53-
await mutateAsync({
58+
const response = await mutateAsync({
5459
assigned_ip: values.assigned_ip,
5560
location_id: values.location_id,
5661
name: values.name,
62+
description: values.description,
5763
});
64+
setState({ enrollResponse: response });
65+
next(AddStandaloneDeviceModalStep.FINISH_CLI);
5866
},
59-
[mutateAsync],
67+
[mutateAsync, next, setState],
6068
);
6169

6270
if (initIp === undefined) return null;
@@ -65,10 +73,7 @@ export const SetupCliStep = () => {
6573
<div className="setup-cli-step">
6674
<MessageBox
6775
type={MessageBoxType.INFO}
68-
message={
69-
// eslint-disable-next-line max-len
70-
'Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.'
71-
}
76+
message={localLL.stepMessage()}
7277
dismissId="add-standalone-device-cli-setup-step-header"
7378
/>
7479
<StandaloneDeviceModalForm

web/src/pages/overview/modals/AddStandaloneDeviceModal/steps/SetupManualStep/SetupManualStep.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const SetupManualStep = () => {
3535
const queryClient = useQueryClient();
3636

3737
const {
38-
standaloneDevice: { createDevice },
38+
standaloneDevice: { createManualDevice: createDevice },
3939
} = useApi();
4040

4141
const { mutateAsync } = useMutation({

web/src/pages/overview/modals/AddStandaloneDeviceModal/store.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { Subject } from 'rxjs';
33
import { createWithEqualityFn } from 'zustand/traditional';
44

55
import { SelectOption } from '../../../../shared/defguard-ui/components/Layout/Select/types';
6-
import { CreateStandaloneDeviceResponse, Network } from '../../../../shared/types';
6+
import {
7+
CreateStandaloneDeviceResponse,
8+
Network,
9+
StartEnrollmentResponse,
10+
} from '../../../../shared/types';
711
import {
812
AddStandaloneDeviceModalChoice,
913
AddStandaloneDeviceModalStep,
@@ -21,6 +25,7 @@ const defaultValues: StoreValues = {
2125
initAvailableIp: undefined,
2226
genKeys: undefined,
2327
manualResponse: undefined,
28+
enrollResponse: undefined,
2429
};
2530

2631
export const useAddStandaloneDeviceModal = createWithEqualityFn<Store>(
@@ -51,6 +56,7 @@ type StoreValues = {
5156
privateKey: string;
5257
};
5358
manualResponse?: CreateStandaloneDeviceResponse;
59+
enrollResponse?: StartEnrollmentResponse;
5460
};
5561

5662
type StoreMethods = {

web/src/shared/hooks/useApi.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,9 @@ const useApi = (props?: HookProps): ApiHook => {
471471
const testDirsync: ApiHook['settings']['testDirsync'] = () =>
472472
client.get('/test_directory_sync').then(unpackRequest);
473473

474-
const createStandaloneDevice: ApiHook['standaloneDevice']['createDevice'] = (data) =>
475-
client.post('/device/network', data).then(unpackRequest);
474+
const createStandaloneDevice: ApiHook['standaloneDevice']['createManualDevice'] = (
475+
data,
476+
) => client.post('/device/network', data).then(unpackRequest);
476477

477478
const deleteStandaloneDevice: ApiHook['standaloneDevice']['deleteDevice'] = (
478479
deviceId,
@@ -494,6 +495,10 @@ const useApi = (props?: HookProps): ApiHook => {
494495
const getStandaloneDevicesList: ApiHook['standaloneDevice']['getDevicesList'] = () =>
495496
client.get('/device/network').then(unpackRequest);
496497

498+
const createStandaloneCliDevice: ApiHook['standaloneDevice']['createCliDevice'] = (
499+
data,
500+
) => client.post('/device/network/start_cli', data).then(unpackRequest);
501+
497502
useEffect(() => {
498503
client.interceptors.response.use(
499504
(res) => {
@@ -538,13 +543,14 @@ const useApi = (props?: HookProps): ApiHook => {
538543
addUsersToGroups,
539544
},
540545
standaloneDevice: {
541-
createDevice: createStandaloneDevice,
546+
createManualDevice: createStandaloneDevice,
542547
deleteDevice: deleteStandaloneDevice,
543548
editDevice: editStandaloneDevice,
544549
getDevice: getStandaloneDevice,
545550
getAvailableIp: getAvailableLocationIp,
546551
validateLocationIp: validateLocationIp,
547552
getDevicesList: getStandaloneDevicesList,
553+
createCliDevice: createStandaloneCliDevice,
548554
},
549555
user: {
550556
getMe,

web/src/shared/types.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,12 @@ export interface ApiHook {
490490
deleteYubiKey: (data: { id: number; username: string }) => EmptyApiResponse;
491491
};
492492
standaloneDevice: {
493-
createDevice: (
493+
createManualDevice: (
494494
data: CreateStandaloneDeviceRequest,
495495
) => Promise<CreateStandaloneDeviceResponse>;
496+
createCliDevice: (
497+
data: CreateStandaloneDeviceRequest,
498+
) => Promise<StartEnrollmentResponse>;
496499
getDevice: (deviceId: number | string) => Promise<StandaloneDevice>;
497500
deleteDevice: (deviceId: number | string) => Promise<void>;
498501
editDevice: (device: StandaloneDevice) => Promise<void>;

0 commit comments

Comments
 (0)