everything switched over, time to test

This commit is contained in:
Luke Berndt
2023-03-30 22:31:15 -04:00
parent 5875364502
commit b8e60ab926
14 changed files with 1214 additions and 1024 deletions

View File

@@ -5,34 +5,34 @@
const LocalStrategy = require("passport-local").Strategy;
const User = require("../../models/user");
/*
By default, LocalStrategy expects to find credentials in parameters named username and password.
If your site prefers to name these fields differently, options are available to change the defaults.
*/
/*
By default, LocalStrategy expects to find credentials in parameters named username and password.
If your site prefers to name these fields differently, options are available to change the defaults.
*/
const local = new LocalStrategy({
usernameField: "email"
}, (email, password, done) => {
User.findOne({
$or: [{
email
}, {
local: {
email
}
}]
}, (err, user) => {
if(!user) {
console.error("Auth Error - user not found: " + email);
return done(null, false, { message: `Invalid email or password` })
usernameField: "email"
}, async (email, password, done) => {
console.log(`${email} ${password}`);
const user = await User.findOne({
$or: [{
email
}, {
local: {
email
}
}]
});
if (!user) {
console.error("Auth Error - user not found: " + email);
return done(null, false, { message: `Invalid email or password` })
}
user.comparePassword(password, (err, isMatch) => {
if (isMatch) {
return done(null, user)
} else {
console.error("Auth Error - password mismatch: " + email);
return done(null, false, { message: "Invalid email or password" })
}
user.comparePassword(password, (err, isMatch) => {
if (isMatch) {
return done(null, user)
} else {
console.error("Auth Error - password mismatch: " + email);
return done(null, false, { message: "Invalid email or password" })
}
})
})
})
})
})
module.exports = local;

View File

@@ -17,7 +17,7 @@ const mailjet = new Mailjet({
exports.isLoggedIn = function(req, res, next) {
exports.isLoggedIn = function (req, res, next) {
if (req.isAuthenticated()) return next();
res.redirect("/login");
};
@@ -25,7 +25,7 @@ exports.isLoggedIn = function(req, res, next) {
// -------------------------------------------
exports.authenticated = function(req, res, next) {
exports.authenticated = function (req, res, next) {
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
console.log("account/server/controllers/users.js - exports.authenticated() Auth check for user: " + req.user + " is: " + req.isAuthenticated());
if (req.isAuthenticated()) {
@@ -60,11 +60,13 @@ exports.authenticated = function(req, res, next) {
}
}
exports.login = function(req, res, next) {
exports.login = function (req, res, next) {
// Do email and password validation for the server
passport.authenticate("local", function(err, user, info) {
passport.authenticate("local", function (err, user, info) {
if (err) return next(err);
if (!user) {
console.log("No user");
return res.json({
success: false,
message: info.message
@@ -81,6 +83,7 @@ exports.login = function(req, res, next) {
req.login(user, loginErr => {
if (loginErr) {
console.log("error")
return res.json({
success: false,
message: loginErr
@@ -124,199 +127,217 @@ exports.login = function(req, res, next) {
})(req, res, next);
};
exports.confirmEmail = function(req, res, next) {
exports.confirmEmail = async function (req, res, next) {
const userId = req.params["userId"];
const token = req.params["token"];
User.findById(userId, (err, user) => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
if (user.confirmEmail) {
console.log("User already confirmed email: " + userId);
res.json({
success: false,
message: "already confirmed email"
});
return;
}
const today = new Date();
if (user.confirmEmailTTL < today) {
res.json({
success: false,
message: "token expired"
});
return;
}
if (user.confirmEmailToken != token) {
console.log(
"Token Mismatch DB: " + user.confirmEmailToken + " submitted: " + token
);
res.json({
success: false,
message: "token mismatch"
});
return;
}
user.confirmEmail = true;
user.confirmEmailToken = "";
user.save(err => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
console.log("User: " + user.email + " confirmed email address");
res.json({
success: true
});
return;
let user = await User.findById(userId).catch(err => {
console.error(err);
res.status(404);
res.json({
success: false,
message: err
});
return;
});
if (!user) {
console.error("User not found: " + userId);
res.status(404);
res.json({
success: false,
message: "User not found"
});
return;
}
if (user.confirmEmail) {
console.log("User already confirmed email: " + userId);
res.status(500);
res.json({
success: false,
message: "already confirmed email"
});
return;
}
const today = new Date();
if (user.confirmEmailTTL < today) {
res.status(500);
console.error("Expired token for confirming email: " + userId);
res.json({
success: false,
message: "token expired"
});
return;
}
if (user.confirmEmailToken != token) {
console.error(
"Token Mismatch DB: " + user.confirmEmailToken + " submitted: " + token
);
res.json({
success: false,
message: "token mismatch"
});
return;
}
user.confirmEmail = true;
user.confirmEmailToken = "";
await user.save().catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
console.log("User: " + user.email + " confirmed email address");
res.json({
success: true
});
};
exports.resetPassword = function(req, res, next) {
exports.resetPassword = async function (req, res, next) {
const userId = req.params["userId"];
const token = req.params["token"];
User.findById(userId, (err, user) => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
const today = new Date();
if (user.resetPasswordTTL < today) {
res.json({
success: false,
message: "token expired"
});
return;
}
if (user.resetPasswordToken != token) {
console.log(
"Token Mismatch DB: " + user.resetPasswordToken + " submitted: " + token
);
res.json({
success: false,
message: "token mismatch"
});
return;
}
user.password = req.body.password
user.confirmEmail = true;
user.confirmEmailToken = "";
user.resetPasswordToken = "";
user.save(err => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
res.json({
success: true
});
return;
let user = await User.findById(userId).catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
if (!user) {
console.error("User not found: " + userId);
res.status(404);
res.json({
success: false,
message: "User not found"
});
return;
}
const today = new Date();
if (user.resetPasswordTTL < today) {
res.status(500);
res.json({
success: false,
message: "token expired"
});
return;
}
if (user.resetPasswordToken != token) {
console.log(
"Token Mismatch DB: " + user.resetPasswordToken + " submitted: " + token
);
res.status(500);
res.json({
success: false,
message: "token mismatch"
});
return;
}
user.password = req.body.password
user.confirmEmail = true;
user.confirmEmailToken = "";
user.resetPasswordToken = "";
await user.save().catch(err => {
console.error(err);
res.json({
success: false,
message: err
});
return;
});
res.json({
success: true
});
return;
};
// -------------------------------------------
exports.sendResetPassword = function(req, res, next) {
User.findOne({
exports.sendResetPassword = async function (req, res, next) {
let user = await User.findOne({
email: req.body.email
}, (err, user) => {
if (err) {
}).catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
if (!user) {
console.error("Reset password failed. No user: " + req.body.email);
res.status(404);
res.json({
success: false,
message: "No account register for " + req.body.email
});
return;
}
const buffer = crypto.randomBytes(20);
const token = buffer.toString("hex");
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
user.resetPasswordToken = token;
user.resetPasswordTTL = tomorrow;
await user.save().catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
const request = mailjet.post("send", {
version: "v3.1"
}).request({
Messages: [{
From: {
Email: admin_email,
Name: site_name + " Admin"
},
To: [{
Email: user.email,
Name: user.firstName + " " + user.lastName
}],
Subject: site_name + " - Password Reset",
TextPart: "It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset. /r" + account_server + "/reset-password/" + user.id + "/" + token,
HTMLPart: "<h3>Thanks for using " + site_name + "!</h3><br />It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset.<p>" + account_server + "/reset-password/" + user.id + "/" + token + "</p>"
}]
});
request
.then(result => {
console.log(`Password reset sent to: ${user.email}`)
})
.catch(err => {
console.error(err.statusCode);
console.error(err);
res.json({
success: false,
message: err
});
return;
}
if (!user) {
console.error("Reset password failed. No user: " + req.body.email);
res.json({
success: false,
message: "No account register for " + req.body.email
});
return;
}
const buffer = crypto.randomBytes(20);
const token = buffer.toString("hex");
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
user.resetPasswordToken = token;
user.resetPasswordTTL = tomorrow;
user.save(err => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
const request = mailjet.post("send", {
version: "v3.1"
}).request({
Messages: [{
From: {
Email: admin_email,
Name: site_name + " Admin"
},
To: [{
Email: user.email,
Name: user.firstName + " " + user.lastName
}],
Subject: site_name + " - Password Reset",
TextPart: "It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset. /r" + account_server + "/reset-password/" + user.id + "/" + token,
HTMLPart: "<h3>Thanks for using " + site_name + "!</h3><br />It looks like you may have forgot your password. Copy this link to your browser to reset your password. Let us know if you are receiving this but did not request a password reset.<p>" + account_server + "/reset-password/" + user.id + "/" + token + "</p>"
}]
});
request
.then(result => {
console.log(`Password reset sent to: ${user.email}`)
})
.catch(err => {
console.error(err.statusCode);
console.error(err);
res.json({
success: false,
message: err
});
return;
});
res.json({
success: true
});
return;
});
res.json({
success: true
});
return;
};
function handleSendConfirmEmail(user) {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
if (user.confirmEmail) {
console.log("Error - Send Confirm Email - User already confirmed email: " + user.email);
reject({
@@ -332,130 +353,133 @@ function handleSendConfirmEmail(user) {
tomorrow.setDate(tomorrow.getDate() + 1);
user.confirmEmailToken = token;
user.confirmEmailTTL = tomorrow;
user.save().then(function(user) {
return mailjet.post("send", {
version: "v3.1"
}).request({
Messages: [{
From: {
Email: admin_email,
Name: site_name + " Admin"
},
To: [{
Email: user.email,
Name: user.firstName + " " + user.lastName
}],
Subject: "Confirm " + site_name + " Account",
TextPart: "Thanks for signing up for " + site_name + ". We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address: " + account_server + "/confirm-email/" + user._id + "/" + token,
HTMLPart: "<h3>Thanks for signing up for " + site_name + "</h3><br />We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address:<p> " + account_server + "/confirm-email/" + user._id + "/" + token + "</p>"
}]
});
}).then(result => {
console.log("Confirm email sent to: " + user.email);
resolve({
success: true,
userId: user._id
});
}).catch(err => {
console.error("Admin Email: " + admin_email + " User Email: " + user.email);
console.error("Error - Send Confirm Email - caught: " + err);
reject({
success: false,
message: err
});
await user.save();
mailjet.post("send", {
version: "v3.1"
}).request({
Messages: [{
From: {
Email: admin_email,
Name: site_name + " Admin"
},
To: [{
Email: user.email,
Name: user.firstName + " " + user.lastName
}],
Subject: "Confirm " + site_name + " Account",
TextPart: "Thanks for signing up for " + site_name + ". We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address: " + account_server + "/confirm-email/" + user._id + "/" + token,
HTMLPart: "<h3>Thanks for signing up for " + site_name + "</h3><br />We just wanted to check and make sure your email address was real. Copy and paste this addres in your browser to confirm your email address:<p> " + account_server + "/confirm-email/" + user._id + "/" + token + "</p>"
}]
});
}).then(result => {
console.log("Confirm email sent to: " + user.email);
resolve({
success: true,
userId: user._id
});
}).catch(err => {
console.error("Admin Email: " + admin_email + " User Email: " + user.email);
console.error("Error - Send Confirm Email - caught: " + err);
res.status(500);
reject({
success: false,
message: err
});
});
};
}
// -------------------------------------------
exports.sendConfirmEmail = function(req, res, next) {
exports.sendConfirmEmail = async function (req, res, next) {
const userId = req.params["userId"];
User.findById(userId, (err, user) => {
if (err) {
console.error("Error - Send Confirm Email: " + err);
res.json({
success: false,
message: err
});
return;
}
if (!user) {
console.error("Error - Send Confirm Email: User not found " + userId);
res.json({
success: false,
message: "user not found"
});
return;
}
handleSendConfirmEmail(user).then(function(result) {
res.json(result);
return;
}).catch(function(error) {
res.json(error);
return;
});
});
let user = await User.findById(userId).catch(err => {
console.error("Error - Send Confirm Email: " + err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
if (!user) {
console.error("Error - Send Confirm Email: User not found " + userId);
res.status(404);
res.json({
success: false,
message: "user not found"
});
return;
}
handleSendConfirmEmail(user).then(function (result) {
res.json(result);
return;
}).catch(function (error) {
res.json(error);
return;
});
}
};
// -------------------------------------------
exports.logout = function(req, res, next) {
exports.logout = function (req, res, next) {
// the logout method is added to the request object automatically by Passport
req.logout();
res.clearCookie('sessionId', {domain: cookie_domain, path:'/'});
res.clearCookie('sessionId', { domain: cookie_domain, path: '/' });
return res.json({
success: true
});
};
exports.terms = function(req, res, next) {
exports.terms = async function (req, res, next) {
const userId = req.params["userId"];
User.findById(userId, (err, user) => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
if (req.user.id != userId) {
console.log(
"Logged in user's ID: " +
req.user.id +
" does not match Param: " +
userId
);
res.json({
success: false,
message: "UserID incorrect"
});
return;
}
user.terms = 1.1; //User.termsVer;
user.save(err => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
res.json({
success: true
});
return;
let user = await User.findById(userId).catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
};
if (req.user.id != userId) {
console.log(
"Logged in user's ID: " +
req.user.id +
" does not match Param: " +
userId
);
res.status(500);
res.json({
success: false,
message: "UserID incorrect"
});
return;
}
user.terms = 1.1; //User.termsVer;
await user.save().catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
res.json({
success: true
});
return;
}
// -------------------------------------------
exports.validateProfile = function(req, res, next) {
console.log("Validating user profile: "+ req.body.email);
exports.validateProfile = function (req, res, next) {
console.log("Validating user profile: " + req.body.email);
if (!req.body.firstName || (req.body.firstName.length < 2)) {
console.error("ERROR: Validate System - req.body.firstName");
res.json({
@@ -492,68 +516,73 @@ exports.validateProfile = function(req, res, next) {
next();
}
exports.updateProfile = function(req, res, next) {
exports.updateProfile = async function (req, res, next) {
const userId = req.params["userId"];
User.findById(userId, (err, user) => {
// is email address already in use?
if (req.user.id != userId) {
console.log(
"ERROR: Logged in user's ID: " +
req.user.id +
" does not match Param: " +
userId
);
res.json({
success: false,
message: "UserID incorrect"
});
return;
}
// go ahead and create the new user
user.firstName = res.locals.firstName
user.lastName = res.locals.lastName
user.screenName = res.locals.screenName
user.location = res.locals.location
user.save(err => {
if (err) {
console.error(err);
res.json({
success: false,
message: err
});
return;
}
var clientUser = (({
firstName,
lastName,
screenName,
location
}) => ({
firstName,
lastName,
screenName,
location
}))(
user
);
console.log("Updated Profile: " + userId)
res.json({
success: true,
user: clientUser
});
return;
user = await User.findById(userId).catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
if (req.user.id != userId) {
console.log(
"ERROR: Logged in user's ID: " +
req.user.id +
" does not match Param: " +
userId
);
res.json({
success: false,
message: "UserID incorrect"
});
return;
}
// go ahead and create the new user
user.firstName = res.locals.firstName
user.lastName = res.locals.lastName
user.screenName = res.locals.screenName
user.location = res.locals.location
await user.save().catch(err => {
console.error(err);
res.json({
success: false,
message: err
});
return;
});
var clientUser = (({
firstName,
lastName,
screenName,
location
}) => ({
firstName,
lastName,
screenName,
location
}))(
user
);
console.log("Updated Profile: " + userId)
res.json({
success: true,
user: clientUser
});
return;
};
// -------------------------------------------
exports.register = function(req, res, next) {
exports.register = async function (req, res, next) {
console.log("Registration request for: " + req.body.email);
User.findOne({
let user = await User.findOne({
$or: [{
email: req.body.email
}, {
@@ -561,49 +590,57 @@ exports.register = function(req, res, next) {
email: req.body.email
}
}]
}, (err, user) => {
// is email address already in use?
if (user) {
res.json({
success: false,
message: "Email already in use"
});
return;
}
// go ahead and create the new user
var user = (({
firstName,
lastName,
screenName,
location,
email,
password
}) => ({
firstName,
lastName,
screenName,
location,
}))(
res.locals
);
user.password = req.body.password;
user.email = req.body.email;
User.create(user).then(function(dbUser) {
console.log("Successfully registered: " + dbUser.email + ", now sending confirmation email.");
// Since regiestration worked, send a confirmation email.
return handleSendConfirmEmail(dbUser);
}).then(function(result) {
res.json(result);
return;
}).catch(function(err) {
console.error("Error creating user: " + user.email + " Error: " +err);
res.json({
success: false,
message: err
});
return;
}).catch(err => {
console.error(err);
res.status(500);
res.json({
success: false,
message: err
});
return;
});
};
// is email address already in use?
if (user) {
res.status(500);
res.json({
success: false,
message: "Email already in use"
});
return;
}
// go ahead and create the new user
user = (({
firstName,
lastName,
screenName,
location,
email,
password
}) => ({
firstName,
lastName,
screenName,
location,
}))(
res.locals
);
user.password = req.body.password;
user.email = req.body.email;
await User.create(user)
console.log("Successfully registered: " + user.email + ", now sending confirmation email.");
// Since regiestration worked, send a confirmation email.
handleSendConfirmEmail(user).then(function (result) {
res.json(result);
return;
}).catch(function (err) {
console.error("Error creating user: " + user.email + " Error: " + err);
res.json({
success: false,
message: err
});
return;
});
}

View File

@@ -15,39 +15,39 @@ const app = express()
// -------------------------------------------
async function connect() {
const connect = async () => {
// Demonstrate the readyState and on event emitters
console.log(mongoose.connection.readyState); //logs 0
mongoose.connection.on('connecting', () => {
console.log('Mongoose is connecting')
console.log(mongoose.connection.readyState); //logs 2
mongoose.connection.on('connecting', () => {
console.log('Mongoose is connecting')
console.log(mongoose.connection.readyState); //logs 2
});
mongoose.connection.on('connected', () => {
console.log('Mongoose is connected');
console.log(mongoose.connection.readyState); //logs 1
console.log('Mongoose is connected');
console.log(mongoose.connection.readyState); //logs 1
});
mongoose.connection.on('disconnecting', () => {
console.log('Mongoose is disconnecting');
console.log(mongoose.connection.readyState); // logs 3
console.log('Mongoose is disconnecting');
console.log(mongoose.connection.readyState); // logs 3
});
// Connect to a MongoDB server running on 'localhost:27017' and use the
// 'test' database.
await mongoose.connect(secrets.db, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }, (err, res) => {
if (err) {
console.log(`Mongoose - Error connecting to ${secrets.db}. ${err}`)
} else {
console.log(`Mongoose Successfully connected to ${secrets.db}.`)
}
})
await mongoose.connect(secrets.db, { useNewUrlParser: true, useUnifiedTopology: true }).catch(err => {
console.error(`Mongoose - Error connecting to ${secrets.db}. ${err}`)
});
console.log("All Done");
}
connect();
mongoose.connection.on('error', err => {
console.error("Mongoose Error!")
console.error(err);
});
});
mongoose.connection.on('disconnected', () => {
console.log('Mongoose disconnected');
console.log(mongoose.connection.readyState); //logs 0
@@ -72,16 +72,16 @@ configurePassport(app, passport)
configureExpress(app, passport)
app.all('*', function(req, res, next) {
var allowedOrigins = [account_server, admin_server, frontend_server];
var origin = req.headers.origin;
if(allowedOrigins.indexOf(origin) > -1){
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
app.all('*', function (req, res, next) {
var allowedOrigins = [account_server, admin_server, frontend_server];
var origin = req.headers.origin;
if (allowedOrigins.indexOf(origin) > -1) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// -------------------------------------------
@@ -94,8 +94,8 @@ app.post("/users/:userId/reset-password/:token", users.resetPassword)
app.post("/api/send-reset-password", users.sendResetPassword)
app.post("/users/:userId", users.isLoggedIn, users.validateProfile, users.updateProfile)
app.post("/users/:userId/terms", users.isLoggedIn, users.terms)
app.post("/users/:userId/send-confirm", users.sendConfirmEmail)
app.post("/users/:userId/confirm/:token", users.confirmEmail)
app.post("/users/:userId/send-confirm", users.sendConfirmEmail)
app.post("/users/:userId/confirm/:token", users.confirmEmail)
app.get("*", (req, res, next) => {
res.sendFile(__dirname + '/public/index.html');

View File

@@ -2,7 +2,7 @@
import React, { useEffect, useState, } from "react";
import { useNavigate, useSearchParams, Link } from 'react-router-dom';
import { loginUser } from "../features/user/userSlice";
import { useSelector, useDispatch } from 'react-redux'
import {
Container,
Header,
@@ -35,66 +35,52 @@ const Login = (props) => {
const navigate = useNavigate();
let [searchParams, setSearchParams] = useSearchParams();
const nextLocation = searchParams.get("nextLocation");
const dispatch = useDispatch();
const handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
const loginSubmit = (event) => {
const loginSubmit = async (event) => {
event.preventDefault();
/*
var success = { type: "path", nextPathname: "/" };
if (nextLocation) {
success.type = "location";
switch (this.props.nextLocation) {
case "frontend":
success.nextLocation = process.env.REACT_APP_FRONTEND_SERVER;
break;
default:
case "admin":
success.nextLocation = process.env.REACT_APP_ADMIN_SERVER;
break;
const result = await dispatch(loginUser({email,password})).unwrap();
if (result.success) {
if (nextLocation) {
switch (this.props.nextLocation) {
case "frontend":
window.location = process.env.REACT_APP_FRONTEND_SERVER;
break;
default:
case "admin":
window.location = process.env.REACT_APP_ADMIN_SERVER;
break;
}
} else {
navigate("/");
}
console.log(result)
} else {
console.error(result);
setLoginMessage(result.message);
if (result.message === "unconfirmed email") {
/*data.userId = response.data.userId;
dispatch(loginEmailError(data));*/
navigate("/wait-confirm-email");
}
}
// Passed in via react-redux. Returns a promise.
this.props
.manualLogin(
{
// this function is passed in via react-redux
email,
password
},
success
) // holds the path to redirect to after login (if any)
.then(loginMessage => {
if (loginMessage) {
// report to the user is there was a problem during login
this.setState({
loginMessage
});
}
});*/
}
/*
let loginMessageDisplay =(<></>)
if (this.state.loginMessage) {
loginMessage = (
if (loginMessage) {
loginMessageDisplay = (
<Message icon>
<Icon name="lemon" />
<Message.Content>
<Message.Header>Problems...</Message.Header>
{this.state.loginMessage}
{loginMessage}
</Message.Content>
</Message>
);
}
*/
return (
<Container>
<Header as="h1">{process.env.REACT_APP_SITE_NAME}</Header>
@@ -176,7 +162,7 @@ const Login = (props) => {
<Button type="submit" size="large" value="Login" fluid>
Login
</Button>
{loginMessage}
{loginMessageDisplay}
</Segment>
</Form>
<div style={forgotStyle} >

View File

@@ -1,45 +1,37 @@
import React, { Component } from "react"
import React, { useEffect, useState, } from "react";
import { useSelector, useDispatch } from 'react-redux'
import { useNavigate, useSearchParams, Link } from 'react-router-dom';
import UserForm from "./UserForm";
import {
Container,
Header
} from "semantic-ui-react";
import { updateProfile } from "../features/user/userSlice";
class Profile extends Component {
constructor(props) {
super(props);
this.handleProfileSubmit = this.handleProfileSubmit.bind(this);
}
const Profile = (props) => {
const [message, setMessage] = useState("");
const user = useSelector((state) => state.user);
const dispatch = useDispatch();
const handleProfileSubmit = async (updatedUser) => {
updatedUser.userId = user.userId
state = {
requestMessage: ""
const result = await dispatch(updateProfile(updatedUser)).unwrap();
if (result.success) {
console.log(result)
} else {
setMessage = result.message;
}
}
handleProfileSubmit(user) {
user.userId = this.props.user.userId
this.props
.updateProfile(user)
.then(requestMessage => {
if (requestMessage) {
// report to the user is there was a problem during registration
this.setState({
requestMessage
});
}
});
}
render() {
// The Key on UserForm get updated when the user is loaded. This forces the Component to be completely redrawn, otherwise the changes in the Prop are not copied to State.
return(
<Container text>
<Header as="h1">Update Profile</Header>
<UserForm onSubmit={this.handleProfileSubmit} user={this.props.user} isEditing={true} requestMessage={this.state.requestMessage} isWaiting={this.props.isWaiting} key={this.props.user.userId}/>
<UserForm onSubmit={handleProfileSubmit} user={user} isEditing={true} requestMessage={message} isWaiting={props.isWaiting} key={user.userId}/>
</Container>
)
}
}
export default Profile

View File

@@ -1,4 +1,7 @@
import React, { Component } from "react";
import React, { useState } from "react";
import { registerUser } from "../features/user/userSlice";
import { useNavigate, useSearchParams, Link } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux'
import {
Container,
Header,
@@ -8,53 +11,39 @@ import {
import UserForm from "./UserForm";
// ----------------------------------------------------
class Register extends Component {
constructor(props) {
super(props);
this._onRegisterSubmit = this._onRegisterSubmit.bind(this);
function Register(props) {
const [requestMessage, setRequestMessage] = useState("");
const dispatch = useDispatch();
const navigate = useNavigate();
const onRegisterSubmit = async(user) => {
const result = await dispatch(registerUser(user)).unwrap();
if (result.success) {
navigate("/sent-confirm-email");
} else {
console.error(result);
setRequestMessage(result.message);
}
};
state = {
requestMessage: ""
}
_onRegisterSubmit(user) {
this.props
.manualRegister(user)
.then(requestMessage => {
if (requestMessage) {
// report to the user is there was a problem during registration
this.setState({
requestMessage
});
}
});
}
render() {
return (
<div>
<Container text>
return (
<div>
<Container text>
<Message icon>
<Icon name='trophy'/>
<Message.Content>
<Message.Header>Welcome!</Message.Header>
<p>Creating an account on OpenMHz is the first step in sharing radio system recordings using Trunk Recorder.</p>
<p>After creating an account, head over <a href="https://github.com/robotastic/trunk-recorder">here</a> to learn more about Trunk Recorder</p>
<p> - Luke</p>
</Message.Content>
</Message>
</Container>
<Container text>
<Header style={{ paddingTop: '20px' }} as="h1">Register</Header>
<UserForm onSubmit={this._onRegisterSubmit} requestMessage={this.state.requestMessage} isWaiting={this.props.isWaiting}/>
</Container>
</div>
);
}
<Icon name='trophy'/>
<Message.Content>
<Message.Header>Welcome!</Message.Header>
<p>Creating an account on OpenMHz is the first step in sharing radio system recordings using Trunk Recorder.</p>
<p>After creating an account, head over <a href="https://github.com/robotastic/trunk-recorder">here</a> to learn more about Trunk Recorder</p>
<p> - Luke</p>
</Message.Content>
</Message>
</Container>
<Container text>
<Header style={{ paddingTop: '20px' }} as="h1">Register</Header>
<UserForm onSubmit={onRegisterSubmit} requestMessage={requestMessage} isWaiting={props.isWaiting}/>
</Container>
</div>
);
}
export default Register;

View File

@@ -1,4 +1,7 @@
import React, { Component } from "react";
import React, { useState } from "react";
import { resetPassword } from "../features/user/userSlice";
import { useParams, useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux'
import {
Container,
Form,
@@ -6,101 +9,101 @@ import {
Button,
} from "semantic-ui-react";
class ResetPassword extends Component {
constructor(props) {
super(props);
this.handleResetPassword = this.handleResetPassword.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
state = {
checkInputMessages: [],
password: "",
confirmPassword: "",
passwordError: false,
confirmPasswordError: false,
sentReset: false
};
const ResetPassword = (props) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [passwordError, setPasswordError] = useState(false);
const [confirmPasswordError, setConfirmPasswordError] = useState(false);
const [checkInputMessages, setCheckInputMessages] = useState([]);
const { userId, token } = useParams();
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
handleResetPassword(event) {
const handleResetPassword = async () => {
let error = false;
var checkInputMessages = [];
var inputMessages = [];
if (this.state.password === "") {
this.setState({ passwordError: true });
checkInputMessages.push("Password is required");
if (password === "") {
setPasswordError(true);
inputMessages.push("Password is required");
error = true;
} if (this.state.password.length < 7) {
this.setState({ passwordError: true });
checkInputMessages.push("Password must be 7 charecters or more");
error = true;
}else {
this.setState({ passwordError: false });
}
if (this.state.password !== this.state.confirmPassword) {
this.setState({ confirmPasswordError: true });
checkInputMessages.push("The passwords did not match");
} else if (password.length < 7) {
setPasswordError(true);
inputMessages.push("Password must be 7 characters or more");
error = true;
} else {
this.setState({ confirmPasswordError: false });
setPasswordError(false);
}
const { match: { params } } = this.props;
this.setState({ checkInputMessages: checkInputMessages });
if (password !== confirmPassword) {
setConfirmPasswordError(true);
inputMessages.push("The passwords did not match");
error = true;
} else {
setConfirmPasswordError(false);
}
const { match: { params } } = props;
setCheckInputMessages(inputMessages);
if (!error) {
this.props.resetPassword(params.userId, params.token, this.state.password).then(resetSent => {
this.setState({resetSent: true});
});
const result = await dispatch(resetPassword({ userId, token, password })).unwrap();
if (result.success) {
navigate("/login");
} else {
console.error(result);
}
}
}
};
render() {
return (
<div>
<Container text>
return (
<div>
<Container text>
<Segment>
<Form
className="raised padding segment"
onSubmit={this.handleSubmit}
>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
name="password"
type="password"
onChange={this.handleInputChange}
error={this.state.passwordError}
label="Password"
placeholder="Password..."
<Form className="raised padding segment">
<Form.Group widths="equal">
<Form.Field>
<Form.Input
name="password"
type="password"
onChange={e => setPassword(e.target.value)}
error={passwordError}
label="Password"
placeholder="Password..."
/>
</Form.Field>
<Form.Field>
<Form.Input
name="confirmPassword"
type="password"
onChange={e => setConfirmPassword(e.target.value)}
error={confirmPasswordError}
label="Confirm Password"
placeholder="Confirm Password..."
/>
</Form.Field>
</Form.Group>
<Button
size="large"
content="Reset Password"
onClick={handleResetPassword}
fluid
/>
</Form.Field>
<Form.Field>
<Form.Input
name="confirmPassword"
type="password"
onChange={this.handleInputChange}
error={this.state.confirmPasswordError}
label="Confirm Password"
placeholder="Confirm Password..."
/>
</Form.Field>
</Form.Group>
<Button
size="large"
content="Reset Password"
onClick={this.handleResetPassword}
fluid
/>
</Form>
</Segment>
{checkInputMessages.length > 0 && (
<div style={{ marginTop: "10px" }}>
{checkInputMessages.map((msg) => (
<p key={msg} style={{ color: "red" }}>
{msg}
</p>
))}
</div>
)}
</Form>
</Segment>
</Container>
</div>
);
}
}
</div>
);
};
export default ResetPassword;

View File

@@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { useState } from "react";
import {
Form,
Grid,
@@ -7,106 +7,99 @@ import {
Message,
Icon
} from "semantic-ui-react";
import { sendResetPassword } from "../features/user/userSlice";
import { Link } from "react-router-dom";
import { useParams, useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux'
const returnStyle = {
marginTop: "30px"
};
class SendResetPassword extends Component {
constructor(props) {
super(props);
this.handleSendResetPassword = this.handleSendResetPassword.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
state = {
checkInputMessages: "",
email: "",
emailError: false,
sentReset: false
const SendResetPassword = (props) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [checkInputMessages, setCheckInputMessages] = useState("");
const [email, setEmail] = useState("");
const [emailError, setEmailError] = useState(false);
const [resetSent, setResetSent] = useState(false);
const returnStyle = {
marginTop: "30px"
};
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
handleSendResetPassword(event) {
if (this.state.email === "") {
this.setState({ emailError: true, checkInputMessages: "Email is required"});
const handleSendResetPassword = async () => {
if (email === "") {
setEmailError(true);
setCheckInputMessages("Email is required");
} else {
this.setState({ emailError: false });
this.props.sendResetPassword({email: this.state.email}).then(resetMessage => {
if (resetMessage) {
// report to the user is there was a problem during login
this.setState({resetSent: false, checkInputMessages: resetMessage, emailError: true});
} else {
this.setState({resetSent: true});
const result = await dispatch(sendResetPassword({ email })).unwrap();
if (result.success) {
setResetSent(true);
setEmailError(true);
} else {
setCheckInputMessages(result.message);
console.error(result);
}
});
}
}
render() {
var resetMessage = "";
if (this.state.checkInputMessages.length) {
resetMessage = (
<Message compact warning icon>
<Icon name="lightning" />
<Message.Content>
<Message.Header>Problems...</Message.Header>
};
{this.state.checkInputMessages}
</Message.Content>
</Message>
);
}
return (
<Grid verticalAlign='middle' centered>
<Grid.Row>
<Grid.Column width="6">
{ !this.state.resetSent ? (
<div>
<h2>Forgot your password?</h2>
<Form className="raised padding segment" onSubmit={this.handleSubmit}>
Enter the email address for your account.
<Form.Field>
<Form.Input
type="text"
name="email"
onChange={this.handleInputChange}
error={this.state.emailError}
label="Email"
placeholder="Email..."
/>
</Form.Field>
<Button
size="large"
content="Reset Password"
onClick={this.handleSendResetPassword}
fluid
/>
</Form>
{resetMessage}
</div>
)
:( <Segment raised padded='very'>
<h2>Reset Link Sent </h2>
<div>An email with a link to reset your password was sent to: {this.state.email}</div>
<Link to="/" style={returnStyle}><Icon name="left arrow"/>Back to OpenMHz</Link>
</Segment>
)}
</Grid.Column>
</Grid.Row>
</Grid>
var resetMessage = "";
if (checkInputMessages.length) {
resetMessage = (
<Message compact warning icon>
<Icon name="lightning" />
<Message.Content>
<Message.Header>Problems...</Message.Header>
{checkInputMessages}
</Message.Content>
</Message>
);
}
return (
<Grid verticalAlign="middle" centered>
<Grid.Row>
<Grid.Column width={6}>
{!resetSent ? (
<div>
<h2>Forgot your password?</h2>
<Form className="raised padding segment">
Enter the email address for your account.
<Form.Field>
<Form.Input
type="text"
name="email"
onChange={e => setEmail(e.target.value)}
value={email}
error={emailError}
label="Email"
placeholder="Email..."
/>
</Form.Field>
<Button
size="large"
content="Reset Password"
onClick={handleSendResetPassword}
fluid
/>
</Form>
{resetMessage}
</div>
) : (
<Segment raised padded="very">
<h2>Reset Link Sent </h2>
<div>
An email with a link to reset your password was sent to: {email}
</div>
<Link to="/" style={returnStyle}>
<Icon name="left arrow" />
Back to OpenMHz
</Link>
</Segment>
)}
</Grid.Column>
</Grid.Row>
</Grid>
);
}
export default SendResetPassword;

View File

@@ -1,28 +1,25 @@
import React, { Component } from "react"
import {
Container,
Message,
Icon
} from "semantic-ui-react";
import React from "react";
import { Container, Message, Icon } from "semantic-ui-react";
import { useSelector } from 'react-redux'
class SentConfirmEmail extends Component {
const SentConfirmEmail = ({ user }) => {
const { email } = useSelector((state) => state.user);
return (
<div>
<Container text>
<Message icon>
<Icon name='envelope outline' />
<Message.Content>
<Message.Header>Confirmation email sent!</Message.Header>
<p>
We sent an email to the address you gave us: {email}<br />
Please click on the link in the email to verify the email address.
</p>
</Message.Content>
</Message>
</Container>
</div>
);
};
render() {
return (
<div>
<Container text>
<Message icon>
<Icon name='envelope outline' />
<Message.Content>
<Message.Header>Confirmation email sent!</Message.Header>
<p>We sent an email to the address you gave us: {this.props.user.email}<br />
Please click on the link in the email to verify the email address.</p>
</Message.Content>
</Message>
</Container>
</div>
)
}
}
export default SentConfirmEmail
export default SentConfirmEmail;

View File

@@ -1,4 +1,5 @@
import React, { Component } from "react";
import React, { useEffect, useState, } from "react";
import {
Form,
Dimmer,
@@ -10,270 +11,260 @@ import {
// ----------------------------------------------------
class UserForm extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.checkInputs = this.checkInputs.bind(this);
this.render = this.render.bind(this)
this.state = {
checkInputMessages: [],
registerMessages: "",
firstName: "",
lastName: "",
screenName: "",
email: "",
location: "",
password: "",
confirmPassword: "",
firstNameError: false,
lastNameError: false,
screenNameError: false,
emailError: false,
locationError: false,
passwordError: false,
confirmPasswordError: false,
isEditing: this.props.isEditing
};
if (this.props.isEditing) {
this.state.firstName = this.props.user.firstName;
this.state.lastName = this.props.user.lastName;
this.state.screenName = this.props.user.screenName;
this.state.email = this.props.user.email;
this.state.location = this.props.user.location;
const UserForm = (props) => {
const [checkInputMessages, setCheckInputMessages] = useState([]);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [screenName, setScreenName] = useState("");
const [email, setEmail] = useState("");
const [location, setLocation] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [firstNameError, setFirstNameError] = useState(false);
const [lastNameError, setLastNameError] = useState(false);
const [screenNameError, setScreenNameError] = useState(false);
const [emailError, setEmailError] = useState(false);
const [locationError, setLocationError] = useState(false);
const [passwordError, setPasswordError] = useState(false);
const [confirmPasswordError, setConfirmPasswordError] = useState(false);
const [isEditing, setIsEditing] = useState(props.isEditing);
const requestMessage = props.requestMessage;
useEffect(() => {
if (props.isEditing) {
setFirstName(props.user.firstName);
setLastName(props.user.lastName);
setScreenName(props.user.screenName);
setEmail(props.user.email);
setLocation(props.user.location);
}
}
}, [props.isEditing, props.user]);
checkInputs() {
const checkInputs = () => {
let error = false;
var checkInputMessages = [];
let inputMessages = [];
if (this.state.firstName === "") {
this.setState({ firstNameError: true });
checkInputMessages.push("First Name is required");
if (firstName === "") {
setFirstNameError(true);
inputMessages.push("First Name is required");
error = true;
} else {
this.setState({ firstNameError: false });
setFirstNameError(false);
}
if (this.state.lastName === "") {
this.setState({ lastNameError: true });
checkInputMessages.push("Last Name is required");
if (lastName === "") {
setLastNameError(true);
inputMessages.push("Last Name is required");
error = true;
} else {
this.setState({ lastNameError: false });
setLastNameError(false);
}
if (this.state.email === "") {
this.setState({ emailError: true });
checkInputMessages.push("Email is required");
if (email === "") {
setEmailError(true);
inputMessages.push("Email is required");
error = true;
} else {
this.setState({ emailError: false });
setEmailError(false);
}
if (this.state.location === "") {
this.setState({ locationError: true });
checkInputMessages.push("Location is required");
if (location === "") {
setLocationError(true);
inputMessages.push("Location is required");
error = true;
} else {
this.setState({ locationError: false });
setLocationError(false);
}
if (this.state.screenName === "") {
this.setState({ screenNameError: true });
checkInputMessages.push("Screen Name is required");
if (screenName === "") {
setScreenNameError(true);
inputMessages.push("Screen Name is required");
error = true;
} else {
this.setState({ screenNameError: false });
setScreenNameError(false);
}
if (this.props.isEditing) {
this.setState({ checkInputMessages: checkInputMessages });
if (props.isEditing) {
setCheckInputMessages(checkInputMessages);
return error;
}
if (this.state.password === "") {
this.setState({ passwordError: true });
checkInputMessages.push("Password is required");
if (password === "") {
setPasswordError(true);
inputMessages.push("Password is required");
error = true;
} if (this.state.password.length < 7) {
this.setState({ passwordError: true });
checkInputMessages.push("Password must be 7 charecters or more");
} if (password.length < 7) {
setPasswordError(true);
inputMessages.push("Password must be 7 charecters or more");
error = true;
} else {
this.setState({ passwordError: false });
setPasswordError(false);
}
if (this.state.password !== this.state.confirmPassword) {
this.setState({ confirmPasswordError: true });
checkInputMessages.push("The passwords did not match");
if (password !== confirmPassword) {
setConfirmPasswordError(true);
inputMessages.push("The passwords did not match");
error = true;
} else {
this.setState({ confirmPasswordError: false });
setConfirmPasswordError(false);
}
this.setState({ checkInputMessages: checkInputMessages });
setCheckInputMessages(inputMessages);
return error;
}
handleInputChange = (e, { name, value }) => this.setState({ [name]: value });
handleSubmit(event) {
const handleSubmit = (event) => {
event.preventDefault();
let inputError = this.checkInputs();
let inputError = checkInputs();
if (!inputError) {
const user = (({ firstName, lastName, screenName, location, email, password }) => ({ firstName, lastName, screenName, location, email, password }))(this.state);
this.props.onSubmit(user);
const user ={ firstName, lastName, screenName, location, email, password };
props.onSubmit(user);
}
}
render() {
var registerMessage = "";
var floatStyle = {
overflow: 'auto'
}
var dimmerProps = {};
if (this.props.isWaiting) {
dimmerProps["active"] = true;
}
const { isEditing } = this.props;
if (this.state.checkInputMessages.length || this.props.requestMessage.length) {
var counter = 0;
registerMessage = (
<Message compact warning icon>
<Icon name="lightning" />
<Message.Content>
<Message.Header>Problems...</Message.Header>
<ul>
{this.state.checkInputMessages.map(function (listValue) {
return <li key={`ListItem_${counter++}`}>{listValue}</li>;
})}
{this.props.requestMessage.length > 0 &&
<li key="req-mess">{this.props.requestMessage}</li>
}
</ul>
</Message.Content>
</Message>
);
}
return (
<div>
<Form
className="raised padding segment"
onSubmit={this.handleSubmit}
>
<Dimmer {...dimmerProps}>
<Loader indeterminate>Updating Profile</Loader>
</Dimmer>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
type="text"
name="firstName"
onChange={this.handleInputChange}
error={this.state.firstNameError}
defaultValue={this.state.firstName}
label="First name"
placeholder="First Name..."
/>
</Form.Field>
<Form.Field>
<Form.Input
type="text"
name="lastName"
onChange={this.handleInputChange}
error={this.state.lastNameError}
defaultValue={this.state.lastName}
label="Last name"
placeholder="Last Name..."
/>
</Form.Field>
</Form.Group>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
type="text"
name="email"
onChange={this.handleInputChange}
error={this.state.emailError}
defaultValue={this.state.email}
disabled={isEditing}
label="Email"
placeholder="Email..."
/>
var registerMessage = "";
var floatStyle = {
overflow: 'auto'
}
var dimmerProps = {};
if (props.isWaiting) {
dimmerProps["active"] = true;
}
</Form.Field>
<Form.Field>
<Form.Input
type="text"
name="screenName"
onChange={this.handleInputChange}
error={this.state.screenNameError}
defaultValue={this.state.screenName}
label="Screen Name"
placeholder="Screen Name..."
/>
<p style={{ fontStyle: "italic" }}>
Shown on OpenMHz for each of your systems
</p>
</Form.Field>
</Form.Group>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
type="text"
name="location"
onChange={this.handleInputChange}
error={this.state.locationError}
defaultValue={this.state.location}
label="General Location"
placeholder="City, State..."
/>
</Form.Field>
</Form.Group>
{isEditing !== true && (
<Form.Group widths="equal">
<Form.Field>
<Form.Input
name="password"
type="password"
onChange={this.handleInputChange}
error={this.state.passwordError}
disabled={isEditing}
label="Password"
placeholder="Password..."
/>
</Form.Field>
<Form.Field>
<Form.Input
name="confirmPassword"
type="password"
onChange={this.handleInputChange}
error={this.state.confirmPasswordError}
disabled={isEditing}
label="Confirm Password"
placeholder="Confirm Password..."
/>
</Form.Field>
</Form.Group>
)}
<div style={floatStyle}>
<Button type="submit" size="large" floated="right" value="Login" color='blue'>
{isEditing !== true ? 'Register' : 'Update'}
</Button>
</div>
</Form>
{registerMessage}
</div>
if (checkInputMessages.length || requestMessage.length) {
var counter = 0;
registerMessage = (
<Message compact warning icon>
<Icon name="lightning" />
<Message.Content>
<Message.Header>Problems...</Message.Header>
<ul>
{checkInputMessages.map(function (listValue) {
return <li key={`ListItem_${counter++}`}>{listValue}</li>;
})}
{requestMessage.length > 0 &&
<li key="req-mess">{requestMessage}</li>
}
</ul>
</Message.Content>
</Message>
);
}
return (
<div>
<Form
className="raised padding segment"
onSubmit={handleSubmit}
>
<Dimmer {...dimmerProps}>
<Loader indeterminate>Updating Profile</Loader>
</Dimmer>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
type="text"
name="firstName"
onChange={e => setFirstName(e.target.value)}
error={firstNameError}
value={firstName}
label="First name"
placeholder="First Name..."
/>
</Form.Field>
<Form.Field>
<Form.Input
type="text"
name="lastName"
onChange={e => setLastName(e.target.value)}
error={lastNameError}
value={lastName}
label="Last name"
placeholder="Last Name..."
/>
</Form.Field>
</Form.Group>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
type="text"
name="email"
onChange={e => setEmail(e.target.value)}
error={emailError}
value={email}
disabled={isEditing}
label="Email"
placeholder="Email..."
/>
</Form.Field>
<Form.Field>
<Form.Input
type="text"
name="screenName"
onChange={e => setScreenName(e.target.value)}
error={screenNameError}
value={screenName}
label="Screen Name"
placeholder="Screen Name..."
/>
<p style={{ fontStyle: "italic" }}>
Shown on OpenMHz for each of your systems
</p>
</Form.Field>
</Form.Group>
<Form.Group widths="equal">
<Form.Field>
<Form.Input
type="text"
name="location"
onChange={e => setLocation(e.target.value)}
error={locationError}
value={location}
label="General Location"
placeholder="City, State..."
/>
</Form.Field>
</Form.Group>
{isEditing !== true && (
<Form.Group widths="equal">
<Form.Field>
<Form.Input
name="password"
type="password"
onChange={e => setPassword(e.target.value)}
error={passwordError}
disabled={isEditing}
label="Password"
placeholder="Password..."
/>
</Form.Field>
<Form.Field>
<Form.Input
name="confirmPassword"
type="password"
onChange={e => setConfirmPassword(e.target.value)}
error={confirmPasswordError}
disabled={isEditing}
label="Confirm Password"
placeholder="Confirm Password..."
/>
</Form.Field>
</Form.Group>
)}
<div style={floatStyle}>
<Button type="submit" size="large" floated="right" value="Login" color='blue'>
{isEditing !== true ? 'Register' : 'Update'}
</Button>
</div>
</Form>
{registerMessage}
</div>
);
}
export default UserForm;

View File

@@ -1,52 +1,43 @@
import React, { Component } from "react"
import {
Container,
Header,
Button,
Message,
Icon
} from "semantic-ui-react";
class WaitConfirmEmail extends Component {
constructor(props) {
super(props);
this.handleSendConfirmEmail = this.handleSendConfirmEmail.bind(this);
import React, { useState } from "react";
import { sendConfirmEmail } from "../features/user/userSlice";
import { useNavigate } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux'
import { Container, Header, Button, Message, Icon } from "semantic-ui-react";
}
handleSendConfirmEmail(event) {
this.props.sendConfirmEmail(this.props.user.userId).then(loginMessage => {
if (loginMessage) {
// report to the user is there was a problem during login
this.setState({
loginMessage
});
}
});
const WaitConfirmEmail = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { email, userId } = useSelector((state) => state.user);
const handleSendConfirmEmail = async (event) => {
const message = await dispatch(sendConfirmEmail(userId)).unwrap();
navigate("/sent-confirm-email")
};
render() {
return(
<Container text>
<Message icon>
<Icon name='envelope outline'/>
<Message.Content>
<Message.Header>Confirm your email address</Message.Header>
<p>We sent an email to the address you gave us: {this.props.user.email}<br/>
Please click on the link in the email to verify the email address.</p>
<Header as='h3'>Didn't get an email?</Header>
<p>So, you have waited a bit and haven't gotten anything?</p>
return (
<Container text>
<Message icon>
<Icon name="envelope outline" />
<Message.Content>
<Message.Header>Confirm your email address</Message.Header>
<p>
We sent an email to the address you gave us: {email}
<br />
Please click on the link in the email to verify the email address.
</p>
<Header as="h3">Didn't get an email?</Header>
<p>So, you have waited a bit and haven't gotten anything?</p>
<p>...and you checked your spam folder?</p>
<p>Well, click below.</p>
<Button
size="large"
content="Resend Email Confirmation"
onClick={this.handleSendConfirmEmail}
/>
</Message.Content>
</Message>
<Button
size="large"
content="Resend Email Confirmation"
onClick={handleSendConfirmEmail}
/>
</Message.Content>
</Message>
</Container>
);
};
</Container>
)
}
}
export default WaitConfirmEmail
export default WaitConfirmEmail;

View File

@@ -22,11 +22,33 @@ export const authenticateUser = createAsyncThunk(
export const loginUser = createAsyncThunk(
'user/login',
async ({ getState }) => {
async (data) => {
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/login"
const res = await fetch(url, {
method: 'POST',
credentials: "include"
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
}).then(
(data) => data.json()
)
return res;
}
)
export const registerUser = createAsyncThunk(
'user/register',
async (data) => {
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/register"
const res = await fetch(url, {
method: 'POST',
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
}).then(
(data) => data.json()
)
@@ -36,6 +58,24 @@ export const loginUser = createAsyncThunk(
export const updateProfile = createAsyncThunk(
'user/updateProfile',
async (user) => {
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + user.userId;
const res = await fetch(url, {
method: 'POST',
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user)
}).then(
(data) => data.json()
)
return res;
}
)
export const logoutUser = createAsyncThunk(
'user/logout',
async ({ getState }) => {
@@ -50,6 +90,63 @@ export const logoutUser = createAsyncThunk(
}
)
export const sendConfirmEmail = createAsyncThunk(
'user/sendConfirmEmail',
async (userId) => {
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + userId + "/send-confirm";
const res = await fetch(url, {
method: 'POST',
credentials: "include",
headers: {
"Content-Type": "application/json",
}
}).then(
(data) => data.json()
)
return res;
}
)
export const resetPassword = createAsyncThunk(
'user/resetPassword',
async (data) => {
const {userId, token, password} = data;
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/users/" + userId + "/confirm/" + token
const res = await fetch(url, {
method: 'POST',
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({password})
}).then(
(data) => data.json()
)
return res;
}
)
export const sendResetPassword = createAsyncThunk(
'user/sendResetPassword',
async (data) => {
const {userId, token, password} = data;
const url = process.env.REACT_APP_ACCOUNT_SERVER + "/api/send-reset-password"
const res = await fetch(url, {
method: 'POST',
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
}).then(
(data) => data.json()
)
return res;
}
)
export const userSlice = createSlice({
name: 'user',
initialState: {
@@ -59,7 +156,12 @@ export const userSlice = createSlice({
userId: "",
admin: false,
email: "",
firstName: "",
lastName: "",
location: "",
screenName: ""
},
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
@@ -70,6 +172,44 @@ export const userSlice = createSlice({
},
},
extraReducers: {
[loginUser.fulfilled]: (state, data) => {
const {payload} = data;
if (payload.success) {
state.authenticated = true;
state.hasAuthenticated = true;
state.userId = payload.user.userId;
state.admin = payload.user.admin;
state.email = payload.user.email;
state.firstName = payload.user.firstName;
state.lastName = payload.user.lastName;
state.location = payload.user.location;
state.screenName = payload.user.screenName;
} else {
state.authenticated = false;
state.hasAuthenticated = true;
}
},
[updateProfile.fulfilled]: (state, data) => {
const {payload} = data;
if (payload.success) {
state.authenticated = true;
state.hasAuthenticated = true;
state.userId = payload.user.userId;
state.admin = payload.user.admin;
state.email = payload.user.email;
state.firstName = payload.user.firstName;
state.lastName = payload.user.lastName;
state.location = payload.user.location;
state.screenName = payload.user.screenName;
} else {
state.authenticated = false;
state.hasAuthenticated = true;
}
},
[loginUser.rejected]: (state, { payload }) => {
state.authenticated = false;
state.hasAuthenticated = true;
},
[authenticateUser.fulfilled]: (state, data) => {
const {payload} = data;
if (payload.success) {
@@ -78,6 +218,10 @@ export const userSlice = createSlice({
state.userId = payload.user.userId;
state.admin = payload.user.admin;
state.email = payload.user.email;
state.firstName = payload.user.firstName;
state.lastName = payload.user.lastName;
state.location = payload.user.location;
state.screenName = payload.user.screenName;
} else {
state.authenticated = false;
state.hasAuthenticated = true;

View File

@@ -8,7 +8,7 @@ const configureExpress = require("./config/express");
const systems = require("./controllers/systems");
const groups = require("./controllers/groups");
const talkgroups = require("./controllers/talkgroups");
const Permission = require("./models/permission");
require("./models/user");
const multer = require('multer');
@@ -60,26 +60,6 @@ mongoose.connection.on('disconnected', () => {
const isDev = process.env.NODE_ENV === "development"
/*
async function getRole(req) {
let promise = new Promise((resolve, reject) => {
if (req.params.shortName && req.user) {
var short_name = req.params.shortName.toLowerCase();
var userId = req.user._id;
Permission.findOne({'userId': userId, 'shortName': short_name}, function(err, permission) {
if (err) {
console.error('Error - getRole userId: ' + userId + ' shortName: ' + short_name + ' error: ' + err);
reject(-1);
}
console.log('Found - getRole userId: ' + userId + ' shortName: ' + short_name + ' role: ' + permission.role);
resolve(permission.role);
});
} else {
reject(-1);
}});
return promise;
}*/

View File

@@ -0,0 +1,87 @@
// Defining a User Model in mongoose
// Code modified from https://github.com/sahat/hackathon-starter
const bcrypt = require("bcrypt");
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
local: {
name: String,
email: String,
password: String
},
email: {
type: String,
unique: true,
lowercase: true
},
password: String,
screenName: {
type: String,
unique: true,
lowercase: true
},
firstName: String,
lastName: String,
location: String,
email: String,
resetPasswordToken: String,
resetPasswordTTL: Date,
confirmEmail: {
type: Boolean,
default: false
},
confirmEmailToken: String,
confirmEmailTTL: Date,
admin: {
type: Boolean,
default: false
},
terms: {
type: Number,
default: 0
},
ver: {
type: Number,
default: 1.1
},
sysCount: Number,
lastLogin: { type : Date, default: Date.now }
})
/**
* Password hash middleware.
*/
UserSchema.pre("save", function(next) {
var user = this
if (!user.isModified("password")) return next()
bcrypt.genSalt(8, (err, salt) => {
if (err) return next(err)
bcrypt.hash(user.password, salt, null, (err, hash) => {
if (err) return next(err)
user.password = hash;
user.local.password = hash;
next()
})
})
})
/*
Defining our own custom document instance method
*/
UserSchema.methods = {
comparePassword: function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.local.password, (err, isMatch) => {
if (err) return cb(err)
cb(null, isMatch)
})
}
}
/**
* Statics
*/
UserSchema.statics = {}
module.exports = mongoose.model("User", UserSchema)