diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..64abe40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+package-lock.json
+*.sqlite
+.env
diff --git a/index.js b/index.js
index 88b1dea..137366e 100644
--- a/index.js
+++ b/index.js
@@ -1,6 +1,7 @@
-const { launchWeb } = require('./web.js');
-const { EventEmitter } = require('events');
-const Sequelize = require('sequelize');
+const { launchWeb } = require("./web.js");
+const { EventEmitter } = require("events");
+const Sequelize = require("sequelize");
+require('dotenv').config()
const sequelize = new Sequelize("database", "user", "password", {
host: "localhost",
@@ -21,35 +22,41 @@ const contactsDB = sequelize.define("contacts", {
type: Sequelize.STRING,
},
called: {
- type: Sequelize.INTEGER,// 0: not called - 1: called - 2: ongoing call (- 3: no response ?)
+ type: Sequelize.INTEGER, // 0: not called - 1: called - 2: ongoing call (- 3: no response ?)
defaultValue: false,
},
vote: {
type: Sequelize.BOOLEAN,
defaultValue: false,
- }
+ },
});
contactsDB.sync();
global.database = {};
-global.database.contacts = contactsDB
+global.database.contacts = contactsDB;
const submitEvent = new EventEmitter();
launchWeb(submitEvent);
-submitEvent.on('call', async (call) => {
+submitEvent.on("call", async (call) => {
let vote;
- if(call.vote) {
+ if (call.vote) {
console.log(`${call.phone} va voter`);
vote = 1;
} else {
console.log(`${call.phone} ne va pas voter`);
vote = 0;
}
- await global.database.contacts.update({ vote: vote }, { where: { phone: call.phone } })
+ await global.database.contacts.update(
+ { vote: vote, called: 1 },
+ { where: { phone: call.phone } },
+ );
});
-submitEvent.on('add', (request) => {
+submitEvent.on("add", (request) => {
console.log(request);
});
+global.events = {
+ submitEvent: submitEvent,
+}
diff --git a/package.json b/package.json
index 7923993..96b2e37 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,11 @@
"author": "Ninjdai",
"license": "MIT",
"dependencies": {
+ "dotenv": "^16.3.1",
"express": "^4.18.2",
+ "express-session": "^1.17.3",
"sequelize": "^6.35.1",
+ "serve-favicon": "^2.5.0",
"sqlite3": "^5.1.6"
}
}
diff --git a/src/html/callHTML.js b/src/html/callHTML.js
index 7c0e7e7..78bb181 100644
--- a/src/html/callHTML.js
+++ b/src/html/callHTML.js
@@ -1,4 +1,6 @@
-function generateCallHTML(phoneNumber, name, callcount) {
+const { navbar } = require('../../utils/navbar.js');
+
+function generateCallHTML(phoneNumber, name, callcount, session) {
const head = `
AutoCallMenu`;
const form = `
`;
- const body = `Bienvenue :3
Appel: ${name} - ${phoneNumber}
${callcount} appels restants
${form}`;
+ const body = `${navbar(session)}Bienvenue :3
Appel: ${name} - ${phoneNumber}
${callcount} appels restants
${form}`;
return `${head}${body}`;
}
diff --git a/src/html/pages/inputs/add.js b/src/html/pages/inputs/add.js
new file mode 100644
index 0000000..1cea5fe
--- /dev/null
+++ b/src/html/pages/inputs/add.js
@@ -0,0 +1,8 @@
+module.exports = {
+ path: "/inputs/add",
+ requiresLogin: true,
+ type: "post",
+ async execute(request, response) {
+ global.events.submitEvent.emit("add", request.body);
+ },
+}
diff --git a/src/html/pages/inputs/call.js b/src/html/pages/inputs/call.js
new file mode 100644
index 0000000..74d7391
--- /dev/null
+++ b/src/html/pages/inputs/call.js
@@ -0,0 +1,9 @@
+module.exports = {
+ path: "/inputs/call",
+ requiresLogin: true,
+ type: "post",
+ async execute(request, response) {
+ global.events.submitEvent.emit("call", request.body);
+ response.redirect("/calls");
+ },
+}
diff --git a/src/html/pages/root/call.js b/src/html/pages/root/call.js
new file mode 100644
index 0000000..f4a8ea0
--- /dev/null
+++ b/src/html/pages/root/call.js
@@ -0,0 +1,36 @@
+const { generateCallHTML } = require("../../../../src/html/callHTML.js");
+const { getRandomUser } = require('../../../../src/database/getUser.js');
+const { navbar } = require('../../../../utils/navbar.js');
+
+module.exports = {
+ path: "/calls",
+ requiresLogin: true,
+ type: "get",
+ async execute(request, response) {
+ const res = await generateCallResponse(request.session);
+ response.send(res.replaceAll("", navbar(request.session)));
+ },
+}
+
+async function generateCallResponse(session) {
+ const user = await getRandomUser();
+ let callHTML;
+ if (user) callHTML = generateCallHTML(user.phone, user.name, user.count, session);
+ else callHTML = `
+
+
+
+
+
+
+
+ AutoCallMenu
+
+
+
+ Bienvenue :3
+ 0 appels restants !
+
+ `;
+ return callHTML;
+}
diff --git a/src/html/pages/root/index.js b/src/html/pages/root/index.js
new file mode 100644
index 0000000..455357e
--- /dev/null
+++ b/src/html/pages/root/index.js
@@ -0,0 +1,11 @@
+const { readFile } = require('fs').promises;
+const { navbar } = require('../../../../utils/navbar.js');
+
+module.exports = {
+ path: "/",
+ type: "get",
+ async execute(request, response) {
+ const res = await readFile(`${process.env.WWW}/index.html`, "utf8")
+ response.send(res.replaceAll("", navbar(request.session)));
+ },
+}
diff --git a/src/html/pages/root/login_get.js b/src/html/pages/root/login_get.js
new file mode 100644
index 0000000..9563978
--- /dev/null
+++ b/src/html/pages/root/login_get.js
@@ -0,0 +1,15 @@
+const { readFile } = require('fs').promises;
+const { navbar } = require('../../../../utils/navbar.js');
+
+module.exports = {
+ path: "/login",
+ type: "get",
+ async execute(request, response, args) {
+ if (request.session.user) return response.redirect("/");
+ if(args?.fail) {
+ console.log('Failed login attempt !')
+ }
+ const res = await readFile(`${process.env.WWW}/login.html`, "utf8");
+ response.send(res.replaceAll("", navbar(request.session)));
+ },
+}
diff --git a/src/html/pages/root/login_post.js b/src/html/pages/root/login_post.js
new file mode 100644
index 0000000..ef85f42
--- /dev/null
+++ b/src/html/pages/root/login_post.js
@@ -0,0 +1,16 @@
+const { readFile } = require('fs').promises;
+
+module.exports = {
+ path: "/login",
+ type: "post",
+ async execute(request, response) {
+ const { username, password } = request.body;
+ console.log(request.body);
+ if (username == "ninjdai" && password == "azerty") {
+ request.session.user = { username };
+ response.redirect("/");
+ } else {
+ response.redirect("/login?fail=true");
+ }
+ },
+}
diff --git a/src/html/pages/root/logout.js b/src/html/pages/root/logout.js
new file mode 100644
index 0000000..9873594
--- /dev/null
+++ b/src/html/pages/root/logout.js
@@ -0,0 +1,9 @@
+module.exports = {
+ path: "/logout",
+ type: "get",
+ async execute(request, response) {
+ request.session.destroy(() => {
+ response.redirect("/login");
+ });
+ },
+}
diff --git a/utils/handler.js b/utils/handler.js
new file mode 100644
index 0000000..aaeafef
--- /dev/null
+++ b/utils/handler.js
@@ -0,0 +1,29 @@
+const fs = require('fs');
+const pagesPath = "./src/html/pages/";
+
+function deployHandler() {
+ global.handler = {
+ get: {},
+ post: {},
+ };
+ let numberOfPages = 0;
+ const PagesCategories = fs
+ .readdirSync(pagesPath)
+ .filter((file) => !file.includes("."));
+ for (const category of PagesCategories) {
+ const pageFiles = fs
+ .readdirSync(`${pagesPath}${category}`)
+ .filter((file) => file.endsWith(".js"));
+ for (const file of pageFiles) {
+ const page = require(`.${pagesPath}${category}/${file}`);
+ global.handler[page.type][page.path] = page;
+ console.log(
+ `\x1b[32mChargement de POST: ${page.path} !\x1b[0m`,
+ );
+ numberOfPages++;
+ console.log(numberOfPages + " pages chargées ! ");
+ }
+ }
+}
+
+module.exports = { deployHandler };
diff --git a/utils/navbar.js b/utils/navbar.js
new file mode 100644
index 0000000..6c74758
--- /dev/null
+++ b/utils/navbar.js
@@ -0,0 +1,18 @@
+module.exports = {
+ navbar(session) {
+ let logField;
+ if(session.user) {
+ logField = `${session.user.username} ✖ `
+ } else {
+ logField = `Login`;
+ }
+ return `
+
+ `;
+ }
+}
diff --git a/web.js b/web.js
index 779d3db..e1d1c78 100644
--- a/web.js
+++ b/web.js
@@ -1,41 +1,67 @@
-const express = require('express');
-const { readFile } = require('fs').promises;
-const { generateCallHTML } = require('./src/html/callHTML.js');
-const { getRandomUser } = require('./src/database/getUser.js');
+const express = require("express");
+const session = require("express-session");
+const { readFile } = require("fs").promises;
+const { deployHandler } = require("./utils/handler.js");
+const favicon = require("serve-favicon");
-function launchWeb(submitEvent){
+function launchWeb() {
const app = express();
app.use(express.json()); // Used to parse JSON bodies
app.use(express.urlencoded({ extended: false })); //Parse URL-encoded bodies
+ app.use(
+ session({
+ secret: process.env.SECRET,
+ resave: false,
+ saveUninitialized: true,
+ }),
+ );
- app.use('/assets', express.static('./www/assets'))
+ app.use("/assets", express.static(`${process.env.WWW}/assets`));
+ app.use(favicon(`${process.env.WWW}/assets/images/favicon.ico`));
- app.get('/', async (request, response) => {
- response.send(await readFile('./www/index.html', 'utf8'));
- });
- app.get('/call', async (request, response) => {
- response.send(await generateCallResponse());
+ deployHandler();
+
+ app.post("*", async (request, response) => {
+ console.log("POST: " + request.originalUrl);
+ if (!global.handler.post[request.originalUrl]) return;
+ if (global.handler.post[request.originalUrl].requiresLogin && !request.session.user) {
+ return response.redirect("/login");
+ }
+ return await global.handler.post[request.originalUrl].execute(
+ request,
+ response,
+ );
});
- app.post('/inputs/call', async (request, response) => {
- submitEvent.emit('call', request.body);
- response.send(await generateCallResponse());
- });
- app.post('/inputs/add', async (request, response) => {
- submitEvent.emit('add', request);
+ app.get("*", async (request, response) => {
+
+ const [path, args] = parseURL(request.originalUrl);
+ //console.log(parseURL(request.originalUrl));
+ console.log(`GET: ${path}${args ? "?"+args : ""}`);
+
+ if (!global.handler.get[path]) return;
+ if (global.handler.get[path].requiresLogin && !request.session.user) {
+ return response.redirect("/login");
+ }
+ return await global.handler.get[path].execute(
+ request,
+ response,
+ args,
+ );
});
const PORT = process.env.PORT || 3000;
- app.listen(PORT, () => { console.log(`App available at http://localhost:${PORT}`) });
-};
+ app.listen(PORT, () => {
+ console.log(`App available at http://localhost:${PORT}`);
+ });
+}
-async function generateCallResponse(){
- const user = await getRandomUser();
- let callHTML;
- if(user) callHTML = generateCallHTML( user.phone, user.name, user.count );
- else callHTML = `Plus d'utilisateur à appeler !`;
- return callHTML;
+function parseURL(URL) {
+ const spURL = URL.split("?");
+ const path = spURL[0];
+ const args = spURL[1];
+ return [path, args];
}
module.exports = { launchWeb };
diff --git a/www/assets/css/login.css b/www/assets/css/login.css
new file mode 100644
index 0000000..0c08547
--- /dev/null
+++ b/www/assets/css/login.css
@@ -0,0 +1,121 @@
+
+body {font-family: Arial, Helvetica, sans-serif;}
+
+/* Full-width input fields */
+input[type=text], input[type=password] {
+ width: 100%;
+ padding: 12px 20px;
+ margin: 8px 0;
+ display: inline-block;
+ border: 1px solid #ccc;
+ box-sizing: border-box;
+}
+
+/* Set a style for all buttons */
+button {
+ background-color: #04AA6D;
+ color: white;
+ padding: 14px 20px;
+ margin: 8px 0;
+ border: none;
+ cursor: pointer;
+ width: 100%;
+}
+
+button:hover {
+ opacity: 0.8;
+}
+
+/* Extra styles for the cancel button */
+.cancelbtn {
+ width: auto;
+ padding: 10px 18px;
+ background-color: #f44336;
+}
+
+/* Center the image and position the close button */
+.imgcontainer {
+ text-align: center;
+ margin: 24px 0 12px 0;
+ position: relative;
+}
+
+img.avatar {
+ width: 40%;
+ border-radius: 50%;
+}
+
+.container {
+ padding: 16px;
+}
+
+span.psw {
+ float: right;
+ padding-top: 16px;
+}
+
+/* The Modal (background) */
+.modal {
+ display: none; /* Hidden by default */
+ position: fixed; /* Stay in place */
+ z-index: 1; /* Sit on top */
+ left: 0;
+ top: 0;
+ width: 100%; /* Full width */
+ height: 100%; /* Full height */
+ overflow: auto; /* Enable scroll if needed */
+ background-color: rgb(0,0,0); /* Fallback color */
+ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
+ padding-top: 60px;
+}
+
+/* Modal Content/Box */
+.modal-content {
+ background-color: #fefefe;
+ margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */
+ border: 1px solid #888;
+ width: 80%; /* Could be more or less, depending on screen size */
+}
+
+/* The Close Button (x) */
+.close {
+ position: absolute;
+ right: 25px;
+ top: 0;
+ color: #000;
+ font-size: 35px;
+ font-weight: bold;
+}
+
+.close:hover,
+.close:focus {
+ color: red;
+ cursor: pointer;
+}
+
+/* Add Zoom Animation */
+.animate {
+ -webkit-animation: animatezoom 0.6s;
+ animation: animatezoom 0.6s
+}
+
+@-webkit-keyframes animatezoom {
+ from {-webkit-transform: scale(0)}
+ to {-webkit-transform: scale(1)}
+}
+
+@keyframes animatezoom {
+ from {transform: scale(0)}
+ to {transform: scale(1)}
+}
+
+/* Change styles for span and cancel button on extra small screens */
+@media screen and (max-width: 300px) {
+ span.psw {
+ display: block;
+ float: none;
+ }
+ .cancelbtn {
+ width: 100%;
+ }
+}
diff --git a/www/assets/css/navbar.css b/www/assets/css/navbar.css
new file mode 100644
index 0000000..e820f26
--- /dev/null
+++ b/www/assets/css/navbar.css
@@ -0,0 +1,68 @@
+ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ background-color: #333;
+}
+
+li {
+ float: left;
+}
+
+li a {
+ display: block;
+ color: white;
+ text-align: center;
+ padding: 14px 16px;
+ text-decoration: none;
+}
+
+li a:hover:not(.active) {
+ background-color: #111;
+}
+
+.active {
+ background-color: #04AA6D;
+}
+
+.dropbtn {
+ background-color: #4CAF50;
+ color: white;
+ padding: 16px;
+ font-size: 16px;
+ border: none;
+ cursor: pointer;
+}
+
+.dropdown {
+ position: relative;
+ display: inline-block;
+}
+
+.dropdown-content {
+ display: none;
+ position: absolute;
+ right: 0;
+ background-color: #f9f9f9;
+ min-width: 160px;
+ box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
+ z-index: 1;
+}
+
+.dropdown-content a {
+ color: black;
+ padding: 12px 16px;
+ text-decoration: none;
+ display: block;
+}
+
+.dropdown-content a:hover {background-color: #f1f1f1;}
+
+.dropdown:hover .dropdown-content {
+ display: block;
+}
+
+.dropdown:hover .dropbtn {
+ background-color: #3e8e41;
+}
diff --git a/www/assets/images/favicon.ico b/www/assets/images/favicon.ico
new file mode 100644
index 0000000..447175b
Binary files /dev/null and b/www/assets/images/favicon.ico differ
diff --git a/www/index.html b/www/index.html
index 79316d9..41e1ffb 100644
--- a/www/index.html
+++ b/www/index.html
@@ -7,7 +7,8 @@
AutoCallMenu
+
Bienvenue :3
- Accéder aux téléphones
+ Accéder aux téléphones