"use strict"; import { createServer, IncomingMessage, ServerResponse } from "http"; import { parse as urlParse } from "querystring"; import { readFileSync } from "fs"; import { createHash } from "crypto"; import { Router, route } from "./router.js"; import { getCookies, renderTemplate, redirect, consumePostForm, setCookies } from "./utils.js"; import { User, UserDB } from "./userdb.js"; const udb = new UserDB(); const hasher = (hashable) => createHash("sha256").update(hashable).digest("hex"); /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const mainPage = function (req, res, path, params) { res.writeHead(200, { "Content-Type": "text/html" }); res.write( `Hello on ${path}!\n You can go on:
` + `1. Parameters display (in JSON): *click*
` + `2. Parameters display (in TEXT): *click*
` + `3. See some pretty cats: *click*
` + `4. Open dashboard: *click*` ); res.end(); } const showParameters = function (type) { const contentType = type === "JSON" ? "application/json" : "text/plain"; const serializer = type === "JSON" ? JSON.stringify : (obj => Array.from(Object.keys(obj)).map(e => `Key: ${e}; Value: ${obj[e]}`).join("\n")); /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ return function (req, res, path, params) { res.writeHead(200, { "Content-Type": contentType }); res.write(serializer(params)); res.end(); } } /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const showACat = function (req, res, path, params) { res.writeHead(200, { "Content-Type": "image/png" }); res.write(readFileSync("./Cat_poster_1.png")); res.end(); } const router = new Router(); router.addRoute(route("GET", "/"), mainPage); router.addRoute(route("GET", "/sparamsAsJson"), showParameters("JSON")); router.addRoute(route("GET", "/sparamsAsText"), showParameters("TEXT")); router.addRoute(route("GET", "/showMeACat"), showACat); /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const userRegisterView = (req, res, path, params) => { const cookies = getCookies(req); if (cookies["user"]) return redirect(res, "/dashboard"); return renderTemplate(res, "./templates/register.html"); } /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const userRegister = async function (req, res, path, params) { const cookies = getCookies(req); if (cookies["user"]) return redirect(res, "/dashboard"); const response = { ok: true, message: "" }; try { const post = await consumePostForm(req); console.log(post); const login = post.login; const password = post.password; const passwordRepeated = post.passwordRepeated; const status = udb.getUsersCount() === 0 ? "admin" : "user"; if (udb.hasUser(login)) { throw new Error("User already exists!"); } else if (password != passwordRepeated) { throw new Error("Passwords must match!"); } else { udb.putUser(new User(login, hasher(password), status)); } } catch (e) { console.error(e); response.ok = false; response.message = e.message; } res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.write(JSON.stringify(response)); res.end(); } /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const userLoginView = (req, res, path, params) => { const cookies = getCookies(req); if (cookies["user"]) return redirect(res, "/dashboard"); return renderTemplate(res, "./templates/login.html"); } /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const userLogin = async function (req, res, path, params) { const cookies = getCookies(req); if (cookies["user"]) return redirect(res, "/dashboard"); let response = { ok: true, message: "" }; try { const post = await consumePostForm(req); const login = post.login; const password = hasher(post.password); if (!udb.hasUser(login)) { throw new Error("User not exists!"); } const user = udb.getUser(login); if (password !== user.password) { throw new Error("Password doesn't match!"); } response["user"] = { login, status: user.status }; } catch (e) { console.error(e); response.ok = false; response.message = e.message; } res.statusCode = 200; res.setHeader("Content-Type", "application/json"); if (response.ok) { setCookies(res, { user: JSON.stringify(response["user"]) }); } res.write(JSON.stringify(response)); res.end(); } /** * * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const userDashboardView = function (req, res, path, params) { const cookies = getCookies(req); if (!cookies["user"]) return redirect(res, "/login"); const user = JSON.parse(cookies["user"]); return renderTemplate(res, "./templates/dashboard.html", [ ["user.name", user.login], ["user.status", user.status], ]); } /** * Sadly, not working( * @param {IncomingMessage} req * @param {ServerResponse} res * @param {String} path * @param {Object} params */ const userDashboardLogout = function (req, res, path, params) { let cookies = getCookies(req); if (cookies["user"]) { cookies["user"] = ""; setCookies(res, cookies); } return redirect(res, "/login"); } router.addRoute(route("GET", "/register"), userRegisterView); router.addRoute(route("POST", "/register"), userRegister); router.addRoute(route("GET", "/login"), userLoginView); router.addRoute(route("POST", "/login"), userLogin); router.addRoute(route("GET", "/dashboard"), userDashboardView); router.addRoute(route("GET", "/dashboard/logout"), userDashboardLogout); /** * * @param {IncomingMessage} req * @param {ServerResponse} res */ const requestDispatcher = function (req, res) { const [path, params] = req.url.includes("?") ? req.url.split("?", 2) : [req.url, ""]; const parsedParams = urlParse(params); router.dispatch(route(req.method, path))(req, res, path, parsedParams); } createServer(requestDispatcher).listen(9889);