Merge branch 'main' of https://github.com/Newtbot/MP
This commit is contained in:
commit
7dceee0b79
136
Sean/inusers.js
136
Sean/inusers.js
@ -1,136 +0,0 @@
|
|||||||
const allUsers = <%- JSON.stringify(allUsers) %>;
|
|
||||||
|
|
||||||
document.getElementById('downloadButton').addEventListener('click', function () {
|
|
||||||
console.log('Download button clicked');
|
|
||||||
downloadExcel(allUsers);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('addUserLink').addEventListener('click', function () {
|
|
||||||
document.getElementById('downloadButtonContainer').style.display = 'none';
|
|
||||||
document.getElementById('userDataContainer').style.display = 'none';
|
|
||||||
document.getElementById('createUserForm').style.display = 'block';
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('userDataLink').addEventListener('click', function () {
|
|
||||||
document.getElementById('downloadButtonContainer').style.display = 'block';
|
|
||||||
document.getElementById('userDataContainer').style.display = 'block';
|
|
||||||
document.getElementById('createUserForm').style.display = 'none';
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('userForm').addEventListener('submit', function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// Use FormData directly
|
|
||||||
const formData = new FormData(document.getElementById('userForm'));
|
|
||||||
|
|
||||||
// Check password complexity
|
|
||||||
const password = formData.get('password');
|
|
||||||
const confirmPassword = formData.get('confirmPassword');
|
|
||||||
|
|
||||||
if (!isStrongPassword(password)) {
|
|
||||||
alert('Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if passwords match
|
|
||||||
if (password !== confirmPassword) {
|
|
||||||
alert('Passwords do not match. Please enter the same password in both fields.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a fetch request
|
|
||||||
fetch('/createUser', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: formData.get('name'),
|
|
||||||
username: formData.get('username'),
|
|
||||||
email: formData.get('email'),
|
|
||||||
password: password, // Use the validated password
|
|
||||||
jobTitle: formData.get('jobTitle'),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
console.log('Success:', data);
|
|
||||||
|
|
||||||
// Show an alert with the received data
|
|
||||||
alert(`User Registered!`);
|
|
||||||
|
|
||||||
// Optionally, you can clear the form or take other actions after registration
|
|
||||||
document.getElementById('userForm').reset();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Fetch Error:', error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Function to validate password complexity
|
|
||||||
function isStrongPassword(password) {
|
|
||||||
// Password must be at least 10 characters long
|
|
||||||
if (password.length < 10) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password must contain at least one uppercase letter
|
|
||||||
if (!/[A-Z]/.test(password)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password must contain at least one lowercase letter
|
|
||||||
if (!/[a-z]/.test(password)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password must contain at least one digit
|
|
||||||
if (!/\d/.test(password)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password must contain at least one symbol
|
|
||||||
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function downloadExcel(allUsers) {
|
|
||||||
if (allUsers && allUsers.length > 0) {
|
|
||||||
const workbook = new ExcelJS.Workbook();
|
|
||||||
const worksheet = workbook.addWorksheet('All Users');
|
|
||||||
const headers = ['Name', 'Username', 'Email', 'Password', 'Last Login', 'Job Title'];
|
|
||||||
worksheet.addRow(headers);
|
|
||||||
allUsers.forEach(user => {
|
|
||||||
const rowData = [
|
|
||||||
user.name || '',
|
|
||||||
user.username || '',
|
|
||||||
user.email || '',
|
|
||||||
user.password || '',
|
|
||||||
user.lastLogin ? new Date(user.lastLogin).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }) : '',
|
|
||||||
user.jobTitle || ''
|
|
||||||
];
|
|
||||||
worksheet.addRow(rowData);
|
|
||||||
});
|
|
||||||
workbook.xlsx.writeBuffer().then(buffer => {
|
|
||||||
const currentDate = new Date();
|
|
||||||
const formattedDate = currentDate.toISOString().split('T')[0];
|
|
||||||
const formattedTime = currentDate.toTimeString().split(' ')[0].replace(/:/g, '-');
|
|
||||||
const fileName = `user_data_${formattedDate}_${formattedTime}.xlsx`;
|
|
||||||
const blob = new Blob([buffer], {
|
|
||||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
||||||
});
|
|
||||||
saveAs(blob, fileName);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('No data available for download.');
|
|
||||||
}
|
|
||||||
}
|
|
@ -37,6 +37,9 @@ module.exports = (sequelize) => {
|
|||||||
reset_token_expiry: {
|
reset_token_expiry: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
},
|
},
|
||||||
|
sessionid: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
hooks: {
|
hooks: {
|
||||||
beforeCreate: async (user) => {
|
beforeCreate: async (user) => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const session = require("express-session");
|
const session = require("express-session");
|
||||||
const rateLimit = require('express-rate-limit');
|
const rateLimit = require('express-rate-limit');
|
||||||
|
const cookieParser = require('cookie-parser');
|
||||||
const bodyParser = require("body-parser");
|
const bodyParser = require("body-parser");
|
||||||
const bcrypt = require("bcrypt");
|
const bcrypt = require("bcrypt");
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
@ -10,21 +10,37 @@ const otpGenerator = require('otp-generator');
|
|||||||
const { body, validationResult } = require('express-validator');
|
const { body, validationResult } = require('express-validator');
|
||||||
const validator = require('validator');
|
const validator = require('validator');
|
||||||
const { format } = require('date-fns');
|
const { format } = require('date-fns');
|
||||||
|
const helmet = require('helmet');
|
||||||
const { Sequelize } = require('sequelize');
|
const { Sequelize } = require('sequelize');
|
||||||
const { transporter } = require("./modules/nodeMailer");
|
const { transporter } = require("./modules/nodeMailer");
|
||||||
|
|
||||||
const { sequelize, User } = require("./modules/mysql");
|
const { sequelize, User } = require("./modules/mysql");
|
||||||
const userLogs= require('./models/userLogs')(sequelize); // Adjust the path based on your project structure
|
const userLogs= require('./models/userLogs')(sequelize); // Adjust the path based on your project structure
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
const nonce = crypto.randomBytes(16).toString('base64');
|
||||||
|
|
||||||
|
console.log('Nonce:', nonce);
|
||||||
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
app.use(cookieParser());
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
|
app.use(
|
||||||
|
helmet.contentSecurityPolicy({
|
||||||
|
directives: {
|
||||||
|
defaultSrc: ["'self'",`'nonce-${nonce}'`],
|
||||||
|
scriptSrc: ["'self'",`'nonce-${nonce}'`,"'strict-dynamic'", 'cdn.jsdelivr.net', 'fonts.googleapis.com', 'stackpath.bootstrapcdn.com', 'code.jquery.com', 'cdnjs.cloudflare.com'],
|
||||||
|
styleSrc: ["'self'",`'nonce-${nonce}'`, 'cdn.jsdelivr.net', 'fonts.googleapis.com'],
|
||||||
|
imgSrc: ["'self'"],
|
||||||
|
fontSrc: ["'self'", 'fonts.gstatic.com'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
app.use(session({
|
app.use(session({
|
||||||
secret: process.env.key,
|
secret: process.env.key,
|
||||||
@ -189,7 +205,20 @@ app.post("/verify-otp", [
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sessionToken = crypto.randomBytes(32).toString('hex');
|
const sessionToken = crypto.randomBytes(32).toString('hex');
|
||||||
|
const username = req.body.username; // Replace with the actual username
|
||||||
|
|
||||||
|
User.update({ sessionid: sessionToken }, { where: { username } })
|
||||||
|
.then(([rowsUpdated]) => {
|
||||||
|
if (rowsUpdated > 0) {
|
||||||
|
console.log(`SessionId updated for user: ${username}`);
|
||||||
|
} else {
|
||||||
|
console.error('User not found.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error updating sessionId:', error);
|
||||||
|
});
|
||||||
|
|
||||||
req.session.authenticated = true;
|
req.session.authenticated = true;
|
||||||
req.session.username = req.body.username;
|
req.session.username = req.body.username;
|
||||||
req.session.sessionToken = sessionToken;
|
req.session.sessionToken = sessionToken;
|
||||||
@ -200,7 +229,6 @@ app.post("/verify-otp", [
|
|||||||
// Log anti-CSRF token
|
// Log anti-CSRF token
|
||||||
console.log(`Generated Anti-CSRF Token: ${csrfTokenSession}`);
|
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
|
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}`);
|
console.log(`Generated Session Token: ${sessionToken}`);
|
||||||
@ -232,6 +260,7 @@ app.post("/verify-otp", [
|
|||||||
} else {
|
} else {
|
||||||
console.log("Session destroyed.");
|
console.log("Session destroyed.");
|
||||||
// Log the logout activity using Sequelize
|
// Log the logout activity using Sequelize
|
||||||
|
await User.update({ sessionid: null }, { where: { username } })
|
||||||
await userLogs.create({ username, activity: "User logged out. Session destroyed." });
|
await userLogs.create({ username, activity: "User logged out. Session destroyed." });
|
||||||
// Clear the session token cookie
|
// Clear the session token cookie
|
||||||
res.clearCookie('sessionToken');
|
res.clearCookie('sessionToken');
|
||||||
@ -267,7 +296,7 @@ app.post("/verify-otp", [
|
|||||||
const currentUsername = req.session.username;
|
const currentUsername = req.session.username;
|
||||||
|
|
||||||
// Render the inusers page with JSON data
|
// Render the inusers page with JSON data
|
||||||
res.render("inusers", { allUsers, csrfToken: csrfTokenSession, currentUsername });
|
res.render("inusers", { nonce: nonce, allUsers, csrfToken: csrfTokenSession, currentUsername });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching all users:", error);
|
console.error("Error fetching all users:", error);
|
||||||
res.status(500).send("Internal Server Error");
|
res.status(500).send("Internal Server Error");
|
||||||
@ -324,6 +353,14 @@ app.post(
|
|||||||
return res.status(400).json({ errors: errors.array() });
|
return res.status(400).json({ errors: errors.array() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sessionTokencookie = req.cookies['sessionToken'];
|
||||||
|
|
||||||
|
// Verify sessionToken with the one stored in the database
|
||||||
|
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(403).json({ error: 'Invalid sessionToken' });
|
||||||
|
}
|
||||||
// Validate the anti-CSRF token
|
// Validate the anti-CSRF token
|
||||||
const submittedCSRFToken = req.body.csrf_token;
|
const submittedCSRFToken = req.body.csrf_token;
|
||||||
|
|
||||||
@ -595,7 +632,14 @@ app.post("/reset-password", async (req, res) => {
|
|||||||
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
|
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
|
||||||
return res.status(403).json({ error: 'CSRF token mismatch' });
|
return res.status(403).json({ error: 'CSRF token mismatch' });
|
||||||
}
|
}
|
||||||
|
const sessionTokencookie = req.cookies['sessionToken'];
|
||||||
|
|
||||||
|
// Verify sessionToken with the one stored in the database
|
||||||
|
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(403).json({ error: 'Invalid sessionToken' });
|
||||||
|
}
|
||||||
// Sanitize the inputs
|
// Sanitize the inputs
|
||||||
const sanitizedUsername = validator.escape(username);
|
const sanitizedUsername = validator.escape(username);
|
||||||
const sanitizedPassword = validator.escape(password);
|
const sanitizedPassword = validator.escape(password);
|
||||||
@ -686,7 +730,6 @@ app.get('/api/users', async (req, res) => {
|
|||||||
|
|
||||||
app.get('/api/searchUser', async (req, res) => {
|
app.get('/api/searchUser', async (req, res) => {
|
||||||
const { username } = req.query;
|
const { username } = req.query;
|
||||||
console.log(username);
|
|
||||||
try {
|
try {
|
||||||
// Find the user in the database by username
|
// Find the user in the database by username
|
||||||
const user = await User.findOne({ where: { username } });
|
const user = await User.findOne({ where: { username } });
|
||||||
@ -709,13 +752,22 @@ app.delete('/api/deleteUser/:username', async (req, res) => {
|
|||||||
const { username } = req.params;
|
const { username } = req.params;
|
||||||
const creatorUsername = req.session.username;
|
const creatorUsername = req.session.username;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Extract CSRF token from the request body
|
// Retrieve sessionToken from cookies
|
||||||
|
const sessionTokencoookie = req.cookies['sessionToken'];
|
||||||
|
// Retrieve CSRF token from the request body
|
||||||
const { csrfToken } = req.body;
|
const { csrfToken } = req.body;
|
||||||
|
console.log(csrfToken);
|
||||||
// Compare CSRF token with the one stored in the session
|
// Compare CSRF token with the one stored in the session
|
||||||
if (csrfToken !== csrfTokenSession) {
|
if (csrfToken !== csrfTokenSession) {
|
||||||
return res.status(403).json({ success: false, error: 'CSRF token mismatch' });
|
return res.status(403).json({ success: false, error: 'CSRF token mismatch' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify sessionToken with the one stored in the database
|
||||||
|
const user = await User.findOne({ where: { sessionid: sessionTokencoookie } });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(403).json({ success: false, error: 'Invalid sessionToken or user not found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log deletion activity to UserLogs model
|
// Log deletion activity to UserLogs model
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
58
Sean/views/forgot-password.css
Normal file
58
Sean/views/forgot-password.css
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.success-message {
|
||||||
|
color: green;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
@ -5,66 +5,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Forgot Password - Your Website</title>
|
<title>Forgot Password - Your Website</title>
|
||||||
<style>
|
<link rel="stylesheet" href="forgot-password.css">
|
||||||
body {
|
|
||||||
font-family: 'Arial', sans-serif;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
padding: 10px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
.error-message {
|
|
||||||
color: red;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.success-message {
|
|
||||||
color: green;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="forgotPasswordForm">
|
<div id="forgotPasswordForm">
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
document.addEventListener("DOMContentLoaded", async function () {
|
|
||||||
console.log("DOM Loaded");
|
|
||||||
|
|
||||||
// Extract data from sensorData
|
|
||||||
const sensorData = JSON.parse('<%- JSON.stringify(sensorData) %>');
|
|
||||||
console.log("Sensor Data:", sensorData);
|
|
||||||
|
|
||||||
// Fetch location names from the server
|
|
||||||
const locationNames = await fetch('/api/locations') // Adjust the API endpoint
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => data.map(location => ({ id: location.id, name: location.name })))
|
|
||||||
.catch(error => console.error('Error fetching location names:', error));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Group sensorData by locationid
|
|
||||||
const groupedData = groupBy(sensorData, 'locationid');
|
|
||||||
|
|
||||||
// Get the content div
|
|
||||||
const contentDiv = document.getElementById('content');
|
|
||||||
|
|
||||||
// Create a chart for each location
|
|
||||||
Object.keys(groupedData).forEach(locationId => {
|
|
||||||
const locationData = groupedData[locationId];
|
|
||||||
|
|
||||||
// Find the corresponding location name
|
|
||||||
const locationName = locationNames.find(location => location.id === parseInt(locationId, 10))?.name || `Unknown Location ${locationId}`;
|
|
||||||
|
|
||||||
// Create a container for the chart
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.className = 'chart-container';
|
|
||||||
|
|
||||||
// Create a title for the container with location name
|
|
||||||
const title = document.createElement('h4');
|
|
||||||
title.textContent = `Location: ${locationName}`;
|
|
||||||
container.appendChild(title);
|
|
||||||
|
|
||||||
// Get labels (Location IDs)
|
|
||||||
const labels = locationData.map(data => new Date(data.createdAt).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }));
|
|
||||||
|
|
||||||
// Create datasets for each measurement
|
|
||||||
const datasets = [
|
|
||||||
{
|
|
||||||
label: 'CO',
|
|
||||||
data: locationData.map(data => data.measurement.co),
|
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.5)', // Red color
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'O3',
|
|
||||||
data: locationData.map(data => data.measurement.o3),
|
|
||||||
backgroundColor: 'rgba(54, 162, 235, 0.5)', // Blue color
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'NO2',
|
|
||||||
data: locationData.map(data => data.measurement.no2),
|
|
||||||
backgroundColor: 'rgba(255, 206, 86, 0.5)', // Yellow color
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'SO2',
|
|
||||||
data: locationData.map(data => data.measurement.so2),
|
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.5)', // Green color
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Create a canvas element for each location
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
canvas.width = 400;
|
|
||||||
canvas.height = 200;
|
|
||||||
|
|
||||||
// Append canvas to the container
|
|
||||||
container.appendChild(canvas);
|
|
||||||
|
|
||||||
// Append container to the content div
|
|
||||||
contentDiv.appendChild(container);
|
|
||||||
|
|
||||||
// Create a bar chart for each location
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: labels,
|
|
||||||
datasets: datasets,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
beginAtZero: true,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper function to group data by a specified key
|
|
||||||
function groupBy(arr, key) {
|
|
||||||
return arr.reduce((acc, obj) => {
|
|
||||||
const groupKey = obj[key];
|
|
||||||
acc[groupKey] = acc[groupKey] || [];
|
|
||||||
acc[groupKey].push(obj);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
|||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const mysql = require('mysql');
|
|
||||||
|
|
||||||
// Replace with your MySQL connection details
|
|
||||||
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 mysqlConnection = mysql.createConnection(mysqlConfig);
|
|
||||||
|
|
||||||
// Middleware to check if the user is authenticated
|
|
||||||
function isAuthenticated(req, res, next) {
|
|
||||||
if (req.session && req.session.authenticated) {
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
res.redirect('/login');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InUsers route (renders the InUsers tab)
|
|
||||||
router.get('/', isAuthenticated, (req, res) => {
|
|
||||||
// Fetch all user data from the database
|
|
||||||
const userDataQuery = 'SELECT * FROM users';
|
|
||||||
|
|
||||||
mysqlConnection.query(userDataQuery, (error, userData) => {
|
|
||||||
if (error) {
|
|
||||||
console.error('Error fetching user data:', error);
|
|
||||||
res.status(500).send('Internal Server Error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the inusers page with user data
|
|
||||||
res.render('inusers', { userData: userData });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// User Data route
|
|
||||||
router.get('/userdata', isAuthenticated, (req, res) => {
|
|
||||||
// Fetch all user data from the database
|
|
||||||
const userDataQuery = 'SELECT * FROM users';
|
|
||||||
|
|
||||||
mysqlConnection.query(userDataQuery, (error, userData) => {
|
|
||||||
if (error) {
|
|
||||||
console.error('Error fetching user data:', error);
|
|
||||||
res.status(500).send('Internal Server Error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the user-data page with user data
|
|
||||||
res.render('user-data', { userData: userData });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Edit User Data route
|
|
||||||
router.get('/edituserdata', isAuthenticated, (req, res) => {
|
|
||||||
res.render('edit-user-data');
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
@ -7,7 +7,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>In-House Users</title>
|
<title>In-House Users</title>
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
|
||||||
</head>
|
</head>
|
||||||
@ -173,19 +173,21 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script nonce="<%= nonce %>">
|
||||||
const allUsers = <%- JSON.stringify(allUsers) %>;
|
const allUsers = <%- JSON.stringify(allUsers) %>;
|
||||||
const currentUsername = '<%= currentUsername %>';
|
const currentUsername = '<%= currentUsername %>';
|
||||||
|
const nonce = "<%= nonce %>"
|
||||||
|
console.log('Nonce:', nonce);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.4.min.js" nonce="<%= nonce %>"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js" nonce="<%= nonce %>" ></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js" nonce="<%= nonce %>"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js" nonce="<%= nonce %>"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.2.1/exceljs.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.2.1/exceljs.min.js" nonce="<%= nonce %>"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" nonce="<%= nonce %>"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js" nonce="<%= nonce %>"></script>
|
||||||
<script src="inusers.js"></script>
|
<script src="inusers.js" nonce="<%= nonce %>"></script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -33,7 +33,7 @@ $(document).ready(function () {
|
|||||||
$('#logsContainer').hide();
|
$('#logsContainer').hide();
|
||||||
$('#additional-text').hide();
|
$('#additional-text').hide();
|
||||||
$('#additional-text2').hide();
|
$('#additional-text2').hide();
|
||||||
$('#additional-text2').hide();
|
$('#additional-text3').hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#searchUserButton').on('click', function () {
|
$('#searchUserButton').on('click', function () {
|
||||||
@ -124,6 +124,7 @@ $('#searchResultsList').on('click', '.deleteUserButton', function () {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
credentials: 'include', // Include cookies in the request
|
||||||
body: JSON.stringify({ csrfToken }), // Include CSRF token in the request body
|
body: JSON.stringify({ csrfToken }), // Include CSRF token in the request body
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
@ -243,8 +244,8 @@ function resetFormFields() {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
credentials: 'include', // Include cookies in the request
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: name,
|
name: name,
|
||||||
username: username,
|
username: username,
|
||||||
@ -330,6 +331,7 @@ $('#resetPasswordForm').on('submit', function (e) {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
credentials: 'include', // Include cookies in the request
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
@ -356,8 +358,6 @@ $('#resetPasswordForm').on('submit', function (e) {
|
|||||||
$('#resetPassword').val('');
|
$('#resetPassword').val('');
|
||||||
$('#resetConfirmPassword').val('');
|
$('#resetConfirmPassword').val('');
|
||||||
|
|
||||||
// You might want to hide the reset password form after submission
|
|
||||||
$('#resetPasswordFormContainer').hide();
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// Handle 404 error separately to show an alert
|
// Handle 404 error separately to show an alert
|
||||||
|
67
Sean/views/login.css
Normal file
67
Sean/views/login.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
body {
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 300px;
|
||||||
|
margin: auto; /* Center the container horizontally */
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: calc(100% - 16px); /* Adjust the width and center the input */
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-link-container {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-link {
|
||||||
|
color: #4caf50;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
@ -4,75 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Login - Eco Saver</title>
|
<title>Login - Eco Saver</title>
|
||||||
<style>
|
<link rel="stylesheet" href="login.css">
|
||||||
body {
|
|
||||||
font-family: 'Arial', sans-serif;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-container {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
width: 300px;
|
|
||||||
margin: auto; /* Center the container horizontally */
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: calc(100% - 16px); /* Adjust the width and center the input */
|
|
||||||
padding: 8px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
padding: 10px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reset-link-container {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reset-link {
|
|
||||||
color: #4caf50;
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
|
|
||||||
*{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: 'Poppins',sans-serif;
|
|
||||||
}
|
|
||||||
body{
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
background: linear-gradient(135deg, #71b7e6, #9b59b6);
|
|
||||||
}
|
|
||||||
.container{
|
|
||||||
max-width: 700px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 25px 30px;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 5px 10px rgba(0,0,0,0.15);
|
|
||||||
}
|
|
||||||
.container .title{
|
|
||||||
font-size: 25px;
|
|
||||||
font-weight: 500;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.container .title::before{
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 3px;
|
|
||||||
width: 30px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: linear-gradient(135deg, #71b7e6, #9b59b6);
|
|
||||||
}
|
|
||||||
.content form .user-details{
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin: 20px 0 12px 0;
|
|
||||||
}
|
|
||||||
form .user-details .input-box{
|
|
||||||
margin-bottom: 15px;
|
|
||||||
width: calc(100% / 2 - 20px);
|
|
||||||
}
|
|
||||||
form .input-box span.details{
|
|
||||||
display: block;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.user-details .input-box input{
|
|
||||||
height: 45px;
|
|
||||||
width: 100%;
|
|
||||||
outline: none;
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding-left: 15px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-bottom-width: 2px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
.user-details .input-box input:focus,
|
|
||||||
.user-details .input-box input:valid{
|
|
||||||
border-color: #9b59b6;
|
|
||||||
}
|
|
||||||
form .gender-details .gender-title{
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
form .category{
|
|
||||||
display: flex;
|
|
||||||
width: 80%;
|
|
||||||
margin: 14px 0 ;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
form .category label{
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
form .category label .dot{
|
|
||||||
height: 18px;
|
|
||||||
width: 18px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 10px;
|
|
||||||
background: #d9d9d9;
|
|
||||||
border: 5px solid transparent;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
#dot-1:checked ~ .category label .one,
|
|
||||||
#dot-2:checked ~ .category label .two,
|
|
||||||
#dot-3:checked ~ .category label .three{
|
|
||||||
background: #9b59b6;
|
|
||||||
border-color: #d9d9d9;
|
|
||||||
}
|
|
||||||
form input[type="radio"]{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
form .button{
|
|
||||||
height: 45px;
|
|
||||||
margin: 35px 0
|
|
||||||
}
|
|
||||||
form .button input{
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: linear-gradient(135deg, #71b7e6, #9b59b6);
|
|
||||||
}
|
|
||||||
form .button input:hover{
|
|
||||||
/* transform: scale(0.99); */
|
|
||||||
background: linear-gradient(-135deg, #71b7e6, #9b59b6);
|
|
||||||
}
|
|
||||||
@media(max-width: 584px){
|
|
||||||
.container{
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
form .user-details .input-box{
|
|
||||||
margin-bottom: 15px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
form .category{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.content form .user-details{
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
.user-details::-webkit-scrollbar{
|
|
||||||
width: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media(max-width: 459px){
|
|
||||||
.container .content .category{
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
50
Sean/views/otp.css
Normal file
50
Sean/views/otp.css
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
text-align: center;
|
||||||
|
margin: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
max-width: 300px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
@ -4,58 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Enter OTP</title>
|
<title>Enter OTP</title>
|
||||||
<style>
|
<link rel="stylesheet" href="otp.css">
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
text-align: center;
|
|
||||||
margin: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
max-width: 300px;
|
|
||||||
margin: 20px auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Enter OTP</h2>
|
<h2>Enter OTP</h2>
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
/* Responsive CSS Here */
|
|
||||||
@media screen and (max-width: 950px) {
|
|
||||||
.nav-img {
|
|
||||||
height: 25px;
|
|
||||||
}
|
|
||||||
.nav-option {
|
|
||||||
gap: 30px;
|
|
||||||
}
|
|
||||||
.nav-option h3 {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
.report-topic-heading,
|
|
||||||
.item1,
|
|
||||||
.items {
|
|
||||||
width: 800px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 850px) {
|
|
||||||
.nav-img {
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
.nav-option {
|
|
||||||
gap: 30px;
|
|
||||||
}
|
|
||||||
.nav-option h3 {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
.report-topic-heading,
|
|
||||||
.item1,
|
|
||||||
.items {
|
|
||||||
width: 700px;
|
|
||||||
}
|
|
||||||
.navcontainer {
|
|
||||||
width: 100vw;
|
|
||||||
position: absolute;
|
|
||||||
transition: all 0.6s ease-in-out;
|
|
||||||
top: 0;
|
|
||||||
left: -100vw;
|
|
||||||
}
|
|
||||||
.nav {
|
|
||||||
width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.navclose {
|
|
||||||
left: 00px;
|
|
||||||
}
|
|
||||||
.searchbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.main {
|
|
||||||
padding: 40px 30px 30px 30px;
|
|
||||||
}
|
|
||||||
.searchbar2 {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
margin: 0 0 40px 0;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.searchbar2 input {
|
|
||||||
width: 250px;
|
|
||||||
height: 42px;
|
|
||||||
border-radius: 50px 0 0 50px;
|
|
||||||
background-color: var(--background-color3);
|
|
||||||
padding: 0 20px;
|
|
||||||
font-size: 15px;
|
|
||||||
border: 2px solid var(--secondary-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 490px) {
|
|
||||||
.message {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.logosec {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.logo {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
.menuicn {
|
|
||||||
height: 25px;
|
|
||||||
}
|
|
||||||
.nav-img {
|
|
||||||
height: 25px;
|
|
||||||
}
|
|
||||||
.nav-option {
|
|
||||||
gap: 25px;
|
|
||||||
}
|
|
||||||
.nav-option h3 {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.nav-upper-options {
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
.recent-Articles {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
.report-topic-heading,
|
|
||||||
.item1,
|
|
||||||
.items {
|
|
||||||
width: 550px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 400px) {
|
|
||||||
.recent-Articles {
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
.view {
|
|
||||||
width: 60px;
|
|
||||||
font-size: 10px;
|
|
||||||
height: 27px;
|
|
||||||
}
|
|
||||||
.report-header {
|
|
||||||
height: 60px;
|
|
||||||
padding: 10px 10px 5px 10px;
|
|
||||||
}
|
|
||||||
.searchbtn img {
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 320px) {
|
|
||||||
.recent-Articles {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.view {
|
|
||||||
width: 50px;
|
|
||||||
font-size: 8px;
|
|
||||||
height: 27px;
|
|
||||||
}
|
|
||||||
.report-header {
|
|
||||||
height: 60px;
|
|
||||||
padding: 10px 5px 5px 5px;
|
|
||||||
}
|
|
||||||
.t-op {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.t-op-nextlvl {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
.report-topic-heading,
|
|
||||||
.item1,
|
|
||||||
.items {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
.report-body {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.label-tag {
|
|
||||||
width: 70px;
|
|
||||||
}
|
|
||||||
.searchbtn {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
.searchbar2 input {
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
<!-- setup-mfa.ejs (or your template engine's file) -->
|
|
||||||
<h2>Setup Multi-Factor Authentication</h2>
|
|
||||||
<p>Scan the QR code below with your authenticator app:</p>
|
|
||||||
<img src="<%= qrCodeDataUri %>" alt="QR Code">
|
|
||||||
|
|
||||||
<form action="/setup-mfa" method="post">
|
|
||||||
<label for="mfaCode">Enter MFA Code:</label>
|
|
||||||
<input type="text" id="mfaCode" name="mfaCode" required>
|
|
||||||
<button type="submit">Verify and Save</button>
|
|
||||||
</form>
|
|
@ -56,15 +56,16 @@ async function addUser(user) {
|
|||||||
|
|
||||||
//api/v0/auth/login
|
//api/v0/auth/login
|
||||||
async function loginUser(user) {
|
async function loginUser(user) {
|
||||||
|
//console.log(user);
|
||||||
//look up username or email in db
|
//look up username or email in db
|
||||||
const userRes = await userModel.findOne({
|
const userRes = await userModel.findOne({
|
||||||
where: {
|
where: {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{
|
{
|
||||||
username: user.userInfo,
|
username: user.username,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
email: user.userInfo,
|
email: user.username,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -37,6 +37,9 @@ function isAddress(value){
|
|||||||
return addressRegex.test(value);
|
return addressRegex.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//generate me an regex for alpha
|
||||||
|
//https://stackoverflow.com/questions/11522529/regexp-for-alphabets-with-spaces
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isAlphaNumericwithSpaces,
|
isAlphaNumericwithSpaces,
|
||||||
isAlphaNumericWithSpacesAndDash,
|
isAlphaNumericWithSpacesAndDash,
|
||||||
|
16
consumerWebsite/modules/nodemailer.js
Normal file
16
consumerWebsite/modules/nodemailer.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const nodemailer = require("nodemailer");
|
||||||
|
const dotenv = require("dotenv");
|
||||||
|
const path = require('path')
|
||||||
|
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
|
||||||
|
|
||||||
|
let transporter = nodemailer.createTransport({
|
||||||
|
service: 'gmail',
|
||||||
|
host: 'smtp.gmail.com',
|
||||||
|
port: 587,
|
||||||
|
secure: false,
|
||||||
|
auth: {
|
||||||
|
user:
|
||||||
|
pass:
|
||||||
|
},
|
||||||
|
});
|
||||||
|
module.exports = { transporter };
|
@ -254,3 +254,7 @@ form
|
|||||||
color: #4eae3a;
|
color: #4eae3a;
|
||||||
border-color: #4eae3a;
|
border-color: #4eae3a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: black;
|
||||||
|
}
|
@ -59,7 +59,7 @@ button.btn-secondary:hover{
|
|||||||
.navbar-expand-lg.top-nav .navbar-nav .nav-link{
|
.navbar-expand-lg.top-nav .navbar-nav .nav-link{
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
color: #4e3914;
|
color: #4e3914;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
@ -149,42 +149,33 @@ app.auth = (function (app) {
|
|||||||
function setToken(token) {
|
function setToken(token) {
|
||||||
localStorage.setItem("APIToken", token);
|
localStorage.setItem("APIToken", token);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
function setUserId(userid) {
|
|
||||||
console.log("userid", userid);
|
|
||||||
localStorage.setItem("userid", userid);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUsername(username) {
|
|
||||||
localStorage.setItem("username", username);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function getToken() {
|
function getToken() {
|
||||||
return localStorage.getItem("APIToken");
|
return localStorage.getItem("APIToken");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isLoggedIn(callback) {
|
function isLoggedIn(callback) {
|
||||||
if (getToken()) {
|
if (getToken()) {
|
||||||
return app.api.get("user/me", function (error, data) {
|
return app.api.get("user/me", function (error, data) {
|
||||||
if (!error) app.auth.user = data;
|
if (!error) app.auth.user = data;
|
||||||
$.scope.getUsername.push(data.username);
|
//$.scope.getUsername.push(data);
|
||||||
return callback(error, data);
|
return callback(error, data);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
function logIn(args, callback) {
|
function showUser(){
|
||||||
app.api.post("auth/login", args, function (error, data) {
|
app.api.get("user/me", function (error, data) {
|
||||||
if (data.login) {
|
if (!error) app.auth.user = data;
|
||||||
setToken(data.token);
|
$.scope.getUsername.push(data);
|
||||||
}
|
|
||||||
callback(error, !!data.token);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function logOut(callback) {
|
function logOut(callback) {
|
||||||
//call logout route
|
//call logout route
|
||||||
@ -210,14 +201,14 @@ app.auth = (function (app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function forceLogin() {
|
function forceLogin() {
|
||||||
app.auth.isLoggedIn(function (error, isLoggedIn) {
|
app.auth.isLoggedIn(function (error, isLoggedIn) {
|
||||||
if (error || !isLoggedIn) {
|
if (error || !isLoggedIn) {
|
||||||
app.auth.logOut(function () {
|
app.auth.logOut(function () {
|
||||||
location.replace(`/login`);
|
location.replace(`/login`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function logInRedirect() {
|
function logInRedirect() {
|
||||||
window.location.href =
|
window.location.href =
|
||||||
@ -228,29 +219,28 @@ app.auth = (function (app) {
|
|||||||
function homeRedirect() {
|
function homeRedirect() {
|
||||||
window.location.href = location.href.replace(location.replace(`/`)) || "/";
|
window.location.href = location.href.replace(location.replace(`/`)) || "/";
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
//if isLoggedin is true, redirect user away from login / register page
|
|
||||||
function redirectIfLoggedIn() {
|
function redirectIfLoggedIn() {
|
||||||
$.holdReady(true);
|
if (getToken()){
|
||||||
app.auth.isLoggedIn(function (error, isLoggedIn) {
|
homeRedirect();
|
||||||
if (error || isLoggedIn) {
|
}
|
||||||
location.replace(`/`);
|
logInRedirect();
|
||||||
} else {
|
|
||||||
$.holdReady(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getToken: getToken,
|
getToken: getToken,
|
||||||
setToken: setToken,
|
setToken: setToken,
|
||||||
isLoggedIn: isLoggedIn,
|
isLoggedIn: isLoggedIn,
|
||||||
//logIn: logIn,
|
|
||||||
logOut: logOut,
|
logOut: logOut,
|
||||||
forceLogin,
|
forceLogin,
|
||||||
logInRedirect,
|
logInRedirect,
|
||||||
homeRedirect,
|
homeRedirect,
|
||||||
redirectIfLoggedIn,
|
showUser,
|
||||||
|
//redirectIfLoggedIn,
|
||||||
};
|
};
|
||||||
})(app);
|
})(app);
|
||||||
|
|
||||||
|
@ -283,9 +283,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$( document ).ready( function(){
|
$( document ).ready( function(){
|
||||||
console.log('jq-repeat', $.scope)
|
|
||||||
//$.jqrepeat = $.scope
|
|
||||||
|
|
||||||
$( '[jq-repeat]' ).each(function(key, value){
|
$( '[jq-repeat]' ).each(function(key, value){
|
||||||
make(value);
|
make(value);
|
||||||
});
|
});
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -6,7 +6,6 @@ const router = express.Router();
|
|||||||
// /user/register
|
// /user/register
|
||||||
router.post("/register", async (req, res, next) => {
|
router.post("/register", async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
console.log(req.body);
|
|
||||||
let Res = await addUser(req.body);
|
let Res = await addUser(req.body);
|
||||||
if (Res == false) {
|
if (Res == false) {
|
||||||
let error = new Error("UserRegFailed");
|
let error = new Error("UserRegFailed");
|
||||||
@ -36,7 +35,6 @@ router.post("/login", async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
//pass res back to form to be set in local storage
|
//pass res back to form to be set in local storage
|
||||||
console.log("my res" , Res);
|
|
||||||
return res.json({
|
return res.json({
|
||||||
message: "User login successfully",
|
message: "User login successfully",
|
||||||
token: Res.token,
|
token: Res.token,
|
||||||
@ -51,6 +49,12 @@ router.post("/login", async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//contact
|
||||||
|
//auth/contact
|
||||||
|
|
||||||
|
router.post("/contact", async (req, res, next) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
@ -68,8 +68,8 @@ router.get("/profile", function (req, res, next) {
|
|||||||
|
|
||||||
|
|
||||||
//forgot password page
|
//forgot password page
|
||||||
router.get("/forgotPassword", function (req, res, next) {
|
router.get("/forgotpassword", function (req, res, next) {
|
||||||
res.render("forgotPassword");
|
res.render("forgotpassword");
|
||||||
});
|
});
|
||||||
|
|
||||||
//resetted password page
|
//resetted password page
|
||||||
|
@ -7,8 +7,6 @@ const router = express.Router();
|
|||||||
//getbyid
|
//getbyid
|
||||||
router.get("/me", async function (req, res, next) {
|
router.get("/me", async function (req, res, next) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
//console.log(req.user);
|
|
||||||
let user = await getUserID(req.user);
|
let user = await getUserID(req.user);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
let error = new Error("User not found");
|
let error = new Error("User not found");
|
||||||
@ -24,6 +22,7 @@ router.get("/me", async function (req, res, next) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//logout
|
//logout
|
||||||
router.delete('/logout', async function(req, res, next){
|
router.delete('/logout', async function(req, res, next){
|
||||||
try{
|
try{
|
||||||
|
@ -8,154 +8,361 @@
|
|||||||
<%- include('top') %>
|
<%- include('top') %>
|
||||||
<link rel="stylesheet" href="css/api.css" media="all">
|
<link rel="stylesheet" href="css/api.css" media="all">
|
||||||
|
|
||||||
<body class="one-content-column-version">
|
<body class="one-content-column-version">
|
||||||
<div class="left-menu">
|
<div class="left-menu">
|
||||||
<div class="content-logo">
|
<div class="content-logo">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img alt="platform by Emily van den Heever from the Noun Project" title="platform by Emily van den Heever from the Noun Project" src="images/apilogo.png" height="32" />
|
<img alt="platform by Emily van den Heever from the Noun Project"
|
||||||
<span>API Documentation</span>
|
title="platform by Emily van den Heever from the Noun Project" src="images/apilogo.png"
|
||||||
</div>
|
height="32" />
|
||||||
<button class="burger-menu-icon" id="button-menu-mobile">
|
<span>API Documentation</span>
|
||||||
<svg width="34" height="34" viewBox="0 0 100 100"><path class="line line1" d="M 20,29.000046 H 80.000231 C 80.000231,29.000046 94.498839,28.817352 94.532987,66.711331 94.543142,77.980673 90.966081,81.670246 85.259173,81.668997 79.552261,81.667751 75.000211,74.999942 75.000211,74.999942 L 25.000021,25.000058"></path><path class="line line2" d="M 20,50 H 80"></path><path class="line line3" d="M 20,70.999954 H 80.000231 C 80.000231,70.999954 94.498839,71.182648 94.532987,33.288669 94.543142,22.019327 90.966081,18.329754 85.259173,18.331003 79.552261,18.332249 75.000211,25.000058 75.000211,25.000058 L 25.000021,74.999942"></path></svg>
|
</div>
|
||||||
</button>
|
<button class="burger-menu-icon" id="button-menu-mobile">
|
||||||
</div>
|
<svg width="34" height="34" viewBox="0 0 100 100">
|
||||||
<div class="mobile-menu-closer"></div>
|
<path class="line line1"
|
||||||
<div class="content-menu">
|
d="M 20,29.000046 H 80.000231 C 80.000231,29.000046 94.498839,28.817352 94.532987,66.711331 94.543142,77.980673 90.966081,81.670246 85.259173,81.668997 79.552261,81.667751 75.000211,74.999942 75.000211,74.999942 L 25.000021,25.000058">
|
||||||
<div class="content-infos">
|
</path>
|
||||||
<div class="info"><b>Version:</b> 1.0.5</div>
|
<path class="line line2" d="M 20,50 H 80"></path>
|
||||||
<div class="info"><b>Last Updated:</b> 15th Sep, 2021</div>
|
<path class="line line3"
|
||||||
</div>
|
d="M 20,70.999954 H 80.000231 C 80.000231,70.999954 94.498839,71.182648 94.532987,33.288669 94.543142,22.019327 90.966081,18.329754 85.259173,18.331003 79.552261,18.332249 75.000211,25.000058 75.000211,25.000058 L 25.000021,74.999942">
|
||||||
<ul>
|
</path>
|
||||||
<li class="scroll-to-link active" data-target="content-get-started">
|
</svg>
|
||||||
<a>GET STARTED</a>
|
</button>
|
||||||
</li>
|
</div>
|
||||||
<li class="scroll-to-link" data-target="content-get-characters">
|
<div class="mobile-menu-closer"></div>
|
||||||
<a>Get Characters</a>
|
<div class="content-menu">
|
||||||
</li>
|
<div class="content-infos">
|
||||||
<li class="scroll-to-link" data-target="content-errors">
|
<div class="info"><b>Version:</b> 0</div>
|
||||||
<a>Errors</a>
|
<div class="info"><b>Last Updated:</b> 22th January 2024</div>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
<ul>
|
||||||
</div>
|
<li class="scroll-to-link active" data-target="content-get-started">
|
||||||
</div>
|
<a>GET STARTED</a>
|
||||||
|
</li>
|
||||||
|
<li class="scroll-to-link" data-target="content-get-characters">
|
||||||
<div class="content-page">
|
<a>Get Data From API</a>
|
||||||
<div class="content">
|
</li>
|
||||||
<div class="overflow-hidden content-section" id="content-get-started">
|
<li class="scroll-to-link" data-target="content-errors">
|
||||||
<h1>Get started</h1>
|
<a>Errors</a>
|
||||||
<p>
|
</li>
|
||||||
The Westeros API provides programmatic access to read Game of Thrones data. Retrieve a character, provide an oauth connexion, retrieve a familly, filter them, etc.
|
</ul>
|
||||||
</p>
|
</div>
|
||||||
<p>
|
</div>
|
||||||
To use this API, you need an <strong>API key</strong>. Please contact us at <a href="mailto:jon.snow@nightswatch.wes">jon.snow@nightswatch.wes</a> to get your own API key.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="overflow-hidden content-section" id="content-get-characters">
|
|
||||||
<h2>get characters</h2>
|
|
||||||
<p>
|
|
||||||
To get characters you need to make a POST call to the following url :<br>
|
|
||||||
<code class="higlighted break-word">http://api.westeros.com/character/get</code>
|
|
||||||
</p>
|
|
||||||
<br>
|
|
||||||
<h4>QUERY PARAMETERS</h4>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Field</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Description</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>secret_key</td>
|
|
||||||
<td>String</td>
|
|
||||||
<td>Your API key.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>search</td>
|
|
||||||
<td>String</td>
|
|
||||||
<td>(optional) A search word to find character by name.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>house</td>
|
|
||||||
<td>String</td>
|
|
||||||
<td>
|
|
||||||
(optional) a string array of houses:
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>alive</td>
|
|
||||||
<td>Boolean</td>
|
|
||||||
<td>
|
|
||||||
(optional) a boolean to filter alived characters
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>gender</td>
|
|
||||||
<td>String</td>
|
|
||||||
<td>
|
|
||||||
(optional) a string to filter character by gender:<br> m: male<br> f: female
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>offset</td>
|
|
||||||
<td>Integer</td>
|
|
||||||
<td>(optional - default: 0) A cursor for use in pagination. Pagination starts offset the specified offset.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>limit</td>
|
|
||||||
<td>Integer</td>
|
|
||||||
<td>(optional - default: 10) A limit on the number of objects to be returned, between 1 and 100.</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="overflow-hidden content-section" id="content-errors">
|
|
||||||
<h2>Errors</h2>
|
|
||||||
<p>
|
|
||||||
The Westeros API uses the following error codes:
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Error Code</th>
|
|
||||||
<th>Meaning</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>X000</td>
|
|
||||||
<td>
|
|
||||||
Some parameters are missing. This error appears when you don't pass every mandatory parameters.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>X001</td>
|
|
||||||
<td>
|
|
||||||
Unknown or unvalid <code class="higlighted">secret_key</code>. This error appears if you use an unknow API key or if your API key expired.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>X002</td>
|
|
||||||
<td>
|
|
||||||
Unvalid <code class="higlighted">secret_key</code> for this domain. This error appears if you use an API key non specified for your domain. Developper or Universal API keys doesn't have domain checker.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>X003</td>
|
|
||||||
<td>
|
|
||||||
Unknown or unvalid user <code class="higlighted">token</code>. This error appears if you use an unknow user <code class="higlighted">token</code> or if the user <code class="higlighted">token</code> expired.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
<div class="content-page">
|
||||||
|
<div class="content">
|
||||||
|
<div class="overflow-hidden content-section" id="content-get-started">
|
||||||
|
<h1>Get started</h1>
|
||||||
|
<p>
|
||||||
|
The following API is provided by the Eco saver developer team. It allows you to get Location and
|
||||||
|
Sensor and Sensor Data from the Eco saver database.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To use this API, you need an <strong>API key</strong>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-hidden content-section" id="content-get-location">
|
||||||
|
<h2>Get all location</h2>
|
||||||
|
<p>
|
||||||
|
To get Location of sensors you need to make a GET call to the following url :<br>
|
||||||
|
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Return Response :<br>
|
||||||
|
<code class="higlighted break-word">{"status":"200"}</code>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h4>QUERY PARAMETERS</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Field</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Authorization</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Your API key.</td>
|
||||||
|
<td>(Required) Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide your
|
||||||
|
API key here}"</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-hidden content-section" id="content-get-location-by-id">
|
||||||
|
<h2>Get location by ID</h2>
|
||||||
|
<p>
|
||||||
|
To get Location you need to make a GET call to the following url :<br>
|
||||||
|
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/{id}</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Return Response :<br>
|
||||||
|
<code class="higlighted break-word">{"status":"200"}</code>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h4>QUERY PARAMETERS</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Field</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Authorization</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>(Required) Your API key.</td>
|
||||||
|
<td>Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide your
|
||||||
|
API key here}"</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-hidden content-section" id="content-add-location">
|
||||||
|
<h2>Add Location (Only for system or admin API key)</h2>
|
||||||
|
<p>
|
||||||
|
To add an Location you need to make a POST call to the following url :<br>
|
||||||
|
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/new</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Example :<br>
|
||||||
|
<code class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "SAMPLE", "added_by": "system" , "description": "test"}'</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Return Response :<br>
|
||||||
|
<code class="higlighted break-word">{"status":"200"}</code>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h4>QUERY PARAMETERS</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Field</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Authorization</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Your API key.</td>
|
||||||
|
<td>(Required) Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: {provide your
|
||||||
|
API key here}"</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Location name</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Location name.</td>
|
||||||
|
<td>(Required) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide
|
||||||
|
your API key here" -d '{"name":"Location name"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Added by </td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>System or Admin</td>
|
||||||
|
<td>(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide
|
||||||
|
your API key here" -d '{"added_by":"system"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Description</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Description of Location</td>
|
||||||
|
<td>(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide
|
||||||
|
your API key here" -d '{"description":"test"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- update -->
|
||||||
|
<div class="overflow-hidden content-section" id="content-update-location-by-id">
|
||||||
|
<h2>Update Location (Only for system or admin API key)</h2>
|
||||||
|
<p>
|
||||||
|
To update an Location you need to make a PUT call to the following url :<br>
|
||||||
|
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/update</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Example :<br>
|
||||||
|
<code class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/update -H "Content-Type: application/json" -X POST -d '{"id": "7" , "name": "SAMPLE", "added_by": "system" , "description": "test"}'</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Return Response :<br>
|
||||||
|
<code class="higlighted break-word">{"status":"200","message":"Location 7 updated"}</code>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h4>QUERY PARAMETERS</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Field</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Authorization</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Your API key.</td>
|
||||||
|
<td>(Required) example: curl https://api.teeseng.uk/api/v0/location/update -H "Authorization: {provide your
|
||||||
|
API key here}"</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ID</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Location ID</td>
|
||||||
|
<td>(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/update -H "Authorization: provide
|
||||||
|
your API key here" -d '{"id": "7"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Location name</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Location name.</td>
|
||||||
|
<td>(Optional) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide
|
||||||
|
your API key here" -d '{"name":"Location name"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Added by </td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>System or Admin</td>
|
||||||
|
<td>(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide
|
||||||
|
your API key here" -d '{"added_by":"system"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Description</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Description of Location</td>
|
||||||
|
<td>(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide
|
||||||
|
your API key here" -d '{"description":"test"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- delete location -->
|
||||||
|
<div class="overflow-hidden content-section" id="content-update-location-by-id">
|
||||||
|
<h2>Delete Location (Only for system or admin API key)</h2>
|
||||||
|
<p>
|
||||||
|
To delete an Location you need to make a DELETE call to the following url :<br>
|
||||||
|
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/delete</code>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
Example :<br>
|
||||||
|
<code class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/delete -H "Content-Type: application/json" -X POST -d '{"id": "7"}'</code>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h4>QUERY PARAMETERS</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Field</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Authorization</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Your API key.</td>
|
||||||
|
<td>(Required) example: curl https://api.teeseng.uk/api/v0/location/delete -H "Authorization: {provide your
|
||||||
|
API key here}"</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ID</td>
|
||||||
|
<td>JSON</td>
|
||||||
|
<td>Location ID</td>
|
||||||
|
<td>(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/delete -H "Authorization: provide
|
||||||
|
your API key here" -d '{"id": "7"}'</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="overflow-hidden content-section" id="content-errors">
|
||||||
|
<h2>Errors</h2>
|
||||||
|
<p>
|
||||||
|
The Westeros API uses the following error codes:
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Error Code</th>
|
||||||
|
<th>Meaning</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>X000</td>
|
||||||
|
<td>
|
||||||
|
Some parameters are missing. This error appears when you don't pass every mandatory
|
||||||
|
parameters.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>403</td>
|
||||||
|
<td>
|
||||||
|
Unknown or unvalid <code class="higlighted">secret_key</code>. This error appears if
|
||||||
|
you use an unknow API key or if your API key expired.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>500</td>
|
||||||
|
<td>
|
||||||
|
Unvalid <code class="higlighted">secret_key</code> No API key was supplied. Invalid
|
||||||
|
request.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>X003</td>
|
||||||
|
<td>
|
||||||
|
Unknown or unvalid user <code class="higlighted">token</code>. This error appears if
|
||||||
|
you use an unknow user <code class="higlighted">token</code> or if the user <code
|
||||||
|
class="higlighted">token</code> expired.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -82,9 +82,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Bootstrap core JavaScript -->
|
|
||||||
<script src="vendor/jquery/jquery.min.js"></script>
|
|
||||||
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="js/learnmore.js"></script>
|
<script src="js/learnmore.js"></script>
|
||||||
<script src="js/search.js"></script>
|
<script src="js/search.js"></script>
|
||||||
<script src="js/api.js"></script>
|
<script src="js/api.js"></script>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<input type="text" id="email" placeholder="Email" required />
|
<input type="text" id="email" placeholder="Email" required />
|
||||||
<input type="password" id="password" placeholder="Password" required />
|
<input type="password" id="password" placeholder="Password" required />
|
||||||
<input type="password" id="confirmPassword" placeholder="Confirm Password" required />
|
<input type="password" id="confirmPassword" placeholder="Confirm Password" required />
|
||||||
<input type="submit" onclick="validateReset()" value="Reset Password" />
|
<input type="submit" value="Reset Password" />
|
||||||
</form>
|
</form>
|
||||||
<br>
|
<br>
|
||||||
<a>Dont have an account?</a> <a href="/login">Sign Up</a>
|
<a>Dont have an account?</a> <a href="/login">Sign Up</a>
|
||||||
|
@ -91,7 +91,6 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<title>EcoSaver - Your Air Quality Index Source</title>
|
<title>EcoSaver - Your Air Quality Index Source</title>
|
||||||
<link rel="stylesheet" href="css/style.css">
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<!-- jquery app.js -->
|
<!-- jquery app.js -->
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
|
|
||||||
@ -83,7 +82,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<!-- profile button -->
|
<!-- profile button -->
|
||||||
<div class="form-inline mt-2 mt-md-0">
|
<div class="form-inline mt-2 mt-md-0">
|
||||||
<a id="cl-profile-button" class="btn btn-outline-danger my-2 my-sm-0" href="/profile"
|
<a id="cl-profile-button" class="btn btn-outline-info my-2 my-sm-0" href="/profile"
|
||||||
style="display: none;">
|
style="display: none;">
|
||||||
<i class="fas fa-sign-out"></i>
|
<i class="fas fa-sign-out"></i>
|
||||||
Profile
|
Profile
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<%- include('logintop') %>
|
<%- include('logintop') %>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
app.auth.redirectIfLoggedIn();
|
// Require login to see this page.
|
||||||
|
//app.auth.redirectIfLoggedIn();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -11,44 +12,61 @@
|
|||||||
<!-- localhost/api/v0/user/register -->
|
<!-- localhost/api/v0/user/register -->
|
||||||
<!-- evalAjax Fires when status 200 is returned -->
|
<!-- evalAjax Fires when status 200 is returned -->
|
||||||
<form action="auth/register" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();">
|
<form action="auth/register" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();">
|
||||||
<input type="text" name="firstname" placeholder="First Name" required />
|
<input type="text" name="firstname" placeholder="First Name" required pattern="^[a-zA-Z\s]+$" />
|
||||||
<input type="text" name="lastname" placeholder="Last Name" required />
|
<input type="text" name="lastname" placeholder="Last Name" required pattern="^[a-zA-Z\s]+$" />
|
||||||
<input type="text" name="username" placeholder="Username" required />
|
<input type="text" name="username" placeholder="Username" required pattern="^\w+$" />
|
||||||
<input type="text" name="email" placeholder="Email" required />
|
<input type="email" name="email" placeholder="Email" required
|
||||||
<input type="text" name="address" placeholder="Address" required />
|
pattern="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" />
|
||||||
<input type="text" name="phone" placeholder="Phone Number" required />
|
<input type="text" name="address" placeholder="Address" required
|
||||||
<input type="password" name="password" placeholder="Password" required />
|
pattern="^(\d{1,3}.)?.+\s(\d{6})$" />
|
||||||
<input type="password" name="confirmPassword" placeholder="Confirm Password" required />
|
<input type="text" name="phone" placeholder="Phone Number" required
|
||||||
|
pattern="^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,8}$" />
|
||||||
|
<input type="password" id="password" name="password" placeholder="Password" required />
|
||||||
|
<input type="password" id="confirmPassword" name="confirmPassword" placeholder="Confirm Password"
|
||||||
|
required />
|
||||||
<input type="submit" value="Signup" />
|
<input type="submit" value="Signup" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form login iot-card">
|
<div class="form login iot-card">
|
||||||
<header>Login</header>
|
<header>Login</header>
|
||||||
<div class="card-header shadow actionMessage" style="display:none"></div>
|
<div class="card-header shadow actionMessage" style="display:none"></div>
|
||||||
<!-- evalAjax Fires when status 200 is returned -->
|
<!-- evalAjax Fires when status 200 is returned -->
|
||||||
<form action="auth/login" onsubmit="formAJAX(this)"
|
<form action="auth/login" onsubmit="formAJAX(this)" evalAJAX="app.auth.homeRedirect();
|
||||||
evalAJAX="app.auth.homeRedirect();
|
app.auth.setToken(data.token);">
|
||||||
app.auth.setToken(data.token);
|
<input type="text" name="username" placeholder="Email address | Username" required
|
||||||
app.auth.setUserId(data.userid);
|
pattern="^\w+$" />
|
||||||
app.auth.setUsername(data.username);
|
|
||||||
">
|
|
||||||
|
|
||||||
<input type="text" name="userInfo" placeholder="Email address | Username" required />
|
|
||||||
<input type="password" name="password" placeholder="Password" required />
|
|
||||||
<a href="/forgotPassword">Forgot password?</a>
|
|
||||||
<input type="submit" value="Login" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="text" name="userInfo" placeholder="Email address | Username" required />
|
|
||||||
<input type="password" name="password" placeholder="Password" required />
|
<input type="password" name="password" placeholder="Password" required />
|
||||||
<a href="/resetPassword">Forgot password?</a>
|
<a href="/forgotpassword">Forgot password?</a>
|
||||||
<input type="submit" value="Login" />
|
<input type="submit" value="Login" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
//both password fields must match
|
||||||
|
var password = document.getElementById("password");
|
||||||
|
var confirm_password = document.getElementById("confirmPassword");
|
||||||
|
|
||||||
|
function validatePassword() {
|
||||||
|
var passwordValue = password.value;
|
||||||
|
|
||||||
|
// Strong password regex pattern
|
||||||
|
var strongPasswordPattern = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
|
||||||
|
|
||||||
|
if (passwordValue != confirm_password.value) {
|
||||||
|
confirm_password.setCustomValidity("Passwords Don't Match");
|
||||||
|
} else if (!strongPasswordPattern.test(passwordValue)) {
|
||||||
|
confirm_password.setCustomValidity("Password must be at least 8 characters long and include at least one letter, one number, and one special character.");
|
||||||
|
} else {
|
||||||
|
confirm_password.setCustomValidity('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
password.onchange = validatePassword;
|
||||||
|
confirm_password.onkeyup = validatePassword;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const wrapper = document.querySelector(".wrapper"),
|
const wrapper = document.querySelector(".wrapper"),
|
||||||
signupHeader = document.querySelector(".signup header"),
|
signupHeader = document.querySelector(".signup header"),
|
||||||
loginHeader = document.querySelector(".login header");
|
loginHeader = document.querySelector(".login header");
|
||||||
|
@ -33,10 +33,8 @@
|
|||||||
<!-- Mustache JS -->
|
<!-- Mustache JS -->
|
||||||
<script src="https://sso.theta42.com/static/js/mustache.min.js"></script>
|
<script src="https://sso.theta42.com/static/js/mustache.min.js"></script>
|
||||||
<!-- jQuery library -->
|
<!-- jQuery library -->
|
||||||
<script
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||||
src="https://code.jquery.com/jquery-3.7.1.min.js"
|
|
||||||
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<!-- Bootstrap 5 JavaScript -->
|
<!-- Bootstrap 5 JavaScript -->
|
||||||
<script
|
<script
|
||||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||||
@ -63,6 +61,7 @@
|
|||||||
//check if user is logged in
|
//check if user is logged in
|
||||||
app.auth.isLoggedIn(function (error, data) {
|
app.auth.isLoggedIn(function (error, data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
app.auth.showUser();
|
||||||
$("#cl-logout-button").show("fast");
|
$("#cl-logout-button").show("fast");
|
||||||
$("#cl-profile-button").show("fast");
|
$("#cl-profile-button").show("fast");
|
||||||
$("#cl-login-button").hide("fast");
|
$("#cl-login-button").hide("fast");
|
||||||
@ -71,7 +70,8 @@
|
|||||||
}
|
}
|
||||||
$("body").show("fast");
|
$("body").show("fast");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -94,7 +94,7 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||||
<ul class="navbar-nav ms-auto">
|
<ul class="navbar-nav ms-auto">
|
||||||
<li jq-repeat="getUsername" class="nav-item">
|
<li jq-repeat="getUsername" class="nav-item">
|
||||||
{{ username }}
|
<a class="nav-link"> Welcome {{ user.username }} </a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/">Home</a>
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
3394
package-lock.json
generated
3394
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -35,14 +35,13 @@
|
|||||||
"mqtt": "^5.3.3",
|
"mqtt": "^5.3.3",
|
||||||
"mysql2": "^3.7.1",
|
"mysql2": "^3.7.1",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"nodemailer": "^6.9.7",
|
"nodemailer": "^6.9.8",
|
||||||
"otp-generator": "^4.0.1",
|
"otp-generator": "^4.0.1",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"sanitize-html": "^2.11.0",
|
"sanitize-html": "^2.11.0",
|
||||||
"sequelize": "^6.35.2",
|
"sequelize": "^6.35.2",
|
||||||
"sequelize-cli": "^6.6.2",
|
"sequelize-cli": "^6.6.2",
|
||||||
"sql": "^0.78.0",
|
|
||||||
"validator": "^13.11.0"
|
"validator": "^13.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
17
webserver/functions/getAPIKey.js
Normal file
17
webserver/functions/getAPIKey.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//model for getting API key from database
|
||||||
|
|
||||||
|
async function getAPIKey() {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { getAPIKey }
|
@ -1,39 +1,45 @@
|
|||||||
const { checkAPikey } = require('../functions/database.js');
|
const { checkAPikey } = require("../functions/database.js");
|
||||||
async function apikeyCheck(req, res, next) {
|
async function apikeyCheck(req, res, next) {
|
||||||
//const authHeader = req.headers.authorization
|
//const authHeader = req.headers.authorization
|
||||||
try{
|
try {
|
||||||
let apikey = req.headers.authorization
|
let apikey = req.headers.authorization;
|
||||||
if(!apikey){
|
if (!apikey) {
|
||||||
throw new Error('No API key was supplied. Invalid request')
|
res.status(401).json({
|
||||||
}
|
message: "No API key was supplied. Invalid request",
|
||||||
else{
|
});
|
||||||
//split the string by the -
|
//throw new Error("No API key was supplied. Invalid request");
|
||||||
let splitAPIkey = apikey.split('-');
|
} else {
|
||||||
let rowid = splitAPIkey[0];
|
//split the string by the -
|
||||||
|
let splitAPIkey = apikey.split("-");
|
||||||
//rejoin withouth the rowid
|
let rowid = splitAPIkey[0];
|
||||||
let SuppliedKey = splitAPIkey.slice(1).join('-');
|
|
||||||
if (checkAPikey(SuppliedKey , rowid))
|
|
||||||
{
|
|
||||||
//get permission
|
|
||||||
let permission = await checkAPikey(SuppliedKey , rowid);
|
|
||||||
console.log(permission);
|
|
||||||
if (req.method === 'GET' && permission === 'canRead'){
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
//['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)
|
|
||||||
if (["GET" , "POST" , "PUT" , "DELETE"].includes(req.method) && permission === 'canWrite'){
|
|
||||||
console.log('write')
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
throw new Error('Your API key does not have the correct permissions to access this resource')
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(error){
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//rejoin withouth the rowid
|
||||||
|
let SuppliedKey = splitAPIkey.slice(1).join("-");
|
||||||
|
if (checkAPikey(SuppliedKey, rowid)) {
|
||||||
|
//get permission
|
||||||
|
let permission = await checkAPikey(SuppliedKey, rowid);
|
||||||
|
console.log(permission);
|
||||||
|
if (req.method === "GET" && permission === "canRead") {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
//['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)
|
||||||
|
if (
|
||||||
|
["GET", "POST", "PUT", "DELETE"].includes(req.method) &&
|
||||||
|
permission === "canWrite"
|
||||||
|
) {
|
||||||
|
console.log("write");
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
//throw status 403
|
||||||
|
res.status(403).json({
|
||||||
|
message:
|
||||||
|
"Your API key does not have the correct permissions to access this resource",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { apikeyCheck };
|
module.exports = { apikeyCheck };
|
||||||
@ -56,4 +62,4 @@ If it's correct API key and has canWrite perms
|
|||||||
I allow it to access put and post
|
I allow it to access put and post
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user