diff options
author | Lars Krönner | 2020-10-10 18:40:46 +0200 |
---|---|---|
committer | GitHub | 2020-10-10 18:40:46 +0200 |
commit | c2cb67575c8ee623b775d1f5fd28c0a60a9288dc (patch) | |
tree | a9285cef4e2dc3451ed609be2a34bbf21580c35e | |
parent | 16ebbb932c0b780c11d3e574bc24a515eb095f5f (diff) | |
parent | 1f45ede8253421439e07790375b72a31ceef33ed (diff) |
Merge pull request #4 from marvinborner/profile
Profile
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | app.js | 2 | ||||
-rw-r--r-- | db.js | 19 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | profile.txt | 1 | ||||
-rw-r--r-- | profile/index.js | 106 | ||||
-rw-r--r-- | profile/public/index.html | 34 | ||||
-rw-r--r-- | profile/public/script.js | 62 | ||||
-rw-r--r-- | profile/public/style.css | 44 | ||||
-rw-r--r-- | profile/public/uploads/.gitkeep | 0 | ||||
-rw-r--r-- | quotes/public/index.html | 12 | ||||
-rw-r--r-- | tables.sql | 38 |
12 files changed, 315 insertions, 6 deletions
@@ -1,6 +1,8 @@ *.db *lock* node_* +.vscode/ *.env *.csv users.json +profile/public/uploads/**/* @@ -6,6 +6,7 @@ const { auth, checkUser, checkAdmin } = require("./auth"); const mottovote = require("./mottovote"); const quotes = require("./quotes"); const poll = require("./poll"); +const profile = require("./profile"); const admin = require("./admin"); const app = express(); @@ -31,6 +32,7 @@ app.use("/", express.static(__dirname + "/overview/public")); app.use("/mottovote", checkUser, mottovote); app.use("/quotes", checkUser, quotes); app.use("/poll", checkUser, poll); +app.use("/profile", checkUser, profile); app.use("/admin", checkAdmin, admin); app.use("/auth", auth); @@ -40,6 +40,11 @@ class DB { "INSERT INTO class (name) VALUES ('TGM13.1'), ('TGM13.2'), ('TGTM13.1'), ('TGI13.1'), ('TGI13.2'), ('teacher')", ); + + const types = ["number", "file", "date", "text", "color"]; + await this.query("INSERT INTO profile_input_types (type) VALUES (?), (?), (?), (?), (?)", types); + + // User polls fs.readFile(__dirname + "/poll.txt", "utf8", (err, data) => { if (err) throw err; @@ -56,6 +61,7 @@ class DB { }); }); + // Motto votes fs.readFile(__dirname + "/mottos.txt", "utf8", (err, data) => { if (err) throw err; @@ -66,6 +72,19 @@ class DB { }); }); + // User profile + fs.readFile(__dirname + "/profile.txt", "utf8", (err, data) => { + if (err) throw err; + + const questions = data.split("\n"); + questions.forEach((question) => { + if (question) { + const [q, type] = question.split(" - "); + this.query("INSERT INTO profile_questions (question, question_type) VALUE (?, ?)", [q, types.indexOf(type) + 1]); + } + }); + }); + const classes = data.split("--"); const userPasswords = {}; console.log("Generating users"); diff --git a/package.json b/package.json index 1775768..d1525be 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "connect-redis": "^5.0.0", "dotenv": "^8.2.0", "express": "^4.17.1", + "express-fileupload": "^1.2.0", "express-rate-limit": "^5.1.3", "express-session": "^1.17.1", "mariadb": "^2.4.2", diff --git a/profile.txt b/profile.txt new file mode 100644 index 0000000..58b3deb --- /dev/null +++ b/profile.txt @@ -0,0 +1 @@ +Was ist dein Alter? - number
\ No newline at end of file diff --git a/profile/index.js b/profile/index.js new file mode 100644 index 0000000..1c5752f --- /dev/null +++ b/profile/index.js @@ -0,0 +1,106 @@ +const express = require("express"); +const db = require("../db"); +const fileupload = require("express-fileupload"); +const app = express.Router(); + +app.use(fileupload({})); + +app.use("/", express.static(__dirname + "/public/")); + +app.get("/user/:uid", async (req, res) => { }); + +// Basic API +app.get("/api/user", async (req, res) => { + const user = (await db.query("SELECT name, surname FROM users WHERE id = ?", [req.session.uid]))[0]; + res.json(user); +}); + +app.get("/api/questions", async (req, res) => { + const questions = await db.query("SELECT q.id, q.question, t.type FROM profile_questions q INNER JOIN profile_input_types t ON t.id = q.question_type"); + const answers = await db.query("SELECT answer, question_id FROM profile_answers WHERE user_id = ?", [ + req.session.uid, + ]); + + for (const answer of answers) { + const qid = questions.findIndex((question) => question.id === answer.question_id); + if (qid !== undefined) questions[qid].answer = answer.answer; + } + res.json(questions); +}); + +app.post("/api/add", async (req, res) => { + try { + for (let qid in req.body) { + if (!req.body.hasOwnProperty(qid) || req.body[qid] === "dbg-image") continue; + let answer = req.body[qid].replace(/</g, "<").replace(/>/g, ">"); + await db.query("INSERT INTO profile_answers (question_id, user_id, answer) VALUES (?, ?, ?)", [ + qid, + req.session.uid, + answer + ]); + } + for (let fid in req.files) { + if (!req.files.hasOwnProperty(fid)) return; + + let image, imageType, imageName; + + image = req.files[fid]; + imageType = image.name.split(".").reverse()[0]; + imageName = `${req.session.uid}_${(new Date()).getTime()}.${imageType}`; + image.mv(__dirname + "/public/uploads/" + imageName); + await db.query("INSERT INTO profile_answers (question_id, user_id, answer) VALUES (?, ?, ?)", [ + qid, + req.session.uid, + imageName, + ]); + } + res.send("ok"); + } catch (e) { + console.error(e); + res.send("error"); + } +}); + +app.put("/api/update", async (req, res) => { + try { + for (let qid in req.body) { + if (!req.body.hasOwnProperty(qid) || req.body[qid] === "dbg-image") continue; + let answer = req.body[qid].replace(/</g, "<").replace(/>/g, ">"); + await db.query("UPDATE profile_answers SET answer = ? WHERE question_id = ? AND user_id = ?", [ + answer, + qid, + req.session.uid, + ]); + } + for (let fid in req.files) { + if (!req.files.hasOwnProperty(fid)) return; + + let image, imageType, imageName; + + image = req.files[fid]; + imageType = image.name.split(".").reverse()[0]; + imageName = `${req.session.uid}_${(new Date()).getTime()}.${imageType}`; + image.mv(__dirname + "/public/uploads/" + imageName); + await db.query("UPDATE profile_answers SET answer = ? WHERE question_id = ? AND user_id = ?", [ + imageName, + fid, + req.session.uid, + ]); + } + res.send("ok"); + } catch (e) { + console.error(e); + res.send("error"); + } +}); + +// Comments API +app.get("/api/comments/:uid", async (req, res) => { }); + +app.post("/api/comment", async (req, res) => { }); + +app.put("/api/comment", async (req, res) => { }); + +app.delete("/api/comment", async (req, res) => { }); + +module.exports = app; diff --git a/profile/public/index.html b/profile/public/index.html new file mode 100644 index 0000000..fd7b507 --- /dev/null +++ b/profile/public/index.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html lang="de"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <link + rel="stylesheet" + href="https://unpkg.com/purecss@2.0.3/build/pure-min.css" + integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ" + crossorigin="anonymous" + /> + <link rel="stylesheet" href="style.css" type="text/css" media="all" /> + <title>Steckbrief</title> + </head> + + <body> + <div class="pure-menu pure-menu-horizontal"> + <a href="/" class="pure-menu-item pure-menu-link">Home</a> + <a href="/auth/api/logout" class="pure-menu-item pure-menu-link">Logout</a> + </div> + <main> + <h1 id="username"></h1> + <form class="pure-form pure-form-stacked"> + <fieldset> + <legend>Steckbrief</legend> + + <!-- TODO: Consider autosave --> + <button type="submit" class="pure-button pure-button-primary">Wohooo</button> + </fieldset> + </form> + </main> + <script src="script.js"></script> + </body> +</html> diff --git a/profile/public/script.js b/profile/public/script.js new file mode 100644 index 0000000..a386f56 --- /dev/null +++ b/profile/public/script.js @@ -0,0 +1,62 @@ +const fs = document.querySelector("fieldset"); +const form = document.querySelector("form"); +let init = true; + +function updateHeading(user) { + document.getElementById("username").textContent = `${user.name} ${user.surname}`; +} + +function appendQuestions(question) { + const div = document.createElement("div"); + + const label = document.createElement("label"); + label.for = "id_" + question.id; + label.textContent = question.question; + div.appendChild(label); + + if (question.type === "file" && question.answer) { + const img = document.createElement("img"); + img.src = "uploads/" + question.answer; + img.alt = "Image"; + div.appendChild(img); + } + + const field = document.createElement("input"); + field.id = "id_" + question.id; + field.name = question.id; + if (question.answer !== undefined) init = false; + field.value = question.answer; + field.placeholder = question.question; + field.type = question.type; + if (question.type === "file") field.accept = "image/*"; + + div.appendChild(field); + fs.insertBefore(div, fs.querySelector("button")); +} + +form.addEventListener("submit", async (evt) => { + evt.preventDefault(); + const url = init ? "api/add" : "api/update"; + const method = init ? "POST" : "PUT"; + + const inputs = form.querySelectorAll("input"); + const body = new FormData(); + for (const input of inputs) { + if (input.type !== "file") body.append(input.name, input.value); + else body.append(input.name, input.files[0] ?? "dbg-image"); + } + + const resp = await fetch(url, { method, body }); + const res = await resp.text(); + if (res !== "ok") alert("AHHHH"); +}); + +fetch("api/user") + .then((response) => response.json()) + .then(updateHeading) + .catch(console.error); + +fetch("api/questions") + .then((response) => response.json()) + .then((response) => response.forEach(appendQuestions)) + .catch(console.error); diff --git a/profile/public/style.css b/profile/public/style.css new file mode 100644 index 0000000..e674e71 --- /dev/null +++ b/profile/public/style.css @@ -0,0 +1,44 @@ +html, +body { + padding: 0; + margin: 0; + height: 100%; + width: 100%; + background-color: #eec0c6; + background-image: linear-gradient(315deg, #eec0c6 0%, #7ee8fa 74%); +} + +div { + background: white; +} + +main { + position: absolute; + max-height: 80%; + overflow-y: auto; + width: 50%; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + padding: 20px; + border-radius: 10px; + background: white; +} + +input, +button, +select { + width: 100%; +} + +img { + max-width: 80%; + max-height: 80%; +} + +@media only screen and (max-width: 600px) { + main { + width: calc(100% - 50px); + } +} diff --git a/profile/public/uploads/.gitkeep b/profile/public/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/profile/public/uploads/.gitkeep diff --git a/quotes/public/index.html b/quotes/public/index.html index 8853ee4..b91d1e1 100644 --- a/quotes/public/index.html +++ b/quotes/public/index.html @@ -32,17 +32,17 @@ </form> <button id="open_TGI13.1">TGI13.1</button> - <ul style="display: none" id="TGI13.1"></ul> + <ul style="display: none;" id="TGI13.1"></ul> <button id="open_TGI13.2">TGI13.2</button> - <ul style="display: none" id="TGI13.2"></ul> + <ul style="display: none;" id="TGI13.2"></ul> <button id="open_TGM13.1">TGM13.1</button> - <ul style="display: none" id="TGM13.1"></ul> + <ul style="display: none;" id="TGM13.1"></ul> <button id="open_TGM13.2">TGM13.2</button> - <ul style="display: none" id="TGM13.2"></ul> + <ul style="display: none;" id="TGM13.2"></ul> <button id="open_TGTM13.1">TGTM13.1</button> - <ul style="display: none" id="TGTM13.1"></ul> + <ul style="display: none;" id="TGTM13.1"></ul> <button id="open_teacher">Lehrer</button> - <ul style="display: none" id="teacher"></ul> + <ul style="display: none;" id="teacher"></ul> </main> <script src="script.js" charset="utf-8"></script> @@ -4,6 +4,10 @@ -- DROP TABLE IF EXISTS quotes; -- DROP TABLE IF EXISTS ranking_questions; -- DROP TABLE IF EXISTS ranking_answers; +-- DROP TABLE IF EXISTS profile_comments; +-- DROP TABLE IF EXISTS profile_answers; +-- DROP TABLE IF EXISTS profile_questions; +-- DROP TABLE IF EXISTS profile_input_types; -- DROP TABLE IF EXISTS users; -- DROP TABLE IF EXISTS types; -- DROP TABLE IF EXISTS class; @@ -85,3 +89,37 @@ CREATE TABLE IF NOT EXISTS motto_votes( CONSTRAINT `fk_voted_user` FOREIGN KEY (user_id) REFERENCES users (id), CONSTRAINT `fk_voted_vote` FOREIGN KEY (motto_id) REFERENCES mottos (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS profile_input_types( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + type VARCHAR(20) NOT NULL UNIQUE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS profile_questions( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + question VARCHAR(255) NOT NULL UNIQUE, + question_type INTEGER NOT NULL, + + CONSTRAINT `fk_profile_question_type` FOREIGN KEY (question_type) REFERENCES profile_input_types (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS profile_answers( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + question_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + answer TEXT NULL, -- Consider VARCHAR + + UNIQUE KEY uk_answer (question_id, user_id), + CONSTRAINT `fk_profile_user` FOREIGN KEY (user_id) REFERENCES users (id), + CONSTRAINT `fk_profile_question` FOREIGN KEY (question_id) REFERENCES profile_questions (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS profile_comments( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + profile_id INTEGER NOT NULL, -- User's profile + user_id INTEGER NOT NULL, -- User who commented + comment TEXT NOT NULL, + + CONSTRAINT `fk_user_profile` FOREIGN KEY (profile_id) REFERENCES users (id), + CONSTRAINT `fk_user_commenter` FOREIGN KEY (user_id) REFERENCES users (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8;
\ No newline at end of file |