227 lines
6.2 KiB
TypeScript
227 lines
6.2 KiB
TypeScript
import Button from '@mui/material/Button';
|
|
import { styled } from '@mui/material/styles';
|
|
import TextField from '@mui/material/TextField';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
|
|
import AuthenticationPage from '../../components/UI/AuthenticationPage';
|
|
import { colors } from '../../components/UI/styles';
|
|
|
|
import type { ChangeEvent, FormEvent } from 'react';
|
|
import type { AuthenticationPageProps, TranslatedProps, User } from '../../interface';
|
|
|
|
const StyledAuthForm = styled('form')`
|
|
width: 350px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
`;
|
|
|
|
const ErrorMessage = styled('div')`
|
|
color: ${colors.errorText};
|
|
`;
|
|
|
|
function useNetlifyIdentifyEvent(eventName: 'login', callback: (login: User) => void): void;
|
|
function useNetlifyIdentifyEvent(eventName: 'logout', callback: () => void): void;
|
|
function useNetlifyIdentifyEvent(eventName: 'error', callback: (err: Error) => void): void;
|
|
function useNetlifyIdentifyEvent(
|
|
eventName: 'login' | 'logout' | 'error',
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
callback: (input?: any) => void,
|
|
): void {
|
|
useEffect(() => {
|
|
window.netlifyIdentity?.on(eventName, callback);
|
|
}, [callback, eventName]);
|
|
}
|
|
|
|
export interface GitGatewayAuthenticationPageProps
|
|
extends TranslatedProps<AuthenticationPageProps> {
|
|
handleAuth: (email: string, password: string) => Promise<User | string>;
|
|
}
|
|
|
|
const GitGatewayAuthenticationPage = ({
|
|
inProgress = false,
|
|
config,
|
|
onLogin,
|
|
handleAuth,
|
|
t,
|
|
}: GitGatewayAuthenticationPageProps) => {
|
|
const [loggedIn, setLoggedIn] = useState(false);
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [errors, setErrors] = useState<{
|
|
identity?: string;
|
|
server?: string;
|
|
email?: string;
|
|
password?: string;
|
|
}>({});
|
|
|
|
useEffect(() => {
|
|
if (!loggedIn && window.netlifyIdentity && window.netlifyIdentity.currentUser()) {
|
|
onLogin(window.netlifyIdentity.currentUser());
|
|
window.netlifyIdentity.close();
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
const handleIdentityLogin = useCallback(
|
|
(user: User) => {
|
|
onLogin(user);
|
|
window.netlifyIdentity?.close();
|
|
},
|
|
[onLogin],
|
|
);
|
|
|
|
useNetlifyIdentifyEvent('login', handleIdentityLogin);
|
|
|
|
const handleIdentityLogout = useCallback(() => {
|
|
window.netlifyIdentity?.open();
|
|
}, []);
|
|
|
|
useNetlifyIdentifyEvent('logout', handleIdentityLogout);
|
|
|
|
const handleIdentityError = useCallback(
|
|
(err: Error) => {
|
|
if (err?.message?.match(/^Failed to load settings from.+\.netlify\/identity$/)) {
|
|
window.netlifyIdentity?.close();
|
|
setErrors({ identity: t('auth.errors.identitySettings') });
|
|
}
|
|
},
|
|
[t],
|
|
);
|
|
|
|
useNetlifyIdentifyEvent('error', handleIdentityError);
|
|
|
|
const handleIdentity = useCallback(() => {
|
|
const user = window.netlifyIdentity?.currentUser();
|
|
if (user) {
|
|
onLogin(user);
|
|
} else {
|
|
window.netlifyIdentity?.open();
|
|
}
|
|
}, [onLogin]);
|
|
|
|
const handleEmailChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
|
setEmail(event.target.value);
|
|
}, []);
|
|
|
|
const handlePasswordChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
|
setPassword(event.target.value);
|
|
}, []);
|
|
|
|
const handleLogin = useCallback(
|
|
async (e: FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
|
|
const validationErrors: typeof errors = {};
|
|
if (!email) {
|
|
validationErrors.email = t('auth.errors.email');
|
|
}
|
|
if (!password) {
|
|
validationErrors.password = t('auth.errors.password');
|
|
}
|
|
|
|
if (Object.keys(validationErrors).length > 0) {
|
|
setErrors(validationErrors);
|
|
return;
|
|
}
|
|
|
|
let response: User | string;
|
|
try {
|
|
response = await handleAuth(email, password);
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
response = e.message;
|
|
} else {
|
|
response = 'Unknown authentication error';
|
|
}
|
|
}
|
|
|
|
if (typeof response === 'string') {
|
|
setErrors({ server: response });
|
|
setLoggedIn(false);
|
|
return;
|
|
}
|
|
|
|
onLogin(response);
|
|
},
|
|
[email, handleAuth, onLogin, password, t],
|
|
);
|
|
|
|
if (window.netlifyIdentity) {
|
|
if (errors.identity) {
|
|
return (
|
|
<AuthenticationPage
|
|
logoUrl={config.logo_url}
|
|
siteUrl={config.site_url}
|
|
onLogin={handleIdentity}
|
|
pageContent={
|
|
<a
|
|
href="https://docs.netlify.com/visitor-access/git-gateway/#setup-and-settings"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{errors.identity}
|
|
</a>
|
|
}
|
|
t={t}
|
|
/>
|
|
);
|
|
} else {
|
|
return (
|
|
<AuthenticationPage
|
|
logoUrl={config.logo_url}
|
|
siteUrl={config.site_url}
|
|
onLogin={handleIdentity}
|
|
buttonContent={t('auth.loginWithNetlifyIdentity')}
|
|
t={t}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<AuthenticationPage
|
|
logoUrl={config.logo_url}
|
|
siteUrl={config.site_url}
|
|
pageContent={
|
|
<StyledAuthForm onSubmit={handleLogin}>
|
|
{!errors.server ? null : <ErrorMessage>{String(errors.server)}</ErrorMessage>}
|
|
<TextField
|
|
type="text"
|
|
name="email"
|
|
label="Email"
|
|
value={email}
|
|
onChange={handleEmailChange}
|
|
fullWidth
|
|
variant="outlined"
|
|
error={Boolean(errors.email)}
|
|
helperText={errors.email ?? undefined}
|
|
/>
|
|
<TextField
|
|
type="password"
|
|
name="password"
|
|
label="Password"
|
|
value={password}
|
|
onChange={handlePasswordChange}
|
|
fullWidth
|
|
variant="outlined"
|
|
error={Boolean(errors.password)}
|
|
helperText={errors.password ?? undefined}
|
|
/>
|
|
<Button
|
|
variant="contained"
|
|
type="submit"
|
|
disabled={inProgress}
|
|
sx={{ width: 120, alignSelf: 'center' }}
|
|
>
|
|
{inProgress ? t('auth.loggingIn') : t('auth.login')}
|
|
</Button>
|
|
</StyledAuthForm>
|
|
}
|
|
t={t}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default GitGatewayAuthenticationPage;
|