FIrst commit

This commit is contained in:
2023-11-23 16:01:56 +01:00
commit 5aab318e79
14 changed files with 1193 additions and 0 deletions

55
.eslintrc.json Normal file
View File

@@ -0,0 +1,55 @@
{
"extends": [
"eslint:recommended",
"plugin:security/recommended"
],
"env": {
"node": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 2022
},
"rules": {
"arrow-spacing": ["warn", { "before": true, "after": true }],
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": "error",
"comma-style": "error",
"curly": ["error", "multi-line", "consistent"],
"dot-location": ["error", "property"],
"handle-callback-err": "off",
"indent": ["error", "tab"],
"keyword-spacing": "error",
"max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }],
"no-console": "off",
"no-empty-function": "error",
"no-floating-decimal": "error",
"no-inline-comments": "error",
"no-lonely-if": "error",
"no-multi-spaces": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
"no-trailing-spaces": ["error"],
"no-var": "error",
"object-curly-spacing": ["error", "always"],
"prefer-const": "error",
"quotes": ["error", "single"],
"semi": ["error", "always"],
"space-before-blocks": "error",
"space-before-function-paren": ["error", {
"anonymous": "never",
"named": "never",
"asyncArrow": "always"
}],
"space-in-parens": "error",
"space-infix-ops": "error",
"space-unary-ops": "error",
"spaced-comment": "error",
"yoda": "error",
"security/detect-non-literal-fs-filename": "off",
"security/detect-non-literal-require": "off",
"security/detect-object-injection": "off"
}
}

175
.gitignore vendored Normal file
View File

@@ -0,0 +1,175 @@
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Caches
.cache
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store

109
Classes/Computer.js Normal file
View File

@@ -0,0 +1,109 @@
import { database } from './Database';
class Computer {
constructor(id, brand, model, state, status) {
this.id = id;
this.brand = brand;
this.model = model;
this.status = status;
this.state = state;
}
async get (id) {
try {
const rows = await database.execute('SELECT * FROM computers WHERE id = ?', [id]);
if (rows.length === 0) {
throw new Error('Computer not found');
} else {
this.id = rows[0].id;
this.brand = rows[0].brand;
this.model = rows[0].model;
this.status = rows[0].status;
this.state = rows[0].state;
}
} catch (error) {
console.error('Error getting computer:', error);
throw error;
}
}
async create() {
try {
const result = await database.execute(
'INSERT INTO computers (brand, model, state, status) VALUES (?, ?, ?, ?)',
[this.brand, this.model, this.state, this.status]
);
this.id = result.insertId;
} catch (error) {
console.error('Error creating computer:', error);
throw error;
}
}
async update() {
try {
await database.execute(
'UPDATE computers SET brand = ?, model = ?, state = ?, status = ? WHERE id = ?',
[this.brand, this.model, this.state, this.status, this.id]
);
} catch (error) {
console.error('Error updating computer:', error);
throw error;
}
}
async delete() {
try {
await database.execute('DELETE FROM computers WHERE id = ?', [this.id]);
} catch (error) {
console.error('Error deleting computer:', error);
throw error;
}
}
}
// Computer list class
class Computers extends Array {
async getAll() {
try {
const rows = await database.execute('SELECT * FROM computers');
this.length = 0; // Clear the existing array
rows.forEach(row => {
const computer = new Computer(row.id, row.brand, row.model, row.state, row.status);
this.push(computer);
});
} catch (error) {
console.error('Error getting computers:', error);
throw error;
}
}
async create(brand, model, state, status) {
try {
const computer = new Computer(null, brand, model, state, status);
await computer.create();
this.push(computer);
} catch (error) {
console.error('Error creating computer:', error);
throw error;
}
}
async delete(id) {
try {
const computerIndex = this.findIndex(computer => computer.id === id);
if (computerIndex === -1) {
throw new Error('Computer not found');
}
const computer = this[computerIndex];
await computer.delete();
this.splice(computerIndex, 1);
} catch (error) {
console.error('Error deleting computer:', error);
throw error;
}
}
}
export { Computer, Computers };

52
Classes/Database.js Normal file
View File

@@ -0,0 +1,52 @@
const mysql = require('mysql2/promise');
class Database {
constructor(config) {
this.pool = mysql.createPool(config);
}
async query(sql, params) {
const connection = await this.pool.getConnection();
try {
const [rows] = await connection.query(sql, params);
return rows;
} finally {
connection.release();
}
}
async execute(sql, params) {
const connection = await this.pool.getConnection();
try {
const [result] = await connection.execute(sql, params);
return result;
} finally {
connection.release();
}
}
async transaction(callback) {
const connection = await this.pool.getConnection();
try {
await connection.beginTransaction();
const result = await callback(connection);
await connection.commit();
return result;
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
}
}
const database = new Database({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME
});
export { Database, database };

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# inventory
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.js
```
This project was created using `bun init` in bun v1.0.13. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.

83
Routes/computer.js Normal file
View File

@@ -0,0 +1,83 @@
import express from 'express';
import { Computer, Computers } from '../Classes/Computer';
const router = express.Router();
// GET all computers
router.get('/', async (req, res) => {
const computers = new Computers();
await computers.getAll();
try {
res.status(200).json(computers);
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// GET a computer by ID
router.get('/:id', async (req, res) => {
try {
const computer = new Computer();
await computer.get(req.params.id);
if (computer.brand === null) {
res.status(404).json({ error: 'Computer not found' });
} else {
res.status(200).json(computer);
}
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// POST a new computer
router.post('/', async (req, res) => {
try {
const { brand, model, state, status } = req.body;
if (!brand || !model || !state || !status) {
res.status(400).json({ error: 'Bad Request' });
}
const computer = new Computer(null, brand, model, state, status);
await computer.create();
res.status(201).json({ message: 'Computer created successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// PUT (update) a computer by ID
router.put('/:id', async (req, res) => {
try {
const computer = new Computer();
await computer.get(req.params.id);
if (computer.brand === null) {
res.status(404).json({ error: 'Computer not found' });
}
const { brand, model, state, status } = req.body;
computer.brand = brand;
computer.model = model;
computer.state = state;
computer.status = status;
await computer.update();
res.status(200).json({ message: 'Computer updated successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// DELETE a computer by ID
router.delete('/:id', async (req, res) => {
try {
const computer = new Computer();
await computer.get(req.params.id);
if (computer.brand === null) {
res.status(404).json({ error: 'Computer not found' });
}
await computer.delete();
res.status(200).json({ message: 'Computer deleted successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
export default router;

67
Routes/user.js Normal file
View File

@@ -0,0 +1,67 @@
import express from 'express';
import { database } from '../Classes/Database';
import jwt from 'jsonwebtoken';
const router = express.Router();
// POST to login using username and password
router.post('/login', async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
res.status(400).json({ error: 'Bad Request' });
}
const rows = await database.execute('SELECT * FROM users WHERE username = ?', [username]);
if (rows.length === 0) {
res.status(401).json({ error: 'Unauthorized' });
} else {
const isPasswordValid = await Bun.password.verify(password, rows[0].password);
if (!isPasswordValid) {
res.status(401).json({ error: 'Unauthorized' });
} else {
const token = jwt.sign({ username, password }, process.env.SECRET);
res.status(200).json({ token });
}
}
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// POST to check if token is valid
router.post('/verify', async (req, res) => {
try {
const token = req.body.token;
if (!token) {
res.status(401).json({ error: 'Unauthorized' });
} else {
jwt.verify(token, process.env.SECRET, async (err, decoded) => {
if (err) res.status(401).json({ error: 'Unauthorized' });
const rows = await database.execute('SELECT * FROM users WHERE username = ?', [decoded.username]);
if (rows.length === 0) res.status(401).json({ error: 'Unauthorized' });
const isPasswordValid = await Bun.password.verify(decoded.password, rows[0].password);
if (!isPasswordValid) res.status(401).json({ error: 'Unauthorized' });
res.status(200).json({ message: 'Authorized' });
});
}
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// POST to register a new user
router.post('/register', async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
res.status(400).json({ error: 'Bad Request' });
}
const hashedPassword = await Bun.password.hash(password);
await database.execute('INSERT INTO users (username, password) VALUES (?, ?)', [username, hashedPassword]);
res.status(201).json({ message: 'User created successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
export default router;

BIN
bun.lockb Executable file

Binary file not shown.

33
database.sql Normal file
View File

@@ -0,0 +1,33 @@
SET default_storage_engine = InnoDB;
DROP DATABASE IF EXISTS `inventory`;
CREATE DATABASE IF NOT EXISTS `inventory`
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
DROP USER IF EXISTS 'inventory';
CREATE USER 'inventory'@'localhost' IDENTIFIED BY 'irCd3CH9MELH7Pa9KEHWxoz4S987iGWCP+JQZe4w+3s=';
GRANT ALL PRIVILEGES ON inventory.* TO 'inventory'@'localhost';
USE `inventory`;
CREATE TABLE IF NOT EXISTS computers (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
brand VARCHAR(255) NOT NULL,
model VARCHAR(255) NOT NULL,
status VARCHAR(255) NOT NULL,
state VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
INSERT INTO computers (brand, model, status, state) VALUES
('Dell', 'Latitude 7400', 'Available', 'RAS'),
('Dell', 'Latitude 7400', 'Available', 'RAS'),
('Dell', 'Latitude 7400', 'Available', 'RAS');
CREATE TABLE IF NOT EXISTS users (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;

40
index.js Normal file
View File

@@ -0,0 +1,40 @@
import express from 'express';
import morgan from 'morgan';
import computerRouter from './Routes/computer.js';
import userRouter from './Routes/user.js';
import jwt from 'jsonwebtoken';
const app = express();
app.use(express.json());
app.use(morgan('dev'));
app.use(express.static('public'));
const authMiddleware = async (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ message: 'No auth token provided' });
}
try {
const decoded = jwt.verify(token, process.env.SECRET);
if (!decoded) {
return res.status(401).json({ message: 'Invalid auth token' });
}
const rows = await database.execute('SELECT * FROM users WHERE username = ?', [decoded.username]);
if (rows.length === 0) res.status(401).json({ error: 'Unauthorized' });
const isPasswordValid = await Bun.password.verify(decoded.password, rows[0].password);
if (!isPasswordValid) res.status(401).json({ error: 'Unauthorized' });
next();
} catch (error) {
return res.status(401).json({ message: 'Invalid auth token' });
}
};
computerRouter.use(authMiddleware);
app.use('/computer', computerRouter);
app.use('/user', userRouter);
// Start the server
app.listen(3000, () => {
console.log('Server is running on port 3000');
});

22
jsconfig.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"types": [
"bun-types" // add Bun global
]
}
}

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "inventory",
"module": "index.js",
"type": "module",
"devDependencies": {
"bun-types": "latest",
"eslint": "^8.54.0"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"mysql2": "^3.6.3"
}
}

354
public/c/index.html Normal file
View File

@@ -0,0 +1,354 @@
<!DOCTYPE html>
<html>
<head>
<title>Computer Inventory</title>
<style>
h1, h2 {
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd; /* Add border-right for separation */
}
th:last-child, td:last-child {
border-right: none; /* Remove border-right for last column */
}
tr:hover {
background-color: #f5f5f5;
}
.edit-button {
background-color: #4CAF50;
color: white;
padding: 4px 8px; /* Reduce padding */
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 15px;
}
.edit-button:hover {
background-color: #45a049;
}
.delete-button {
background-color: #f44336;
color: white;
padding: 4px 8px; /* Reduce padding */
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 15px;
}
.delete-button:hover {
background-color: #d32f2f;
}
.add-button {
background-color: #2196F3;
color: white;
padding: 4px 8px; /* Reduce padding */
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 18px; /* Reduce font size */
}
.add-button:hover {
background-color: #1976D2;
}
.computer-table-container {
width: 95vw;
padding: 16px;
background-color: #f2f2f2;
margin: 0 auto;
border-radius: 8px; /* Add rounded border */
}
.computer-table {
width: 100%;
border-collapse: collapse;
}
.computer-table th, .computer-table td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd; /* Add border-right for separation */
}
.computer-table th:last-child, .computer-table td:last-child {
border-right: none; /* Remove border-right for last column */
}
.computer-table tr:hover {
background-color: #f5f5f5;
}
.computer-table input[type="text"] {
width: 100%;
padding: 8px;
margin-bottom: 12px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.form-container {
text-align: center; /* Center the form */
}
.form-container input[type="text"] {
width: 100%;
padding: 8px;
margin-bottom: 12px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
</style>
</head>
<body>
<h1>Computer Inventory</h1>
<br>
<div class="computer-table-container">
<table class="computer-table">
<tr>
<th>ID</th>
<th>Brand</th>
<th>Model</th>
<th>Status</th>
<th>State</th>
<th>Actions</th>
</tr>
</table>
<br>
<div class="form-container">
<input type="text" id="brand" name="brand" placeholder="Brand" required>
<input type="text" id="model" name="model" placeholder="Model" required>
<input type="text" id="status" name="status" placeholder="Status" required>
<input type="text" id="state" name="state" placeholder="State" required>
<button class="add-button" onclick="addComputer()">Add Computer</button>
</div>
</div>
<script>
function createTable() {
fetch(`http://localhost:3000/computer`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `${localStorage.getItem('token')}`
}
})
.then(response => response.json())
.then(data => {
const table = document.querySelector('.computer-table');
table.innerHTML = '<tr><th>ID</th><th>Brand</th><th>Model</th><th>Status</th><th>State</th><th>Actions</th></tr>';
data.forEach(computer => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${computer.id}</td>
<td>${computer.brand}</td>
<td>${computer.model}</td>
<td>${computer.status}</td>
<td>${computer.state}</td>
<td>
<button class="edit-button" onclick="editComputer(${computer.id})">Edit</button>
<button class="delete-button" onclick="deleteComputer(${computer.id})">Delete</button>
</td>
`;
document.querySelector('.computer-table').appendChild(row);
});
})
.catch(error => console.error(error));
}
// Function to handle edit button click
function editComputer(id) {
const table = document.querySelector('.computer-table');
const rows = table.querySelectorAll('tr');
let row;
for(let i = 0; i < rows.length; i++) {
if (rows[i].cells[0].innerText == id) {
row = rows[i];
break;
}
}
// Get the cells of the row
const cells = row.querySelectorAll('td');
// Replace the "Edit" button with a checkmark and a crossmark
const editButton = row.querySelector('.edit-button');
editButton.innerHTML = '&#10003;'; // Checkmark button
editButton.classList.add('confirm-button');
editButton.setAttribute('onclick', `confirmEdit(${id})`);
const deleteButton = row.querySelector('.delete-button');
deleteButton.innerHTML = '&#10005;'; // Crossmark button
deleteButton.classList.add('cancel-button');
deleteButton.setAttribute('onclick', `cancelEdit(${id})`);
// Enable editing for each cell
cells.forEach(cell => {
if (cell.innerText == id || cell.innerText == "✓ ✕") return;
const value = cell.innerText;
cell.innerHTML = `<input type="text" value="${value}">`;
});
}
// Function to handle confirm edit button click
function confirmEdit(id) {
// Get the row of the computer being edited
const table = document.querySelector('.computer-table');
const rows = table.querySelectorAll('tr');
let row;
for(let i = 0; i < rows.length; i++) {
if (rows[i].cells[0].innerText == id) {
row = rows[i];
break;
}
}
// Get the cells of the row
const cells = row.querySelectorAll('td');
const values = [];
cells.forEach(cell => {
if (cell.innerText == id || cell.innerText == "✓ ✕") return;
const value = cell.querySelector('input').value;
values.push(value);
cell.innerHTML = `${value}`;
});
// Send a PUT request to update the computer
fetch(`http://localhost:3000/computer/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `${localStorage.getItem('token')}`
},
body: JSON.stringify({
brand: values[0],
model: values[1],
status: values[2],
state: values[3]
})
})
.then(response => {
if (response.ok) {
createTable();
} else {
alert('Failed to update computer');
console.error('Failed to update computer');
}
})
.catch(error => console.error(error));
}
// Function to handle cancel edit button click
function cancelEdit(id) {
createTable();
}
// Function to handle delete button click
function deleteComputer(id) {
// Display a confirmation dialog
const confirmDelete = confirm("Are you sure you want to delete this computer?");
if (confirmDelete) {
// Send a DELETE request to the server to delete the computer
fetch(`http://localhost:3000/computer/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `${localStorage.getItem('token')}`
},
})
.then(response => {
if (response.ok) {
// Refresh the table by re-fetching the computer data
createTable();
} else {
alert('Failed to delete computer');
console.error('Failed to delete computer');
}
})
.catch(error => console.error(error));
}
}
// Function to handle add computer button click
function addComputer() {
const brand = document.getElementById('brand').value;
const model = document.getElementById('model').value;
const status = document.getElementById('status').value;
const state = document.getElementById('state').value;
// Send a POST request to add the computer
fetch('http://localhost:3000/computer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `${localStorage.getItem('token')}`
},
body: JSON.stringify({
brand: brand,
model: model,
status: status,
state: state
})
})
.then(response => {
if (response.ok) {
createTable();
document.getElementById('brand').value = '';
document.getElementById('model').value = '';
document.getElementById('status').value = '';
document.getElementById('state').value = '';
} else {
alert('Failed to add computer');
console.error('Failed to add computer');
}
})
.catch(error => console.error(error));
}
// check if user is logged in
window.onload = checkIfLoggedIn();
async function checkIfLoggedIn() {
const token = localStorage.getItem('token');
if (token) {
const valid = await checkIfTokenIsValid(token);
if (!valid) {
localStorage.removeItem('token');
window.location.href = '../';
}
else {
createTable();
}
}
else {
window.location.href = '../';
}
}
async function checkIfTokenIsValid(token) {
// Make a POST request to the login endpoint
return await fetch('http://localhost:3000/user/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
})
.then(response => {
if (response.status === 200) {
return true;
} else {
return false;
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred');
});
}
</script>
</body>
</html>

170
public/index.html Normal file
View File

@@ -0,0 +1,170 @@
<!-- FILEPATH: /home/emerald/Documents/IPR/Inventory/public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Login Page</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
.container {
text-align: center;
}
.menu {
margin-top: 20px;
}
/* CSS styles for inputs */
input[type="text"],
input[type="password"] {
padding: 10px;
margin-bottom: 10px;
width: 200px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button[type="submit"] {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button[type="submit"]:hover {
background-color: #45a049;
}
/* CSS styles for the selection menu */
.selection-menu {
display: none;
}
.selection-menu h2 {
margin-top: 20px;
}
.selection-menu button {
margin: 10px;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.selection-menu button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<div class="container">
<form id="loginForm">
<h1>Login</h1>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required><br><br>
<button type="submit">Login</button>
</form>
<div id="menu" class="menu selection-menu">
<h2>Menu</h2>
<button onclick="redirectTo('computers')">Computers</button>
<button onclick="redirectTo('tablets')">Tablets</button>
<button onclick="redirectTo('projectors')">Projectors</button>
</div>
</div>
<script>
const loginForm = document.getElementById('loginForm');
const menu = document.getElementById('menu');
loginForm.addEventListener('submit', function(event) {
event.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
// Make a POST request to the login endpoint
fetch('http://localhost:3000/user/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
})
.then(response => response.json())
.then(data => {
// Check if login is valid
if (data.token) {
localStorage.setItem('token', data.token);
loginForm.style.display = 'none'; // Hide the login form
menu.style.display = 'block';
} else {
alert('Invalid login');
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred');
});
});
function redirectTo(page) {
// Redirect to the proper page based on the user's choice
window.location.href = `/${page.charAt(0)}`;
}
// check if user is logged in
window.onload = checkIfLoggedIn();
async function checkIfLoggedIn() {
const token = localStorage.getItem('token');
if (token) {
loginForm.style.display = 'none'; // Hide the login form
menu.style.display = 'block';
const valid = await checkIfTokenIsValid(token);
if (!valid) {
localStorage.removeItem('token');
loginForm.style.display = 'block'; // Show the login form
menu.style.display = 'none';
}
}
}
async function checkIfTokenIsValid(token) {
// Make a POST request to the login endpoint
return await fetch('http://localhost:3000/user/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ token })
})
.then(response => {
if (response.status === 200) {
return true;
} else {
return false;
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred');
});
}
</script>
</body>
</html>