Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/resources/scripts/components/auth/LoginCheckpointContainer.tsx
10264 views
1
import React, { useState } from 'react';
2
import { Link, RouteComponentProps } from 'react-router-dom';
3
import loginCheckpoint from '@/api/auth/loginCheckpoint';
4
import LoginFormContainer from '@/components/auth/LoginFormContainer';
5
import { ActionCreator } from 'easy-peasy';
6
import { StaticContext } from 'react-router';
7
import { useFormikContext, withFormik } from 'formik';
8
import useFlash from '@/plugins/useFlash';
9
import { FlashStore } from '@/state/flashes';
10
import Field from '@/components/elements/Field';
11
import tw from 'twin.macro';
12
import Button from '@/components/elements/Button';
13
14
interface Values {
15
code: string;
16
recoveryCode: '';
17
}
18
19
type OwnProps = RouteComponentProps<Record<string, string | undefined>, StaticContext, { token?: string }>;
20
21
type Props = OwnProps & {
22
clearAndAddHttpError: ActionCreator<FlashStore['clearAndAddHttpError']['payload']>;
23
};
24
25
const LoginCheckpointContainer = () => {
26
const { isSubmitting, setFieldValue } = useFormikContext<Values>();
27
const [isMissingDevice, setIsMissingDevice] = useState(false);
28
29
return (
30
<LoginFormContainer title={'Device Checkpoint'} css={tw`w-full flex`}>
31
<div css={tw`mt-6`}>
32
<Field
33
light
34
name={isMissingDevice ? 'recoveryCode' : 'code'}
35
title={isMissingDevice ? 'Recovery Code' : 'Authentication Code'}
36
description={
37
isMissingDevice
38
? 'Enter one of the recovery codes generated when you setup 2-Factor authentication on this account in order to continue.'
39
: 'Enter the two-factor token generated by your device.'
40
}
41
type={'text'}
42
autoComplete={'one-time-code'}
43
autoFocus
44
/>
45
</div>
46
<div css={tw`mt-6`}>
47
<Button size={'xlarge'} type={'submit'} disabled={isSubmitting} isLoading={isSubmitting}>
48
Continue
49
</Button>
50
</div>
51
<div css={tw`mt-6 text-center`}>
52
<span
53
onClick={() => {
54
setFieldValue('code', '');
55
setFieldValue('recoveryCode', '');
56
setIsMissingDevice((s) => !s);
57
}}
58
css={tw`cursor-pointer text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`}
59
>
60
{!isMissingDevice ? "I've Lost My Device" : 'I Have My Device'}
61
</span>
62
</div>
63
<div css={tw`mt-6 text-center`}>
64
<Link
65
to={'/auth/login'}
66
css={tw`text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`}
67
>
68
Return to Login
69
</Link>
70
</div>
71
</LoginFormContainer>
72
);
73
};
74
75
const EnhancedForm = withFormik<Props, Values>({
76
handleSubmit: ({ code, recoveryCode }, { setSubmitting, props: { clearAndAddHttpError, location } }) => {
77
loginCheckpoint(location.state?.token || '', code, recoveryCode)
78
.then((response) => {
79
if (response.complete) {
80
// @ts-expect-error this is valid
81
window.location = response.intended || '/';
82
return;
83
}
84
85
setSubmitting(false);
86
})
87
.catch((error) => {
88
console.error(error);
89
setSubmitting(false);
90
clearAndAddHttpError({ error });
91
});
92
},
93
94
mapPropsToValues: () => ({
95
code: '',
96
recoveryCode: '',
97
}),
98
})(LoginCheckpointContainer);
99
100
export default ({ history, location, ...props }: OwnProps) => {
101
const { clearAndAddHttpError } = useFlash();
102
103
if (!location.state?.token) {
104
history.replace('/auth/login');
105
106
return null;
107
}
108
109
return (
110
<EnhancedForm clearAndAddHttpError={clearAndAddHttpError} history={history} location={location} {...props} />
111
);
112
};
113
114