Merge branch 'main' into new

This commit is contained in:
Leo 2024-01-21 02:37:44 +08:00
commit 9b5ac4fa3a
17 changed files with 722 additions and 406 deletions

View File

@ -370,14 +370,15 @@ app.post(
} }
// Hash the password // Hash the password
const hashedPassword = await bcrypt.hash(password, 10); const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Start a transaction // Start a transaction
const t = await sequelize.transaction(); const t = await sequelize.transaction();
try { try {
// Create the user // Create the user
const newUser = await User.create({ await User.create({
name, name,
username, username,
email, email,
@ -536,7 +537,8 @@ app.post("/forgot-password", async (req, res) => {
} }
// Hash the new password // Hash the new password
const hashedPassword = await bcrypt.hash(sanitizedPassword, 10); const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password and clear reset token // Update user's password and clear reset token
const updateQuery = { const updateQuery = {

BIN
Sean/views/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -7,69 +7,29 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>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="style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
<style>
.chart-container {
width: 400px;
height: 250px;
margin: 20px;
border: 1px solid #ddd;
display: inline-block;
}
body {
margin: 0;
font-family: 'Arial', sans-serif;
}
#navbar {
background-color: #333;
overflow: hidden;
text-align: center;
}
#navbar h1 {
color: white;
padding: 14px 16px;
margin: 0;
font-size: 24px;
}
#navbar a {
display: inline-block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 18px;
}
#navbar a:hover {
background-color: #555;
}
#content {
padding: 16px;
text-align: center;
}
</style>
</head> </head>
<body> <body>
<header>
<h1>ECOSAVER MANAGEMENT</h1>
</header>
<div id="navbar"> <nav>
<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="#">Sensors</a> <a href="#">Sensors</a>
<a href="/logout">Logout</a> <a href="/logout">Logout</a>
</div> </nav>
<div id="content"> <main>
<h2>Welcome to the Home Page, <%= username %>!</h2> <h2>Welcome to the Home Page, <%= username %>!</h2>
</main>
<footer>
Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com
</footer>
</body> </body>
</html> </html>

View File

@ -7,35 +7,34 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>In-House Users</title> <title>In-House Users</title>
<link rel="stylesheet" href="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="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
</head> </head>
<body> <body>
<header>
<div id="navbar"> <h1>ECOSAVER MANAGEMENT</h1>
<h1>Eco Saver</h1> </header>
<nav>
<a href="#" id="userDataLink">User Data</a> <a href="#" id="userDataLink">User Data</a>
<a href="#" id="addUserLink">Add User</a> <a href="#" id="addUserLink">Add User</a>
<a href="#" id="deleteUserLink">Delete User</a> <a href="#" id="deleteUserLink">Delete User</a>
<a href="#" id="resetPasswordLink">Reset Password</a> <a href="#" id="resetPasswordLink">Reset Password</a>
<a href="#" id="logsLink">Logs</a> <a href="#" id="logsLink">Logs</a>
<a href="/home" id="homeLink">Home</a> <a href="/home" id="homeLink">Home</a>
</div> </nav>
<div id="content"> <main>
<h2>Welcome to the In-House Users Page</h2> <h2>Welcome to the In-House Users Page</h2>
</main>
<div id="downloadButtonContainer"> <div id="downloadButtonContainer">
<button id="downloadButton">Download as Excel</button> <button id="downloadButton">Download as Excel</button>
</div> </div>
<div id="userDataContainer"> <div id="userDataContainer">
<h3>All Users</h3> <table class="nice-table">
<table>
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -63,7 +62,7 @@
</table> </table>
</div> </div>
<div id="createUserForm" class="user-creation-container" style="display: none;"> <div id="createUserForm" class="user-creation-container" style="display: none;">
<div class="title">Registration</div> <h3>Registration</h3>
<div class="content"> <div class="content">
<form action="/createUser" id="userForm" method="post"> <form action="/createUser" id="userForm" method="post">
<div class="user-details"> <div class="user-details">
@ -102,9 +101,21 @@
</form> </form>
</div> </div>
</div> </div>
<div id="additional-text" style="display: none;">
<div class="condition">
<span>Conditions for creating a user:</span>
<ul>
<li class="error">Username has to be different.</li>
<li class="error">Email has to be different.</li>
<li class="error">Password must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.</li>
</ul>
</div>
</div>
<div id="resetPasswordFormContainer" style="display: none;"> <div id="resetPasswordFormContainer" style="display: none;">
<h3>Reset Password</h3>
<div id="resetPasswordForm" class="user-creation-container"> <div id="resetPasswordForm" class="user-creation-container">
<div class="title">Reset Password</div>
<div class="content"> <div class="content">
<form id="resetPasswordForm"> <form id="resetPasswordForm">
<div class="user-details"> <div class="user-details">
@ -129,6 +140,15 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div id="additional-text2" style="display: none;">
<div class="condition">
<span>Please remind user to reset their password again for safety reason</span>
<span>Conditions for Reseting user Password:</span>
<ul>
<li class="error">Password must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.</li>
</ul>
</div>
</div> </div>
<div id="deleteUserContainer" style="display: none;"> <div id="deleteUserContainer" style="display: none;">
<h3>Delete User</h3> <h3>Delete User</h3>
@ -143,7 +163,11 @@
</ul> </ul>
</div> </div>
</div> </div>
<div id="additional-text3" style="display: none;">
<div class="condition">
<span>Please ensure correct user is being deleted user cannot be retived again upon deletion</span>
</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 -->
@ -152,8 +176,7 @@
<script> <script>
const allUsers = <%- JSON.stringify(allUsers) %>; const allUsers = <%- JSON.stringify(allUsers) %>;
const currentUsername = '<%= currentUsername %>'; 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>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
@ -164,7 +187,6 @@
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js"></script>
<script src="inusers.js"></script> <script src="inusers.js"></script>
</div>
</body> </body>

View File

@ -1,3 +1,4 @@
$(document).ready(function () { $(document).ready(function () {
$('#resetPasswordLink').on('click', function () { $('#resetPasswordLink').on('click', function () {
$('#resetPasswordFormContainer').show(); $('#resetPasswordFormContainer').show();
@ -6,15 +7,21 @@ $(document).ready(function () {
$('#downloadButtonContainer').hide(); $('#downloadButtonContainer').hide();
$('#deleteUserContainer').hide(); $('#deleteUserContainer').hide();
$('#logsContainer').hide(); $('#logsContainer').hide();
$('#additional-text').hide();
$('#additional-text2').show();
$('#additional-text3').hide();
}); });
$('#addUserLink').on('click', function () { $('#addUserLink').on('click', function () {
$('#resetPasswordFormContainer').hide(); $('#resetPasswordFormContainer').hide();
$('#createUserForm').show(); $('#createUserForm').show();
$('#additional-text').show();
$('#userDataContainer').hide(); $('#userDataContainer').hide();
$('#downloadButtonContainer').hide(); $('#downloadButtonContainer').hide();
$('#deleteUserContainer').hide(); $('#deleteUserContainer').hide();
$('#logsContainer').hide(); $('#logsContainer').hide();
$('#additional-text2').hide();
$('#additional-text3').hide();
}); });
$('#userDataLink').on('click', function () { $('#userDataLink').on('click', function () {
@ -24,6 +31,9 @@ $(document).ready(function () {
$('#downloadButtonContainer').show(); $('#downloadButtonContainer').show();
$('#deleteUserContainer').hide(); $('#deleteUserContainer').hide();
$('#logsContainer').hide(); $('#logsContainer').hide();
$('#additional-text').hide();
$('#additional-text2').hide();
$('#additional-text2').hide();
}); });
$('#searchUserButton').on('click', function () { $('#searchUserButton').on('click', function () {
@ -41,6 +51,9 @@ $('#deleteUserContainer').show();
$('#userDataContainer').hide(); $('#userDataContainer').hide();
$('#downloadButtonContainer').hide(); $('#downloadButtonContainer').hide();
$('#logsContainer').hide(); $('#logsContainer').hide();
$('#additional-text').hide();
$('#additional-text2').hide();
$('#additional-text3').show();
}); });
$('#downloadButton').on('click', function () { $('#downloadButton').on('click', function () {
@ -54,10 +67,10 @@ $('#logsLink').on('click', function () {
$('#userDataContainer').hide(); $('#userDataContainer').hide();
$('#downloadButtonContainer').hide(); $('#downloadButtonContainer').hide();
$('#deleteUserContainer').hide(); $('#deleteUserContainer').hide();
$('#additional-text').hide();
// Show logs container
$('#logsContainer').show(); $('#logsContainer').show();
$('#additional-text2').hide();
$('#additional-text3').hide();
fetchLogs(); fetchLogs();
}); });
@ -69,6 +82,7 @@ function searchUser(username) {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} else { } else {
alert('No user found with the given username.');
throw new Error(`HTTP error! Status: ${response.status}`); throw new Error(`HTTP error! Status: ${response.status}`);
} }
}) })
@ -393,12 +407,12 @@ function displayLogs(logs) {
// Create the table and header row // Create the table and header row
const table = $('<table>').addClass('logs-table'); const table = $('<table>').addClass('logs-table');
const headerRow = '<tr><th>ID</th><th>Username</th><th>Activity</th><th>Timestamp</th></tr>'; const headerRow = '<thead><tr><th>ID</th><th>Username</th><th>Activity</th><th>Timestamp</th></tr></thead>';
table.append(headerRow); table.append(headerRow);
// Add each log as a row in the table // Add each log as a row in the table
logs.forEach(log => { logs.forEach(log => {
const row = `<tr><td>${log.id}</td><td>${log.username}</td><td>${log.activity}</td><td>${log.timestamp}</td></tr>`; const row = `<tbody><tr><td>${log.id}</td><td>${log.username}</td><td>${log.activity}</td><td>${log.timestamp}</td></tr></tbody>`;
table.append(row); table.append(row);
}); });

View File

@ -1,35 +1,63 @@
/* style.css */ /* style.css */
body { body {
margin: 0; font-family: 'Open Sans', sans-serif;
font-family: 'Arial', sans-serif; background-color: #f4f4f4;
} margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh; /* Ensure the body takes at least the full height of the viewport */
}
#navbar { header {
background-color: #333; background-color: #333;
overflow: hidden; color: #fff;
text-align: center; text-align: center;
} padding: 1em;
#navbar h1 { text-align: left; /* Adjust text alignment to the left */
color: white; }
padding: 14px 16px;
margin: 0;
font-size: 24px;
}
#navbar a { nav {
display: inline-block; background-color: #444;
color: white; padding: 0.5em;
text-align: center; }
padding: 14px 16px;
text-decoration: none;
font-size: 18px;
}
#navbar a:hover { nav a {
background-color: #555; color: #fff;
} text-decoration: none;
padding: 0.5em 1em;
margin: 0 0.5em;
border-radius: 3px;
}
nav a:hover {
background-color: #555;
}
main {
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-image: url('3.jpg');
background-size: cover;
background-repeat: no-repeat;
background-position: center ;
color: #000;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 5px;
margin-left: 20px; /* Adjust the margin-left to shift the entire content to the left */
}
footer {
text-align: center;
padding: 1em;
background-color: #333;
color: #fff;
margin-top: auto; /* Push the footer to the bottom */
}
#content { #content {
padding: 16px; padding: 16px;
@ -60,7 +88,41 @@ body {
td { td {
white-space: nowrap; white-space: nowrap;
} }
.nice-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.nice-table th, .nice-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
.nice-table th {
background-color: #333;
color: #fff;
}
.nice-table tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
.nice-table tbody tr:hover {
background-color: #ddd;
}
.nice-table td:last-child {
text-align: left;
}
/* Style for the "No users available" message */
.nice-table td[colspan="4"] {
text-align: center;
font-style: italic;
color: #888;
}
/* Additional styles for Bootstrap form */ /* Additional styles for Bootstrap form */
form { form {
max-width: 400px; max-width: 400px;
@ -81,146 +143,346 @@ body {
border-color: #0056b3; border-color: #0056b3;
} }
/* User Creation Form Styles */ /* User Creation Form Styles */
.user-creation-container .input-box { form#createUserForm {
margin-bottom: 20px; margin-left: 20px;
width: 100%; }
} .user-creation-container {
.user-creation-container .input-box span.details {
display: block;
font-weight: 500;
margin-bottom: 5px;
}
.user-creation-container .user-details .input-box input {
height: 45px;
width: 100%;
outline: none;
font-size: 16px;
border-radius: 5px;
padding-left: 15px;
border: 1px solid #ccc;
border-bottom-width: 2px;
transition: all 0.3s ease;
}
.user-creation-container .user-details .input-box input:focus,
.user-creation-container .user-details .input-box input:valid {
border-color: #9b59b6;
}
.user-creation-container form .gender-details .gender-title {
font-size: 20px;
font-weight: 500;
}
.user-creation-container form .category {
display: flex; display: flex;
width: 100%;
margin: 14px 0;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; /* Add this line to align items at the start of the cross-axis */
} }
.user-creation-container form .category label { .form-container {
width: 70%;
}
.user-creation-container .user-details {
display: flex; display: flex;
align-items: center; flex-wrap: wrap; /* Allow items to wrap to the next line if needed */
cursor: pointer;
} }
.user-creation-container form .category label .dot { .user-creation-container .user-details .input-box {
height: 18px; width: 48%; /* Adjust as needed, leave some space for margins */
width: 18px; margin-bottom: 10px; /* Add margin between input boxes */
border-radius: 50%;
margin-right: 10px;
background: #d9d9d9;
border: 5px solid transparent;
transition: all 0.3s ease;
} }
#dot-1:checked ~ .user-creation-container form .category label .one, .user-creation-container .user-details .input-box select {
#dot-2:checked ~ .user-creation-container form .category label .two, width: calc(100% - 6px); /* Adjust as needed, considering the select border */
#dot-3:checked ~ .user-creation-container form .category label .three {
background: #9b59b6;
border-color: #d9d9d9;
} }
.user-creation-container form input[type="radio"] { /* Add this to your existing styles */
display: none;
.user-creation-container {
display: flex;
} }
.user-creation-container form .button { #additional-text {
height: 45px; width: 30%; /* Adjust the width as needed */
margin: 35px 0; margin-left: 800px; /* Push it to the right */
margin-top: -750px; /* Adjust the negative margin to move it up */
padding: 10px; /* Add padding for spacing */
background-color: #f4f4f4; /* Add background color if desired */
border-radius: 5px; /* Add border-radius for rounded corners */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Add box shadow for a subtle effect */
border: 1px solid #ddd; /* Add a 1px solid border with light gray color */
font-family: 'Open Sans', sans-serif;
} }
.user-creation-container form .button input { #additional-text p {
height: 100%; font-size: 16px; /* Adjust font size as needed */
width: 100%; line-height: 1.6; /* Adjust line height for better readability */
border-radius: 5px;
border: none;
color: #fff;
font-size: 18px;
font-weight: 500;
letter-spacing: 1px;
cursor: pointer;
transition: all 0.3s ease;
background: linear-gradient(135deg, #71b7e6, #9b59b6);
} }
.user-creation-container form .button input:hover { #additional-text .condition {
background: linear-gradient(-135deg, #71b7e6, #9b59b6); margin-bottom: 10px; /* Add space between conditions */
} }
@media (max-width: 584px) { #additional-text .condition span {
.user-creation-container .user-details .input-box { font-weight: bold; /* Make condition labels bold */
margin-bottom: 20px;
width: 100%;
}
.user-creation-container form .category {
width: 100%;
}
.user-creation-container .content form .user-details {
max-height: 300px;
overflow-y: scroll;
}
.user-creation-container .user-details::-webkit-scrollbar {
width: 5px;
}
} }
@media (max-width: 459px) { #additional-text .condition.error {
.user-creation-container .container .content .category { color: red; /* Change text color for error conditions */
flex-direction: column;
}
} }
#createUserForm { #additional-text .condition.success {
display: none; color: green; /* Change text color for success conditions */
} }
#createUserForm .container { .user-creation-container .input-box {
margin-top: 20px; margin-bottom: 20px;
} width: 100%;
}
#createUserForm form { .user-creation-container .input-box span.details {
max-width: 400px; display: block;
margin: auto; font-weight: 500;
} margin-bottom: 5px;
}
#createUserForm .mb-3 { .user-creation-container .user-details .input-box input {
margin-bottom: 1rem; height: 45px;
} width: 100%;
outline: none;
font-size: 16px;
border-radius: 5px;
padding-left: 15px;
border: 1px solid #ccc;
border-bottom-width: 2px;
transition: all 0.3s ease;
}
#createUserForm .btn-primary { .user-creation-container .user-details .input-box input:focus,
background-color: #007bff; .user-creation-container .user-details .input-box input:valid {
border-color: #007bff; border-color: #9b59b6;
} }
.user-creation-container form .gender-details .gender-title {
font-size: 20px;
font-weight: 500;
}
.user-creation-container form .category {
display: flex;
width: 100%;
margin: 14px 0;
justify-content: space-between;
}
.user-creation-container form .category label {
display: flex;
align-items: center;
cursor: pointer;
}
.user-creation-container form .category label .dot {
height: 18px;
width: 18px;
border-radius: 50%;
margin-right: 10px;
background: #d9d9d9;
border: 5px solid transparent;
transition: all 0.3s ease;
}
#dot-1:checked ~ .user-creation-container form .category label .one,
#dot-2:checked ~ .user-creation-container form .category label .two,
#dot-3:checked ~ .user-creation-container form .category label .three {
background: #9b59b6;
border-color: #d9d9d9;
}
.user-creation-container form input[type="radio"] {
display: none;
}
.user-creation-container form .button {
height: 45px;
margin: 35px 0;
}
.user-creation-container form .button input {
height: 100%;
width: 100%;
border-radius: 5px;
border: none;
color: #fff;
font-size: 18px;
font-weight: 500;
letter-spacing: 1px;
cursor: pointer;
transition: all 0.3s ease;
background: linear-gradient(135deg, #71b7e6, #9b59b6);
}
.user-creation-container form .button input:hover {
background: linear-gradient(-135deg, #71b7e6, #9b59b6);
}
@media (max-width: 584px) {
.user-creation-container .user-details .input-box {
margin-bottom: 20px;
width: 100%;
}
.user-creation-container form .category {
width: 100%;
}
.user-creation-container .content form .user-details {
max-height: 300px;
overflow-y: scroll;
}
.user-creation-container .user-details::-webkit-scrollbar {
width: 5px;
}
}
@media (max-width: 459px) {
.user-creation-container .container .content .category {
flex-direction: column;
}
}
#createUserForm {
display: none;
}
#createUserForm .container {
margin-top: 20px;
}
#createUserForm form {
max-width: 400px;
margin: auto;
}
#createUserForm .mb-3 {
margin-bottom: 1rem;
}
#createUserForm .btn-primary {
background-color: #007bff;
border-color: #007bff;
}
#createUserForm .btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
/* Shift the form to the left */
#createUserForm {
margin-left: 20px;
}
#createUserForm.user-creation-container {
margin-left: 0; /* Reset the margin-left */
max-width: 400px; /* Set a maximum width to the form container */
margin: 20px; /* Add margin for spacing */
}
#createUserForm h3 {
text-align: left; /* Align the heading to the left */
margin-bottom: 20px; /* Add spacing below the heading */
}
#createUserForm .content {
padding: 16px; /* Add padding to the form content */
text-align: left; /* Align the form content to the left */
}
#createUserForm .user-details {
width: 100%; /* Make the user-details section full-width */
}
#createUserForm .input-box {
margin-bottom: 20px;
width: 100%;
}
#createUserForm .input-box span.details {
display: block;
font-weight: 500;
margin-bottom: 5px;
}
#createUserForm .user-details .input-box input {
height: 45px;
width: 100%;
outline: none;
font-size: 16px;
border-radius: 5px;
padding-left: 15px;
border: 1px solid #ccc;
border-bottom-width: 2px;
transition: all 0.3s ease;
}
#resetPasswordFormContainer {
margin-left: 20px; /* Adjust the margin-left to shift the form container to the left */
max-width: 400px; /* Set a maximum width to the form container */
margin: 20px; /* Add margin for spacing */
}
#resetPasswordFormContainer h3 {
text-align: left; /* Align the heading to the left */
margin-bottom: 20px; /* Add spacing below the heading */
}
#resetPasswordFormContainer .content {
padding: 16px; /* Add padding to the form content */
text-align: left; /* Align the form content to the left */
}
#resetPasswordFormContainer .user-details {
width: 100%; /* Make the user-details section full-width */
}
#resetPasswordFormContainer .input-box {
margin-bottom: 20px;
width: 100%;
}
#resetPasswordFormContainer .input-box span.details {
display: block;
font-weight: 500;
margin-bottom: 5px;
}
#resetPasswordFormContainer .user-details .input-box input {
height: 45px;
width: 100%;
outline: none;
font-size: 16px;
border-radius: 5px;
padding-left: 15px;
border: 1px solid #ccc;
border-bottom-width: 2px;
transition: all 0.3s ease;
}
#deleteUserContainer {
margin-left: 20px; /* Adjust the margin-left to shift the container to the left */
max-width: 400px; /* Set a maximum width to the container */
margin: 20px; /* Add margin for spacing */
}
#deleteUserContainer h3 {
text-align: left; /* Align the heading to the left */
margin-bottom: 20px; /* Add spacing below the heading */
}
#deleteUserContainer .search-container {
text-align: left; /* Align the search container to the left */
margin-bottom: 20px; /* Add spacing below the search container */
}
#deleteUserContainer .search-container input {
width: 80%; /* Adjust the width of the search input */
padding: 10px; /* Add padding to the search input */
margin-right: 10px; /* Add spacing to the right of the search input */
}
#deleteUserContainer .search-container button {
padding: 10px;
background-color: #007bff; /* Set background color to match other buttons */
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
/* Hover styles */
&:hover {
background-color: #0056b3; /* Change background color on hover */
}
}
#searchResultsContainer {
display: none;
text-align: left; /* Align the search results container to the left */
}
#searchResultsContainer h4 {
margin-bottom: 10px; /* Add spacing below the search results heading */
}
#createUserForm .btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.logs-table { .logs-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
@ -236,3 +498,62 @@ body {
.logs-table th { .logs-table th {
background-color: #f2f2f2; background-color: #f2f2f2;
} }
#additional-text2 {
width: 30%; /* Adjust the width as needed */
margin-left: 800px; /* Push it to the right */
margin-top: -500px; /* Adjust the negative margin to move it up */
padding: 10px; /* Add padding for spacing */
background-color: #f4f4f4; /* Add background color if desired */
border-radius: 5px; /* Add border-radius for rounded corners */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Add box shadow for a subtle effect */
border: 1px solid #ddd; /* Add a 1px solid border with light gray color */
font-family: 'Open Sans', sans-serif;
}
#additional-text2 p {
font-size: 16px; /* Adjust font size as needed */
line-height: 1.6; /* Adjust line height for better readability */
}
#additional-text2 .condition {
margin-bottom: 10px; /* Add space between conditions */
}
#additional-text2 .condition span {
font-weight: bold; /* Make condition labels bold */
}
#additional-text2 .condition.error {
color: red; /* Change text color for error conditions */
}
#additional-text2 .condition.success {
color: green; /* Change text color for success conditions */
}
#additional-text3 {
width: 30%; /* Adjust the width as needed */
margin-left: 800px; /* Push it to the right */
margin-top: -150px; /* Adjust the negative margin to move it up */
padding: 10px; /* Add padding for spacing */
background-color: #f4f4f4; /* Add background color if desired */
border-radius: 5px; /* Add border-radius for rounded corners */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Add box shadow for a subtle effect */
border: 1px solid #ddd; /* Add a 1px solid border with light gray color */
font-family: 'Open Sans', sans-serif;
}
#additional-text3 p {
font-size: 16px; /* Adjust font size as needed */
line-height: 1.6; /* Adjust line height for better readability */
}
#additional-text3 .condition {
margin-bottom: 10px; /* Add space between conditions */
}
#additional-text3 .condition span {
font-weight: bold; /* Make condition labels bold */
}

View File

@ -1,101 +0,0 @@
/* user-creation.css */
.user-creation-container {
margin-top: 20px;
}
.user-creation-container form {
max-width: 400px;
margin: auto;
}
.user-creation-container .user-details .input-box {
margin-bottom: 15px;
width: 100%;
}
.user-creation-container .user-details .input-box input,
.user-creation-container .user-details .input-box select {
height: 45px;
width: 100%;
outline: none;
font-size: 16px;
border-radius: 5px;
padding-left: 15px;
border: 1px solid #ccc;
border-bottom-width: 2px;
transition: all 0.3s ease;
}
.user-creation-container .user-details .input-box input:focus,
.user-creation-container .user-details .input-box input:valid,
.user-creation-container .user-details .input-box select:focus {
border-color: #9b59b6;
}
.user-creation-container form .category {
display: flex;
width: 100%;
margin: 14px 0;
justify-content: space-between;
}
.user-creation-container form .category select {
height: 45px;
width: 100%; /* Adjusted width to match other input boxes */
outline: none;
font-size: 16px;
border-radius: 5px;
padding-left: 15px;
border: 1px solid #ccc;
transition: all 0.3s ease;
}
.user-creation-container form .button {
height: 45px;
margin: 35px 0;
}
.user-creation-container form .button input {
height: 100%;
width: 100%;
border-radius: 5px;
border: none;
color: #fff;
font-size: 18px;
font-weight: 500;
letter-spacing: 1px;
cursor: pointer;
transition: all 0.3s ease;
background: linear-gradient(135deg, #71b7e6, #9b59b6);
}
.user-creation-container form .button input:hover {
background: linear-gradient(-135deg, #71b7e6, #9b59b6);
}
@media (max-width: 584px) {
.user-creation-container .user-details .input-box {
margin-bottom: 15px;
width: 100%;
}
.user-creation-container form .category {
width: 100%;
}
.user-creation-container .content form .user-details {
max-height: 300px;
overflow-y: scroll;
}
.user-creation-container .user-details::-webkit-scrollbar {
width: 5px;
}
}
@media (max-width: 459px) {
.user-creation-container .container .content .category {
flex-direction: column;
}
}

View File

@ -64,7 +64,6 @@ const apikeyModel = sequelize.define(
module.exports = { apikeyModel }; module.exports = { apikeyModel };
/* /*
class AuthToken extends Model { class AuthToken extends Model {
check(){ check(){

View File

@ -2,14 +2,12 @@ const { sequelize } = require("../database/mySql.js");
const { apikeyModel } = require("../database/model/apikeyModel.js"); const { apikeyModel } = require("../database/model/apikeyModel.js");
const { userModel } = require("../database/model/userModel.js"); const { userModel } = require("../database/model/userModel.js");
const { Op, Sequelize } = require("sequelize"); const { Op, Sequelize } = require("sequelize");
const { hashAPIKey } = require("../functions/bcrypt.js");
const { generateUUID } = require("../functions/generateUUID.js"); const { generateUUID } = require("../functions/generateUUID.js");
const { hashPassword } = require("../functions/bcrypt.js"); const { hashPassword , comparePassword , hashAPIKey } = require("../functions/bcrypt.js");
//helper function
async function getUser() {
const user = await userModel.findAll();
return user;
}
//api/v0/user/register //api/v0/user/register
/* Registering new user /* Registering new user
@ -22,15 +20,76 @@ async function addUser(user) {
//hash password //hash password
let hash = await hashPassword(user.password); let hash = await hashPassword(user.password);
await userModel.create({ const addRes = await userModel.create({
username: user.username, username: user.username,
password: hash, password: hash,
email: user.email, email: user.email,
address: user.address, address: user.address,
phone: user.phone, phone: user.phone,
}); });
if (addRes){
return true;
}
else{
return false;
}
} }
//add token to db
async function addToken(userid , token) {
console.log(userid);
console.log(token);
}
async function loginUser(user) {
//look up username or email in db
const userRes = await userModel.findOne({
where: {
[Op.or]: [
{
username: user.userInfo,
},
{
email: user.userInfo,
},
],
},
})
//if user exists
if (userRes){
//compare password
let match = await comparePassword(user.password, userRes.password);
if (match){
console.log(userRes.id);
console.log(userRes.username);
//generate token
let token = await generateUUID();
//add to db
addToken(userRes.id, token);
//sucessful login
/*
1) generate token
2) store in db and localstorage (maybe hash it?)
3) return userid and username and token and store in localstorage
*/
return { token: token, userid: userRes.id, username: userRes.username };
}
else {
return false;
}
}
else{
return false;
}
}
async function getAPIKey() { async function getAPIKey() {
const apikey = await apikeyModel.findAll(); const apikey = await apikeyModel.findAll();
return apikey; return apikey;
@ -44,8 +103,6 @@ async function getAPIKey() {
5) you give the user rowid-uuidv4 5) you give the user rowid-uuidv4
6) store in database 6) store in database
*/ */
async function addAPIKey(userId, permission) { async function addAPIKey(userId, permission) {
let token = await generateUUID(); let token = await generateUUID();
let usertoken = userId + "-" + token; let usertoken = userId + "-" + token;
@ -66,8 +123,7 @@ async function addAPIKey(userId, permission) {
} }
module.exports = { module.exports = {
getUser,
addUser, addUser,
getAPIKey, loginUser,
addAPIKey, addAPIKey,
}; };

View File

@ -29,9 +29,14 @@ async function hashAPIKey(apikey) {
return await bcrypt.hash(apikey, saltRounds); return await bcrypt.hash(apikey, saltRounds);
} }
async function comparePassword(password, hash) {
return await bcrypt.compare(password, hash);
}
module.exports = { module.exports = {
hashPassword, hashPassword,
hashAPIKey, hashAPIKey,
comparePassword
}; };

View File

@ -0,0 +1,4 @@
/*v
1) check if token proided by JSON req is valid against db
2) if valid its passed to next()
*/

View File

@ -28,12 +28,19 @@ app.use("/", require("../routes/render")); //consumerWebsite\routes\render.js
// Catch 404 and forward to error handler. If none of the above routes are // Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called. // used, this is what will be called.
app.use(function (req, res, next) { app.use(function (req, res, next) {
if (req.is("application/json")) {
var err = new Error("Not Found"); var err = new Error("Not Found");
err.message = "Page not found"; err.message = "Page not found";
err.status = 404; err.status = 404;
next(err); next(err);
}
else{
res.status(404).render("404");
}
}); });
// Error handler. This is where `next()` will go on error // Error handler. This is where `next()` will go on error
app.use(function (err, req, res, next) { app.use(function (err, req, res, next) {
console.error(err.status || res.status, err.name, req.method, req.url); console.error(err.status || res.status, err.name, req.method, req.url);

View File

@ -66,7 +66,7 @@ body {
header { header {
font-size: 30px; font-size: 30px;
text-align: center; text-align: center;
color: #fff; color: #000000;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
} }
@ -93,6 +93,7 @@ body {
margin-top: 50px; margin-top: 50px;
} }
form form
input { input {
height: 60px; height: 60px;
@ -106,6 +107,7 @@ form
border-radius: 8px; border-radius: 8px;
background: #fff; background: #fff;
} }
.form.login .form.login
input { input {
border: 1px border: 1px

View File

@ -12,7 +12,7 @@ app.util = (function (app) {
function actionMessage(message, $target, type, callback) { function actionMessage(message, $target, type, callback) {
message = message || ""; message = message || "";
$target = $target.closest("div.card").find(".actionMessage"); $target = $target.closest("div.iot-card").find(".actionMessage");
type = type || "info"; type = type || "info";
callback = callback || function () {}; callback = callback || function () {};
@ -29,9 +29,6 @@ app.util = (function (app) {
}); });
} else { } else {
if (type) $target.addClass("bg-" + type); if (type) $target.addClass("bg-" + type);
message =
'<button class="action-close btn btn-sm btn-outline-dark float-right"><i class="fa-solid fa-xmark"></i></button>' +
message;
$target.html(message).slideDown("fast"); $target.html(message).slideDown("fast");
} }
setTimeout(callback, 10); setTimeout(callback, 10);
@ -137,6 +134,7 @@ app.api = (function (app) {
complete: function (res, text) { complete: function (res, text) {
callback( callback(
text !== "success" ? res.statusText : null, text !== "success" ? res.statusText : null,
//console.log(res.responseText),
JSON.parse(res.responseText), JSON.parse(res.responseText),
res.status res.status
); );
@ -153,6 +151,14 @@ app.auth = (function (app) {
localStorage.setItem("APIToken", token); localStorage.setItem("APIToken", token);
} }
function setUserId(userId) {
localStorage.setItem("userId", userId);
}
function setUsername(username) {
localStorage.setItem("username", username);
}
function getToken() { function getToken() {
return localStorage.getItem("APIToken"); return localStorage.getItem("APIToken");
} }
@ -167,7 +173,7 @@ app.auth = (function (app) {
callback(null, false); callback(null, false);
} }
} }
/*
function logIn(args, callback) { function logIn(args, callback) {
app.api.post("auth/login", args, function (error, data) { app.api.post("auth/login", args, function (error, data) {
if (data.login) { if (data.login) {
@ -176,9 +182,14 @@ app.auth = (function (app) {
callback(error, !!data.token); callback(error, !!data.token);
}); });
} }
*/
function logOut(callback) { function logOut(callback) {
localStorage.removeItem("APIToken"); localStorage.removeItem("APIToken");
localStorage.removeItem("userId");
localStorage.removeItem("username");
//remove token from db NOT the api key.
callback(); callback();
} }
@ -196,46 +207,50 @@ app.auth = (function (app) {
function logInRedirect() { function logInRedirect() {
window.location.href = window.location.href =
//window.location.href = location.href.replace(location.origin+'/login', '') || '/'
location.href.replace(location.replace(`/login`)) || "/"; location.href.replace(location.replace(`/login`)) || "/";
} }
function homeRedirect() {
window.location.href = location.href.replace(location.replace(`/`)) || "/";
}
return { return {
getToken: getToken, getToken: getToken,
setToken: setToken, setToken: setToken,
setUserId: setUserId,
setUsername: setUsername,
isLoggedIn: isLoggedIn, isLoggedIn: isLoggedIn,
logIn: logIn, //logIn: logIn,
logOut: logOut, logOut: logOut,
forceLogin, forceLogin,
logInRedirect, logInRedirect,
homeRedirect,
}; };
})(app); })(app);
//ajax form submit //ajax form submit
function formAJAX( btn, del ) { function formAJAX(btn, del) {
event.preventDefault(); // avoid to execute the actual submit of the form. event.preventDefault(); // avoid to execute the actual submit of the form.
var $form = $(btn).closest( '[action]' ); // gets the 'form' parent var $form = $(btn).closest("[action]"); // gets the 'form' parent
var formData = $form.find( '[name]' ).serializeObject(); // builds query formDataing var formData = $form.find("[name]").serializeObject(); // builds query formDataing
var method = $form.attr('method') || 'post'; var method = $form.attr("method") || "post";
// if( !$form.validate()) { // if( !$form.validate()) {
// app.util.actionMessage('Please fix the form errors.', $form, 'danger') // app.util.actionMessage('Please fix the form errors.', $form, 'danger')
// return false; // return false;
// } // }
app.util.actionMessage( app.util.actionMessage("Loading...", $form, "info");
'<div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div>',
$form,
'info'
);
//console.log('Data being sent to', $form.attr('action'), formData) //console.log('Data being sent to', $form.attr('action'), formData)
app.api[method]($form.attr('action'), formData, function(error, data){ app.api[method]($form.attr("action"), formData, function (error, data) {
//console.log('Data back from the server', error, data) //console.log('Data back from the server', error, data)
app.util.actionMessage(data.message, $form, error ? 'danger' : 'success'); //re-populate table app.util.actionMessage(data.message, $form, error ? "danger" : "success"); //re-populate table
if(!error){ if (!error) {
$form.trigger("reset"); $form.trigger("reset");
eval($form.attr('evalAJAX')); //gets JS to run after completion eval($form.attr("evalAJAX")); //gets JS to run after completion
} }
}); });
} }

View File

@ -77,10 +77,4 @@ router.get('/contact', function(req, res, next) {
res.render('contact'); res.render('contact');
}); });
//404 page
router.get('*', function(req, res, next) {
res.render('404');
});
module.exports = router; module.exports = router;

View File

@ -1,41 +1,60 @@
const { getUser, addUser } = require("../functions/apiDatabase.js"); const { addUser, loginUser } = require("../functions/apiDatabase.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) => {
try {
const location = await getUser();
res.status(200).json(location);
} catch (error) {
console.error(error);
next(error);
}
});
// /user/register // /user/register
router.post("/register", async (req, res, next) => { router.post("/register", async (req, res, next) => {
try { try {
console.log("this is " , req.body); let Res = await addUser(req.body);
await addUser(req.body); if (Res == false) {
res.status(200).json({ register: true }); let error = new Error("UserRegFailed");
error.message = "The user failed to be craated";
error.status = 400;
return next(error);
}
else{
return res.json({
message: "User created successfully",
});
}
} catch (error) { } catch (error) {
console.error(error); console.error(error);
next(error); next(error);
} }
}); });
//login //login
router.post("/login", async (req, res, next) => {
try {
let Res = await loginUser(req.body);
if (Res == false) {
let error = new Error("User Login Failed");
error.status = 400;
return next(error);
}
else{
//pass res back to form to be set in local storage
console.log(Res);
return res.json({
message: "User login successfully",
token: Res.token,
userId: Res.userid,
username: Res.username,
});
}
} catch (error) {
console.error(error);
next(error);
}
});
//update //update
//delete //delete
//getbyid //getbyid
module.exports = router; module.exports = router;
/* /*
curl localhost/api/v0/user/register -H "Content-Type: application/json" -X POST -d '{"username": curl localhost/api/v0/user/register -H "Content-Type: application/json" -X POST -d '{"username":

View File

@ -2,14 +2,11 @@
<body> <body>
<section class="wrapper"> <section class="wrapper">
<div class="form signup" > <div class="form signup iot-card">
<!--<div class="form signup card" --> <!--<div class="form signup card" -->
<header>Signup</header> <header>Signup</header>
<!-- Return message from api -->
<div class="actionMessage" style="display:none"></div>
<!-- localhost/api/v0/user/register --> <!-- localhost/api/v0/user/register -->
<!-- evalAjax Fires when status 200 is returned -->
<!-- evalAjax Fires when status is returned -->
<form action="user/register" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();"> <form action="user/register" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();">
<input type="text" name="username" placeholder="Username" required /> <input type="text" name="username" placeholder="Username" required />
<input type="text" name="email" placeholder="Email" required /> <input type="text" name="email" placeholder="Email" required />
@ -21,7 +18,7 @@
</form> </form>
</div> </div>
<div class="form login"> <div class="form login iot-card">
<header>Login</header> <header>Login</header>
<div class="card-header shadow actionMessage" style="display:none"></div> <div class="card-header shadow actionMessage" style="display:none"></div>
<!-- evalAjax Fires when status 200 is returned --> <!-- evalAjax Fires when status 200 is returned -->
@ -34,7 +31,7 @@
<input type="text" name="userInfo" placeholder="Email address | Username" required /> <input type="text" name="userInfo" placeholder="Email address | Username" required />
<input type="password" name="password" placeholder="Password" required /> <input type="password" name="password" placeholder="Password" required />
<a href="/forgotPassword">Forgot password?</a> <a href="/resetPassword">Forgot password?</a>
<input type="submit" value="Login" /> <input type="submit" value="Login" />
</form> </form>
</div> </div>