From dd3213822999b8f285ea5fa5b32c3cce7cbc878a Mon Sep 17 00:00:00 2001 From: Ninjdai Date: Wed, 6 Dec 2023 19:11:14 +0100 Subject: [PATCH] Added basic log-in setup and upgraded overall visuals --- .gitignore | 4 + index.js | 27 ++++--- package.json | 3 + src/html/callHTML.js | 6 +- src/html/pages/inputs/add.js | 8 ++ src/html/pages/inputs/call.js | 9 +++ src/html/pages/root/call.js | 36 +++++++++ src/html/pages/root/index.js | 11 +++ src/html/pages/root/login_get.js | 15 ++++ src/html/pages/root/login_post.js | 16 ++++ src/html/pages/root/logout.js | 9 +++ utils/handler.js | 29 +++++++ utils/navbar.js | 18 +++++ web.js | 76 +++++++++++++------ www/assets/css/login.css | 121 ++++++++++++++++++++++++++++++ www/assets/css/navbar.css | 68 +++++++++++++++++ www/assets/images/favicon.ico | Bin 0 -> 967 bytes www/index.html | 3 +- www/login.html | 31 ++++++++ 19 files changed, 452 insertions(+), 38 deletions(-) create mode 100644 .gitignore create mode 100644 src/html/pages/inputs/add.js create mode 100644 src/html/pages/inputs/call.js create mode 100644 src/html/pages/root/call.js create mode 100644 src/html/pages/root/index.js create mode 100644 src/html/pages/root/login_get.js create mode 100644 src/html/pages/root/login_post.js create mode 100644 src/html/pages/root/logout.js create mode 100644 utils/handler.js create mode 100644 utils/navbar.js create mode 100644 www/assets/css/login.css create mode 100644 www/assets/css/navbar.css create mode 100644 www/assets/images/favicon.ico create mode 100644 www/login.html 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 = `
@@ -12,7 +14,7 @@ function generateCallHTML(phoneNumber, name, callcount) {
`; - 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 0000000000000000000000000000000000000000..447175b1c1fc9460aaa0fd4daf3f22e0c8771fbf GIT binary patch literal 967 zcmV;&133JNP)LZ&KgS07z`ApT#FREc40-0GJ3e8n?bBCgv z9jZazsfKB#ES>ybs&#llMsPH--Ann)6m=7z1G|s$QuR4Ap1ZT5akD)0^a*0mZv>1xqxoI zUgGzcgA6ppVr7`}_fCo0p8ou`V3@~177*j7Q=$+av8wY#!M+<71Ok;H1I<({MvQ+? zsfC+Us0A&s0b~JFamguwB5|w6yfTRZ8G#d_Ss)8djC(K@U!4;51K}L$Lmntv_d<{j zO!---fW1~->rMaX!z(pyKFA0f8mh!hym1ONhGSNBXV*mG4k(&d1~P-AVt0BJ>{L>8 z^1jOFs{l8ED8HR9QQOl$25zB=aSvkrL(1JihtQp`2n(4#Un2JwX(&;?71svsDFy%ioCB$)X z3pm2}XQa|!i4#-(MuVo!R`t&HMZ3vyl(%M+z*JnwR}-pfRw+7(Pdvp{og6JnQ-l~l zmqUW&I-TskLDk8FEIq|rMI#3b{;I{4pUj$0R76_B@kP$xR<{M`sRkL$(_1|C`20Hy zDuGzyNDhhT4J?stbnLg2{Q#UoduomM+M$9D&ys~r#m{sJ``J<3)Avd-$cL$JUUf_W zRUbKAFbOYBY{g7`%odIMJA>A;j_&CR81~r4`~-!hF_Ngoj6cdIVfPcOuHKlgz%wYu zvjZ^^$a+GJ_Y^gg_Sh+dvo5>NHZi^nG4VR5NH}J#?!9Aq+Pj=bX$Q!}#>mqOM8%o(kxk068yT_I z_T9CX`>)voJ3!htmGX;-if_3@5EWx}!JypDQcF0#2vdFvQ}HfCBAVC^ZlNI-D@%^{ psd$_`wW7SW*n28bq6GX~{s1s2m8G!8mM8!K002ovPDHLkV1g!px~>2K literal 0 HcmV?d00001 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

    diff --git a/www/login.html b/www/login.html new file mode 100644 index 0000000..4d95b9e --- /dev/null +++ b/www/login.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + +