Merge branch 'main' into Dev-branch

This commit is contained in:
newtbot 2024-01-19 03:23:13 +08:00
commit 8dd917e541
72 changed files with 3135 additions and 3223 deletions

View File

@ -4,3 +4,12 @@ How to get started with this project?
Security in GIT repo. Security in GIT repo.
i repeat DO NOT USE CREDS IN CODE! Please use .env files (https://www.npmjs.com/package/dotenv) i repeat DO NOT USE CREDS IN CODE! Please use .env files (https://www.npmjs.com/package/dotenv)
## Workload
1) Ti Seng
* Webserver Microservices
* IoT sensor
* All Database / Backend Functions
* consumer website api and user function
2) Sean
* Admin Website Microservice

View File

@ -1,35 +1,50 @@
// models/User.js
const { Sequelize, DataTypes } = require('sequelize'); const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize(process.env.database, process.env.user, process.env.password, { const bcrypt = require('bcrypt');
host: process.env.host,
dialect: 'mysql',
timezone: 'Z', // Set the timezone to UTC
});
const User = sequelize.define('User', { module.exports = (sequelize) => {
name: { const User = sequelize.define('users', {
type: DataTypes.STRING, id: {
allowNull: false, type: DataTypes.INTEGER,
}, primaryKey: true,
username: { autoIncrement: true,
type: DataTypes.STRING, },
allowNull: false, name: {
unique: true, type: DataTypes.STRING,
}, allowNull: false,
email: { },
type: DataTypes.STRING, username: {
allowNull: false, type: DataTypes.STRING,
unique: true, allowNull: false,
}, unique: true,
password: { },
type: DataTypes.STRING, email: {
allowNull: false, type: DataTypes.STRING,
}, allowNull: false,
jobTitle: { unique: true,
type: DataTypes.STRING, },
allowNull: false, 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;
};

30
Sean/models/userLogs.js Normal file
View File

@ -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;
};

View File

@ -2,45 +2,47 @@ const mysql = require("mysql2");
const path = require("path"); const path = require("path");
require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const fs = require('fs'); const fs = require('fs');
/* const UserModel = require('../models/User');// Adjust the path based on your project structure
const mysqlConfig = { const { Sequelize } = require('sequelize');
host: process.env.host,
user: process.env.user,
password: process.env.password,
database: process.env.database,
timezone: "Z", // Set the timezone to UTC
};
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({ const sequelize = new Sequelize(
host: process.env.host, "adminusers",
user: process.env.DB_USER, process.env.DB_USER,
password: process.env.DB_PASS, process.env.DB_PASS,
database: "database", {
ssl: { host: "mpsqldatabasean.mysql.database.azure.com",
ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')), dialect: 'mysql',
} // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy';
}); attributeBehavior: 'escape',
*/ dialectOptions: {
const connection = mysql.createConnection({ ssl: {
host: process.env.host, ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')),
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,
};

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title> <title>Home</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style> <style>
.chart-container {
width: 400px;
height: 250px;
margin: 20px;
border: 1px solid #ddd;
display: inline-block;
}
body { body {
margin: 0; margin: 0;
font-family: 'Arial', sans-serif; font-family: 'Arial', sans-serif;
@ -45,64 +53,23 @@
text-align: center; text-align: center;
} }
table {
border-collapse: collapse;
width: 80%;
margin: 20px auto;
font-size: 16px;
}
th, td {
border: 1px solid #dddddd;
text-align: left;
padding: 12px;
}
th {
background-color: #f2f2f2;
}
tr:hover {
background-color: #f5f5f5;
}
td {
white-space: nowrap;
}
</style> </style>
</head> </head>
<body> <body>
<div id="navbar"> <div id="navbar">
<h1>Eco Saver</h1> <h1>Eco Saver</h1>
<a href="/inusers">In-House Users</a> <a href="/inusers">In-House Users</a>
<a href="#">Users</a> <a href="#">Users</a>
<a href="#">Data Analysis</a> <a href="#">Sensors</a>
<a href="#">Logout</a> <a href="/logout">Logout</a>
</div> </div>
<div id="content"> <div id="content">
<h2>Welcome to the Home Page, <%= username %>!</h2> <h2>Welcome to the Home Page, <%= username %>!</h2>
<h3>Last 10 Logins:</h3>
<table>
<thead>
<tr>
<th>Username</th>
<th>Last Login Time</th>
</tr>
</thead>
<tbody>
<% loginLogs.forEach(log => { %>
<tr>
<td><%= log.username %></td>
<td><%= new Date(log.lastLogin).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }) %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</body> </body>
</html> </html>

107
Sean/views/home.js Normal file
View File

@ -0,0 +1,107 @@
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;
}, {});
}
});

View File

@ -10,6 +10,7 @@
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/css/bootstrap.min.css">
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="/user-creation.css"> <link rel="stylesheet" href="/user-creation.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
@ -40,7 +41,6 @@
<th>Name</th> <th>Name</th>
<th>Username</th> <th>Username</th>
<th>Email</th> <th>Email</th>
<th>Last Login</th>
<th>Job Title</th> <th>Job Title</th>
</tr> </tr>
</thead> </thead>
@ -51,7 +51,6 @@
<td><%- user.name %></td> <td><%- user.name %></td>
<td><%- user.username %></td> <td><%- user.username %></td>
<td><%- user.email %></td> <td><%- user.email %></td>
<td><%- new Date(user.lastLogin).toLocaleString('en-US', { timeZone: 'Asia/Singapore' }) %></td>
<td><%- user.jobTitle %></td> <td><%- user.jobTitle %></td>
</tr> </tr>
<% }); %> <% }); %>
@ -92,10 +91,11 @@
<span class="details">Job Title</span> <span class="details">Job Title</span>
<select name="jobTitle" id="jobTitle"> <select name="jobTitle" id="jobTitle">
<option value="admin">Admin</option> <option value="admin">Admin</option>
<option value="dataAnalyst">Data Analyst</option> <option value="user">User</option>
</select> </select>
</div> </div>
</div> </div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button"> <div class="button">
<input type="submit" value="Register"> <input type="submit" value="Register">
</div> </div>
@ -121,6 +121,7 @@
<input type="password" name="confirmPassword" id="resetConfirmPassword" placeholder="Confirm new password" required> <input type="password" name="confirmPassword" id="resetConfirmPassword" placeholder="Confirm new password" required>
</div> </div>
</div> </div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button"> <div class="button">
<input type="submit" value="Reset Password"> <input type="submit" value="Reset Password">
</div> </div>
@ -128,6 +129,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div id="deleteUserContainer" style="display: none;"> <div id="deleteUserContainer" style="display: none;">
<h3>Delete User</h3> <h3>Delete User</h3>
<div class="search-container"> <div class="search-container">
@ -136,15 +138,21 @@
</div> </div>
<div id="searchResultsContainer" style="display: none;"> <div id="searchResultsContainer" style="display: none;">
<h4>Search Results</h4> <h4>Search Results</h4>
<ul id="searchResultsList"></ul> <ul id="searchResultsList">
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
</ul>
</div> </div>
</div> </div>
<div id="logsContainer" style="display: none;"> <div id="logsContainer" style="display: none;">
<!-- Content for logs will be added here --> <!-- Content for logs will be added here -->
</div> </div>
<script> <script>
const allUsers = <%- JSON.stringify(allUsers) %>; const allUsers = <%- JSON.stringify(allUsers) %>;
const currentUsername = '<%= currentUsername %>';
</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"></script>
@ -153,6 +161,7 @@
<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"></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"></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"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js"></script>
<script src="inusers.js"></script> <script src="inusers.js"></script>
</div> </div>

View File

@ -1,6 +1,3 @@
$(document).ready(function () { $(document).ready(function () {
$('#resetPasswordLink').on('click', function () { $('#resetPasswordLink').on('click', function () {
$('#resetPasswordFormContainer').show(); $('#resetPasswordFormContainer').show();
@ -30,9 +27,10 @@ $(document).ready(function () {
}); });
$('#searchUserButton').on('click', function () { $('#searchUserButton').on('click', function () {
console.log('Search button clicked');
const searchUsername = $('#searchUserInput').val(); const searchUsername = $('#searchUserInput').val();
// Call the function to search for the user // Call the function to search for the user
searchUser(searchUsername); searchUser(searchUsername);
}); });
@ -62,81 +60,7 @@ $('#logsLink').on('click', function () {
fetchLogs(); fetchLogs();
}); });
function fetchLogs() {
// Make a fetch request to your server endpoint for logs
fetch('/api/getLogs')
.then(response => response.json())
.then(logs => {
// Process and display logs in the logs container
displayLogs(logs);
})
.catch(error => {
console.error('Error fetching logs:', error);
// Handle errors, e.g., display an alert
});
}
// Update the displayLogs function to generate a table
function displayLogs(logs) {
const logsContainer = $('#logsContainer');
// Clear previous logs
logsContainer.empty();
if (logs && logs.length > 0) {
// Create the table and header row
const table = $('<table>').addClass('logs-table');
const headerRow = '<tr><th>ID</th><th>Username</th><th>Activity</th><th>Timestamp</th></tr>';
table.append(headerRow);
// Add each log as a row in the table
logs.forEach(log => {
const row = `<tr><td>${log.id}</td><td>${log.username}</td><td>${log.activity}</td><td>${log.timestamp}</td></tr>`;
table.append(row);
});
// Append the table to the logsContainer
logsContainer.append(table);
// Add a download button at the top with the current date and time in the file name
const currentDate = new Date();
const formattedDate = currentDate.toISOString().split('T')[0];
const formattedTime = currentDate.toTimeString().split(' ')[0].replace(/:/g, '-');
const downloadButton = $('<button>').text('Download Log').on('click', function () {
downloadLogs(logs, `log_${formattedDate}_${formattedTime}.csv`);
});
// Prepend the download button to the logsContainer
logsContainer.prepend(downloadButton);
} else {
// Display a message if no logs are available
logsContainer.html('<p>No logs available.</p>');
}
}
function downloadLogs(logs, filename) {
if (logs && logs.length > 0) {
const csvContent = 'data:text/csv;charset=utf-8,';
const header = 'ID,Username,Activity,Timestamp\n';
const rows = logs.map(log => `${log.id},${log.username},${log.activity},"${log.timestamp}"`).join('\n');
const data = header + rows;
const encodedData = encodeURI(csvContent + data);
// Create a hidden anchor element to trigger the download
const link = document.createElement('a');
link.setAttribute('href', encodedData);
link.setAttribute('download', 'logs.csv');
document.body.appendChild(link);
// Trigger the download
link.click();
// Remove the link from the DOM
document.body.removeChild(link);
} else {
console.error('No logs available for download.');
}
}
}); });
function searchUser(username) { function searchUser(username) {
@ -148,9 +72,9 @@ function searchUser(username) {
throw new Error(`HTTP error! Status: ${response.status}`); throw new Error(`HTTP error! Status: ${response.status}`);
} }
}) })
.then(users => { .then(user => {
// Display search results // Display search results
displaySearchResults(users); displaySearchResults(user);
}) })
.catch(error => { .catch(error => {
console.error('Search error:', error); console.error('Search error:', error);
@ -160,31 +84,33 @@ function searchUser(username) {
// Function to display search results // Function to display search results
function displaySearchResults(users) { function displaySearchResults(users) {
const searchResultsList = $('#searchResultsList'); const searchResultsList = $('#searchResultsList');
// Clear previous results // Clear previous results
searchResultsList.empty(); searchResultsList.empty();
if (users && users.length > 0) { const listItem = `<li>${users.username} - <button class="deleteUserButton" data-username="${users.username}">Delete</button></li>`;
users.forEach(user => {
const listItem = `<li>${user.username} - <button class="deleteUserButton" data-username="${user.username}">Delete</button></li>`;
searchResultsList.append(listItem); searchResultsList.append(listItem);
});
// Show the search results container // Show the search results container
$('#searchResultsContainer').show(); $('#searchResultsContainer').show();
} else { }
// Hide the search results container if no results
$('#searchResultsContainer').hide();
}
}
// Event listener for delete user button in search results // Event listener for delete user button in search results
$('#searchResultsList').on('click', '.deleteUserButton', function () { $('#searchResultsList').on('click', '.deleteUserButton', function () {
const usernameToDelete = $(this).data('username'); const usernameToDelete = $(this).data('username');
const csrfToken = $('[name="csrf_token"]').val(); // Access the CSRF token by name
console.log(csrfToken);
console.log('Before fetch for user deletion'); console.log('Before fetch for user deletion');
// Make a fetch request to delete the user
// Make a fetch request to delete the user with CSRF token in headers
fetch(`/api/deleteUser/${usernameToDelete}`, { fetch(`/api/deleteUser/${usernameToDelete}`, {
method: 'DELETE', method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ csrfToken }), // Include CSRF token in the request body
}) })
.then(response => { .then(response => {
console.log('Inside fetch response handler'); console.log('Inside fetch response handler');
@ -278,8 +204,7 @@ function resetFormFields() {
$('#confirmPassword').val(''); $('#confirmPassword').val('');
$('#jobTitle').val(''); $('#jobTitle').val('');
} }
const csrf_token = $('#userForm input[name="csrf_token"]').val();
$('#userForm').on('submit', function (e) { $('#userForm').on('submit', function (e) {
e.preventDefault(); e.preventDefault();
@ -299,38 +224,40 @@ function resetFormFields() {
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.'); 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; return;
} }
fetch('/createUser', { fetch('/createUser', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
name: name, name: name,
username: username, username: username,
email: email, email: email,
password: password, password: password,
jobTitle: jobTitle, jobTitle: jobTitle,
csrf_token: csrf_token, // Include the CSRF token in the body
}), }),
}) })
.then(response => { .then(response => {
if (response.status === 201) { if (response.ok) {
// Status 201 indicates successful creation // Status 201 indicates successful creation
return response.json(); return response.json();
} else { } else {
return response.json().then(data => { return response.json().then(data => {
throw new Error(data.error || `HTTP error! Status: ${response.status}`); throw new Error(data.error || `HTTP error! Status: ${response.status}`);
}); });
} }
}) })
.then(data => { .then(data => {
console.log('User registration success:', data); console.log('User registration success:', data);
alert('User registered successfully!'); alert('User registered successfully!');
resetFormFields(); resetFormFields();
}) })
.catch(error => { .catch(error => {
console.error('User registration error:', error); console.error('User registration error:', error);
handleRegistrationError(error); handleRegistrationError(error);
}); });
}); });
@ -369,9 +296,8 @@ $('#resetPasswordForm').on('submit', function (e) {
const username = $('#resetUsername').val(); const username = $('#resetUsername').val();
const password = $('#resetPassword').val(); const password = $('#resetPassword').val();
const confirmPassword = $('#resetConfirmPassword').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 // Validate passwords
if (password !== confirmPassword) { if (password !== confirmPassword) {
@ -385,8 +311,7 @@ $('#resetPasswordForm').on('submit', function (e) {
return; return;
} }
// Make a fetch request fetch('/reset-password', {
fetch('/reset-password', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -395,6 +320,7 @@ $('#resetPasswordForm').on('submit', function (e) {
username: username, username: username,
password: password, password: password,
confirmPassword: confirmPassword, confirmPassword: confirmPassword,
csrf_token: csrf_token
}), }),
}) })
.then(response => { .then(response => {
@ -429,5 +355,154 @@ $('#resetPasswordForm').on('submit', function (e) {
}); });
}); });
// Declare a variable to store fetched logs
let logs = [];
// Function to fetch logs from the server
function fetchLogs() {
// Make a fetch request to your server endpoint for logs
fetch('/api/getLogs')
.then(response => response.json())
.then(data => {
// Assign the logs to the variable
logs = data;
// Process and display logs in the logs container
displayLogs(logs);
})
.catch(error => {
console.error('Error fetching logs:', error);
// Handle errors, e.g., display an alert
});
}
// Update the displayLogs function to generate a table
function displayLogs(logs) {
const logsContainer = $('#logsContainer');
// Clear previous logs and date filter elements
logsContainer.empty();
if (logs && logs.length > 0) {
// Add date filter elements
logsContainer.append(`
<label for="datePicker">Filter by Date:</label>
<input type="text" id="datePicker">
<button onclick="applyDateFilter()">Apply Filter</button>
`);
// Create the table and header row
const table = $('<table>').addClass('logs-table');
const headerRow = '<tr><th>ID</th><th>Username</th><th>Activity</th><th>Timestamp</th></tr>';
table.append(headerRow);
// Add each log as a row in the table
logs.forEach(log => {
const row = `<tr><td>${log.id}</td><td>${log.username}</td><td>${log.activity}</td><td>${log.timestamp}</td></tr>`;
table.append(row);
});
// Append the table to the logsContainer
logsContainer.append(table);
// Add a download button at the top with the current date and time in the file name
const currentDate = new Date();
const formattedDate = currentDate.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
const formattedTime = currentDate.toTimeString().split(' ')[0].replace(/:/g, '-');
const downloadButton = $('<button>').text('Download Log').on('click', function () {
downloadLogs(logs, `log_${formattedDate}_${formattedTime}.csv`);
});
// Prepend the download button to the logsContainer
logsContainer.prepend(downloadButton);
} else {
// Display a message if no logs are available
logsContainer.html('<p>No logs available.</p>');
}
// Initialize Flatpickr for the date picker
flatpickr("#datePicker", {
dateFormat: "m/d/Y, h:i:S K", // Adjust the format to match your logs timestamp format
});
}
// Function to apply date filter
function applyDateFilter() {
const selectedDate = $("#datePicker").val();
const formattedSelectedDate = new Date(selectedDate).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
const filteredLogs = logs.filter(log => {
const formattedLogDate = new Date(log.timestamp).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
return formattedLogDate === formattedSelectedDate;
});
displayLogs(filteredLogs);
}
function downloadLogs(logs, filename) {
if (logs && logs.length > 0) {
const csvContent = 'data:text/csv;charset=utf-8,';
const header = 'ID,Username,Activity,Timestamp\n';
const rows = logs.map(log => `${log.id},${log.username},${log.activity},"${log.timestamp}"`).join('\n');
const data = header + rows;
const encodedData = encodeURI(csvContent + data);
// Create a hidden anchor element to trigger the download
const link = document.createElement('a');
link.setAttribute('href', encodedData);
link.setAttribute('download', 'logs.csv');
document.body.appendChild(link);
// Trigger the download
link.click();
// Remove the link from the DOM
document.body.removeChild(link);
} else {
console.error('No logs available for download.');
}
}
fetchLogs();
// Assuming EJS is properly configured to evaluate expressions
// Assuming allUsers is an array containing user information
const user = allUsers.find(user => user.username === currentUsername);
const userRole = user?.jobTitle;
// Function to enable/disable actions based on user role
function handleUserRoleAccess() {
// Disable user creation, deletion, and password reset for non-admin users
if (userRole !== 'admin') {
document.getElementById('addUserLink').style.display = 'none';
document.getElementById('deleteUserLink').style.display = 'none';
document.getElementById('resetPasswordLink').style.display = 'none';
}
// Allow admin users to view logs
if (userRole === 'admin') {
document.getElementById('logsLink').classList.remove('hidden');
}
}
// Call the function to handle user role access when the page loads
handleUserRoleAccess();

View File

@ -86,7 +86,6 @@ button:hover {
<label for="password">Password</label> <label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Enter your password" required> <input type="password" id="password" name="password" placeholder="Enter your password" required>
<button type="submit">Login</button> <button type="submit">Login</button>
</form> </form>
@ -95,4 +94,5 @@ button:hover {
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -69,6 +69,7 @@
<label for="otp">OTP:</label> <label for="otp">OTP:</label>
<input type="text" id="otp" name="otp" required> <input type="text" id="otp" name="otp" required>
<br> <br>
<button type="submit">Submit OTP</button> <button type="submit">Submit OTP</button>
</form> </form>
</body> </body>

5
api.MD
View File

@ -171,10 +171,7 @@ http://localhost/api/v0/sensor-data/data?week=1&sensorid=1&locationid=1&page=2&p
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"}'
curl localhost/api/v0/user/new -H "Content-Type: application/json" -X POST -d '{"username": "system", "password": "thisisthesystemuserpasswordnoob", "email": "system@ecosaver.com", "address": "Nanyang Polytechnic 180 Ang Mo Kio Avenue 8 Singapore 569830", "phone": "12345678"}'
curl localhost/api/v0/user/new -H "Content-Type: application/json" -X POST -d '{"username": "testuser", "password": "thisisthesystemuserpasswordnoob", "email": "testuser@ecosaver.com", "address": "Nanyang Polytechnic 180 Ang Mo Kio Avenue 8 Singapore 569830", "phone": "12345678"}'
curl localhost/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d '{"userid": "2", "permission": "canRead"}' curl localhost/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d '{"userid": "2", "permission": "canRead"}'

Binary file not shown.

View File

@ -1,22 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world!</h1>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.14.3/dist/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -1,37 +0,0 @@
function validateForm() {
var username = document.getElementById('email').value;
var password = document.getElementById('password').value;
// Perform basic validation
if (!email || !password) {
alert('Please enter both email and password');
return;
}
// If validation passes, send data to the server
sendDataToServer(email, password);
}
function sendDataToServer(email, password) {
// Use AJAX or fetch to send data to the server
// Example using fetch:
fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
})
.then(response => response.json())
.then(data => {
// Handle the response from the server
console.log(data);
if (data.success) {
// Redirect or perform other actions for successful login
alert('Login successful');
} else {
alert('Login failed. Please check your credentials.');
}
})
.catch(error => console.error('Error:', error));
}

View File

@ -1,44 +0,0 @@
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql');
const app = express();
const port = 3000;
app.use(bodyParser.json());
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'your_mysql_password',
database: 'your_database_name',
});
db.connect(err => {
if (err) {
console.error('Error connecting to MySQL:', err);
} else {
console.log('Connected to MySQL');
}
});
app.post('/signup', (req, res) => {
const { username, password } = req.body;
// Perform server-side validation if needed
const sql = 'INSERT INTO users (username, password) VALUES (?, ?)';
db.query(sql, [username, password], (err, result) => {
if (err) {
console.error('Error executing SQL query:', err);
res.status(500).json({ success: false, message: 'Internal Server Error' });
} else {
console.log('User signed up successfully');
res.json({ success: true, message: 'User signed up successfully' });
}
});
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

View File

@ -4,15 +4,31 @@ const { userModel } = require("../database/model/userModel.js");
const { Op, Sequelize } = require("sequelize"); const { Op, Sequelize } = require("sequelize");
const { hashAPIKey } = require("../functions/bcrypt.js"); const { hashAPIKey } = require("../functions/bcrypt.js");
const { generateUUID } = require("../functions/generateUUID.js"); const { generateUUID } = require("../functions/generateUUID.js");
const { hashPassword } = require("../functions/bcrypt.js");
async function getUser() { async function getUser() {
const user = await userModel.findAll(); const user = await userModel.findAll();
return user; return user;
} }
//api/v0/user/register
/* Registering new user
1) req.body is taken from html form or wtv
2) bcrpyt and hash the password on the server side
3) pass to db
*/
async function addUser(user) { async function addUser(user) {
//console.log(user); console.log(user);
await userModel.create(user); //hash password
let hash = await hashPassword(user.password);
await userModel.create({
username: user.username,
password: hash,
email: user.email,
address: user.address,
phone: user.phone,
});
} }
async function getAPIKey() { async function getAPIKey() {
@ -46,7 +62,6 @@ async function addAPIKey(userId, permission) {
//user token with - //user token with -
//"apikey": "1-9beba05f-1bf1-4d8a-9ee8-9f61e1428e20"
return usertoken; return usertoken;
} }

View File

@ -1,217 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>N & LW Lawn Care - Landscaping Bootstrap4 HTML5 Responsive Template </title>
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Fontawesome CSS -->
<link href="css/all.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<!-- Navigation -->
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container">
<a class="navbar-brand" href="index.html">
<img src="images/logo.png" alt="logo" />
</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="fas fa-bars"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="news.html">News</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="contact.html">Contact</a>
</li>
<li class="nav-item">
<a class="nav-link" href="profile.html">Profile</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- full Title -->
<div class="full-title">
<div class="container">
<!-- Page Heading/Breadcrumbs -->
<h1 class="mt-4 mb-3">Contact
</h1>
</div>
</div>
<!-- Page Content -->
<div class="container">
<div class="breadcrumb-main">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="index.html">Home</a>
</li>
<li class="breadcrumb-item active">Contact</li>
</ol>
</div>
<!-- Content Row -->
<div class="row">
<!-- Map Column -->
<div class="col-lg-8 mb-4">
<!-- Embedded Google Map -->
<iframe width="100%" height="300px" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps?hl=en&amp;ie=UTF8&amp;q=Singapore+410645&amp;t=m&amp;z=15&amp;output=embed"></iframe>
</div>
<!-- Contact Details Column -->
<div class="col-lg-4 mb-4 contact-right">
<h3>Contact Details</h3>
<p>
Blk 645 Jalan Tenaga
<br>S(410645)
<br>
</p>
<p>
<abbr title="Phone">P</abbr>: (+65) 90064959
</p>
<p>
<abbr title="Email">E</abbr>:
<a href="mailto:name@example.com">leongdingxuan@gmail.com
</a>
</p>
<p>
<abbr title="Hours">H</abbr>: Monday - Friday: 9:00 AM to 5:00 PM
</p>
</div>
</div>
<!-- /.row -->
<!-- Contact Form -->
<!-- In order to set the email address and subject line for the contact form go to the bin/contact_me.php file. -->
<div class="row">
<div class="col-lg-8 mb-4 contact-left">
<h3>Send us a Message</h3>
<form name="sentMessage" id="contactForm" novalidate>
<div class="control-group form-group">
<div class="controls">
<label>Full Name:</label>
<input type="text" class="form-control" id="name" required data-validation-required-message="Please enter your name.">
<p class="help-block"></p>
</div>
</div>
<div class="control-group form-group">
<div class="controls">
<label>Phone Number:</label>
<input type="tel" class="form-control" id="phone" required data-validation-required-message="Please enter your phone number.">
</div>
</div>
<div class="control-group form-group">
<div class="controls">
<label>Email Address:</label>
<input type="email" class="form-control" id="email" required data-validation-required-message="Please enter your email address.">
</div>
</div>
<div class="control-group form-group">
<div class="controls">
<label>Message:</label>
<textarea rows="5" cols="100" class="form-control" id="message" required data-validation-required-message="Please enter your message" maxlength="999" style="resize:none"></textarea>
</div>
</div>
<div id="success"></div>
<!-- For success/fail messages -->
<button type="submit" class="btn btn-primary" id="sendMessageButton">Send Message</button>
</form>
</div>
</div>
<!-- /.row -->
</div>
<!-- /.container -->
<!--footer starts from here-->
<footer class="footer">
<div class="container bottom_border">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6 col">
<h5 class="headin5_amrc col_white_amrc pt2">Find us</h5>
<p><i class="fa fa-location-arrow"></i> Blk 645 Jalan Tenaga</p>
<p><i class="fa fa-phone"></i> +65 90064959</p>
<p><i class="fa fa fa-envelope"></i> Leongdingxuan@gmail.com </p>
</div>
<div class="col-lg-3 col-md-6 col-sm-6 col">
<h5 class="headin5_amrc col_white_amrc pt2">Follow us</h5>
<!--headin5_amrc ends here-->
<ul class="footer_ul2_amrc">
<li>
<a href="#"><i class="fab fa-facebook-f fleft padding-right"></i> </a>
<a href="#">https://www.facebook.com/</a></p>
</li>
<li>
<a href="#"><i class="fab fa-instagram fleft padding-right"></i> </a>
<a href="#">https://www.instagram.com/</a></p>
</li>
<li>
<a href="#"><i class="fab fa-twitter fleft padding-right"></i> </a>
<a href="#">https://twitter.com/</a></p>
</li>
</ul>
<!--footer_ul2_amrc ends here-->
</div>
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="headin5_amrc col_white_amrc pt2">Quick links</h5>
<!--headin5_amrc-->
<ul class="footer_ul_amrc">
<li><a href="#">Home</a></li>
<li><a href="#">News</a></li>
<li><a href="#">Contact</a></li>
</ul>
<!--footer_ul_amrc ends here-->
</div>
<div class="col-lg-3 col-md-6 col-sm-6 ">
<h5 class="headin5_amrc col_white_amrc pt2">Recent posts</h5>
<!--headin5_amrc-->
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>Singapore Government ...</p>
<span>29 Sep 2023</span>
</div>
</ul>
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>High risk of severe ...</p>
<span>22 Jun 2023</span>
</div>
</ul>
</div>
</div>
</div>
<div class="container text-center">
<br>
<p>All Rights Reserved. &copy; 2023 <a href="#">EcoSaver</a>
</p>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Contact form JavaScript -->
<script src="js/jqBootstrapValidation.js"></script>
<script src="js/contact_me.js"></script>
</body>
</html>

View File

@ -0,0 +1,224 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Fontawesome CSS -->
<link href="css/all.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
<link href="css/contact.css" rel="stylesheet">
</head>
<body>
<!-- Navigation -->
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container">
<a class="navbar-brand" href="index.html">
<img src="images/logo.png" alt="logo" />
</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false"
aria-label="Toggle navigation">
<span class="fas fa-bars"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="news.html">News</a>
</li>
<li class="nav-item">
<a class="nav-link" href="contactform.html">Contact</a>
</li>
<li class="nav-item">
<a class="nav-link" href="profile.html">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="signuplogin.html">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- full Title -->
<div class="full-title">
<div class="container">
<!-- Page Heading/Breadcrumbs -->
<h1 class="mt-4 mb-3">Contact
</h1>
</div>
</div>
<!-- Page Content -->
<div class="container">
<div class="breadcrumb-main">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="index.html">Home</a>
</li>
<li class="breadcrumb-item active">Contact</li>
</ol>
</div>
<!-- Content Row -->
<div class="row">
<!-- Map Column -->
<div class="col-lg-8 mb-4">
<!-- Embedded Google Map -->
<iframe width="100%" height="300px" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"
src="http://maps.google.com/maps?hl=en&amp;ie=UTF8&amp;q=Singapore+408866&amp;t=m&amp;z=15&amp;output=embed"></iframe>
</div>
<!-- Contact Details Column -->
<div class="col-lg-4 mb-4 contact-right">
<h3>Contact Details</h3>
<p>
50 Ubi Ave 3
<br>S(408866)
<br>
</p>
<p>
<abbr title="Phone">P</abbr>: (+65) 90064959
</p>
<p>
<abbr title="Email">E</abbr>:
<a href="mailto:name@example.com">leongdingxuan@gmail.com
</a>
</p>
<p>
<abbr title="Hours">H</abbr>: Monday - Friday: 9:00 AM to 5:00 PM
</p>
</div>
</div>
<!-- /.row -->
<!-- Contact Form -->
<!-- In order to set the email address and subject line for the contact form go to the bin/contact_me.php file. -->
<div class="row">
<div class="col-lg-8 mb-4 contact-left">
<h3>Send us a Message</h3>
<form id="form">
<input type="hidden" name="access_key" value="">
<div class="mb-3">
<label for="name">Full Name</label>
<input type="text" name="name" id="name" required>
</div>
<div class="mb-3">
<label for="email">Email address</label>
<input type="email" name="email" id="email" required>
</div>
<div class="mb-3">
<label for="message">Message</label>
<textarea name="message" id="message" rows="3" required></textarea>
</div>
<button type="submit">Submit Form</button>
</form>
</div>
</div>
</div>
<!-- /.row -->
</div>
<!-- /.container -->
<!--footer starts from here-->
<footer class="footer">
<div class="container bottom_border">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6 col">
<h5 class="headin5_amrc col_white_amrc pt2">Find us</h5>
<p><i class="fa fa-location-arrow"></i> Blk 645 Jalan Tenaga</p>
<p><i class="fa fa-phone"></i> +65 90064959</p>
<p><i class="fa fa fa-envelope"></i> Leongdingxuan@gmail.com </p>
</div>
<div class="col-lg-3 col-md-6 col-sm-6 col">
<h5 class="headin5_amrc col_white_amrc pt2">Follow us</h5>
<!--headin5_amrc ends here-->
<ul class="footer_ul2_amrc">
<li>
<a href="#"><i class="fab fa-facebook-f fleft padding-right"></i> </a>
<a href="#">https://www.facebook.com/</a></p>
</li>
<li>
<a href="#"><i class="fab fa-instagram fleft padding-right"></i> </a>
<a href="#">https://www.instagram.com/</a></p>
</li>
<li>
<a href="#"><i class="fab fa-twitter fleft padding-right"></i> </a>
<a href="#">https://twitter.com/</a></p>
</li>
</ul>
<!--footer_ul2_amrc ends here-->
</div>
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="headin5_amrc col_white_amrc pt2">Quick links</h5>
<!--headin5_amrc-->
<ul class="footer_ul_amrc">
<li><a href="#">Home</a></li>
<li><a href="#">News</a></li>
<li><a href="#">Contact</a></li>
</ul>
<!--footer_ul_amrc ends here-->
</div>
<div class="col-lg-3 col-md-6 col-sm-6 ">
<h5 class="headin5_amrc col_white_amrc pt2">Recent posts</h5>
<!--headin5_amrc-->
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>Singapore's air quality ...</p>
<span>7 oct 2023</span>
</div>
</ul>
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>Singapore Government ...</p>
<span>29 Sep 2023</span>
</div>
</ul>
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>High risk of severe ...</p>
<span>22 Jun 2023</span>
</div>
</ul>
</div>
</div>
</div>
<div class="container text-center">
<br>
<p>All Rights Reserved. &copy; 2023 <a href="#">EcoSaver</a>
</p>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Contact form JavaScript -->
<script src="js/jqBootstrapValidation.js"></script>
<script src="js/contact.js"></script>
</body>
</html>

View File

@ -0,0 +1,34 @@
.contact-left {
width: 100%; /* Make the form take the whole width */
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
input,
textarea,
button {
width: 100%;
margin-bottom: 10px;
padding: 10px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
button {
background-color: #007bff;
color: #fff;
cursor: pointer;
max-width: 150px;
margin-left: auto;
}
button:hover {
background-color: #0056b3;
}

View File

@ -0,0 +1,83 @@
@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 {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f0faff;
}
.wrapper {
position: relative;
max-width: 470px;
width: 100%;
border-radius: 12px;
padding: 20px 30px 120px;
background: #fff;
box-shadow: 0 5px 10px rgba(0,
0,
0,
0.1);
overflow: hidden;
}
.form header {
font-size: 30px;
text-align: center;
color: #333;
font-weight: 600;
cursor: pointer;
}
.wrapper.active .form.login header {
opacity: 1;
}
.wrapper.active .signup header {
opacity: 0.6;
}
.wrapper form {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 40px;
}
form a {
color: #333;
text-decoration: none;
}
form input {
height: 60px;
outline: none;
border: none;
padding: 0 15px;
font-size: 16px;
font-weight: 400;
color: #333;
border-radius: 8px;
background: #fff;
border: 1px solid #aaa;
}
form input[type="submit"] {
margin-top: 15px;
padding: none;
font-size: 18px;
font-weight: 500;
cursor: pointer;
background: #4070f4;
color: #fff;
}

View File

@ -0,0 +1,6 @@
img {
display: block;
margin: auto;
max-width: 100%;
max-height: 100vh;
}

View File

@ -0,0 +1,80 @@
@import url('https://fonts.googleapis.com/css?family=Lato:400,700&display=swap');
body {
margin: 0;
padding: 0;
font-family: 'Lato', sans-serif;
background-color: #f9f9f9;
color: #000000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
table {
border-collapse: collapse;
margin: 0 auto;
width: 600px;
}
td {
padding: 15px;
}
.main-header {
background-color: #4070f4;
}
.main-header img {
width: 10%;
vertical-align: middle; /* Center the image vertically */
}
.main-header h1 {
color: #ffffff;
font-size: 28px;
text-align: center;
}
.content-section {
background-color: #ffffff;
padding: 40px;
}
.content-section h2 {
font-size: 18px;
line-height: 25.2px;
color: #666666;
}
.footer {
background-color: #1c103b;
text-align: center;
}
.footer p {
font-size: 14px;
color: #ffffff;
}
.wrapper {
position: relative;
max-width: 470px;
width: 100%;
border-radius: 12px;
padding: 20px
30px
120px;
background: #4070f4;
box-shadow: 0
5px
10px
rgba(
0,
0,
0,
0.1
);
overflow: hidden;
}

View File

@ -0,0 +1,205 @@
@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 {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f0faff;
}
.wrapper {
position: relative;
max-width: 470px;
width: 100%;
border-radius: 12px;
padding: 20px
30px
120px;
background: #4070f4;
box-shadow: 0
5px
10px
rgba(
0,
0,
0,
0.1
);
overflow: hidden;
}
.form.login {
position: absolute;
left: 50%;
bottom: -86%;
transform: translateX(
-50%
);
width: calc(
100% +
220px
);
padding: 35px
140px;
border-radius: 50%;
height: 100%;
background: #fff;
transition: all
0.6s
ease;
}
.wrapper.active
.form.login {
bottom: -10%;
border-radius: 35%;
box-shadow: 0 -5px
10px rgba(0, 0, 0, 0.1);
}
.form
header {
font-size: 30px;
text-align: center;
color: #fff;
font-weight: 600;
cursor: pointer;
}
.form.login
header {
color: #333;
opacity: 0.6;
}
.wrapper.active
.form.login
header {
opacity: 1;
}
.wrapper.active
.signup
header {
opacity: 0.6;
}
.wrapper
form {
display: flex;
flex-direction: column;
gap: 15px;
margin-top: 50px;
}
form
input {
height: 60px;
outline: none;
border: none;
padding: 0
15px;
font-size: 16px;
font-weight: 400;
color: #333;
border-radius: 8px;
background: #fff;
}
.form.login
input {
border: 1px
solid
#aaa;
}
.form.login
input:focus {
box-shadow: 0
1px 0
#ddd;
}
form
.checkbox {
display: flex;
align-items: center;
gap: 10px;
}
.checkbox
input[type="checkbox"] {
height: 16px;
width: 16px;
accent-color: #fff;
cursor: pointer;
}
form
.checkbox
label {
cursor: pointer;
color: #fff;
}
form a {
color: #333;
text-decoration: none;
}
form
a:hover {
text-decoration: underline;
}
form
input[type="submit"] {
margin-top: 15px;
padding: none;
font-size: 18px;
font-weight: 500;
cursor: pointer;
}
.form.login
input[type="submit"] {
background: #4070f4;
color: #fff;
border: none;
}
.form.login .forgot-password-section {
display: none;
margin-top: 20px;
}
.form.login .forgot-password-section p {
color: #333;
font-size: 16px;
margin-bottom: 10px;
}
.form.login .forgot-password-section input[type="text"],
.form.login .forgot-password-section input[type="submit"] {
width: 100%;
height: 50px;
outline: none;
border: 1px solid #aaa;
padding: 0 15px;
font-size: 16px;
font-weight: 400;
color: #333;
border-radius: 8px;
margin-bottom: 15px;
}
.form.login .forgot-password-section input[type="submit"] {
background: #4070f4;
color: #fff;
border: none;
cursor: pointer;
}
.form.login .back-to-login {
display: block;
color: #4070f4;
text-decoration: none;
font-size: 14px;
cursor: pointer;
}
.form.login .back-to-login:hover {
text-decoration: underline;
}

View File

@ -577,7 +577,6 @@ footer p {
text-align: center; text-align: center;
} }
.pricing-box{ .pricing-box{
padding: 30px 0px; padding: 30px 0px;
} }

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="css/forgot.css" />
</head>
<body>
<section class="wrapper">
<div class="form">
<header>Reset Password</header>
<form action="resetpassword.html">
<input type="text" id="email" placeholder="Email" required />
<input type="password" id="password" placeholder="Password" required />
<input type="password" id="confirmPassword" placeholder="Confirm Password" required />
<input type="submit" onclick="validateReset()" value="Reset Password" />
</form>
<br>
<a>Dont have an account?</a> <a href="signuplogin.html">Sign Up</a>
</div>
</section>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,75 +0,0 @@
$(function() {
$("#contactForm input,#contactForm textarea").jqBootstrapValidation({
preventSubmit: true,
submitError: function($form, event, errors) {
// additional error messages or events
},
submitSuccess: function($form, event) {
event.preventDefault(); // prevent default submit behaviour
// get values from FORM
var name = $("input#name").val();
var email = $("input#email").val();
var phone = $("input#phone").val();
var message = $("textarea#message").val();
var firstName = name; // For Success/Failure Message
// Check for white space in name for Success/Fail message
if (firstName.indexOf(' ') >= 0) {
firstName = name.split(' ').slice(0, -1).join(' ');
}
$this = $("#sendMessageButton");
$this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages
$.ajax({
url: "././mail/contact_me.php",
type: "POST",
data: {
name: name,
phone: phone,
email: email,
message: message
},
cache: false,
success: function() {
// Success message
$('#success').html("<div class='alert alert-success'>");
$('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;")
.append("</button>");
$('#success > .alert-success')
.append("<strong>Your message has been sent. </strong>");
$('#success > .alert-success')
.append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
error: function() {
// Fail message
$('#success').html("<div class='alert alert-danger'>");
$('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;")
.append("</button>");
$('#success > .alert-danger').append($("<strong>").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"));
$('#success > .alert-danger').append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
complete: function() {
setTimeout(function() {
$this.prop("disabled", false); // Re-enable submit button when AJAX call is complete
}, 1000);
}
});
},
filter: function() {
return $(this).is(":visible");
},
});
$("a[data-toggle=\"tab\"]").click(function(e) {
e.preventDefault();
$(this).tab("show");
});
});
/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
$('#success').html('');
});

View File

@ -0,0 +1,153 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>EcoSaver</title>
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Fontawesome CSS -->
<link href="css/all.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
<link href="css/learnmore.css" rel="stylesheet">
</head>
<body>
<!-- Navigation -->
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container">
<a class="navbar-brand" href="index.html">
<img src="images/logo.png" alt="logo" />
</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false"
aria-label="Toggle navigation">
<span class="fas fa-bars"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link active" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="news.html">News</a>
</li>
<li class="nav-item">
<a class="nav-link" href="contactform.html">Contact</a>
</li>
<li class="nav-item">
<!--<img src="images/profile-logo.png" alt="Profile Logo" class="profile-logo"> -->
<a class="nav-link" href="profile.html">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="signuplogin.html">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<br>
<br>
<div class="map">
<img src="images/map.png" alt="Map Image">
</div>
<br>
<br>
<!--footer starts from here-->
<footer class="footer">
<div class="container bottom_border">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6 col">
<h5 class="headin5_amrc col_white_amrc pt2">Find us</h5>
<!--headin5_amrc-->
<p><i class="fa fa-location-arrow"></i> Blk 645 Jalan Tenaga</p>
<p><i class="fa fa-phone"></i> +65 90064959</p>
<p><i class="fa fa fa-envelope"></i> Leongdingxuan@gmail.com </p>
</div>
<div class="col-lg-3 col-md-6 col-sm-6 col">
<h5 class="headin5_amrc col_white_amrc pt2">Follow us</h5>
<!--headin5_amrc ends here-->
<ul class="footer_ul2_amrc">
<li>
<a href="#"><i class="fab fa-facebook-f fleft padding-right"></i> </a>
<a href="#">https://www.facebook.com/</a></p>
</li>
<li>
<a href="#"><i class="fab fa-instagram fleft padding-right"></i> </a>
<a href="#">https://www.instagram.com/</a></p>
</li>
<li>
<a href="#"><i class="fab fa-twitter fleft padding-right"></i> </a>
<a href="#">https://twitter.com/</a></p>
</li>
</ul>
<!--footer_ul2_amrc ends here-->
</div>
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="headin5_amrc col_white_amrc pt2">Quick links</h5>
<!--headin5_amrc-->
<ul class="footer_ul_amrc">
<li><a href="#">Home</a></li>
<li><a href="#">News</a></li>
<li><a href="#">Contact</a></li>
</ul>
<!--footer_ul_amrc ends here-->
</div>
<div class="col-lg-3 col-md-6 col-sm-6 ">
<h5 class="headin5_amrc col_white_amrc pt2">News</h5>
<!--headin5_amrc-->
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>Singapore's air quality ...</p>
<span>7 oct 2023</span>
</div>
</ul>
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>Singapore Government ...</p>
<span>29 Sep 2023</span>
</div>
</ul>
<ul class="footer_ul_amrc">
<li class="media">
<div class="media-left">
<img class="img-fluid" src="images/post-img-01.jpg" alt="" />
</div>
<div class="media-body">
<p>High risk of severe ...</p>
<span>22 Jun 2023</span>
</div>
</ul>
</div>
</div>
</div>
<div class="container text-center">
<br>
<p>All Rights Reserved. &copy; 2023 <a href="#">EcoSaver</a>
</p>
</div>
</footer>
<!-- Bootstrap core JavaScript -->
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,26 +0,0 @@
<?php
// Check for empty fields
if(empty($_POST['name']) ||
empty($_POST['email']) ||
empty($_POST['phone']) ||
empty($_POST['message']) ||
!filter_var($_POST['email'],FILTER_VALIDATE_EMAIL))
{
echo "No arguments Provided!";
return false;
}
$name = strip_tags(htmlspecialchars($_POST['name']));
$email_address = strip_tags(htmlspecialchars($_POST['email']));
$phone = strip_tags(htmlspecialchars($_POST['phone']));
$message = strip_tags(htmlspecialchars($_POST['message']));
// Create the email and send the message
$to = 'yourname@yourdomain.com'; // Add your email address inbetween the '' replacing yourname@yourdomain.com - This is where the form will send a message to.
$email_subject = "Website Contact Form: $name";
$email_body = "You have received a new message from your website contact form.\n\n"."Here are the details:\n\nName: $name\n\nEmail: $email_address\n\nPhone: $phone\n\nMessage:\n$message";
$headers = "From: noreply@yourdomain.com\n"; // This is the email address the generated message will be from. We recommend using something like noreply@yourdomain.com.
$headers .= "Reply-To: $email_address";
mail($to,$email_subject,$email_body,$headers);
return true;
?>

View File

@ -36,11 +36,14 @@
<a class="nav-link" href="news.html">News</a> <a class="nav-link" href="news.html">News</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="contact.html">Contact</a> <a class="nav-link" href="contactform.html">Contact</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="profile.html">Profile</a> <a class="nav-link" href="profile.html">Profile</a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="signuplogin.html">Logout</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -89,7 +92,8 @@
class="form-control" placeholder="re enter password" value=""></div> class="form-control" placeholder="re enter password" value=""></div>
</div> </div>
<div class="mt-2 text-center"> <div class="mt-2 text-center">
<button class="btn btn-sm btn-secondary change-password-button" type="button">Change Password</button> <button class="btn btn-sm btn-secondary change-password-button" type="button">Change
Password</button>
</div> </div>
<div class="mt-5 text-center"><button class="btn btn-primary profile-button" type="button">Save <div class="mt-5 text-center"><button class="btn btn-primary profile-button" type="button">Save
Profile</button></div> Profile</button></div>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>Your password has been resetted</title>
<link href="css/reset.css" rel="stylesheet">
</head>
<body>
<table class="main-header">
<tr>
<td style="text-align: center;">
<img src="images/passwordreset.png" alt="Image">
<h1>Your password has been resetted</h1>
</td>
</tr>
</table>
<table class="content-section">
<tr>
<td>
<p>Hello,</p>
<p>Please check your email to reset your password.</p>
</td>
</tr>
</table>
<table class="footer">
<tr>
<td>
<p>&copy; 2023 EcoSaver</p>
</td>
</tr>
</table>
</body>
</html>

View File

@ -13,7 +13,7 @@
<section class="wrapper"> <section class="wrapper">
<div class="form signup"> <div class="form signup">
<header>Signup</header> <header>Signup</header>
<form action="/api/v0/user/register" method="POST"> <form action="routes/user.js/register" method="get">
<input type="text" id="username" placeholder="Username" required /> <input type="text" id="username" placeholder="Username" required />
<input type="text" id="email" placeholder="Email" required /> <input type="text" id="email" placeholder="Email" required />
<input type="text" id="address" placeholder="Address" required /> <input type="text" id="address" placeholder="Address" required />
@ -24,7 +24,7 @@
<input type="checkbox" id="signupCheck" /> <input type="checkbox" id="signupCheck" />
<label for="signupCheck">I accept all terms & conditions</label> <label for="signupCheck">I accept all terms & conditions</label>
</div> </div>
<input type="submit" value="Signup" /> <input type="submit" onclick="validateFormSignup()" value="Signup" />
</form> </form>
</div> </div>

View File

@ -1,9 +1,9 @@
const { getUser, addUser } = require("../functions/apiDatabase.js"); const { getUser, addUser } = require("../functions/apiDatabase.js");
const { hashPassword } = require("../functions/bcrypt.js");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
//get all users
router.get("/", async (req, res, next) => { router.get("/", async (req, res, next) => {
try { try {
const location = await getUser(); const location = await getUser();
@ -14,12 +14,12 @@ router.get("/", async (req, res, next) => {
} }
}); });
// /user/register // /user/register
router.post("/register", async (req, res, next) => { router.post("/register", async (req, res, next) => {
try { try {
console.log(req.body);
//await addUser(req.body); //await addUser(req.body);
//res.sendStatus(200); res.sendStatus(200);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
next(error); next(error);

3152
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,13 @@
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"body-parser": "^1.20.2", "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", "dotenv": "^16.3.1",
"ejs": "^3.1.9", "ejs": "^3.1.9",
"esm": "^3.2.25",
"express": "^4.18.2", "express": "^4.18.2",
"express-rate-limit": "^7.1.5", "express-rate-limit": "^7.1.5",
"express-session": "^1.17.3", "express-session": "^1.17.3",
@ -28,13 +33,16 @@
"helmet": "^7.1.0", "helmet": "^7.1.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"mqtt": "^5.3.3", "mqtt": "^5.3.3",
"mysql2": "^3.6.5", "mysql2": "^3.7.1",
"node-fetch": "^3.3.2",
"nodemailer": "^6.9.7", "nodemailer": "^6.9.7",
"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",
"sql": "^0.78.0",
"validator": "^13.11.0" "validator": "^13.11.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -32,7 +32,7 @@ app.set("json spaces", 2);
middleware logic ( called by next() ) middleware logic ( called by next() )
*/ */
app.use("/api/seed/v0", [ apikeyCheck , APIlogger] ,require("../routes/seed_route.js")); app.use("/api/seed/v0", [ apikeyCheck , APIlogger] ,require("../routes/seed_route.js"));
app.use('/api/v0', [apikeyCheck, APIlogger] ,require("../routes/api_route.js")); //webserver\routes\api_route.js app.use('/api/v0', [apikeyCheck, APIlogger] ,require("../routes/api_route.js"));