From 8aeb622b17420b0a42aec29b2ea1e38b8eea4f8a Mon Sep 17 00:00:00 2001 From: BIG2EYEZ Date: Thu, 18 Jan 2024 18:34:09 +0800 Subject: [PATCH 1/3] CHANGE ALL MYSQL TO ORM --- Sean/models/User.js | 77 +-- Sean/models/userLogs.js | 30 ++ Sean/modules/mysql.js | 76 +-- Sean/server.js | 1076 +++++++++++++++++---------------------- Sean/views/inusers.ejs | 2 - Sean/views/inusers.js | 27 +- package-lock.json | 601 +++++++++++++++++++++- package.json | 5 +- 8 files changed, 1169 insertions(+), 725 deletions(-) create mode 100644 Sean/models/userLogs.js diff --git a/Sean/models/User.js b/Sean/models/User.js index ab12ee9..8cda275 100644 --- a/Sean/models/User.js +++ b/Sean/models/User.js @@ -1,35 +1,50 @@ -// models/User.js const { Sequelize, DataTypes } = require('sequelize'); -const sequelize = new Sequelize(process.env.database, process.env.user, process.env.password, { - host: process.env.host, - dialect: 'mysql', - timezone: 'Z', // Set the timezone to UTC -}); +const bcrypt = require('bcrypt'); -const User = sequelize.define('User', { - name: { - type: DataTypes.STRING, - allowNull: false, - }, - username: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - }, - password: { - type: DataTypes.STRING, - allowNull: false, - }, - jobTitle: { - type: DataTypes.STRING, - allowNull: false, - }, -}); +module.exports = (sequelize) => { + const User = sequelize.define('users', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + }, + jobTitle: { + type: DataTypes.STRING, + allowNull: false, + }, + reset_token: { + type: DataTypes.STRING, + }, + reset_token_expiry: { + type: DataTypes.DATE, + }, + }, { + hooks: { + beforeCreate: async (user) => { + user.password = await bcrypt.hash(user.password, 10); + }, + }, + timestamps: false, // Disabling timestamps here + }); -module.exports = User; + return User; +}; diff --git a/Sean/models/userLogs.js b/Sean/models/userLogs.js new file mode 100644 index 0000000..672bfe6 --- /dev/null +++ b/Sean/models/userLogs.js @@ -0,0 +1,30 @@ +// userLogs.js + +const { Sequelize, DataTypes } = require('sequelize'); + +module.exports = (sequelize) => { + const userLogs = sequelize.define('user_logs', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + }, + activity: { + type: DataTypes.STRING, + allowNull: false, + }, + timestamp: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }, { + timestamps: false, // Disabling timestamps + }); + + return userLogs; +}; diff --git a/Sean/modules/mysql.js b/Sean/modules/mysql.js index 2501d2d..cc53850 100644 --- a/Sean/modules/mysql.js +++ b/Sean/modules/mysql.js @@ -2,47 +2,47 @@ const mysql = require("mysql2"); const path = require("path"); require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) const fs = require('fs'); -/* -const mysqlConfig = { - host: process.env.host, - user: process.env.user, - password: process.env.password, - database: process.env.database, - timezone: "Z", // Set the timezone to UTC -}; +const UserModel = require('../models/User');// Adjust the path based on your project structure +const { Sequelize } = require('sequelize'); -const connection = mysql.createConnection(mysqlConfig); -connection.connect((err) => { - if (err) { - console.error("Error connecting to MySQL:", err); - return; - } - console.log("Connected to MySQL"); -}); -*/ -const connection = mysql.createConnection({ - host: process.env.host, - user: process.env.DB_USER, - password: process.env.DB_PASS, - database: "adminusers", - timezone: "Z", // Set the timezone to UTC - ssl: { - ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')), + + const sequelize = new Sequelize( + "adminusers", + process.env.DB_USER, + process.env.DB_PASS, + { + host: "mpsqldatabasean.mysql.database.azure.com", + dialect: 'mysql', + // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; + attributeBehavior: 'escape', + dialectOptions: { + ssl: { + ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')), - } - }); -/* -const connection = mysql.createConnection({ - host: process.env.host, - user: process.env.DB_USER, - password: process.env.DB_PASS, - database: "adminusers", - timezone: "Z", // Set the timezone to UTC - }); -*/ - -module.exports = { connection }; + }, + + }, + }, + + + ); + + sequelize.authenticate().then(() => { + console.log('Connection has been established successfully.'); + }).catch((error) => { + console.error('Unable to connect to the database: ', error); + }); + + const User = UserModel(sequelize); + + // Synchronize the models with the database + sequelize.sync(); + + module.exports = { + sequelize, + User, + }; diff --git a/Sean/server.js b/Sean/server.js index 3b36cf4..00b1949 100644 --- a/Sean/server.js +++ b/Sean/server.js @@ -9,10 +9,13 @@ const nodemailer = require("nodemailer"); const otpGenerator = require('otp-generator'); const { body, validationResult } = require('express-validator'); const validator = require('validator'); +const { format } = require('date-fns'); +const { Sequelize } = require('sequelize'); const { transporter } = require("./modules/nodeMailer"); const { connection } = require("./modules/mysql"); - +const { sequelize, User } = require("./modules/mysql"); +const userLogs= require('./models/userLogs')(sequelize); // Adjust the path based on your project structure const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); @@ -77,57 +80,6 @@ app.get("/login", (req, res) => { res.render("login", { error: null }); }); -const logActivity = async (username, success, message) => { - try { - if (!username) { - console.error("Error logging activity: Username is null or undefined"); - return; - } - - const activity = success ? `successful login: ${message}` : `unsuccessful login: ${message}`; - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - const logParams = [username, activity]; - - connection.query(logSql, logParams, (error, results) => { - if (error) { - console.error("Error executing logSql:", error); - // Handle error (you may want to log it or take other appropriate actions) - } else { - console.log("Activity logged successfully"); - } - }); - } catch (error) { - console.error("Error in logActivity function:", error); - // Handle error (you may want to log it or take other appropriate actions) - } - }; - - const logLogoutActivity = async (username, success, message) => { - try { - if (!username) { - console.error("Error logging activity: Username is null or undefined"); - return; - } - - const activity = success ? `successful logout: ${message}` : `unsuccessful logout: ${message}`; - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - const logParams = [username, activity]; - - connection.query(logSql, logParams, (error, results) => { - if (error) { - console.error("Error executing logSql:", error); - // Handle error (you may want to log it or take other appropriate actions) - } else { - console.log("Activity logged successfully"); - } - }); - } catch (error) { - console.error("Error in logActivity function:", error); - // Handle error (you may want to log it or take other appropriate actions) - } - }; const limiter = rateLimit({ @@ -135,167 +87,162 @@ const logActivity = async (username, success, message) => { max: 5, // limit each IP to 3 requests per windowMs message: 'Too many login attempts from this IP, please try again later.', }); + app.use('/login', limiter); - app.post('/login', [ - body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'), - body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'), + +app.post('/login', [ + body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'), + body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'), ], async (req, res) => { - try { - const errors = validationResult(req); + try { + const errors = validationResult(req); - if (!errors.isEmpty()) { - // Handle validation errors, e.g., return an error message to the client - return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken }); + if (!errors.isEmpty()) { + return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken }); + } + + let { username, password } = req.body; + username = username.trim(); + + const user = await User.findOne({ where: { username } }); + + if (user) { + const isLoginSuccessful = await bcrypt.compare(password, user.password); + + if (isLoginSuccessful) { + await userLogs.create({ username, success: true, activity: "Credentials entered correctly" }); + + + const { otp, expirationTime } = generateOTP(); + + req.session.otp = otp; + req.session.otpExpiration = expirationTime; + req.session.save(); + + try { + await sendOTPByEmail(user.email, otp); + await userLogs.create({ + username: username, + activity: "OTP successfully sent to user", + }); + + } catch (sendOTPError) { + await userLogs.create({ + username: username, + activity: "OTP failed to send to user", + }); + + console.error("Error sending OTP:", sendOTPError); + return res.status(500).send("Internal Server Error"); } - let { username, password } = req.body; - username = username.trim(); + res.render("otp", { error: null, username: user.username, csrfToken: req.session.csrfToken }); + } else { + await userLogs.create({ username, success: false, activity: "Incorrect password" }); - const loginSql = "SELECT * FROM users WHERE username = ?"; + res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); + } + } else { + await userLogs.create({ + username: username, + activity: "User not found", + }); - connection.query(loginSql, [username], async (error, results) => { - if (error) { - console.error("Error executing login query:", error); - res.status(500).send("Internal Server Error"); - return; - } - - if (results.length > 0) { - const isLoginSuccessful = await bcrypt.compare(password, results[0].password); - - if (isLoginSuccessful) { - // Log successful login attempt - await logActivity(username, true, "Credentials entered correctly"); - - const user = results[0]; - const { otp, expirationTime } = generateOTP(); - - // Store the OTP and expiration time in the session for verification - req.session.otp = otp; - req.session.otpExpiration = expirationTime; - req.session.save(); - - // Send OTP via email - try { - await sendOTPByEmail(user.email, otp); - // Log successful OTP sending - await logActivity(username, true, "OTP successfully sent to user"); - } catch (sendOTPError) { - // Log unsuccessful OTP sending - await logActivity(username, false, "OTP failed to send to user"); - console.error("Error sending OTP:", sendOTPError); - res.status(500).send("Internal Server Error"); - return; - } - - // Render OTP input page - res.render("otp", { error: null, username: user.username, csrfToken: req.session.csrfToken }); - } else { - // Log unsuccessful login attempt - await logActivity(username, false, "Incorrect password"); - res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); - } - } else { - // Log unsuccessful login attempt - await logActivity(username, false, "User not found"); - res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); - } - }); - } catch (error) { - console.error("Error in login route:", error); - res.status(500).send("Internal Server Error"); + res.render("login", { error: "Invalid username or password", csrfToken: req.session.csrfToken }); } + } catch (error) { + console.error("Error in login route:", error); + res.status(500).send("Internal Server Error"); + } }); + // OTP verification route app.post("/verify-otp", [ - body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'), -], -async (req, res) => { - try { - const errors = validationResult(req); - - if (!errors.isEmpty()) { - // Handle validation errors, e.g., return an error message to the client - return res.render('otp', { error: 'Invalid OTP. Please try again.', username: req.body.username, csrfToken: req.session.csrfToken }); - } - - const enteredOTP = req.body.otp; - - if (!req.session) { - // If session is not defined, handle accordingly - console.error("Session is not defined."); - return res.status(500).send("Internal Server Error"); - } - - if (enteredOTP === req.session.otp) { - // Log successful OTP entry - if (req.body.username) { - await logActivity(req.body.username, true, "OTP entered correctly"); - } - - // Correct OTP, generate a session token - const sessionToken = crypto.randomBytes(32).toString('hex'); - - // Store the session token in the session - req.session.authenticated = true; - req.session.username = req.body.username; - req.session.sessionToken = sessionToken; - - // Generate and store anti-CSRF token in the session - csrfTokensession = crypto.randomBytes(32).toString('hex'); - - // Set anti-CSRF token in res.locals - - - // Log anti-CSRF token - console.log(`Generated Anti-CSRF Token: ${csrfTokensession}`); - // Set CSRF token as a cookie - - // Implement secure session handling: - // 1. Set secure, HttpOnly, and SameSite flags - // 2. Set an expiration time for the session token - // 3. Regenerate the session after authentication - res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day - - console.log(`Generated Session Token: ${sessionToken}`); - - // Redirect to home page with session token - res.redirect("/home"); - } else { - // Log unsuccessful OTP entry - if (req.body.username) { - await logActivity(req.body.username, false, "Incorrect OTP entered"); - } - - // Incorrect OTP, render login page with error - res.render("login", { error: "Incorrect OTP. Please try again.", csrfToken: req.session.csrfToken }); - } - } catch (error) { - console.error("Error in OTP verification route:", error); - res.status(500).send("Internal Server Error"); - } -}); - - - - app.get("/logout", (req, res) => { + body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'), + ], + async (req, res) => { try { - const username = req.session.username || "Unknown User"; + const errors = validationResult(req); + + if (!errors.isEmpty()) { + return res.render('otp', { error: 'Invalid OTP. Please try again.', username: req.body.username, csrfToken: req.session.csrfToken }); + } + + const enteredOTP = req.body.otp; + + if (!req.session) { + console.error("Session is not defined."); + return res.status(500).send("Internal Server Error"); + } + + const user = await User.findOne({ where: { username: req.body.username } }); + + if (!user) { + console.error("User not found."); + return res.status(500).send("Internal Server Error"); + } + + if (enteredOTP === req.session.otp) { + if (req.body.username) { + await userLogs.create({ username: req.body.username, activity: "OTP entered correctly" }); + } + + const sessionToken = crypto.randomBytes(32).toString('hex'); + + req.session.authenticated = true; + req.session.username = req.body.username; + req.session.sessionToken = sessionToken; + + + csrfTokenSession = crypto.randomBytes(32).toString('hex'); + + // Log anti-CSRF token + console.log(`Generated Anti-CSRF Token: ${csrfTokenSession}`); + + // Set CSRF token as a cookie + res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day + + console.log(`Generated Session Token: ${sessionToken}`); + + res.redirect("/home"); + } else { + if (req.body.username) { + await userLogs.create({ username: req.body.username, activity: "Incorrect OTP entered" }); + } + + res.render("login", { error: "Incorrect OTP. Please try again."}); + } + } catch (error) { + console.error("Error in OTP verification route:", error); + res.status(500).send("Internal Server Error"); + } + }); + + app.get("/logout", async (req, res) => { + try { + const username = req.session.username || "Unknown User"; + + // Log the logout activity using Sequelize + await userLogs.create({ username, activity: "User logged out. Session destroyed." }); + + // Log the user out by clearing the session req.session.destroy(async (err) => { if (err) { console.error("Error destroying session:", err); - await logLogoutActivity(username, false, "User logged out unsucessfully. Session not destroyed."); + + // Log the logout activity using Sequelize + await userLogs.create({ username, activity: "User logged out unsuccessfully. Session not destroyed." }); } else { - - console.log(`Session destroyed.`); + console.log("Session destroyed."); + + // Clear the session token cookie res.clearCookie('sessionToken'); - // Log the logout activity using a separate async function - await logLogoutActivity(username, true, "User logged out. Session destroyed."); } + // Redirect to the login page after logout res.redirect("/login"); }); @@ -305,6 +252,7 @@ async (req, res) => { } }); + app.get("/home", isAuthenticated, (req, res) => { // Render the home page with sensor data @@ -315,21 +263,23 @@ async (req, res) => { -app.get("/inusers", isAuthenticated, (req, res) => { - // Fetch all user data from the database - const allUsersQuery = "SELECT * FROM users"; - - connection.query(allUsersQuery, (error, allUsers) => { - if (error) { + app.get("/inusers", isAuthenticated, async (req, res) => { + try { + // Fetch all user data from the database using Sequelize + const allUsers = await User.findAll({ + attributes: ['name', 'username', 'email', 'jobTitle'], + }); + + const currentUsername = req.session.username; + + // Render the inusers page with JSON data + res.render("inusers", { allUsers, csrfToken: csrfTokenSession, currentUsername }); + } catch (error) { console.error("Error fetching all users:", error); res.status(500).send("Internal Server Error"); - return; } - const currentUsername = req.session.username; - // Render the inusers page with JSON data - res.render("inusers", { allUsers ,csrfToken: csrfTokensession, currentUsername:currentUsername }); }); -}); + function isStrongPassword(password) { // Password must be at least 10 characters long if (password.length < 10) { @@ -359,29 +309,7 @@ function isStrongPassword(password) { return true; } -const logUserCreationActivity = async (creatorUsername, success, message) => { - try { - const activity = success - ? "successful user creation" - : `unsuccessful user creation: ${message}`; - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - const logParams = [creatorUsername, activity]; - connection.query(logSql, logParams, (error, results) => { - if (error) { - console.error("Error logging user creation activity:", error); - // Handle error (you may want to log it or take other appropriate actions) - } else { - console.log("User creation activity logged successfully"); - } - - }); - } catch (error) { - console.error("Error in logUserCreationActivity function:", error); - // Handle error (you may want to log it or take other appropriate actions) - } -}; app.post( '/createUser', @@ -405,13 +333,14 @@ app.post( // Validate the anti-CSRF token const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokensession || submittedCSRFToken !== csrfTokensession) { + if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { return res.status(403).json({ error: 'CSRF token mismatch' }); } // Extract user input const { name, username, email, password, jobTitle } = req.body; console.log(submittedCSRFToken); + // Extract the username of the user creating a new user const creatorUsername = req.session.username; // Adjust this based on how you store the creator's username in your session @@ -421,216 +350,171 @@ app.post( } // Check if the username is already taken - const checkUsernameQuery = "SELECT * FROM users WHERE username = ?"; - connection.query(checkUsernameQuery, [username], (usernameQueryErr, usernameResults) => { - if (usernameQueryErr) { - console.error("Error checking username:", usernameQueryErr); - return res.status(500).json({ error: "Internal Server Error" }); - } + const existingUser = await User.findOne({ where: { username } }); - if (usernameResults.length > 0) { - // Log unsuccessful user creation due to username taken - logUserCreationActivity(creatorUsername, false, "username taken"); - return res.status(400).json({ - error: "Username is already taken", - message: "Username is already taken. Please choose a different username." - }); - } + if (existingUser) { + // Log unsuccessful user creation due to username taken + await userLogs.create({ username: creatorUsername, activity: "username taken" }); - // Check if the email is already taken - const checkEmailQuery = "SELECT * FROM users WHERE email = ?"; - connection.query(checkEmailQuery, [email], (emailQueryErr, emailResults) => { - if (emailQueryErr) { - console.error("Error checking email:", emailQueryErr); - return res.status(500).json({ error: "Internal Server Error" }); - } + return res.status(400).json({ + error: "Username is already taken", + message: "Username is already taken. Please choose a different username." + }); + } - if (emailResults.length > 0) { - // Log unsuccessful user creation due to email taken - logUserCreationActivity(creatorUsername, false, "email taken"); - return res.status(400).json({ - error: "Email is already in use", - message: "Email is already in use. Please choose another email." - }); - } - bcrypt.genSalt(10, (saltError, salt) => { - if (saltError) { - console.error("Error generating salt:", saltError); - return res.status(500).json({ error: "Internal Server Error" }); - } - - bcrypt.hash(req.body.password, salt, (hashError, hashedPassword) => { - if (hashError) { - console.error("Error hashing password:", hashError); - return res.status(500).json({ error: "Internal Server Error" }); - } - - connection.beginTransaction((transactionErr) => { - if (transactionErr) { - console.error("Error starting transaction:", transactionErr); - return res.status(500).json({ error: "Internal Server Error" }); - } + // Check if the email is already taken + const existingEmailUser = await User.findOne({ where: { email } }); - // Define the insert query - const insertUserQuery = - "INSERT INTO users (name, username, email, password, lastLogin, jobTitle) VALUES (?, ?, ?, ?, NULL, ?)"; + if (existingEmailUser) { + // Log unsuccessful user creation due to email taken + await userLogs.create({ username: creatorUsername, activity: "email taken" }); - // Log the query and its parameters - console.log("Insert Query:", insertUserQuery); - console.log("Query Parameters:", [name, username, email, hashedPassword, jobTitle]); + return res.status(400).json({ + error: "Email is already in use", + message: "Email is already in use. Please choose another email." + }); + } - // Execute the query with user data - connection.query(insertUserQuery, [name, username, email, hashedPassword, jobTitle], (queryErr, results) => { - if (queryErr) { - console.error("Error executing query:", queryErr); + // Hash the password + const hashedPassword = await bcrypt.hash(password, 10); - // Rollback the transaction in case of an error - connection.rollback((rollbackErr) => { - if (rollbackErr) { - console.error("Error rolling back transaction:", rollbackErr); - } - // Log unsuccessful user creation due to an error - logUserCreationActivity(creatorUsername, false, "internal error"); - return res.status(500).json({ error: "Internal Server Error" }); - }); - return; - } + // Start a transaction + const t = await sequelize.transaction(); - // Commit the transaction - connection.commit((commitErr) => { - if (commitErr) { - console.error("Error committing transaction:", commitErr); - return res.status(500).json({ error: "Internal Server Error" }); - } - - res.status(200).json({ message: "User created successfully" }); - logUserCreationActivity(creatorUsername, true, "user created successfully"); + try { + // Create the user + const newUser = await User.create({ + name, + username, + email, + password: hashedPassword, + lastLogin: null, + jobTitle, + }, { transaction: t }); - - }); - }); - }); - }); - }); - }); - }); - } catch (error) { - console.error("Error creating user:", error); - return res.status(500).json({ error: "Internal Server Error" }); - } - } - ); + // Commit the transaction + await t.commit(); + + // Log successful user creation + await userLogs.create({ username: creatorUsername, activity: "user created successfully" }); + + return res.status(200).json({ message: "User created successfully" }); + } catch (createUserError) { + // Rollback the transaction in case of an error + await t.rollback(); + + console.error("Error creating user:", createUserError); + + // Log unsuccessful user creation due to an error + await userLogs.create({ username: creatorUsername, activity: "internal error" }); + + return res.status(500).json({ error: "Internal Server Error" }); + } + } catch (error) { + console.error("Error creating user:", error); + return res.status(500).json({ error: "Internal Server Error" }); + } + } +); app.get("/forgot-password", (req, res) => { res.render("forgot-password", { error: null, success: null }); }); -// Handle the submission of the forgot password form -app.post("/forgot-password", (req, res) => { - const { usernameOrEmail } = req.body; +app.post("/forgot-password", async (req, res) => { + let user; // Declare the 'user' variable outside the try-catch block - // Sanitize the input - const sanitizedUsernameOrEmail = validator.escape(usernameOrEmail); + try { + const { usernameOrEmail } = req.body; - // Perform the logic for sending the reset password email + // Sanitize the input + const sanitizedUsernameOrEmail = validator.escape(usernameOrEmail); - // Check if the username or email exists in the database - const checkUserQuery = "SELECT * FROM users WHERE username = ? OR email = ?"; - connection.query( - checkUserQuery, - [sanitizedUsernameOrEmail, sanitizedUsernameOrEmail], - (checkError, checkResults) => { - if (checkError) { - console.error("Error checking user:", checkError); - const error = "An error occurred during the password reset process."; - res.render("forgot-password", { error, success: null }); - } else if (checkResults.length === 0) { - const error = "Username or email not found."; - res.render("forgot-password", { error, success: null }); - } else { - // Assuming the user exists, generate a reset token and send an email - const user = checkResults[0]; - const resetToken = crypto.randomBytes(20).toString("hex"); - const resetTokenExpiry = new Date(Date.now() + 3600000); // Token expires in 1 hour + // Find the user by username or email + user = await User.findOne({ + where: { + [Sequelize.Op.or]: [ + { username: sanitizedUsernameOrEmail }, + { email: sanitizedUsernameOrEmail }, + ], + }, + }); - // Update user with reset token and expiry - const updateQuery = - "UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?"; - connection.query( - updateQuery, - [resetToken, resetTokenExpiry, user.id], - (updateError) => { - if (updateError) { - console.error("Error updating reset token:", updateError); - const error = - "An error occurred during the password reset process."; - res.render("forgot-password", { error, success: null }); - } else { - // Send email with reset link - const resetLink = `http://localhost:3000/reset-password/${resetToken}`; - const mailOptions = { - to: user.email, - subject: "Password Reset", - text: `Click on the following link to reset your password: ${resetLink}`, - }; - transporter.sendMail(mailOptions, (emailError, info) => { - if (emailError) { - console.error("Error sending email:", emailError); - const error = - "An error occurred during the password reset process."; - res.render("forgot-password", { error, success: null }); - } else { - console.log("Email sent: " + info.response); - const success = - "Password reset email sent successfully. Check your inbox."; - res.render("forgot-password", { error: null, success }); - - // Log the successful sending of the reset link in the database - logPasswordResetActivity(user.username,"link sent successfully"); - } - }); - } - } - ); - } + if (!user) { + const error = "Username or email not found."; + return res.render("forgot-password", { error, success: null }); } - ); + + // Generate reset token and update the user + const reset_token = crypto.randomBytes(20).toString("hex"); + const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour + + // Update the user with the reset token and expiry + await User.update( + { + reset_token, + reset_token_expiry, + }, + { + where: { + id: user.id, // Replace 'id' with the actual primary key field of your User model + }, + } + ); + + // Send email with reset link + const resetLink = `http://localhost:3000/reset-password/${reset_token}`; + const mailOptions = { + to: user.email, + subject: "Password Reset", + text: `Click on the following link to reset your password: ${resetLink}`, + }; + + await transporter.sendMail(mailOptions); + + const success = "Password reset email sent successfully. Check your inbox."; + res.render("forgot-password", { error: null, success }); + + // Log the successful sending of the reset link in the database + await userLogs.create({ + username: user.username, + activity: "Password reset link sent successfully", + }); + } catch (error) { + if (user) { + await userLogs.create({ + username: user.username, + activity: "Password reset link unsuccessfully sent. Please check with the administrator.", + }); + } + console.error("Error during password reset:", error); + const errorMessage = "An error occurred during the password reset process."; + res.render("forgot-password", { error: errorMessage, success: null }); + } }); + + -// Function to log the password reset activity in the database -function logPasswordResetActivity(username, activity) { - const logSql = - "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, CURRENT_TIMESTAMP)"; - connection.query(logSql, [username, activity], (error) => { - if (error) { - console.error("Error logging password reset activity:", error); - } else { - console.log("Password reset activity logged successfully"); - } - }); -} - -// Handle Reset Password request -app.post("/reset-password/:token", async (req, res) => { - const { token } = req.params; - const { password, confirmPassword } = req.body; + app.post("/reset-password/:token", async (req, res) => { + try { + const { token } = req.params; + const { password, confirmPassword } = req.body; - // Sanitize the inputs - const sanitizedToken = validator.escape(token); - const sanitizedPassword = validator.escape(password); - const sanitizedConfirmPassword = validator.escape(confirmPassword); + // Sanitize the inputs + const sanitizedToken = validator.escape(token); + const sanitizedPassword = validator.escape(password); + const sanitizedConfirmPassword = validator.escape(confirmPassword); - // Find user with matching reset token and not expired - const selectQuery = - "SELECT * FROM users WHERE reset_token = ? AND reset_token_expiry > NOW()"; - connection.query(selectQuery, [sanitizedToken], async (selectErr, selectResults) => { - if (selectErr) { - console.error("Error querying reset token:", selectErr); - return res.status(500).json({ error: "Error querying reset token" }); - } + // Find user with matching reset token and not expired + const user = await User.findOne({ + where: { + reset_token: sanitizedToken, + reset_token_expiry: { [Sequelize.Op.gt]: new Date() }, + }, + }); - if (selectResults.length === 0) { + if (!user) { // Pass the error to the template when rendering the reset-password page return res.render("reset-password", { token, @@ -658,51 +542,37 @@ app.post("/reset-password/:token", async (req, res) => { } // Hash the new password - const hashedPassword = await new Promise((resolve, reject) => { - bcrypt.genSalt(10, (saltError, salt) => { - if (saltError) { - console.error("Error generating salt:", saltError); - reject("Internal Server Error"); - } - - // Use the generated salt to hash the password - bcrypt.hash(sanitizedPassword, salt, (hashError, hashed) => { - if (hashError) { - console.error("Error hashing password:", hashError); - reject("Internal Server Error"); - } - - resolve(hashed); - }); - }); - }); + const hashedPassword = await bcrypt.hash(sanitizedPassword, 10); // Update user's password and clear reset token - const updateQuery = - "UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE reset_token = ?"; -connection.query(updateQuery, [hashedPassword, sanitizedToken], async (updateErr, updateResults) => { - if (updateErr) { - console.error("Error updating password:", updateErr); - // Pass the error to the template when rendering the reset-password page - res.render("reset-password", { - token, - resetError: "Error updating password", - }); - } else { - // Log password reset activity - const username = selectResults[0].username; // Assuming 'username' is the column name - const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, 'Password Reseted successfully', NOW())"; - connection.query(logQuery, [username], (logErr) => { - if (logErr) { - console.error("Error logging password reset:", logErr); - // You might want to handle the logging error here - } - }); - // Redirect to the success page upon successful password reset - res.redirect("/success"); - } + const updateQuery = { + password: hashedPassword, + reset_token: null, + reset_token_expiry: null, + }; + const whereCondition = { + reset_token: sanitizedToken, + }; + + await User.update(updateQuery, { + where: whereCondition, }); - }); + // Log password reset activity + await userLogs.create({ + username: user.username, + activity: "Password reset successfully", + }); + + // Redirect to the success page upon successful password reset + res.redirect("/success"); + } catch (error) { + console.error("Error during password reset:", error); + // Pass the error to the template when rendering the reset-password page + res.render("reset-password", { + token: req.params.token, + resetError: "Error during password reset", + }); + } }); app.get("/success", (req, res) => { @@ -720,12 +590,13 @@ app.get("/reset-password/:token", (req, res) => { success: null, }); }); + app.post("/reset-password", async (req, res) => { const { username, password, confirmPassword, csrf_token } = req.body; const creatorUsername = req.session.username; const submittedCSRFToken = req.body.csrf_token; - if (!csrfTokensession|| submittedCSRFToken !== csrfTokensession) { + if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) { return res.status(403).json({ error: 'CSRF token mismatch' }); } @@ -747,183 +618,154 @@ app.post("/reset-password", async (req, res) => { }); } - // Generate a random salt - const saltRounds = 10; // You can adjust the number of rounds based on your security requirements - const salt = await bcrypt.genSalt(saltRounds); + try { + // Find the user in the database + const user = await User.findOne({ where: { username: sanitizedUsername } }); - // Hash the new password with the generated salt - const hashedPassword = await bcrypt.hash(sanitizedPassword, salt); + if (!user) { + return res.status(404).json({ error: "User does not exist" }); + } - // Check if the user exists in the database before updating the password - const userExists = await checkIfUserExists(sanitizedUsername); + // Generate a random salt and hash the new password + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds); - if (!userExists) { - return res.status(404).json({ error: "User does not exist" }); + // Update user's password + await User.update( + { password: hashedPassword }, + { where: { username: sanitizedUsername } } + ); + + // Log password reset activity + await userLogs.create({ + username: creatorUsername, + activity: `Password has been reset for ${sanitizedUsername}`, + }); + + // Password update successful + return res.status(200).json({ success: "Password updated successfully" }); + } catch (error) { + console.error("Error updating password:", error); + return res.status(500).json({ error: "Error updating password" }); } - - // Update user's password based on the username - const updateQuery = "UPDATE users SET password = ? WHERE username = ?"; - connection.query(updateQuery, [hashedPassword, sanitizedUsername], async (updateErr, updateResults) => { - if (updateErr) { - console.error("Error updating password:", updateErr); - return res.status(500).json({ error: "Error updating password" }); - } - - // Check if the update affected any rows - if (updateResults.affectedRows > 0) { - // Log password reset activity - const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, NOW())"; - const logActivity = `Password has been reset for ${sanitizedUsername}`; - connection.query(logQuery, [creatorUsername, logActivity], (logErr) => { - if (logErr) { - console.error("Error logging password reset:", logErr); - // You might want to handle the logging error here - } - - // Password update successful - return res.status(200).json({ success: "Password updated successfully" }); - }); - } else { - return res.status(404).json({ - error: "User not found or password not updated.", - }); - } - }); }); -async function checkIfUserExists(username) { - return new Promise((resolve, reject) => { - const query = "SELECT * FROM users WHERE username = ?"; - connection.query(query, [username], (err, results) => { - if (err) { - reject(err); - } else { - resolve(results.length > 0); - } - }); - }); -} -app.get('/searchUser', (req, res) => { - const { username } = req.query; - - // Sanitize the input - const sanitizedUsername = validator.escape(username); - - const query = 'SELECT * FROM users WHERE username = ?'; - - connection.query(query, [sanitizedUsername], (err, results) => { - if (err) { - console.error('MySQL query error:', err); - res.status(500).json({ success: false, error: 'Internal Server Error' }); - } else if (results.length === 0) { - // No user found with the given username - res.status(404).json({ success: false, error: 'User not found' }); - } else { - res.json(results); - } - }); - }); +app.get('/searchUser', async (req, res) => { + const { username } = req.query; -app.get('/api/users', (req, res) => { - const query = 'SELECT * FROM users'; + // Sanitize the input + const sanitizedUsername = validator.escape(username); + + try { + // Find the user in the database + const user = await User.findOne({ where: { username: sanitizedUsername } }); + console.log(user); + if (!user) { + // No user found with the given username + res.status(404).json({ success: false, error: 'User not found' }); + } else { + // User found, return user data + res.json(user); + + } + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error' }); + } +}); + +app.get('/api/users', async (req, res) => { + try { + // Find all users in the database + const users = await User.findAll(); + + // Return the users in the response + res.json(users); + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error' }); + } +}); - connection.query(query, (err, results) => { - if (err) { - console.error('MySQL query error:', err); - res.status(500).json({ success: false, error: 'Internal Server Error' }); - } else { - res.json(results); - } - }); - }); +app.get('/api/searchUser', async (req, res) => { + const { username } = req.query; + console.log(username); + try { + // Find the user in the database by username + const user = await User.findOne({ where: { username } }); + + if (!user) { + // No user found with the given username + res.status(404).json({ success: false, error: 'User not found' }); + } else { + // User found, return user data + res.json(user); + + } + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error' }); + } +}); - // Route to search for a user by username - app.get('/api/searchUser', (req, res) => { - const { username } = req.query; - const query = 'SELECT * FROM users WHERE username = ?'; +app.delete('/api/deleteUser/:username', async (req, res) => { + const { username } = req.params; + const creatorUsername = req.session.username; + + try { + // Extract CSRF token from the request body + const { csrfToken } = req.body; + + // Compare CSRF token with the one stored in the session + if (csrfToken !== csrfTokenSession) { + return res.status(403).json({ success: false, error: 'CSRF token mismatch' }); + } + + // Log deletion activity to UserLogs model + const deletionActivity = `User ${username} has been successfully deleted`; + await userLogs.create({ username: creatorUsername, activity: deletionActivity }); + + // Perform user deletion using the User model + const deletedUser = await User.destroy({ where: { username } }); + + if (!deletedUser) { + res.status(404).json({ success: false, error: 'User not found' }); + } else { + res.json({ success: true, message: 'User deleted successfully' }); + } + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ success: false, error: 'Internal Server Error', details: error.message }); + } +}); - connection.query(query, [username], (err, results) => { - if (err) { - console.error('MySQL query error:', err); - res.status(500).json({ success: false, error: 'Internal Server Error' }); - } else { - res.json(results); - } - }); - }); - - app.delete('/api/deleteUser/:username', async (req, res) => { - const { username } = req.params; - const query = 'DELETE FROM users WHERE username = ?'; - const creatorUsername = req.session.username; - - try { - // Extract CSRF token from the request body - const { csrfToken } = req.body; - console.log(csrfToken); - console.log(csrfTokensession); - // Compare CSRF token with the one stored in the session - if (csrfToken !== csrfTokensession) { - return res.status(403).json({ success: false, error: 'CSRF token mismatch' }); - } - - // Log deletion activity to USER_LOGS - const deletionActivity = `User ${username} has been successfully deleted`; - const logQuery = 'INSERT INTO USER_LOGS (USERNAME, ACTIVITY, TIMESTAMP) VALUES (?, ?, CURRENT_TIMESTAMP)'; - await executeQuery(logQuery, [creatorUsername, deletionActivity]); - - // Perform user deletion - const results = await executeQuery(query, [username]); - - if (results.affectedRows === 0) { - res.status(404).json({ success: false, error: 'User not found' }); - } else { - res.json({ success: true, message: 'User deleted successfully' }); - } - } catch (error) { - console.error('MySQL query error:', error); - res.status(500).json({ success: false, error: 'Internal Server Error', details: error.message }); - } - }); - - - async function executeQuery(sql, values) { - return new Promise((resolve, reject) => { - connection.query(sql, values, (err, results) => { - if (err) { - reject(err); - } else { - resolve(results); - } - }); - }); - } - - app.get('/api/getLogs', (req, res) => { - // Query the database to fetch logs - const query = 'SELECT id, username, activity, timestamp FROM user_logs'; - connection.query(query, (err, results) => { - if (err) { - console.error('Error fetching logs from MySQL:', err); - res.status(500).json({ error: 'Error fetching logs from MySQL' }); - return; - } - - // Format timestamps to a more readable format - const formattedLogs = results.map((log) => ({ - id: log.id, - username: log.username, - activity: log.activity, - timestamp: new Date(log.timestamp).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }), - })); - - // Send the formatted logs as a JSON response - res.json(formattedLogs); - }); - }); + +app.get('/api/getLogs', async (req, res) => { + try { + // Query the database to fetch logs using Sequelize model + const logs = await userLogs.findAll({ + attributes: ['id', 'username', 'activity', 'timestamp'], + }); + + // Format timestamps to a more readable format with timezone conversion + const formattedLogs = logs.map((log) => ({ + id: log.id, + username: log.username, + activity: log.activity, + timestamp: format(new Date(log.timestamp), 'yyyy-MM-dd HH:mm:ssXXX', { timeZone: 'Asia/Singapore' }), + })); + + // Send the formatted logs as a JSON response + res.json(formattedLogs); + } catch (error) { + console.error('Sequelize query error:', error); + res.status(500).json({ error: 'Error fetching logs from Sequelize' }); + } +}); + app.use(express.static("views")); app.listen(PORT, () => { diff --git a/Sean/views/inusers.ejs b/Sean/views/inusers.ejs index eeec05f..4962a64 100644 --- a/Sean/views/inusers.ejs +++ b/Sean/views/inusers.ejs @@ -41,7 +41,6 @@ Name Username Email - Last Login Job Title @@ -52,7 +51,6 @@ <%- user.name %> <%- user.username %> <%- user.email %> - <%- new Date(user.lastLogin).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }) %> <%- user.jobTitle %> <% }); %> diff --git a/Sean/views/inusers.js b/Sean/views/inusers.js index abd9b89..975ed5b 100644 --- a/Sean/views/inusers.js +++ b/Sean/views/inusers.js @@ -27,9 +27,10 @@ $(document).ready(function () { }); $('#searchUserButton').on('click', function () { - console.log('Search button clicked'); + const searchUsername = $('#searchUserInput').val(); // Call the function to search for the user + searchUser(searchUsername); }); @@ -71,9 +72,9 @@ function searchUser(username) { throw new Error(`HTTP error! Status: ${response.status}`); } }) - .then(users => { + .then(user => { // Display search results - displaySearchResults(users); + displaySearchResults(user); }) .catch(error => { console.error('Search error:', error); @@ -83,24 +84,17 @@ function searchUser(username) { // Function to display search results function displaySearchResults(users) { + const searchResultsList = $('#searchResultsList'); // Clear previous results searchResultsList.empty(); - if (users && users.length > 0) { - users.forEach(user => { - const listItem = `
  • ${user.username} -
  • `; + const listItem = `
  • ${users.username} -
  • `; searchResultsList.append(listItem); - }); - // Show the search results container $('#searchResultsContainer').show(); - } else { - // Hide the search results container if no results - $('#searchResultsContainer').hide(); - } -} + } // Event listener for delete user button in search results $('#searchResultsList').on('click', '.deleteUserButton', function () { @@ -303,8 +297,7 @@ $('#resetPasswordForm').on('submit', function (e) { const password = $('#resetPassword').val(); const confirmPassword = $('#resetConfirmPassword').val(); const csrf_token = $('#userForm input[name="csrf_token"]').val(); - console.log('Username:', username); - console.log('New Password:', password); + // Validate passwords if (password !== confirmPassword) { @@ -493,11 +486,7 @@ fetchLogs(); // Assuming allUsers is an array containing user information const user = allUsers.find(user => user.username === currentUsername); const userRole = user?.jobTitle; -console.log('All Users:', allUsers); -console.log('Current Username:', currentUsername); -// Log the user role to the console -console.log('User Role:', userRole); // Function to enable/disable actions based on user role function handleUserRoleAccess() { diff --git a/package-lock.json b/package-lock.json index 77487a2..08346ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,64 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -30,6 +88,11 @@ } } }, + "@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" + }, "@otplib/core": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz", @@ -72,6 +135,12 @@ "@otplib/plugin-thirty-two": "^12.0.1" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true + }, "@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -86,17 +155,17 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "@types/node": { - "version": "20.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", - "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", "requires": { "undici-types": "~5.26.4" } }, "@types/validator": { - "version": "13.11.7", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz", - "integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==" + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, "abbrev": { "version": "1.1.1", @@ -169,6 +238,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -183,6 +257,11 @@ "node-addon-api": "^5.0.0" } }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -259,6 +338,18 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, + "cli-color": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", + "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.61", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + } + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -287,11 +378,25 @@ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -336,6 +441,16 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "csrf": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", @@ -396,11 +511,30 @@ } } }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" }, + "date-fns": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.2.0.tgz", + "integrity": "sha512-E4KWKavANzeuusPi0jUjpuI22SURAznGkx7eZV+4i6x2A+IZxAMcajgkvuDAU1bg40+xuhW1zRdVIIM/4khuIg==" + }, + "date-fns-tz": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-2.0.0.tgz", + "integrity": "sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -502,6 +636,32 @@ "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "requires": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "dependencies": { + "minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -535,6 +695,51 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -555,6 +760,15 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -687,6 +901,21 @@ "validator": "^13.9.0" } }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + } + } + }, "fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -752,6 +981,22 @@ "path-exists": "^4.0.0" } }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + } + } + }, "formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -770,6 +1015,17 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -859,6 +1115,11 @@ "get-intrinsic": "^1.1.3" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -954,11 +1215,24 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "requires": { + "hasown": "^2.0.0" + } + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -969,11 +1243,30 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jake": { "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", @@ -985,6 +1278,61 @@ "minimatch": "^3.1.2" } }, + "js-beautify": { + "version": "1.14.11", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", + "integrity": "sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==", + "requires": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.3", + "glob": "^10.3.3", + "nopt": "^7.2.0" + }, + "dependencies": { + "abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==" + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "nopt": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "requires": { + "abbrev": "^2.0.0" + } + } + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1011,6 +1359,14 @@ "yallist": "^4.0.0" } }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "requires": { + "es5-ext": "~0.10.2" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -1031,6 +1387,21 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, + "memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -1107,17 +1478,19 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.44", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.44.tgz", + "integrity": "sha512-nv3YpzI/8lkQn0U6RkLd+f0W/zy/JnoR5/EyPz/dNkPTBjA2jNLCVxaiQ8QpeLymhSZvX0wCL5s27NQWdOPwAw==", "requires": { "moment": "^2.29.4" + }, + "dependencies": { + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" + } } }, "ms": { @@ -1126,9 +1499,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mysql2": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz", - "integrity": "sha512-pS/KqIb0xlXmtmqEuTvBXTmLoQ5LmAz5NW/r8UyQ1ldvnprNEj3P9GbmuQQ2J0A4LO+ynotGi6TbscPa8OUb+w==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.1.tgz", + "integrity": "sha512-4EEqYu57mnkW5+Bvp5wBebY7PpfyrmvJ3knHcmLkp8FyBu4kqgrF2GxIjsC2tbLNZWqJaL21v/MYH7bU5f03oA==", "requires": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -1180,6 +1553,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -1311,6 +1689,32 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "requires": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==" + } + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -1341,6 +1745,11 @@ "source-map-js": "^1.0.2" } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1400,6 +1809,16 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "retry-as-promised": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", @@ -1517,6 +1936,73 @@ "uuid": "^8.3.2", "validator": "^13.9.0", "wkx": "^0.5.0" + }, + "dependencies": { + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" + } + } + }, + "sequelize-cli": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.2.tgz", + "integrity": "sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg==", + "requires": { + "cli-color": "^2.0.3", + "fs-extra": "^9.1.0", + "js-beautify": "^1.14.5", + "lodash": "^4.17.21", + "resolve": "^1.22.1", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "dependencies": { + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } } }, "sequelize-pool": { @@ -1556,6 +2042,19 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -1596,6 +2095,16 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1619,6 +2128,14 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -1627,6 +2144,11 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "tar": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", @@ -1645,6 +2167,15 @@ "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", "integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==" }, + "timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "requires": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1665,6 +2196,11 @@ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==" }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1682,11 +2218,24 @@ "random-bytes": "~1.0.0" } }, + "umzug": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "requires": { + "bluebird": "^3.7.2" + } + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1736,6 +2285,14 @@ "webidl-conversions": "^3.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, "which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", @@ -1767,6 +2324,16 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 3ca984c..58ffab1 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "body-parser": "^1.20.2", "cookie-parser": "^1.4.6", "csurf": "^1.11.0", + "date-fns": "^3.2.0", + "date-fns-tz": "^2.0.0", "dotenv": "^16.3.1", "ejs": "^3.1.9", "esm": "^3.2.25", @@ -31,7 +33,7 @@ "helmet": "^7.1.0", "moment": "^2.30.1", "mqtt": "^5.3.3", - "mysql2": "^3.6.5", + "mysql2": "^3.7.1", "node-fetch": "^3.3.2", "nodemailer": "^6.9.7", "otp-generator": "^4.0.1", @@ -39,6 +41,7 @@ "qrcode": "^1.5.3", "sanitize-html": "^2.11.0", "sequelize": "^6.35.2", + "sequelize-cli": "^6.6.2", "sql": "^0.78.0", "validator": "^13.11.0" }, From 908a8bd351843284ed1ad5a60d2d015b8380f04f Mon Sep 17 00:00:00 2001 From: newtbot Date: Fri, 19 Jan 2024 03:21:15 +0800 Subject: [PATCH 2/3] ejs templating --- consumerWebsite/database/model/apiKeyModel.js | 45 ++- consumerWebsite/middleware/authChecker.js | 0 consumerWebsite/modules/app.js | 20 +- consumerWebsite/public/404.html | 213 --------------- consumerWebsite/public/index.html | 256 ------------------ .../public/js/{jquery.js => app.js} | 3 +- consumerWebsite/public/js/contact.js | 44 +++ consumerWebsite/public/news.html | 233 ---------------- consumerWebsite/routes/render.js | 60 ++++ consumerWebsite/views/404.ejs | 37 +++ consumerWebsite/views/bot.ejs | 88 ++++++ consumerWebsite/views/index.ejs | 124 +++++++++ consumerWebsite/views/news.ejs | 112 ++++++++ consumerWebsite/views/top.ejs | 124 +++++++++ webserver/database/model/apiKeyModel.js | 2 +- 15 files changed, 648 insertions(+), 713 deletions(-) create mode 100644 consumerWebsite/middleware/authChecker.js delete mode 100644 consumerWebsite/public/404.html delete mode 100644 consumerWebsite/public/index.html rename consumerWebsite/public/js/{jquery.js => app.js} (99%) create mode 100644 consumerWebsite/public/js/contact.js delete mode 100644 consumerWebsite/public/news.html create mode 100644 consumerWebsite/routes/render.js create mode 100644 consumerWebsite/views/404.ejs create mode 100644 consumerWebsite/views/bot.ejs create mode 100644 consumerWebsite/views/index.ejs create mode 100644 consumerWebsite/views/news.ejs create mode 100644 consumerWebsite/views/top.ejs diff --git a/consumerWebsite/database/model/apiKeyModel.js b/consumerWebsite/database/model/apiKeyModel.js index 03bcb95..55e096b 100644 --- a/consumerWebsite/database/model/apiKeyModel.js +++ b/consumerWebsite/database/model/apiKeyModel.js @@ -45,7 +45,7 @@ const apikeyModel = sequelize.define( validate: { notEmpty: true, len: [1, 255], - isIn: [["canRead", "canWrite"]], + isIn: [["canRead", "canWrite" , "auto-generated"]], }, }, createdAt: { @@ -63,3 +63,46 @@ const apikeyModel = sequelize.define( ); module.exports = { apikeyModel }; + + +/* + class AuthToken extends Model { + check(){ + // check expires_on date + return this.is_valid; + } + } + AuthToken.init({ + token:{ + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + allowNull: false, + primaryKey: true + }, + expires_on: { + type: DataTypes.DATE, + allowNull: true, + validate:{ + isDate:true + } + }, + username: { + type: DataTypes.STRING, + ldapModel: 'User', + allowNull: false, + validate:{ + notNull: true, + }, + }, + is_valid: { + type: DataTypes.BOOLEAN, + defaultValue: true + } + }, { + sequelize, + modelName: 'AuthToken', + }); + + + +*/ \ No newline at end of file diff --git a/consumerWebsite/middleware/authChecker.js b/consumerWebsite/middleware/authChecker.js new file mode 100644 index 0000000..e69de29 diff --git a/consumerWebsite/modules/app.js b/consumerWebsite/modules/app.js index 680beba..069240b 100644 --- a/consumerWebsite/modules/app.js +++ b/consumerWebsite/modules/app.js @@ -1,20 +1,23 @@ const express = require("express"); -const helmet = require("helmet"); const path = require("path"); const app = express(); -const port = 80; +const port = 3000; +const ejs = require("ejs"); -const bodyParser = require('body-parser'); // Middleware +const bodyParser = require("body-parser"); // Middleware app.use(bodyParser.urlencoded({ extended: false })); -app.use(helmet()); -//disable x-powered-by header for security reasons -app.disable("x-powered-by"); + app.use(express.json()); app.set("json spaces", 2); -//public folder with path to static files +// Set up the templating engine to build HTML for the front end. +app.set("views", path.join(__dirname, "../views")); +app.set("view engine", "ejs"); + +// Have express server static content( images, CSS, browser JS) from the public +// local folder. app.use(express.static(path.join(__dirname, "../public"))); //middleware logic ( called by next() ) @@ -23,6 +26,9 @@ app.use(express.static(path.join(__dirname, "../public"))); //route logic app.use("/api/v0", require("../routes/api_routes")); //consumerWebsite\routes\api_routes.js +//render logic +app.use("/", require("../routes/render")); //consumerWebsite\routes\render.js + // Catch 404 and forward to error handler. If none of the above routes are // used, this is what will be called. app.use(function (req, res, next) { diff --git a/consumerWebsite/public/404.html b/consumerWebsite/public/404.html deleted file mode 100644 index c78fd96..0000000 --- a/consumerWebsite/public/404.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - N & LW Lawn Care - Landscaping Bootstrap4 HTML5 Responsive Template - - - - - - - - - - - - -
    -
    - -

    404 - Page Not Found -

    -
    -
    - - -
    - - -
    -

    Oops! That page can’t be found.

    -
    - -
    -

    We can’t find the page your are looking for. You can check out our Homepage.

    - Back To Homepage -
    - - -
    - - - -
    -
    -
    -
    -
    Find us
    - -

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s

    -

    9878/25 sec 9 rohini 35

    -

    +91-9999878398

    -

    info@example.com

    -
    -
    -
    Follow us
    - - - -
    - -
    -
    Recent posts
    - - - -
    -
    -
    -
    - - - - - -
    -
    - - - - - - diff --git a/consumerWebsite/public/index.html b/consumerWebsite/public/index.html deleted file mode 100644 index 38274fa..0000000 --- a/consumerWebsite/public/index.html +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - - - - EcoSaver - - - - - - - - - - - -
    - -
    - -
    -
    -

    Services

    - -
    -
    -
    -

    Humidity

    -
    -

    75%

    -
    - -
    -
    -
    -
    -

    Air Quality Index

    -
    -

    18 PSI

    -
    - -
    -
    -
    -
    -

    Temperature

    -
    -

    30°

    -
    - -
    -
    -
    - -
    - -
    -
    -
    - EcoSaver - Your Air Quality Index Source - - - - -
    -

    Welcome to EcoSaver - Your Air Quality Index Source

    -

    We prioritize your well-being by providing up-to-date information on air quality indexes.

    -
    - -
    -

    Our Approach: Smart and Informative

    -

    We believe in offering precise and comprehensive data to empower your decisions for a better - quality of life.

    -
      -
    • Presenting real-time air quality data in a user-friendly format.
    • -
    • Equipping you with insights into the impact of air quality on health and the environment. -
    • -
    • Empowering communities with knowledge to make informed choices for a sustainable future. -
    • -
    -
    -
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    Find us
    - -

    Blk 645 Jalan Tenaga

    -

    +65 90064959

    -

    Leongdingxuan@gmail.com

    -
    - -
    -
    Quick links
    - - - -
    -
    -
    News
    - - - - -
    -
    -
    -
    -
    -

    All Rights Reserved. © 2023 EcoSaver -

    -
    -
    - - - - - - - \ No newline at end of file diff --git a/consumerWebsite/public/js/jquery.js b/consumerWebsite/public/js/app.js similarity index 99% rename from consumerWebsite/public/js/jquery.js rename to consumerWebsite/public/js/app.js index 78db376..3fb3560 100644 --- a/consumerWebsite/public/js/jquery.js +++ b/consumerWebsite/public/js/app.js @@ -1,6 +1,6 @@ var app = {}; -/* + app.api = (function(app){ var baseURL = '/api/v0/' @@ -85,7 +85,6 @@ app.api = (function(app){ return {post: post, get: get, put: put, delete: remove} })(app) -*/ app.auth = (function(app) { var user = {} diff --git a/consumerWebsite/public/js/contact.js b/consumerWebsite/public/js/contact.js new file mode 100644 index 0000000..98a07a6 --- /dev/null +++ b/consumerWebsite/public/js/contact.js @@ -0,0 +1,44 @@ +require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') }) + + +document.addEventListener('DOMContentLoaded', () => { + const form = document.getElementById('form'); + + // Set the new value for the access_key input field + form.querySelector('input[name="access_key"]').value = process.env.emailKey; + + form.addEventListener('submit', async (event) => { + event.preventDefault(); // Prevent default form submission + + // Create a FormData object to include the key + const formData = new FormData(form); + + // Submit the form using fetch API + try { + const response = await fetch('https://api.web3forms.com/submit', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + // Handle the API response + //console.log(result); + + if (result.success) { + // Form submitted successfully, display notification + alert('Form submitted successfully!'); + location.reload() + // You can replace the alert with your custom notification logic + } else { + // Form submission failed, display error notification + alert('Form submission failed. Please try again.'); + // You can replace the alert with your custom error notification logic + } + + } catch (error) { + //console.error('Error:', error); + } + }); +}); + diff --git a/consumerWebsite/public/news.html b/consumerWebsite/public/news.html deleted file mode 100644 index 7d72ac8..0000000 --- a/consumerWebsite/public/news.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - - - - - - N & LW Lawn Care - Landscaping Bootstrap4 HTML5 Responsive Template - - - - - - - - - - - - - -
    -
    - -

    News - -

    -
    -
    - - -
    - -
    - -
    -
    -
    -
    - Card image Blog -
    -

    Singapore's air quality hits unhealthy range, 'slightly hazy' conditions forecast for Saturday

    -

    he National Environment Agency said there has been a "significant increase" in the number of hotspots in Sumatra.

    - Read More → -
    - -
    - -
    - Card image Blog -
    -

    Singapore Government Agencies Stand Ready To Mitigate Impact Of Haze

    -

    As of 29 September 2023, 3pm, the 24-hr Pollutant Standards Index (PSI) is 81 (Moderate range) in the East region of Singapore. Accordingly, the 28 public agencies that make up the Government’s Haze Task Force (HTF), are ready to roll out their respective haze action plans should the air quality deteriorate into the Unhealthy range (24-hour PSI above 100).

    - Read More → -
    - -
    - -
    - Card image Blog -
    -

    High risk of severe transboundary haze in 2023, public advised to be prepared: Singapore institute

    -

    A latest report predicts a high risk of severe haze occurring in Southeast Asia, though not as severe as in 2015

    - Read More → -
    - -
    - -
    - - -
    - -
    - - - -
    -
    -
    Search
    -
    -
    - - - - -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    -
    Find us
    -

    Blk 645 Jalan Tenaga

    -

    +65 90064959

    -

    Leongdingxuan@gmail.com

    -
    - -
    -
    Quick links
    - - - -
    -
    -
    News
    - - - - -
    -
    -
    -
    -
    -

    All Rights Reserved. © 2023 EcoSaver -

    -
    -
    - - - - - - - \ No newline at end of file diff --git a/consumerWebsite/routes/render.js b/consumerWebsite/routes/render.js new file mode 100644 index 0000000..356c63b --- /dev/null +++ b/consumerWebsite/routes/render.js @@ -0,0 +1,60 @@ +/* +'use strict'; + +var router = require('express').Router(); +const conf = require('../conf') + +const values ={ + title: conf.environment !== 'production' ? `` : '' +} + +router.get('/', async function(req, res, next) { + res.render('runner', {...values}); +}); + + +router.get('/topics', function(req, res, next) { + res.render('topics', {...values}); +}); + +router.get('/chat', function(req, res, next) { + res.render('chat', {...values}); +}); + +router.get('/login*', function(req, res, next) { + res.render('login', {redirect: req.query.redirect, ...values}); +}); + +router.get('/runner', function(req, res, next) { + res.render('runner', {...values}); +}); + +router.get('/worker', function(req, res, next) { + res.render('worker', {...values}); +}); + +module.exports = router; + +*/ + +'use strict'; + +var router = require('express').Router(); + +//landing page of index +router.get('/', function(req, res, next) { + res.render('index'); +}); + +//news page +router.get('/news', function(req, res, next) { + res.render('news'); +}); + +//404 page +router.get('*', function(req, res, next) { + res.render('404'); +}); + + +module.exports = router; diff --git a/consumerWebsite/views/404.ejs b/consumerWebsite/views/404.ejs new file mode 100644 index 0000000..ccb157b --- /dev/null +++ b/consumerWebsite/views/404.ejs @@ -0,0 +1,37 @@ +<%- include('top') %> + + +
    +
    + +

    404 + Page Not Found +

    +
    +
    + + +
    + + +
    +

    Oops! That page can’t be found.

    +
    + +
    +

    We can’t find the page your are looking for. You can check out our Homepage.

    + Back To Homepage +
    + + +
    + + +<%- include('bot') %> \ No newline at end of file diff --git a/consumerWebsite/views/bot.ejs b/consumerWebsite/views/bot.ejs new file mode 100644 index 0000000..898fd63 --- /dev/null +++ b/consumerWebsite/views/bot.ejs @@ -0,0 +1,88 @@ + + +
    +
    +
    +
    +
    Find us
    + +

    Blk 645 Jalan Tenaga

    +

    +65 90064959

    +

    Leongdingxuan@gmail.com

    +
    + +
    +
    Quick links
    + + + +
    +
    +
    News
    + + + + +
    +
    +
    +
    +
    +

    All Rights Reserved. © 2023 EcoSaver +

    +
    +
    + + + + + diff --git a/consumerWebsite/views/index.ejs b/consumerWebsite/views/index.ejs new file mode 100644 index 0000000..f1a31a7 --- /dev/null +++ b/consumerWebsite/views/index.ejs @@ -0,0 +1,124 @@ +<%- include('top') %> + + +
    + +
    + +
    +
    +

    Services

    + +
    +
    +
    +

    Humidity

    +
    +

    70% - 75%

    +
    + +
    +
    +
    +
    +

    Air Quality Index

    +
    +

    15 - 18 PSI

    +
    + +
    +
    +
    +
    +

    Temperature

    +
    +

    30° - 37°

    +
    + +
    +
    +
    + +
    + +
    +
    +
    + EcoSaver - Your Air Quality Index Source + + + + + +
    +

    Welcome to EcoSaver - Your Air Quality Index Source

    +

    We prioritize your well-being by providing up-to-date information on air quality indexes.

    +
    + +
    +

    Our Approach: Smart and Informative

    +

    We believe in offering precise and comprehensive data to empower your decisions for a better + quality of life.

    +
      +
    • Presenting real-time air quality data in a user-friendly format.
    • +
    • Equipping you with insights into the impact of air quality on health and the environment. +
    • +
    • Empowering communities with knowledge to make informed choices for a sustainable future. +
    • +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +<%- include('bot') %> diff --git a/consumerWebsite/views/news.ejs b/consumerWebsite/views/news.ejs new file mode 100644 index 0000000..97f27b9 --- /dev/null +++ b/consumerWebsite/views/news.ejs @@ -0,0 +1,112 @@ +<%- include('top') %> + + + +
    +
    + +

    News + +

    +
    +
    + + +
    + +
    + +
    +
    +
    +
    + Card image Blog +
    +

    Singapore's air quality hits unhealthy range, 'slightly hazy' conditions + forecast for Saturday

    +

    he National Environment Agency said there has been a "significant increase" + in the number of hotspots in Sumatra.

    + Read More → +
    + +
    + +
    + Card image Blog +
    +

    Singapore Government Agencies Stand Ready To Mitigate Impact Of Haze

    +

    As of 29 September 2023, 3pm, the 24-hr Pollutant Standards Index (PSI) is + 81 (Moderate range) in the East region of Singapore. Accordingly, the 28 public agencies + that make up the Government’s Haze Task Force (HTF), are ready to roll out their respective + haze action plans should the air quality deteriorate into the Unhealthy range (24-hour PSI + above 100).

    + Read More → +
    + +
    + +
    + Card image Blog +
    +

    High risk of severe transboundary haze in 2023, public advised to be + prepared: Singapore institute

    +

    A latest report predicts a high risk of severe haze occurring in Southeast + Asia, though not as severe as in 2015

    + Read More → +
    + +
    + +
    + + +
    + +
    + + + +
    +
    +
    Search
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    + + +<%- include('bot') %> diff --git a/consumerWebsite/views/top.ejs b/consumerWebsite/views/top.ejs new file mode 100644 index 0000000..30f2ebf --- /dev/null +++ b/consumerWebsite/views/top.ejs @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webserver/database/model/apiKeyModel.js b/webserver/database/model/apiKeyModel.js index 03bcb95..69e45d9 100644 --- a/webserver/database/model/apiKeyModel.js +++ b/webserver/database/model/apiKeyModel.js @@ -45,7 +45,7 @@ const apikeyModel = sequelize.define( validate: { notEmpty: true, len: [1, 255], - isIn: [["canRead", "canWrite"]], + isIn: [["canRead", "canWrite" , "auto-generated"]], }, }, createdAt: { From e63bcf1734fafa927bf0c4a702d316a4e210b3f3 Mon Sep 17 00:00:00 2001 From: newtbot Date: Fri, 19 Jan 2024 16:04:50 +0800 Subject: [PATCH 3/3] register blah --- consumerWebsite/modules/app.js | 4 - consumerWebsite/public/css/sp.css | 48 +++++ consumerWebsite/public/js/app.js | 243 +++++++++++++++--------- consumerWebsite/public/signuplogin.html | 58 ------ consumerWebsite/routes/render.js | 5 + consumerWebsite/routes/user.js | 50 ++++- consumerWebsite/views/logintop.ejs | 96 ++++++++++ consumerWebsite/views/signuplogin.ejs | 53 ++++++ consumerWebsite/views/top.ejs | 20 +- 9 files changed, 417 insertions(+), 160 deletions(-) delete mode 100644 consumerWebsite/public/signuplogin.html create mode 100644 consumerWebsite/views/logintop.ejs create mode 100644 consumerWebsite/views/signuplogin.ejs diff --git a/consumerWebsite/modules/app.js b/consumerWebsite/modules/app.js index 069240b..bd5f1e4 100644 --- a/consumerWebsite/modules/app.js +++ b/consumerWebsite/modules/app.js @@ -4,10 +4,6 @@ const app = express(); const port = 3000; const ejs = require("ejs"); -const bodyParser = require("body-parser"); // Middleware - -app.use(bodyParser.urlencoded({ extended: false })); - app.use(express.json()); app.set("json spaces", 2); diff --git a/consumerWebsite/public/css/sp.css b/consumerWebsite/public/css/sp.css index 0a58704..8c01b06 100644 --- a/consumerWebsite/public/css/sp.css +++ b/consumerWebsite/public/css/sp.css @@ -203,3 +203,51 @@ form .form.login .back-to-login:hover { text-decoration: underline; } + +.top-nav{ + background-color: #ffffff !important; +} +.navbar-expand-lg.top-nav .navbar-nav .nav-link{ + padding: 10px 15px; + color: #4e3914; + font-size: 14px; + font-weight: 300; + text-transform: uppercase; +} +.navbar-expand-lg.top-nav .navbar-nav .nav-link:hover{ + background: #4eae3a; + color: #ffffff; + border-radius: 4.8px; +} +.navbar-expand-lg.top-nav .navbar-nav .nav-link.active{ + background: #4eae3a; + color: #ffffff; + border-radius: 4.8px; +} +.navbar-expand-lg.top-nav .navbar-nav .dropdown-menu{ + margin: 0px; + box-shadow: 3px 5px 15px rgba(0,0,0, .15); + border: none; + padding: 20px; +} +.navbar-expand-lg.top-nav .navbar-nav .dropdown-menu .dropdown-item{ + font-size: 14px; + padding: 0px; + padding-bottom: 15px; + font-weight: 300; +} +.navbar-expand-lg.top-nav .navbar-nav .dropdown-menu .dropdown-item:last-child{ + padding: 0px; +} +.navbar-expand-lg.top-nav .navbar-nav .dropdown-menu .dropdown-item:hover{ + background: none; + color: #4eae3a; +} +.top-nav .navbar-toggler{ + color: #4e3914; + border-color: #4e3914; +} +.top-nav .navbar-toggler:hover{ + color: #4eae3a; + border-color: #4eae3a; +} \ No newline at end of file diff --git a/consumerWebsite/public/js/app.js b/consumerWebsite/public/js/app.js index 3fb3560..e7dd68b 100644 --- a/consumerWebsite/public/js/app.js +++ b/consumerWebsite/public/js/app.js @@ -1,140 +1,202 @@ var app = {}; +app.util = (function (app) { + function getUrlParameter(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"); + var results = regex.exec(location.search); + return results === null + ? "" + : decodeURIComponent(results[1].replace(/\+/g, " ")); + } -app.api = (function(app){ - var baseURL = '/api/v0/' + function actionMessage(message, $target, type, callback) { + message = message || ""; + $target = $target.closest("div.card").find(".actionMessage"); + type = type || "info"; + callback = callback || function () {}; - function post(url, data, callback){ + if ($target.html() === message) return; + + if ($target.html()) { + $target.slideUp("fast", function () { + $target.html(""); + $target.removeClass(function (index, className) { + return (className.match(/(^|\s)bg-\S+/g) || []).join(" "); + }); + if (message) return actionMessage(message, $target, type, callback); + $target.hide(); + }); + } else { + if (type) $target.addClass("bg-" + type); + message = + '' + + message; + $target.html(message).slideDown("fast"); + } + setTimeout(callback, 10); + } + + $.fn.serializeObject = function () { + var arr = $(this).serializeArray(), + obj = {}; + + for (var i = 0; i < arr.length; i++) { + if (obj[arr[i].name] === undefined) { + obj[arr[i].name] = arr[i].value; + } else { + if (!(obj[arr[i].name] instanceof Array)) { + obj[arr[i].name] = [obj[arr[i].name]]; + } + obj[arr[i].name].push(arr[i].value); + } + } + return obj; + }; + + return { + getUrlParameter: getUrlParameter, + actionMessage: actionMessage, + }; +})(app); + +app.api = (function (app) { + var baseURL = "/api/v0/"; + + function post(url, data, callback) { $.ajax({ - type: 'POST', - url: baseURL+url, - headers:{ - 'auth-token': app.auth.getToken() + type: "POST", + url: baseURL + url, + headers: { + //register will getr undefined token + //login will get valid token + "auth-token": app.auth.getToken(), }, data: JSON.stringify(data), contentType: "application/json; charset=utf-8", dataType: "json", - complete: function(res, text){ + complete: function (res, text) { callback( - text !== 'success' ? res.statusText : null, + text !== "success" ? res.statusText : null, JSON.parse(res.responseText), res.status - ) - } + ); + }, }); } - function put(url, data, callback){ + function put(url, data, callback) { $.ajax({ - type: 'PUT', - url: baseURL+url, - headers:{ - 'auth-token': app.auth.getToken() + type: "PUT", + url: baseURL + url, + headers: { + "auth-token": app.auth.getToken(), }, data: JSON.stringify(data), contentType: "application/json; charset=utf-8", dataType: "json", - complete: function(res, text){ + complete: function (res, text) { callback( - text !== 'success' ? res.statusText : null, + text !== "success" ? res.statusText : null, JSON.parse(res.responseText), res.status - ) - } + ); + }, }); } - function remove(url, callback, callback2){ - if(!$.isFunction(callback)) callback = callback2; + function remove(url, callback, callback2) { + if (!$.isFunction(callback)) callback = callback2; $.ajax({ - type: 'delete', - url: baseURL+url, - headers:{ - 'auth-token': app.auth.getToken() + type: "delete", + url: baseURL + url, + headers: { + "auth-token": app.auth.getToken(), }, contentType: "application/json; charset=utf-8", dataType: "json", - complete: function(res, text){ + complete: function (res, text) { callback( - text !== 'success' ? res.statusText : null, + text !== "success" ? res.statusText : null, JSON.parse(res.responseText), res.status - ) - } + ); + }, }); } - function get(url, callback){ + function get(url, callback) { $.ajax({ - type: 'GET', - url: baseURL+url, - headers:{ - 'auth-token': app.auth.getToken() + type: "GET", + url: baseURL + url, + headers: { + "auth-token": app.auth.getToken(), }, contentType: "application/json; charset=utf-8", dataType: "json", - complete: function(res, text){ + complete: function (res, text) { callback( - text !== 'success' ? res.statusText : null, + text !== "success" ? res.statusText : null, JSON.parse(res.responseText), res.status - ) - } + ); + }, }); } - return {post: post, get: get, put: put, delete: remove} -})(app) + return { post: post, get: get, put: put, delete: remove }; +})(app); -app.auth = (function(app) { - var user = {} - function setToken(token){ - localStorage.setItem('APIToken', token); +app.auth = (function (app) { + var user = {}; + function setToken(token) { + localStorage.setItem("APIToken", token); } - function getToken(){ - return localStorage.getItem('APIToken'); + function getToken() { + return localStorage.getItem("APIToken"); } - function isLoggedIn(callback){ - if(getToken()){ - return app.api.get('user/me', function(error, data){ - if(!error) app.auth.user = data; + function isLoggedIn(callback) { + if (getToken()) { + return app.api.get("user/me", function (error, data) { + if (!error) app.auth.user = data; return callback(error, data); }); - }else{ + } else { callback(null, false); } } - function logIn(args, callback){ - app.api.post('auth/login', args, function(error, data){ - if(data.login){ + function logIn(args, callback) { + app.api.post("auth/login", args, function (error, data) { + if (data.login) { setToken(data.token); } callback(error, !!data.token); }); } - function logOut(callback){ - localStorage.removeItem('APIToken'); + function logOut(callback) { + localStorage.removeItem("APIToken"); callback(); } - function forceLogin(){ - $.holdReady( true ); - app.auth.isLoggedIn(function(error, isLoggedIn){ - if(error || !isLoggedIn){ - app.auth.logOut(function(){}) - location.replace(`/login${location.href.replace(location.origin, '')}`); - }else{ - $.holdReady( false ); + function forceLogin() { + $.holdReady(true); + app.auth.isLoggedIn(function (error, isLoggedIn) { + if (error || !isLoggedIn) { + app.auth.logOut(function () {}); + location.replace(`/login`); + } else { + $.holdReady(false); } }); } - function logInRedirect(){ - window.location.href = location.href.replace(location.origin+'/login', '') || '/' + function logInRedirect() { + window.location.href = + location.href.replace(location.replace(`/login`)) || "/"; } return { @@ -145,34 +207,35 @@ app.auth = (function(app) { logOut: logOut, forceLogin, logInRedirect, - } - + }; })(app); //ajax form submit function formAJAX( btn, del ) { - event.preventDefault(); // avoid to execute the actual submit of the form. - var $form = $(btn).closest( '[action]' ); // gets the 'form' parent - var formData = $form.find( '[name]' ).serializeObject(); // builds query formDataing - var method = $form.attr('method') || 'post'; + event.preventDefault(); // avoid to execute the actual submit of the form. + var $form = $(btn).closest( '[action]' ); // gets the 'form' parent + var formData = $form.find( '[name]' ).serializeObject(); // builds query formDataing + var method = $form.attr('method') || 'post'; - // if( !$form.validate()) { - // app.util.actionMessage('Please fix the form errors.', $form, 'danger') - // return false; - // } - - app.util.actionMessage( - '
    Loading...
    ', - $form, - 'info' - ); + // if( !$form.validate()) { + // app.util.actionMessage('Please fix the form errors.', $form, 'danger') + // return false; + // } + + app.util.actionMessage( + '
    Loading...
    ', + $form, + 'info' + ); - app.api[method]($form.attr('action'), formData, function(error, data){ - app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table - if(!error){ - $form.trigger("reset"); - eval($form.attr('evalAJAX')); //gets JS to run after completion - } - }); + //console.log('Data being sent to', $form.attr('action'), formData) + app.api[method]($form.attr('action'), formData, function(error, data){ + //console.log('Data back from the server', error, data) + app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table + if(!error){ + $form.trigger("reset"); + eval($form.attr('evalAJAX')); //gets JS to run after completion + } + }); } \ No newline at end of file diff --git a/consumerWebsite/public/signuplogin.html b/consumerWebsite/public/signuplogin.html deleted file mode 100644 index cc764c6..0000000 --- a/consumerWebsite/public/signuplogin.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - Login & Signup Form - - - - -
    - - - - - -
    - - - - - \ No newline at end of file diff --git a/consumerWebsite/routes/render.js b/consumerWebsite/routes/render.js index 356c63b..471df93 100644 --- a/consumerWebsite/routes/render.js +++ b/consumerWebsite/routes/render.js @@ -51,6 +51,11 @@ router.get('/news', function(req, res, next) { res.render('news'); }); +//login / register page +router.get('/login', function(req, res, next) { + res.render('signuplogin'); +}); + //404 page router.get('*', function(req, res, next) { res.render('404'); diff --git a/consumerWebsite/routes/user.js b/consumerWebsite/routes/user.js index c5bf23a..1471fbe 100644 --- a/consumerWebsite/routes/user.js +++ b/consumerWebsite/routes/user.js @@ -18,8 +18,9 @@ router.get("/", async (req, res, next) => { // /user/register router.post("/register", async (req, res, next) => { try { - //await addUser(req.body); - res.sendStatus(200); + console.log("this is " , req.body); + await addUser(req.body); + res.status(200).json({ register: true }); } catch (error) { console.error(error); next(error); @@ -33,3 +34,48 @@ router.post("/register", async (req, res, next) => { //getbyid module.exports = router; + + +/* + +curl localhost/api/v0/user/register -H "Content-Type: application/json" -X POST -d '{"username": +"testuser123", "password": "thisisthesystemuserpasswordnoob", "email": "testuser123@ecosaver.com", "address": +"Nanyang Polytechnic 180 Ang Mo Kio Avenue 8 Singapore 569830", "phone": "12345678"}' +'use strict'; + +const router = require('express').Router(); +const {User} = require('../models/user'); + +router.get('/', async function(req, res, next){ + try{ + return res.json({ + results: await User[req.query.detail ? "listDetail" : "list"]() + }); + }catch(error){ + next(error); + } +}); + +router.get('/me', async function(req, res, next){ + try{ + + return res.json(await User.get({uid: req.user.uid})); + }catch(error){ + next(error); + } +}); + +router.get('/:uid', async function(req, res, next){ + try{ + return res.json({ + results: await User.get(req.params.uid), + }); + }catch(error){ + next(error); + } +}); + +module.exports = router; + + +*/ \ No newline at end of file diff --git a/consumerWebsite/views/logintop.ejs b/consumerWebsite/views/logintop.ejs new file mode 100644 index 0000000..9bbc946 --- /dev/null +++ b/consumerWebsite/views/logintop.ejs @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/consumerWebsite/views/signuplogin.ejs b/consumerWebsite/views/signuplogin.ejs new file mode 100644 index 0000000..0eda68c --- /dev/null +++ b/consumerWebsite/views/signuplogin.ejs @@ -0,0 +1,53 @@ +<%- include('logintop') %> + + +
    + + + + + +
    + + + + + diff --git a/consumerWebsite/views/top.ejs b/consumerWebsite/views/top.ejs index 30f2ebf..16440bc 100644 --- a/consumerWebsite/views/top.ejs +++ b/consumerWebsite/views/top.ejs @@ -8,7 +8,8 @@ - + @@ -17,11 +18,15 @@ + + - + @@ -38,6 +43,7 @@ app.auth.isLoggedIn(function (error, data) { if (data) { $('#cl-logout-button').show(); + $('#cl-profile-button').show(); $('#cl-login-button').hide(); } else { $('#cl-login-button').show(); @@ -67,16 +73,18 @@ - +