Merge pull request 'login-feedback' (#44) from login-feedback into master

Reviewed-by: Oliver Boehlk <ollitobiasb@gmail.com>
This commit was merged in pull request #44.
This commit is contained in:
2020-04-30 23:04:40 +02:00
7 changed files with 199 additions and 65 deletions

View File

@@ -73,7 +73,7 @@ app.use(passport.session());
app.get("*", function (req, res, next) { app.get("*", function (req, res, next) {
if (!serverStartupComplete) if (!serverStartupComplete)
return res.send("Server is starting..."); return res.send("Server is starting... Please try again soon.");
else return next(); else return next();
}); });
@@ -108,7 +108,7 @@ passport.use('local-login', new LocalStrategy({
email = mysql.escape(email); email = mysql.escape(email);
connection.query(`SELECT * from user WHERE email = ${email} AND deactivated = 0`, function (err, rows) { connection.query(`SELECT * from user WHERE email = ${email} AND deactivated = 0`, function (err, rows) {
if (err) { if (err) {
return done(null, false); return res.status(static.INTERNAL_SERVER_ERROR).send("error querying the database - Please contact sys admin");
} }
if (!rows.length) { if (!rows.length) {
return done(null, false); return done(null, false);
@@ -144,7 +144,7 @@ passport.deserializeUser(function (id, done) {
}); });
app.post("/API/user/login", passport.authenticate('local-login'), function (req, res) { app.post("/API/user/login", passport.authenticate('local-login'), function (req, res) {
res.status(status.OK).send(); res.status(status.OK).send("login success");
}); });
app.put("/API/user/create", function (req, res) { app.put("/API/user/create", function (req, res) {
@@ -155,11 +155,11 @@ app.put("/API/user/create", function (req, res) {
if (DEBUG) return res.status(status.OK).send(); if (DEBUG) return res.status(status.OK).send();
connection.query(`INSERT INTO user (deactivated, email, password) values (1, ${email}, ${password})`, function (err, rows) { connection.query(`INSERT INTO user (deactivated, email, password) values (1, ${email}, ${password})`, function (err, rows) {
if (err) if (err)
return res.status(status.INTERNAL_SERVER_ERROR).send(); return res.send(status.INTERNAL_SERVER_ERROR).send("the user seems to exist already - if you think this is an error contact the sys admin");
return res.status(status.OK).send(); return res.status(status.OK).send("account successfully created");
}); });
} else { } else {
res.status(status.BAD_REQUEST).send(); res.status(status.BAD_REQUEST).send("invalid data supplied");
} }
}); });
@@ -168,7 +168,7 @@ app.all("*", function (req, res, next) {
return next(); return next();
} }
else { else {
res.status(status.UNAUTHORIZED).send(); res.status(status.UNAUTHORIZED).send("please log in before using this site");
} }
}); });
@@ -196,7 +196,7 @@ app.get('/API/day', function (req, res) {
}); });
} }
else else
res.status(status.BAD_REQUEST).send("invalid data provided"); res.status(status.BAD_REQUEST).send("invalid data range provided");
} }
else else

View File

@@ -1292,6 +1292,18 @@
"@babel/runtime": "^7.4.4" "@babel/runtime": "^7.4.4"
} }
}, },
"@material-ui/lab": {
"version": "4.0.0-alpha.51",
"resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.51.tgz",
"integrity": "sha512-X/qv/sZQGhXhKDn83L94gNahGDQj2Rd6r7/9tPpQbSn2A1LAt1+jlTiWD1HUgDXZEPqTsJMajOjWSEmTL7/q7w==",
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/utils": "^4.9.6",
"clsx": "^1.0.4",
"prop-types": "^15.7.2",
"react-is": "^16.8.0"
}
},
"@material-ui/styles": { "@material-ui/styles": {
"version": "4.9.10", "version": "4.9.10",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.10.tgz", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.10.tgz",

View File

@@ -6,6 +6,7 @@
"dependencies": { "dependencies": {
"@material-ui/core": "^4.9.8", "@material-ui/core": "^4.9.8",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.51",
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0", "@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
@@ -13,8 +14,8 @@
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-scripts": "3.4.0", "react-scripts": "3.4.0",
"typeface-roboto": "0.0.75", "recharts": "^1.8.5",
"recharts": "^1.8.5" "typeface-roboto": "0.0.75"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

View File

@@ -10,6 +10,7 @@ import Logo from "../../img/logo.png";
import TextField from '@material-ui/core/TextField'; import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid'; import Grid from '@material-ui/core/Grid';
import { Redirect } from "react-router-dom"; import { Redirect } from "react-router-dom";
import LoginFeedback from './loginfeedback';
const url = "/simcompanies/API/user/create" const url = "/simcompanies/API/user/create"
@@ -33,7 +34,7 @@ const useStyles = makeStyles(theme => ({
const CssTextField = withStyles({ const CssTextField = withStyles({
root: { root: {
'& label.Mui-focused': { '& label.Mui-focused': {
color: 'white', color: '#FFC800',
}, },
'&:hover label': { '&:hover label': {
color: '#FFC800', color: '#FFC800',
@@ -53,20 +54,20 @@ const CssTextField = withStyles({
borderColor: "#FFC800", borderColor: "#FFC800",
}, },
'&.Mui-focused fieldset': { '&.Mui-focused fieldset': {
borderColor: 'white', borderColor: '#FFC800',
}, },
}, },
}, },
})(TextField); })(TextField);
export default function CreateAccount() { export default function CreateAccount() {
const classes = useStyles(); const classes = useStyles();
const [username, setUsername] = React.useState(""); const [username, setUsername] = React.useState("");
const [password, setPassword] = React.useState(""); const [password, setPassword] = React.useState("");
const [login, setLogin] = React.useState(true); const [login, setLogin] = React.useState(true);
const [status, setStatus] = React.useState(null);
const [statusText, setStatusText] = React.useState(null);
const [disabled, setDisabled] = React.useState(false);
const putCreateAccount = async (url, data) => { const putCreateAccount = async (url, data) => {
const response = await fetch(url, { const response = await fetch(url, {
@@ -80,20 +81,36 @@ export default function CreateAccount() {
referrerPolicy: 'no-referrer', referrerPolicy: 'no-referrer',
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
const text = await response.text()
await setStatusText(text)
setStatus(response.status)
if (response.status == "200") { if (response.status == "200") {
setLogin(false) setTimeout(() => { setLogin(false) }, 2000)
} }
setDisabled(false)
setTimeout(() => { setStatus(null) }, 2000)
} }
const handleClick = (e) => { const handleClick = (e) => {
if (username !== "" && password !== "") { if (username !== "" && password !== "") {
setDisabled(true)
putCreateAccount(url, { email: username, password: password }) putCreateAccount(url, { email: username, password: password })
setUsername("") setUsername("")
setPassword("") setPassword("")
} }
} }
const handleKeyDown = (e) => {
if (e.key === "Enter") {
if (username !== "" && password !== "") {
setDisabled(true)
putCreateAccount(url, { email: username, password: password })
setUsername("")
setPassword("")
}
}
}
const handleChangeUSR = (e) => { const handleChangeUSR = (e) => {
setUsername(e.target.value) setUsername(e.target.value)
} }
@@ -122,18 +139,22 @@ export default function CreateAccount() {
<Grid item xs={12} > <Grid item xs={12} >
<Box display="flex" justifyContent="center" > <Box display="flex" justifyContent="center" >
<CssTextField <CssTextField
error={status != null && status != "200"}
id="usernameInput" id="usernameInput"
label="Username" label="Username"
variant="outlined" variant="outlined"
onChange={handleChangeUSR} onChange={handleChangeUSR}
value={username} value={username}
className={classes.input} className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/> />
</Box > </Box >
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Box display="flex" justifyContent="center" > <Box display="flex" justifyContent="center" >
<CssTextField <CssTextField
error={status != null && status != "200"}
id="passwordInput" id="passwordInput"
type="password" type="password"
label="Password" label="Password"
@@ -142,12 +163,14 @@ export default function CreateAccount() {
value={password} value={password}
onChange={handleChangePW} onChange={handleChangePW}
className={classes.input} className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/> />
</Box > </Box >
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Box display="flex" justifyContent="center" > <Box display="flex" justifyContent="center" >
<Button variant="contained" color="secondary" onClick={handleClick}> <Button disabled={disabled} variant="contained" color="secondary" onClick={handleClick} >
Create Account Create Account
</Button> </Button>
</Box > </Box >
@@ -157,6 +180,7 @@ export default function CreateAccount() {
</form> </form>
</CardContent> </CardContent>
</Card> </Card>
<LoginFeedback login={status} text={statusText}></LoginFeedback>
</Box > : <Redirect to={"/login"} /> </Box > : <Redirect to={"/login"} />
) )

View File

@@ -11,6 +11,7 @@ import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid'; import Grid from '@material-ui/core/Grid';
import Link1 from '@material-ui/core/Link'; import Link1 from '@material-ui/core/Link';
import { Redirect } from "react-router-dom"; import { Redirect } from "react-router-dom";
import LoginFeedback from './loginfeedback';
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
@@ -33,7 +34,7 @@ const useStyles = makeStyles(theme => ({
const CssTextField = withStyles({ const CssTextField = withStyles({
root: { root: {
'& label.Mui-focused': { '& label.Mui-focused': {
color: 'white', color: "#FFC800",
}, },
'&:hover label': { '&:hover label': {
color: '#FFC800', color: '#FFC800',
@@ -53,7 +54,7 @@ const CssTextField = withStyles({
borderColor: "#FFC800", borderColor: "#FFC800",
}, },
'&.Mui-focused fieldset': { '&.Mui-focused fieldset': {
borderColor: 'white', borderColor: "#FFC800",
}, },
}, },
}, },
@@ -66,9 +67,12 @@ export default function Login() {
const [username, setUsername] = React.useState(""); const [username, setUsername] = React.useState("");
const [password, setPassword] = React.useState(""); const [password, setPassword] = React.useState("");
const [login, setLogin] = React.useState(true); const [login, setLogin] = React.useState(true);
const [status, setStatus] = React.useState(null);
const [disabled, setDisabled] = React.useState(false);
const [statusText, setStatusText] = React.useState(null);
const postLogin = async (url, data) => { const postLogin = async (url, data) => {
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
mode: 'cors', mode: 'cors',
@@ -81,20 +85,40 @@ export default function Login() {
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
const text = await response.text()
await setStatusText(text)
setStatus(response.status)
if (response.status == "200") { if (response.status == "200") {
setLogin(false) setTimeout(() => { setLogin(false) }, 2000)
} }
setDisabled(false)
setTimeout(() => { setStatus(null) }, 2000)
} }
const handleClick = (e) => { const handleClick = (e) => {
if (username !== "" && password !== "") { if (username !== "" && password !== "") {
setDisabled(true)
postLogin(url, { email: username, password: password }) postLogin(url, { email: username, password: password })
setUsername("") setUsername("")
setPassword("") setPassword("")
} }
} }
const handleKeyDown = (e) => {
if (e.key === "Enter") {
if (username !== "" && password !== "") {
setDisabled(true)
postLogin(url, { email: username, password: password })
setUsername("")
setPassword("")
}
}
}
const handleChangeUSR = (e) => { const handleChangeUSR = (e) => {
setUsername(e.target.value) setUsername(e.target.value)
} }
@@ -125,18 +149,22 @@ export default function Login() {
<Grid item xs={12} > <Grid item xs={12} >
<Box display="flex" justifyContent="center" > <Box display="flex" justifyContent="center" >
<CssTextField <CssTextField
error={status != null && status != "200"}
id="usernameInput" id="usernameInput"
label="Username" label="Username"
variant="outlined" variant="outlined"
value={username} value={username}
onChange={handleChangeUSR} onChange={handleChangeUSR}
className={classes.input} className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/> />
</Box > </Box >
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Box display="flex" justifyContent="center" > <Box display="flex" justifyContent="center" >
<CssTextField <CssTextField
error={status != null && status != "200"}
id="passwordInput" id="passwordInput"
type="password" type="password"
label="Password" label="Password"
@@ -145,6 +173,8 @@ export default function Login() {
value={password} value={password}
onChange={handleChangePW} onChange={handleChangePW}
className={classes.input} className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/> />
</Box > </Box >
</Grid> </Grid>
@@ -159,7 +189,7 @@ export default function Login() {
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Box display="flex" justifyContent="center" > <Box display="flex" justifyContent="center" >
<Button variant="contained" color="secondary" onClick={handleClick}> <Button disabled={disabled} variant="contained" color="secondary" onClick={handleClick}>
Login Login
</Button> </Button>
</Box > </Box >
@@ -169,6 +199,7 @@ export default function Login() {
</form> </form>
</CardContent> </CardContent>
</Card> </Card>
<LoginFeedback login={status} text={statusText}></LoginFeedback>
</Box > : <Redirect to='/' /> </Box > : <Redirect to='/' />
) )

View File

@@ -0,0 +1,66 @@
import React, { useEffect } from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import MuiAlert from '@material-ui/lab/Alert';
function Alert(props) {
return <MuiAlert elevation={6} variant="filled" {...props} />;
}
export default function LoginFeedback(props) {
const [login, setLogin] = React.useState(null);
const [open, setOpen] = React.useState(true);
const [open1, setOpen1] = React.useState(true);
const [output, setOutput] = React.useState(null);
const [change, setChange] = React.useState(false);
useEffect(() => {
if (login != props.login) {
setChange(true)
setOpen(true)
setOpen1(true)
setLogin(props.login)
}
}, [login, setLogin, props.login]);
const handleClose = (event, reason) => {
setOpen(false);
};
const handleClose1 = (event, reason) => {
setOpen1(false);
};
const snack = (login) => {
if (login == "200") {
setOutput(
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
{props.text}
</Alert>
</Snackbar>)
} else if (login != null) {
setOutput(
<Snackbar open={open1} autoHideDuration={3000} onClose={handleClose1}>
<Alert onClose={handleClose1} severity="error">
{props.text}
</Alert>
</Snackbar>
)
} else {
setOutput(null)
}
setChange(false)
}
if (change == true) {
snack(login)
}
return (
<React.Fragment>
{output}
</React.Fragment>
)
}