Merge branch 'main' into Dev-branch
@ -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
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
// 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) => {
|
||||||
|
const User = sequelize.define('users', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
@ -30,6 +31,20 @@ const User = sequelize.define('User', {
|
|||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
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
@ -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;
|
||||||
|
};
|
@ -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",
|
{
|
||||||
|
host: "mpsqldatabasean.mysql.database.azure.com",
|
||||||
|
dialect: 'mysql',
|
||||||
|
// attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy';
|
||||||
|
attributeBehavior: 'escape',
|
||||||
|
dialectOptions: {
|
||||||
ssl: {
|
ssl: {
|
||||||
ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')),
|
ca: fs.readFileSync(path.resolve(__dirname, '../../cert/DigiCertGlobalRootCA.crt.pem')),
|
||||||
}
|
|
||||||
});
|
},
|
||||||
*/
|
|
||||||
const connection = mysql.createConnection({
|
},
|
||||||
host: process.env.host,
|
},
|
||||||
user: process.env.DB_USER,
|
|
||||||
password: process.env.DB_PASS,
|
|
||||||
database: "adminusers",
|
);
|
||||||
timezone: "Z", // Set the timezone to UTC
|
|
||||||
|
sequelize.authenticate().then(() => {
|
||||||
|
console.log('Connection has been established successfully.');
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Unable to connect to the database: ', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = { connection };
|
const User = UserModel(sequelize);
|
||||||
|
|
||||||
|
// Synchronize the models with the database
|
||||||
|
sequelize.sync();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sequelize,
|
||||||
|
User,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
822
Sean/server.js
@ -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,63 +53,22 @@
|
|||||||
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>
|
||||||
|
|
||||||
|
107
Sean/views/home.js
Normal 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;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
|
||||||
@ -304,6 +229,7 @@ function resetFormFields() {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: name,
|
name: name,
|
||||||
@ -311,10 +237,11 @@ function resetFormFields() {
|
|||||||
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 {
|
||||||
@ -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,7 +311,6 @@ $('#resetPasswordForm').on('submit', function (e) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a fetch request
|
|
||||||
fetch('/reset-password', {
|
fetch('/reset-password', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -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();
|
||||||
|
@ -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>
|
||||||
|
@ -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
@ -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"}'
|
||||||
|
@ -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>
|
|
@ -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));
|
|
||||||
}
|
|
@ -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}`);
|
|
||||||
});
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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&ie=UTF8&q=Singapore+410645&t=m&z=15&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. © 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>
|
|
224
consumerWebsite/public/contactform.html
Normal 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&ie=UTF8&q=Singapore+408866&t=m&z=15&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. © 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>
|
34
consumerWebsite/public/css/contact.css
Normal 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;
|
||||||
|
}
|
83
consumerWebsite/public/css/forgot.css
Normal 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;
|
||||||
|
}
|
6
consumerWebsite/public/css/learnmore.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
80
consumerWebsite/public/css/reset.css
Normal 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;
|
||||||
|
}
|
205
consumerWebsite/public/css/sp.css
Normal 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;
|
||||||
|
}
|
@ -577,7 +577,6 @@ footer p {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.pricing-box{
|
.pricing-box{
|
||||||
padding: 30px 0px;
|
padding: 30px 0px;
|
||||||
}
|
}
|
||||||
|
27
consumerWebsite/public/forgotpassword.html
Normal 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>
|
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.7 KiB |
BIN
consumerWebsite/public/images/map.png
Normal file
After Width: | Height: | Size: 495 KiB |
BIN
consumerWebsite/public/images/passwordreset.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB |
@ -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'>×")
|
|
||||||
.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'>×")
|
|
||||||
.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('');
|
|
||||||
});
|
|
153
consumerWebsite/public/learnmore.html
Normal 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. © 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>
|
@ -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;
|
|
||||||
?>
|
|
@ -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>
|
||||||
|
39
consumerWebsite/public/resetpassword.html
Normal 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>© 2023 EcoSaver</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -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>
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
3000
package-lock.json
generated
10
package.json
@ -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": {
|
||||||
|
@ -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"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|