Add support to update user password in selfhosted version with email or smtp
This commit is contained in:
commit
b212e1c105
5 changed files with 159 additions and 3 deletions
1
.env
1
.env
|
@ -1,3 +1,4 @@
|
||||||
VITE_APP_API_URL=http://localhost:2021
|
VITE_APP_API_URL=http://localhost:2021
|
||||||
VITE_IS_LANDING_DEFAULT=false
|
VITE_IS_LANDING_DEFAULT=false
|
||||||
VITE_OPENREPLAY_PROJECT_KEY=
|
VITE_OPENREPLAY_PROJECT_KEY=
|
||||||
|
VITE_IS_SELF_HOSTED=false
|
|
@ -1,3 +1,4 @@
|
||||||
VITE_APP_API_URL=http://localhost:2021
|
VITE_APP_API_URL=http://localhost:2021
|
||||||
VITE_APP_REDIRECT_URL=http://localhost:5173
|
VITE_APP_REDIRECT_URL=http://localhost:5173
|
||||||
VITE_APP_GOOGLE_CLIENT_ID=USE_YOUR_OWN_CLIENT_ID
|
VITE_APP_GOOGLE_CLIENT_ID=USE_YOUR_OWN_CLIENT_ID
|
||||||
|
VITE_IS_SELF_HOSTED=true
|
|
@ -1,3 +1,4 @@
|
||||||
VITE_APP_API_URL=
|
VITE_APP_API_URL=
|
||||||
VITE_APP_REDIRECT_URL=http://localhost:5173
|
VITE_APP_REDIRECT_URL=http://localhost:5173
|
||||||
VITE_APP_GOOGLE_CLIENT_ID=USE_YOUR_OWN_CLIENT_ID
|
VITE_APP_GOOGLE_CLIENT_ID=USE_YOUR_OWN_CLIENT_ID
|
||||||
|
VITE_IS_SELF_HOSTED=true
|
117
src/views/Modals/Inputs/PasswordChangeModal.jsx
Normal file
117
src/views/Modals/Inputs/PasswordChangeModal.jsx
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
FormHelperText,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalDialog,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/joy'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
|
||||||
|
function PassowrdChangeModal({ isOpen, onClose }) {
|
||||||
|
const [password, setPassword] = React.useState('')
|
||||||
|
const [confirmPassword, setConfirmPassword] = React.useState('')
|
||||||
|
const [passwordError, setPasswordError] = React.useState(false)
|
||||||
|
const [passwordTouched, setPasswordTouched] = React.useState(false)
|
||||||
|
const [confirmPasswordTouched, setConfirmPasswordTouched] =
|
||||||
|
React.useState(false)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!passwordTouched || !confirmPasswordTouched) {
|
||||||
|
return
|
||||||
|
} else if (password !== confirmPassword) {
|
||||||
|
setPasswordError('Passwords do not match')
|
||||||
|
} else if (password.length < 8) {
|
||||||
|
setPasswordError('Password must be at least 8 characters')
|
||||||
|
} else if (password.length > 50) {
|
||||||
|
setPasswordError('Password must be less than 50 characters')
|
||||||
|
} else {
|
||||||
|
setPasswordError(null)
|
||||||
|
}
|
||||||
|
}, [password, confirmPassword, passwordTouched, confirmPasswordTouched])
|
||||||
|
|
||||||
|
const handleAction = isConfirmed => {
|
||||||
|
if (!isConfirmed) {
|
||||||
|
onClose(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onClose(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={isOpen} onClose={onClose}>
|
||||||
|
<ModalDialog>
|
||||||
|
<Typography level='h4' mb={1}>
|
||||||
|
Change Password
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography level='body-md' gutterBottom>
|
||||||
|
Please enter your new password.
|
||||||
|
</Typography>
|
||||||
|
<FormControl>
|
||||||
|
<Typography level='body2' alignSelf={'start'}>
|
||||||
|
New Password
|
||||||
|
</Typography>
|
||||||
|
<Input
|
||||||
|
margin='normal'
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
name='password'
|
||||||
|
label='Password'
|
||||||
|
type='password'
|
||||||
|
id='password'
|
||||||
|
value={password}
|
||||||
|
onChange={e => {
|
||||||
|
setPasswordTouched(true)
|
||||||
|
setPassword(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<Typography level='body2' alignSelf={'start'}>
|
||||||
|
Confirm Password
|
||||||
|
</Typography>
|
||||||
|
<Input
|
||||||
|
margin='normal'
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
name='confirmPassword'
|
||||||
|
label='confirmPassword'
|
||||||
|
type='password'
|
||||||
|
id='confirmPassword'
|
||||||
|
value={confirmPassword}
|
||||||
|
onChange={e => {
|
||||||
|
setConfirmPasswordTouched(true)
|
||||||
|
setConfirmPassword(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormHelperText>{passwordError}</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
|
||||||
|
<Button
|
||||||
|
disabled={passwordError != null}
|
||||||
|
onClick={() => {
|
||||||
|
handleAction(true)
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
sx={{ mr: 1 }}
|
||||||
|
>
|
||||||
|
Change Password
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
handleAction(false)
|
||||||
|
}}
|
||||||
|
variant='outlined'
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</ModalDialog>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default PassowrdChangeModal
|
|
@ -24,7 +24,9 @@ import {
|
||||||
GetUserProfile,
|
GetUserProfile,
|
||||||
JoinCircle,
|
JoinCircle,
|
||||||
LeaveCircle,
|
LeaveCircle,
|
||||||
|
UpdatePassword,
|
||||||
} from '../../utils/Fetcher'
|
} from '../../utils/Fetcher'
|
||||||
|
import PassowrdChangeModal from '../Modals/Inputs/PasswordChangeModal'
|
||||||
import APITokenSettings from './APITokenSettings'
|
import APITokenSettings from './APITokenSettings'
|
||||||
import NotificationSetting from './NotificationSetting'
|
import NotificationSetting from './NotificationSetting'
|
||||||
import ThemeToggle from './ThemeToggle'
|
import ThemeToggle from './ThemeToggle'
|
||||||
|
@ -35,6 +37,7 @@ const Settings = () => {
|
||||||
const [circleMemberRequests, setCircleMemberRequests] = useState([])
|
const [circleMemberRequests, setCircleMemberRequests] = useState([])
|
||||||
const [circleInviteCode, setCircleInviteCode] = useState('')
|
const [circleInviteCode, setCircleInviteCode] = useState('')
|
||||||
const [circleMembers, setCircleMembers] = useState([])
|
const [circleMembers, setCircleMembers] = useState([])
|
||||||
|
const [changePasswordModal, setChangePasswordModal] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
GetUserProfile().then(resp => {
|
GetUserProfile().then(resp => {
|
||||||
resp.json().then(data => {
|
resp.json().then(data => {
|
||||||
|
@ -314,7 +317,7 @@ const Settings = () => {
|
||||||
<Typography level='h3'>Account Settings</Typography>
|
<Typography level='h3'>Account Settings</Typography>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Typography level='body-md'>
|
<Typography level='body-md'>
|
||||||
Change your account settings, including your password, display name
|
Change your account settings, type or update your password
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography level='title-md' mb={-1}>
|
<Typography level='title-md' mb={-1}>
|
||||||
Account Type : {getSubscriptionStatus()}
|
Account Type : {getSubscriptionStatus()}
|
||||||
|
@ -365,6 +368,39 @@ const Settings = () => {
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
{import.meta.env.VITE_IS_SELF_HOSTED === 'true' && (
|
||||||
|
<Box>
|
||||||
|
<Typography level='title-md' mb={1}>
|
||||||
|
Password :
|
||||||
|
</Typography>
|
||||||
|
<Typography mb={1} level='body-sm'></Typography>
|
||||||
|
<Button
|
||||||
|
variant='soft'
|
||||||
|
onClick={() => {
|
||||||
|
setChangePasswordModal(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Change Password
|
||||||
|
</Button>
|
||||||
|
{changePasswordModal ? (
|
||||||
|
<PassowrdChangeModal
|
||||||
|
isOpen={changePasswordModal}
|
||||||
|
onClose={password => {
|
||||||
|
if (password) {
|
||||||
|
UpdatePassword(password).then(resp => {
|
||||||
|
if (resp.ok) {
|
||||||
|
alert('Password changed successfully')
|
||||||
|
} else {
|
||||||
|
alert('Password change failed')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setChangePasswordModal(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<NotificationSetting />
|
<NotificationSetting />
|
||||||
<APITokenSettings />
|
<APITokenSettings />
|
||||||
|
|
Loading…
Add table
Reference in a new issue