From df806dda764ff722742301042ab88b3d91f472c5 Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 26 May 2021 18:50:23 +0200 Subject: Switched web framework from Express.js to Fastify & improved the backend --- src/api/git.js | 16 ++++- src/api/sanitization.js | 14 ----- src/api/util.js | 45 +++++++++++++ src/api/v1.js | 163 ++++++++++++++++++++++++++++++------------------ src/app.js | 139 +++++++++++++++++++++++++---------------- 5 files changed, 247 insertions(+), 130 deletions(-) delete mode 100644 src/api/sanitization.js create mode 100644 src/api/util.js (limited to 'src') diff --git a/src/api/git.js b/src/api/git.js index dad7cc6..756136b 100644 --- a/src/api/git.js +++ b/src/api/git.js @@ -231,7 +231,21 @@ async function getCommit(base_dir, repo_name, commit_oid) }; } +async function doesCommitExist(base_dir, repo_name, commit_oid) +{ + const repo = await git.Repository.openBare(`${base_dir}/${repo_name}`) + + try { + await repo.getCommit(commit_oid); + return true; + } + catch { + return false; + } +} + module.exports.getLog = getLog; module.exports.getRepos = getRepos; module.exports.getRepoFile = getRepoFile; -module.exports.getCommit = getCommit; \ No newline at end of file +module.exports.getCommit = getCommit; +module.exports.doesCommitExist = doesCommitExist; \ No newline at end of file diff --git a/src/api/sanitization.js b/src/api/sanitization.js deleted file mode 100644 index 95c8810..0000000 --- a/src/api/sanitization.js +++ /dev/null @@ -1,14 +0,0 @@ -function sanitizeRepoName(dirty) -{ - const valid_repo_name = /^[a-zA-Z0-9\.\-_]+$/; - return valid_repo_name.test(dirty); -} - -function sanitizeCommitID(dirty) -{ - const valid_commit_id = /^[a-fA-F0-9]{40}$/; - return valid_commit_id.test(dirty); -} - -module.exports.sanitizeRepoName = sanitizeRepoName; -module.exports.sanitizeCommitID = sanitizeCommitID; \ No newline at end of file diff --git a/src/api/util.js b/src/api/util.js new file mode 100644 index 0000000..aa31296 --- /dev/null +++ b/src/api/util.js @@ -0,0 +1,45 @@ +const fs = require("fs"); +const git = require("./git"); + +function verifyRepoName(dirty, base_dir) +{ + return new Promise((resolve) => + { + const is_valid_repo_name = /^[a-zA-Z0-9\\.\-_]+$/.test(dirty); + if(!is_valid_repo_name) { + resolve("ERR_REPO_REGEX"); + } + + fs.readdir(base_dir, (err, dir_content) => + { + if(err) { + resolve("ERR_REPO_NOT_FOUND"); + } + + dir_content = dir_content.filter(repo => repo.endsWith(".git")); + if(!dir_content.includes(dirty + ".git")) { + resolve("ERR_REPO_NOT_FOUND"); + } + + resolve(true); + }); + }); +} + +async function verifyCommitID(base_dir, repo, dirty) +{ + if(!/^[a-fA-F0-9]+$/.test(dirty)) { + return "ERR_COMMIT_REGEX"; + } + + const commit_exists = await git.doesCommitExist(base_dir, repo, dirty); + + if(!commit_exists) { + return "ERR_COMMIT_NOT_FOUND"; + } + + return true; +} + +module.exports.verifyRepoName = verifyRepoName; +module.exports.verifyCommitID = verifyCommitID; \ No newline at end of file diff --git a/src/api/v1.js b/src/api/v1.js index 7920afc..3a6f7ea 100644 --- a/src/api/v1.js +++ b/src/api/v1.js @@ -1,74 +1,115 @@ -const express = require("express"); const git = require("./git"); -const sanitization = require("./sanitization"); +const util = require("./util"); -const router = express.Router(); - -router.get("/info", function(req, res) -{ - res.json({ "data": req.settings }); - return; -}); - -router.get("/repos", async function(req, res) +module.exports = function (fastify, opts, done) { - let repos = await git.getRepos(req.settings["base_dir"]); - - if(repos["error"]) { - res.status(500).send("Internal server error!"); - return; - } - - res.json({ "data": repos }); -}); + fastify.route({ + method: "GET", + path: "/info", + handler: (req, reply) => + { + reply.send({ data: opts.config.settings }); + } + }); + fastify.route({ + method: "GET", + path: "/repos", + handler: async (req, reply) => + { + let repos = await git.getRepos(opts.config.settings.base_dir); -router.use("/repos/:repo", async function(req, res, next) -{ - if(!sanitization.sanitizeRepoName(req.params.repo)) { - res.status(400).json({ "error": "Unacceptable git repository name!" }); - return; - } - next(); -}); + if(repos["error"]) { + reply.code(500).send({ error: "Internal server error!" }); + return; + } -router.get("/repos/:repo", async function(req, res) -{ - const repo = `${req.params.repo}.git`; - const desc = await git.getRepoFile(req.settings["base_dir"], repo, "description"); + reply.send({ data: repos }); + } + }); - res.json({ "data": { "name": req.params.repo, "description": desc } }); -}); + fastify.route({ + method: "GET", + path: "/repos/:repo", + handler: async (req, reply) => + { + const repo_verification = await util.verifyRepoName(req.params.repo, opts.config.settings.base_dir); + if(repo_verification !== true) { + if(repo_verification === "ERR_REPO_REGEX") { + reply.code(400).send({ error: "Unacceptable git repository name!" }); + } + else if(repo_verification === "ERR_REPO_NOT_FOUND") { + reply.code(404).send({ error: "Git repository not found!" }); + } + } -router.get("/repos/:repo/log", async function(req, res) -{ - const repo = `${req.params.repo}.git`; - const log = await git.getLog(req.settings["base_dir"], repo); + const repo = `${req.params.repo}.git`; + const desc = await git.getRepoFile(opts.config.settings.base_dir, repo, "description"); - if(log["error"]) { - if(typeof log["error"] === "string") { - res.status(500).json({ "error": log["error"] }); - return; + reply.send({ data: { name: req.params.repo, description: desc } }); } - switch(log["error"]) { - case 404: - res.status(404).json({ "error": "Git repository doesn't exist!" }); - return; - } - return; - } - res.json({ data: log }); -}); + }); -router.get("/repos/:repo/log/:commit", async function(req, res) -{ - if(!sanitization.sanitizeCommitID(req.params.commit)) { - res.status(400).json({ "error": "Unacceptable commit id!" }); - return; - } + fastify.register((fastify_repo, opts_repo, done_repo) => + { + fastify_repo.addHook("onRequest", async (req, reply) => + { + const repo_verification = await util.verifyRepoName(req.params.repo, opts.config.settings.base_dir); + if(repo_verification !== true) { + if(repo_verification === "ERR_REPO_REGEX") { + reply.code(400).send({ error: "Unacceptable git repository name!" }); + } + else if(repo_verification === "ERR_REPO_NOT_FOUND") { + reply.code(404).send({ error: "Git repository not found!" }); + } + } + }); + + fastify_repo.route({ + method: "GET", + path: "/log", + handler: async (req, reply) => + { + const log = await git.getLog(opts.config.settings.base_dir, req.params.repo + ".git"); + + if(log["error"]) { + if(typeof log["error"] === "string") { + reply.code(500).send({ error: log["error"] }); + } + + switch(log["error"]) { + case 404: + reply.code(404).send({ error: "Git repository not found!" }); + } + + return; + } + reply.send({ data: log }); + } + }); - const commit = await git.getCommit(req.settings["base_dir"], req.params.repo, req.params.commit); + fastify_repo.route({ + method: "GET", + path: "/log/:commit", + handler: async (req, reply) => + { + const commit_verification = await util.verifyCommitID(opts.config.settings.base_dir, req.params.repo + ".git", req.params.commit); + if(!commit_verification !== true) { + if(commit_verification === "ERR_COMMIT_REGEX") { + reply.code(400).send({ error: "Unacceptable commit id!" }); + } + else if(commit_verification === "ERR_COMMIT_NOT_FOUND") { + reply.code(404).send({ error: "Commit not found!" }); + } + } + + const commit = await git.getCommit(opts.config.settings.base_dir, req.params.repo, req.params.commit); + + reply.send({ data: commit }); + } + }); - res.json({ data: commit }); -}); + done_repo(); + }, { prefix: "/repos/:repo" }); -module.exports = router; \ No newline at end of file + done(); +} \ No newline at end of file diff --git a/src/app.js b/src/app.js index 3c5ecd9..14b8188 100644 --- a/src/app.js +++ b/src/app.js @@ -1,8 +1,11 @@ -const express = require("express"); +const fastify = require("fastify")(); +const fastify_static = require('fastify-static'); const api = require("./api/v1"); const yaml = require('js-yaml'); const fs = require('fs'); const { exit } = require("process"); +const path = require("path"); +const util = require("./api/util"); const settings = yaml.load(fs.readFileSync(__dirname + "/../settings.yml", 'utf8')); const settings_keys = Object.keys(settings); @@ -23,74 +26,102 @@ if(mandatory_not_included.length !== 0) { exit(1); } -const dist_dir = __dirname + "/../dist"; +const dist_dir = path.join(__dirname, "/../dist"); -const app = express(); +try { + fs.readdirSync(settings["base_dir"]); +} +catch { + console.error(`Error: Tried opening the base directory. No such directory: ${settings["base_dir"]}`); + exit(1); +} -app.get(/.*\.(css|js|ico)$/, (req, res, next) => +fastify.setNotFoundHandler({ + preValidation: (req, reply, done) => done(), + preHandler: (req, reply, done) => done() +}, function (req, reply) { - fs.access(`${dist_dir}${req.path}`, err => - { - if(err) { - next(); - return; - } - res.sendFile(`${req.path}`, { root: dist_dir }); - }); + reply.send("404: Not found"); }); -app.use("/api/v1", (req, res, next) => -{ - req.settings = settings; - next(); -}, api); - -app.get("/", (req, res) => -{ - res.sendFile(`app.html`, { root: dist_dir }); +fastify.route({ + method: "GET", + path: "/app.html", + handler: (req, reply) => reply.redirect("/") }); -const repo_router = express.Router(); +fastify.register(fastify_static, { root: dist_dir }) +fastify.register(api, { prefix: "/api/v1", config: { settings: settings } }); + +fastify.route({ + method: "GET", + path: "/", + handler: (req, reply) => + { + console.log("AAAA faan"); + console.log(dist_dir) + reply.sendFile("app.html"); + } +}); -app.use("/:repo([a-zA-Z0-9-_]+)", (req, res, next) => +fastify.register((fastify_repo, opts, done) => { - console.log("AAAA"); - fs.readdir(settings["base_dir"], (err, dir_content) => + fastify_repo.setNotFoundHandler({ + preValidation: (req, reply, done) => done(), + preHandler: (req, reply, done) => done() + }, function (req, reply) { - if(err) { - res.status(404).send("404: Not found"); - return; + reply.send("404: Not found"); + }); + + fastify_repo.addHook("onRequest", async (req, reply) => + { + const repo_verification = await util.verifyRepoName(req.params.repo, settings.base_dir); + if(repo_verification !== true) { + if(repo_verification === "ERR_REPO_REGEX") { + reply.code(400).send("Error: Unacceptable git repository name!"); + } + else if(repo_verification === "ERR_REPO_NOT_FOUND") { + reply.code(404).send("Error: Git repository not found!"); + } } - - dir_content = dir_content.filter(repo => repo.endsWith(".git")); - if(!dir_content.includes(req.params.repo + ".git")) { - res.status(404).send("404: Not found"); - return; + }); + + fastify_repo.route({ + method: "GET", + path: "/:page", + handler: (req, reply) => + { + if([ "log", "refs", "tree" ].includes(req.params.page)) { + reply.sendFile("app.html"); + } } - else { - next(); + }); + + fastify_repo.route({ + method: "GET", + path: "/log/:subpage", + handler: async (req, reply) => + { + const commit_verification = await util.verifyCommitID(settings.base_dir, req.params.repo + ".git", req.params.subpage); + console.log(commit_verification); + if(commit_verification !== true) { + reply.callNotFound(); + } + + reply.sendFile("app.html"); } }); -}, repo_router); - -repo_router.get(/$|log$|refs$|tree$/, (req, res) => -{ - res.sendFile(`app.html`, { root: dist_dir }); -}); - -repo_router.get(/\/log\/[a-fA-F0-9]{40}$/, (req, res) => -{ - res.sendFile(`app.html`, { root: dist_dir }); -}) -repo_router.use((req, res) => -{ - res.status(404).send("404: Not found eeee"); -}); + done(); +}, { prefix: "/:repo" }); -app.use((req, res) => +fastify.listen(settings["port"],(err, addr) => { - res.status(404).send("404: Not found"); -}) + if(err) { + console.error(err); + exit(1); + } -app.listen(settings["port"], settings["host"], () => console.log(`App is running on ${settings["host"]}:${settings["port"]}`)); \ No newline at end of file + console.log(`App is running on ${addr}`); +}); \ No newline at end of file -- cgit v1.2.3-18-g5258