feat: first time admin user creation through the ui

This commit is contained in:
Deluan
2020-01-25 17:10:16 -05:00
parent b4c95fa8db
commit 58a7879ba8
9 changed files with 301 additions and 147 deletions
+12 -12
View File
@@ -2,7 +2,11 @@ import jwtDecode from 'jwt-decode'
const authProvider = {
login: ({ username, password }) => {
const request = new Request('/app/login', {
let url = '/app/login'
if (localStorage.getItem('initialAccountCreation')) {
url = '/app/createAdmin'
}
const request = new Request(url, {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' })
@@ -17,6 +21,7 @@ const authProvider = {
.then((response) => {
// Validate token
jwtDecode(response.token)
localStorage.removeItem('initialAccountCreation')
localStorage.setItem('token', response.token)
localStorage.setItem('name', response.name)
localStorage.setItem('username', response.username)
@@ -39,19 +44,14 @@ const authProvider = {
return Promise.resolve()
},
checkAuth: () => {
try {
const expireTime = jwtDecode(localStorage.getItem('token')).exp * 1000
const now = new Date().getTime()
return now < expireTime ? Promise.resolve() : Promise.reject()
} catch (e) {
return Promise.reject()
}
},
checkAuth: () =>
localStorage.getItem('token') ? Promise.resolve() : Promise.reject(),
checkError: (error) => {
const { status } = error
// TODO Remove 403?
const { status, message } = error
if (message === 'no users created') {
localStorage.setItem('initialAccountCreation', 'true')
}
if (status === 401 || status === 403) {
removeItems()
return Promise.reject()
+1
View File
@@ -13,6 +13,7 @@ const httpClient = (url, options = {}) => {
const token = response.headers.get('authorization')
if (token) {
localStorage.setItem('token', token)
localStorage.removeItem('initialAccountCreation')
}
return response
})
+141 -32
View File
@@ -71,40 +71,9 @@ const renderInput = ({
/>
)
const Login = ({ location }) => {
const [loading, setLoading] = useState(false)
const FormLogin = ({ loading, handleSubmit, validate }) => {
const translate = useTranslate()
const classes = useStyles()
const notify = useNotify()
const login = useLogin()
const handleSubmit = (auth) => {
setLoading(true)
login(auth, location.state ? location.state.nextPathname : '/').catch(
(error) => {
setLoading(false)
notify(
typeof error === 'string'
? error
: typeof error === 'undefined' || !error.message
? 'ra.auth.sign_in_error'
: error.message,
'warning'
)
}
)
}
const validate = (values) => {
const errors = {}
if (!values.username) {
errors.username = translate('ra.validation.required')
}
if (!values.password) {
errors.password = translate('ra.validation.required')
}
return errors
}
return (
<Form
@@ -162,6 +131,146 @@ const Login = ({ location }) => {
)
}
const FormSignUp = ({ loading, handleSubmit, validate }) => {
const translate = useTranslate()
const classes = useStyles()
return (
<Form
onSubmit={handleSubmit}
validate={validate}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit} noValidate>
<div className={classes.main}>
<Card className={classes.card}>
<div className={classes.avatar}>
<Avatar className={classes.icon}>
<LockIcon />
</Avatar>
</div>
<div className={classes.systemName}>
Thanks for installing Navidrome!
</div>
<div className={classes.systemName}>
To start, create an admin user
</div>
<div className={classes.form}>
<div className={classes.input}>
<Field
autoFocus
name="username"
component={renderInput}
label={'Admin Username'}
disabled={loading}
/>
</div>
<div className={classes.input}>
<Field
name="password"
component={renderInput}
label={translate('ra.auth.password')}
type="password"
disabled={loading}
/>
</div>
<div className={classes.input}>
<Field
name="confirmPassword"
component={renderInput}
label={'Confirm Password'}
type="password"
disabled={loading}
/>
</div>
</div>
<CardActions className={classes.actions}>
<Button
variant="contained"
type="submit"
color="primary"
disabled={loading}
className={classes.button}
fullWidth
>
{loading && <CircularProgress size={25} thickness={2} />}
{translate('Create Admin')}
</Button>
</CardActions>
</Card>
<Notification />
</div>
</form>
)}
/>
)
}
const Login = ({ location }) => {
const [loading, setLoading] = useState(false)
const translate = useTranslate()
const notify = useNotify()
const login = useLogin()
const handleSubmit = (auth) => {
setLoading(true)
login(auth, location.state ? location.state.nextPathname : '/').catch(
(error) => {
setLoading(false)
notify(
typeof error === 'string'
? error
: typeof error === 'undefined' || !error.message
? 'ra.auth.sign_in_error'
: error.message,
'warning'
)
}
)
}
const validateLogin = (values) => {
const errors = {}
if (!values.username) {
errors.username = translate('ra.validation.required')
}
if (!values.password) {
errors.password = translate('ra.validation.required')
}
return errors
}
const validateSignup = (values) => {
const errors = validateLogin(values)
const regex = /^\w+$/g
if (values.username && !values.username.match(regex)) {
errors.username = translate('Please only use letter and numbers')
}
if (!values.confirmPassword) {
errors.confirmPassword = translate('ra.validation.required')
}
if (values.confirmPassword !== values.password) {
errors.confirmPassword = 'Password does not match'
}
return errors
}
if (localStorage.getItem('initialAccountCreation') === 'true') {
return (
<FormSignUp
handleSubmit={handleSubmit}
validate={validateSignup}
loading={loading}
/>
)
}
return (
<FormLogin
handleSubmit={handleSubmit}
validate={validateLogin}
loading={loading}
/>
)
}
Login.propTypes = {
authProvider: PropTypes.func,
previousRoute: PropTypes.string