blob: 79079be10cc612818f57db86ff4e7af1050d404d [file] [log] [blame]
Michelle Sepkap Sime6967fb92022-11-08 08:39:36 -05001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18import GroupAddRounded from '@mui/icons-material/GroupAddRounded';
19import { Box, Card, CardContent, Container, Fab, Input, Typography } from '@mui/material';
20import { HttpStatusCode } from 'jami-web-common';
21import { FormEvent, useEffect, useState } from 'react';
22import { useTranslation } from 'react-i18next';
23import { useNavigate } from 'react-router-dom';
24
25import { checkSetupStatus } from '../App';
26import { apiUrl } from '../utils/constants';
27
28export default function SetupLogin() {
29 const [isSetupComplete, setIsSetupComplete] = useState(false);
30 const [password, setPassword] = useState('');
31 const [passwordRepeat, setPasswordRepeat] = useState('');
32 const [loading, setLoading] = useState(false);
33 const { t } = useTranslation();
34 const navigate = useNavigate();
35
36 useEffect(() => {
37 checkSetupStatus().then(setIsSetupComplete);
38 }, []);
39
40 const adminCreation = async (password: string) => {
41 const url = new URL('/setup/admin/create', apiUrl);
42
43 let response: Response;
44 try {
45 response = await fetch(url, {
46 method: 'POST',
47 headers: {
48 'Content-Type': 'application/json',
49 },
50 body: JSON.stringify({ password }),
51 });
52 } catch (e) {
53 throw new Error(`Admin creation failed`);
54 }
55
56 if (response.status !== HttpStatusCode.Created) {
57 throw new Error('Admin creation failed');
58 }
59 };
60
61 const adminLogin = async (password: string) => {
62 const url = new URL('/setup/admin/login', apiUrl);
63
64 let response: Response;
65 try {
66 response = await fetch(url, {
67 method: 'POST',
68 headers: {
69 'Content-Type': 'application/json',
70 },
71 body: JSON.stringify({ password }),
72 });
73 } catch (err) {
74 throw new Error(`Admin login failed`);
75 }
76
77 if (response.status === HttpStatusCode.Forbidden) {
78 throw new Error('Invalid password');
79 }
80
81 if (response.status !== HttpStatusCode.Ok) {
82 throw new Error('Admin login failed');
83 }
84
85 const data: { accessToken: string } = await response.json();
86 localStorage.setItem('adminAccessToken', data.accessToken);
87 };
88
89 const isValid = isSetupComplete || (password && password === passwordRepeat);
90
91 const handleSubmit = async (e: FormEvent) => {
92 e.preventDefault();
93 setLoading(true);
94 if (!isValid) return;
95
96 try {
97 if (!isSetupComplete) {
98 await adminCreation(password);
99 }
100 await adminLogin(password);
101 } catch (e) {
102 console.error(e);
103 navigate('/login');
104 return;
105 }
106 navigate('/setup');
107 };
108
109 return (
110 <Container className="message-list">
111 <Card>
112 <CardContent component="form" onSubmit={handleSubmit}>
113 <Typography gutterBottom variant="h5" component="h2">
114 {t('setup_login_title')}
115 </Typography>
116 <Typography variant="body2" color="textSecondary" component="p">
117 {t('setup_login_welcome')}
118 <br />
119 {isSetupComplete ? '' : t('setup_login_admin_creation')}
120 </Typography>
121
122 <Box style={{ textAlign: 'center', marginTop: 8, marginBottom: 16 }}>
123 <div>
124 <Input value="admin" name="username" autoComplete="username" disabled />
125 </div>
126 <div>
127 <Input
128 value={password}
129 onChange={(e) => setPassword(e.target.value)}
130 name="password"
131 type="password"
132 placeholder={
133 isSetupComplete ? t('password_placeholder') : t('setup_login_password_placeholder_creation')
134 }
135 autoComplete="new-password"
136 disabled={loading}
137 />
138 </div>
139 {!isSetupComplete && (
140 <div>
141 <Input
142 value={passwordRepeat}
143 onChange={(e) => setPasswordRepeat(e.target.value)}
144 name="password"
145 error={!!passwordRepeat && !isValid}
146 type="password"
147 placeholder={t('setup_login_password_placeholder_repeat')}
148 autoComplete="new-password"
149 disabled={loading}
150 />
151 </div>
152 )}
153 </Box>
154 <Box style={{ textAlign: 'center', marginTop: 24 }}>
155 <Fab variant="extended" color="primary" type="submit" disabled={!isValid || loading}>
156 <GroupAddRounded />
157 {isSetupComplete ? t('login_form_submit_button') : t('admin_creation_submit_button')}
158 </Fab>
159 </Box>
160 </CardContent>
161 </Card>
162 </Container>
163 );
164}