diff options
Diffstat (limited to 'src')
27 files changed, 0 insertions, 2340 deletions
diff --git a/src/api/git.js b/src/api/git.js deleted file mode 100644 index 95759f0..0000000 --- a/src/api/git.js +++ /dev/null @@ -1,415 +0,0 @@ -const { formatDistance } = require('date-fns'); -const fs = require('fs'); -const git = require("nodegit"); -const zlib = require("zlib"); -const { spawn } = require('child_process'); -const whatwg = require("whatwg-url"); -const path = require("path"); - -function addRepoDirSuffix(repo_name) -{ - if(!repo_name.endsWith(".git")) { - return repo_name + ".git"; - } - return repo_name; -} - -async function getLog(base_dir, repo_name) -{ - const repo = await git.Repository.openBare(`${base_dir}/${repo_name}`); - - const walker = git.Revwalk.create(repo); - walker.pushHead(); - - const raw_commits = await walker.getCommitsUntil(() => true); - - const commits = Promise.all(raw_commits.map(async commit => ({ - commit: commit.sha(), - author_full: commit.author().toString(), - author_name: commit.author().name(), - author_email: commit.author().email(), - date: commit.date(), - message: commit.message().replace(/\n/g, ""), - insertions: (await (await commit.getDiff())[0].getStats()).insertions(), - deletions: (await (await commit.getDiff())[0].getStats()).deletions(), - files_changed: (await (await commit.getDiff())[0].getStats()).filesChanged() - }))); - - return await commits; -} - -async function getTimeSinceLatestCommit(base_dir, repo_name) -{ - const repo = await git.Repository.openBare(`${base_dir}/${repo_name}`); - const master_commit = await repo.getMasterCommit(); - - return formatDistance(new Date(), master_commit.date()); -} - -function getRepoFile(base_dir, repo, file) -{ - return new Promise(resolve => - { - fs.readFile(`${base_dir}/${repo}/${file}`, async (err, content) => - { - if(!err) { - resolve(content.toString().replace(/\n/g, "")); - return; - } - resolve(""); - }); - }); -} - -function getRepos(base_dir) -{ - return new Promise((resolve) => - { - fs.readdir(base_dir, (err, dir_content) => - { - if(err) { - resolve({ "error": err }); - return; - } - - dir_content.filter(repo => repo.endsWith(".git")).reduce((acc, repo) => - { - return acc.then((repos) => - { - return getRepoFile(base_dir, repo, "description").then((description) => - { - return getRepoFile(base_dir, repo, "owner").then((owner) => - { - return getTimeSinceLatestCommit(base_dir, repo).then((last_commit_date) => - { - repos[repo.slice(0, -4)] = { "description": description, "owner": owner, "last_updated": last_commit_date }; - return repos; - }); - }); - }); - }); - }, Promise.resolve({})).then((repos) => - { - resolve(repos); - }); - }); - }); -} - -function parseHunkAddDel(hunk) -{ - let new_lines = []; - let deleted_lines = []; - - hunk.forEach((line, index) => - { - if(line.charAt(0) === '+') { - hunk[index] = line.slice(1); - new_lines.push(index); - } - else if(line.charAt(0) === '-') { - hunk[index] = line.slice(1); - deleted_lines.push(index); - } - }); - - return { new: new_lines, deleted: deleted_lines, hunk: hunk.join("\n") }; -} - -async function getCommit(base_dir, repo_name, commit_oid) -{ - repo_name = addRepoDirSuffix(repo_name); - - const repo = await git.Repository.openBare(`${base_dir}/${repo_name}`); - const commit = await repo.getCommit(commit_oid); - const diff = (await commit.getDiff())[0]; - const all_patches = (await diff.toBuf(1)).split('\n'); - - // Get the count of lines for all of patches's headers - const patch_headers = (await diff.toBuf(2)).split('\n'); - const patch_header_data = await patch_headers.reduce((acc, line, index) => - { - return acc.then((arr) => - { - if(/^diff --git/.test(line)) { - arr[0].push(all_patches.indexOf(line)); - - if(arr[2] != undefined) { - arr[1].push(patch_headers.slice(arr[2], index).length); - } - arr[2] = index; - } - else if(index == patch_headers.length - 1 && arr[2] != undefined) { - arr[1].push(patch_headers.slice(arr[2], index).length); - } - return arr; - }); - }, Promise.resolve([ [], [], undefined ])); - - console.log(patch_header_data); - - const patches = await diff.patches(); - const parsed_patches = patches.reduce((acc, patch, patch_index) => - { - return acc.then((arr) => - { - return patch.hunks().then((hunks) => - { - console.log("\n" + patch.newFile().path()); - - const patch_start = patch_header_data[0][patch_index] + patch_header_data[1][patch_index]; - const patch_end = (patch_header_data[0][patch_index + 1] !== undefined) ? patch_header_data[0][patch_index + 1] : all_patches.length - 1; - const patch_content = all_patches.slice(patch_start, patch_end); - - const line_lengths = patch_content.map((line) => line.length).reduce((acc, length) => acc + length); - - if(patch_content.length > 5000 || line_lengths > 5000) { - console.log("Too large!"); - - arr.push({ - from: patch.oldFile().path(), - to: patch.newFile().path(), - additions: patch.lineStats()["total_additions"], - deletions: patch.lineStats()["total_deletions"], - too_large: true, - hunks: null - }); - return arr; - } - - // Go through all of the patch's hunks - // Patches are split into parts of where in the file the change is made. Those parts are called hunks. - return hunks.reduce((acc, hunk, hunk_index) => - { - return acc.then((hunks_data) => - { - const hunk_header = hunk.header(); - const hunk_header_index = patch_content.indexOf(hunk_header.replace(/\n/g, "")); - - if(hunks_data[0] !== undefined) { - const prev_hunk = hunks[hunk_index - 1]; - hunks_data[1].push(Object.assign({ - new_start: prev_hunk.newStart(), - new_lines: prev_hunk.newLines(), - old_start: prev_hunk.oldStart(), - old_lines: prev_hunk.oldLines() - }, parseHunkAddDel(patch_content.slice(hunks_data[0], hunk_header_index)))); - - hunks_data[2] = hunks_data + patch_content.slice(hunks_data[0], hunk_header_index).length; - } - - hunks_data[0] = hunk_header_index; - return hunks_data; - }); - }, Promise.resolve([ undefined, [], 0 ])).then((hunks_data) => - { - const prev_hunk = hunks[hunks.length - 1]; - hunks_data[1].push(Object.assign({ - new_start: prev_hunk.newStart(), - new_lines: prev_hunk.newLines(), - old_start: prev_hunk.oldStart(), - old_lines: prev_hunk.oldLines() - }, parseHunkAddDel(patch_content.slice(hunks_data[0], patch_end)))); - - arr.push({ - from: patch.oldFile().path(), - to: patch.isDeleted() ? "/dev/null" : patch.newFile().path(), - additions: patch.lineStats()["total_additions"], - deletions: patch.lineStats()["total_deletions"], - too_large: false, - hunks: hunks_data[1] - }); - - return arr; - }); - }); - }); - }, Promise.resolve([])); - - return { - hash: commit.sha(), - author: commit.author().toString(), - message: commit.message(), - date: commit.date(), - patches: await parsed_patches - }; -} - -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; - } -} - -function connectToGitHTTPBackend(base_dir, req, reply) -{ - const url_path = req.url.replace(req.params.repo, req.params.repo + ".git"); - const repo = req.params.repo + ".git"; - const repo_path = path.join(base_dir, repo); - - req = req.headers['Content-Encoding'] == 'gzip' ? req.pipe(zlib.createGunzip()) : req; - - const parsed_url = new whatwg.URL(`${req.protocol}://${req.hostname}${url_path}`); - const url_path_parts = parsed_url.pathname.split('/'); - - let service; - let info = false; - - if(/\/info\/refs$/.test(parsed_url.pathname)) { - service = parsed_url.searchParams.get("service"); - info = true; - } - else { - service = url_path_parts[url_path_parts.length-1]; - } - - const content_type = `application/x-${service}-${info ? "advertisement" : "result"}`; - - if(/\.\/|\.\./.test(parsed_url.pathname)) { - reply.header("Content-Type", content_type); - reply.code(404).send("Git repository not found!\n"); - return; - } - - if(service !== 'git-upload-pack') { - reply.header("Content-Type", content_type); - reply.code(403).send("Access denied!\n"); - return; - } - - reply.raw.writeHead(200, { "Content-Type": content_type }); - - const spawn_args = [ "--stateless-rpc", repo_path ]; - - if(info) { - spawn_args.push("--advertise-refs"); - } - - const git_pack = spawn(service, spawn_args); - - if(info) { - const s = '# service=' + service + '\n'; - const n = (4 + s.length).toString(16); - reply.raw.write(Buffer.from((Array(4 - n.length + 1).join('0') + n + s) + '0000')); - } - else { - req.body.on("data", (data) => git_pack.stdin.write(data)); - req.body.on("close", () => git_pack.stdin.end()); - } - - git_pack.on("error", (err) => console.log(err)); - git_pack.stderr.on("data", (stderr) => console.log(stderr)); - - git_pack.stdout.on("data", (data) => - { - reply.raw.write(data); - }); - - git_pack.on("close", () => reply.raw.end()); -} - -async function getTree(base_dir, repo_name, tree_path) -{ - repo_name = addRepoDirSuffix(repo_name); - - const repo = await git.Repository.openBare(`${base_dir}/${repo_name}`); - const master_commit = await repo.getMasterCommit(); - - const tree = await master_commit.getTree(); - - let entries; - if(tree_path) { - try { - const path_entry = await tree.getEntry(tree_path); - - if(path_entry.isBlob()) { - return { type: "blob", content: (await path_entry.getBlob()).content().toString() }; - } - - entries = await (await path_entry.getTree()).entries(); - } - catch(err) { - if(err.errno === -3) { - return { error: 404 }; - } - return { error: 500 }; - } - } - else { - entries = tree.entries(); - } - - return { type: "tree", tree: await entries.reduce((acc, entry) => - { - return acc.then((obj) => - { - return getTreeEntryLastCommit(repo, entry).then((last_commit) => - { - obj[path.parse(entry.path()).base] = { - oid: entry.oid(), - type: entry.isBlob() ? "blob" : "tree", - last_commit: { - id: last_commit.id, - message: last_commit.message, - time: last_commit.time - } - }; - return obj; - }); - }); - }, Promise.resolve({})) }; -} - -async function getTreeEntryLastCommit(repo, tree_entry) -{ - const walker = git.Revwalk.create(repo); - walker.pushHead(); - - const raw_commits = await walker.getCommitsUntil(() => true); - - return raw_commits.reduce((acc, commit) => - { - return acc.then((obj) => - { - if(Object.keys(obj).length == 0) { - return commit.getDiff().then((diffs) => - { - return diffs[0].patches().then((patches) => - { - let matching_path_patch; - if(tree_entry.isBlob()) { - matching_path_patch = patches.find((patch) => patch.newFile().path() === tree_entry.path()); - } - else { - matching_path_patch = patches.find((patch) => path.parse(patch.newFile().path()).dir.startsWith(tree_entry.path())); - } - - if(matching_path_patch) { - obj.id = commit.sha(); - obj.message = commit.message().replace(/\n/g, ""); - obj.time = commit.date(); - } - return obj; - }); - }); - } - - return obj; - }); - }, Promise.resolve({})); -} - -module.exports.getLog = getLog; -module.exports.getRepos = getRepos; -module.exports.getRepoFile = getRepoFile; -module.exports.getCommit = getCommit; -module.exports.doesCommitExist = doesCommitExist; -module.exports.connectToGitHTTPBackend = connectToGitHTTPBackend; -module.exports.getTree = getTree;
\ No newline at end of file diff --git a/src/api/util.js b/src/api/util.js deleted file mode 100644 index aa31296..0000000 --- a/src/api/util.js +++ /dev/null @@ -1,45 +0,0 @@ -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 deleted file mode 100644 index 25a8019..0000000 --- a/src/api/v1.js +++ /dev/null @@ -1,136 +0,0 @@ -const git = require("./git"); -const util = require("./util"); - -module.exports = function (fastify, opts, done) -{ - 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); - - if(repos["error"]) { - reply.code(500).send({ error: "Internal server error!" }); - return; - } - - reply.send({ data: repos }); - } - }); - - 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!" }); - } - } - - const repo = `${req.params.repo}.git`; - const desc = await git.getRepoFile(opts.config.settings.base_dir, repo, "description"); - - reply.send({ data: { name: req.params.repo, description: desc } }); - } - }); - - 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 }); - } - }); - - 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 }); - } - }); - - fastify_repo.route({ - method: "GET", - path: "/tree", - handler: async (req, reply) => - { - const tree_path = (req.query.length !== 0 && req.query.path) ? req.query.path : null; - - const tree = await git.getTree(opts.config.settings.base_dir, req.params.repo, tree_path); - - if(tree.error) { - if(tree.error === 404) { - reply.code(404).send({ error: "Path not found" }); - } - else { - reply.code(500).send({ error: "Internal server error" }); - } - } - reply.send({ data: tree }); - } - }); - - done_repo(); - }, { prefix: "/repos/:repo" }); - - done(); -};
\ No newline at end of file diff --git a/src/app.js b/src/app.js deleted file mode 100644 index 8904219..0000000 --- a/src/app.js +++ /dev/null @@ -1,166 +0,0 @@ -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 git = require("./api/git"); - -const settings = yaml.load(fs.readFileSync(__dirname + "/../settings.yml", 'utf8')); -const settings_keys = Object.keys(settings); - -const mandatory_settings = [ "host", "port", "title", "about", "base_dir" ]; - -// Make sure that all the required settings are present -const settings_not_included = mandatory_settings.filter(x => !settings_keys.includes(x)); -if(settings_not_included.length !== 0) { - console.log(`Error: settings.yml is missing ${(settings_not_included.length > 1) ? "keys" : "key"}:`); - console.log(settings_not_included.join(", ")); - exit(1); -} - -// Make sure that there's not an excessive amount of settings -const mandatory_not_included = settings_keys.filter(x => !mandatory_settings.includes(x)); -if(mandatory_not_included.length !== 0) { - console.log(`Error: settings.yml includes ${(mandatory_not_included.length > 1) ? "pointless keys" : "a pointless key"}:`); - console.log(mandatory_not_included.join(", ")); - exit(1); -} - -// Make sure that the base directory specified in the settings actually exists -try { - fs.readdirSync(settings["base_dir"]); -} -catch { - console.error(`Error: Tried opening the base directory. No such directory: ${settings["base_dir"]}`); - exit(1); -} - -const dist_dir = path.join(__dirname, "/../dist"); - -fastify.setNotFoundHandler({ - preValidation: (req, reply, done) => done(), - preHandler: (req, reply, done) => done() -}, function (req, reply) -{ - reply.send("404: Not found"); -}); - -fastify.addContentTypeParser("application/x-git-upload-pack-request", (req, payload, done) => done(null, payload)); - -fastify.register(fastify_static, { root: dist_dir }); -fastify.register(api, { prefix: "/api/v1", config: { settings: settings } }); - -fastify.route({ - method: "GET", - path: "/", - handler: (req, reply) => reply.sendFile("app.html") -}); - -fastify.route({ - method: "GET", - path: "/app.html", - handler: (req, reply) => reply.redirect("/") -}); - -fastify.register((fastify_repo, opts, done) => -{ - fastify_repo.setNotFoundHandler({ - preValidation: (req, reply, done) => done(), - preHandler: (req, reply, done) => done() - }, function (req, reply) - { - 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("Unacceptable git repository name!\n"); - } - else if(repo_verification === "ERR_REPO_NOT_FOUND") { - reply.code(404).send("Git repository not found!\n"); - } - } - }); - - fastify_repo.route({ - method: "GET", - path: "/:page", - handler: (req, reply) => - { - if([ "log", "refs", "tree" ].includes(req.params.page)) { - reply.sendFile("app.html"); - } - } - }); - - 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"); - } - }); - - fastify_repo.route({ - method: "GET", - path: "/tree/*", - handler: async (req, reply) => - { - reply.sendFile("app.html"); - } - }); - - fastify_repo.route({ - method: "GET", - path: "/info/refs", - handler: (req, reply) => - { - if(!req.query.service) { - reply.code(403).send("Missing service query parameter\n"); - return; - } - else if(req.query.service !== "git-upload-pack") { - reply.code(403).send("Access denied!\n"); - return; - } - else if(Object.keys(req.query).length !== 1) { - reply.header("Content-Type", "application/x-git-upload-pack-advertisement"); - reply.code(403).send("Too many query parameters!\n"); - return; - } - - git.connectToGitHTTPBackend(settings["base_dir"], req, reply); - } - }); - - fastify_repo.route({ - method: "POST", - path: "/git-upload-pack", - handler: (req, reply) => git.connectToGitHTTPBackend(settings["base_dir"], req, reply) - }); - - done(); -}, { prefix: "/:repo" }); - -fastify.listen(settings["port"],(err, addr) => -{ - if(err) { - console.error(err); - exit(1); - } - - console.log(`App is running on ${addr}`); -});
\ No newline at end of file diff --git a/src/frontend/App.vue b/src/frontend/App.vue deleted file mode 100644 index cbdce56..0000000 --- a/src/frontend/App.vue +++ /dev/null @@ -1,11 +0,0 @@ -<template> - <div id="container" class="container-fluid px-0"> - <router-view/> - </div> -</template> - -<script> -export default { - name: "App" -} -</script>
\ No newline at end of file diff --git a/src/frontend/app.html b/src/frontend/app.html deleted file mode 100644 index 348ca45..0000000 --- a/src/frontend/app.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" href="scss/style.scss"> - </head> - <body> - <div id="app"></div> - <script src="app.js"></script> - </body> -</html>
\ No newline at end of file diff --git a/src/frontend/app.js b/src/frontend/app.js deleted file mode 100644 index 1b43bbe..0000000 --- a/src/frontend/app.js +++ /dev/null @@ -1,7 +0,0 @@ -import { createApp } from "vue/dist/vue.esm-bundler"; -import App from "./App.vue"; -import router from "./router"; - -createApp(App) - .use(router) - .mount("#app");
\ No newline at end of file diff --git a/src/frontend/components/BaseBackButton.vue b/src/frontend/components/BaseBackButton.vue deleted file mode 100644 index 64b1286..0000000 --- a/src/frontend/components/BaseBackButton.vue +++ /dev/null @@ -1,25 +0,0 @@ -<template> - <div class="d-inline"> - <router-link :to="to"> - <svg - xmlns="http://www.w3.org/2000/svg" id="back" - height="24px" width="24px" - viewBox="0 0 24 24" fill="#ffffff"> - <path d="M0 0h24v24H0z" fill="none" /> - <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /> - </svg> - </router-link> - </div> -</template> - -<script> -export default { - name: "BaseBackButton", - props: { - to: { - type: String, - required: true - } - } -} -</script>
\ No newline at end of file diff --git a/src/frontend/components/BaseBreadcrumb.vue b/src/frontend/components/BaseBreadcrumb.vue deleted file mode 100644 index df82968..0000000 --- a/src/frontend/components/BaseBreadcrumb.vue +++ /dev/null @@ -1,33 +0,0 @@ -<template> - <nav aria-label="breadcrumb"> - <ol class="breadcrumb"> - <li - v-for="(item, index) in items" class="breadcrumb-item" - :key="index"> - <router-link :to="item.path"> - {{ item.name }} - </router-link> - </li> - <li class="breadcrumb-item active" aria-current="page"> - {{ activeItem }} - </li> - </ol> - </nav> -</template> - - -<script> -export default { - name: "BaseBreadcrumb", - props: { - items: { - type: Array, - required: true - }, - activeItem: { - type: String, - required: true - } - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/components/CommitPatch.vue b/src/frontend/components/CommitPatch.vue deleted file mode 100644 index 2c815ff..0000000 --- a/src/frontend/components/CommitPatch.vue +++ /dev/null @@ -1,149 +0,0 @@ -<script> -import { h } from "vue"; -import hljs from "highlight.js"; -import hljs_languages from "../util/hljs-languages"; - -export default { - name: "CommitPatch", - props: { - patch: { - type: Object, - required: true - } - }, - setup(props) - { - const commit_patch = [ - h("div", { "class": "commit-file-header" }, [ - h("span", { "class": "fw-bold"}, (props.patch["to"] === "/dev/null") ? props.patch["from"] : props.patch["to"]), - h("span", (props.patch["to"] === "/dev/null") ? "Deleted" : "" ), - h("div", { class: "commit-file-add-del" }, [ - h("span", `+${ props.patch["additions"] }`), - h("span", `-${ props.patch["deletions"] }`) - ]) - ]) - ]; - - if(props.patch["too_large"] === false) { - let all_hunks = props.patch["hunks"].map((hunk) => hunk["hunk"]); - - const language = hljs_languages.find((lang) => lang["extensions"].some((extension) => props.patch["to"].endsWith(extension))); - let highlighted = language ? hljs.highlight(language["name"], all_hunks.join("\n")) : hljs.highlightAuto(all_hunks.join("\n")); - console.log(highlighted); - highlighted = highlighted["value"].split("\n"); - - const highlighted_hunks = []; - let hunk_start = 0; - all_hunks.forEach((hunk) => - { - const hunk_row_cnt = hunk.split("\n").length; - highlighted_hunks.push(highlighted.slice(hunk_start, hunk_start + hunk_row_cnt)); - hunk_start = hunk_start + hunk_row_cnt; - }); - - all_hunks = all_hunks.map((hunk) => hunk.split("\n")); - - commit_patch.push(h("table", { cellspacing: "0px" }, [ - h("tbody", [ - props.patch["hunks"].map((hunk, hunk_index) => - { - let new_offset = 0; - let deleted_offset = 0; - const multiline_comments = []; - - return highlighted_hunks[hunk_index].map((line, line_index) => - { - if(/^@@ -[0-9,]+ \+[0-9,]+ @@/.test(all_hunks[hunk_index][line_index])) { - new_offset++; - deleted_offset++; - return h("tr", { class: "commit-file-pos-change" }, [ - h("td", { "patch-line-col-unsel": "..." }), - h("td", { "patch-line-col-unsel": "..." }), - h("td", { "patch-line-col-unsel": "..." }), - h("td", [ - h("code", all_hunks[hunk_index][line_index]) - ]) - ]); - } - else if(/^\\ No newline at end of file$/.test(all_hunks[hunk_index][line_index])) { - new_offset++; - deleted_offset++; - return h("tr", { class: "commit-file-no-newline" }, [ - h("td", ""), - h("td", ""), - h("td", ""), - h("td", [ - h("code", all_hunks[hunk_index][line_index]) - ]) - ]); - } - else { - let first_td; - let second_td; - let third_td; - - if(hunk['new'].includes(line_index)) { - first_td = h("td", ""); - second_td = h("td", { class: "line-highlight-new", "patch-line-col-unsel": Number(hunk["new_start"]) + line_index - new_offset }); - third_td = h("td", { class: "line-new", "patch-line-col-unsel": "+" }); - deleted_offset++; - } - else if(hunk['deleted'].includes(line_index)) { - first_td = h("td", { "patch-line-col-unsel": Number(hunk["old_start"]) + line_index - deleted_offset }); - second_td = h("td", { class: "line-highlight-deleted" }); - third_td = h("td", { class: "line-deleted", "patch-line-col-unsel": "-" }); - new_offset++; - } - else { - first_td = h("td", { class: "line-unchanged", "patch-line-col-unsel": Number(hunk["old_start"]) + line_index - deleted_offset }); - second_td = h("td", { class: "line-unchanged", "patch-line-col-unsel": Number(hunk["new_start"]) + line_index - new_offset }); - third_td = h("td", ""); - } - - let comment_open = line.match(/<span class="hljs-comment">/g); - const comment_open_cnt = (comment_open !== null) ? comment_open.length : 0; - comment_open = (comment_open !== null) ? comment_open[0] : ""; - - let comment_close = line.match(/<\/span>/g); - const comment_close_cnt = (comment_close !== null) ? comment_close.length : 0; - comment_close = (comment_close !== null) ? comment_close[0] : ""; - - if(comment_open_cnt > comment_close_cnt) { - line = line + "</span>"; - console.log("Öppning " + line); - multiline_comments.push(comment_open); - } - else if(comment_open_cnt < comment_close_cnt && multiline_comments.length !== 0) { - line = multiline_comments[multiline_comments.length - 1] + line; - console.log("Stängning " + line + " " + multiline_comments[multiline_comments.length - 1]); - multiline_comments.pop(); - } - else if(multiline_comments.length !== 0) { - line = multiline_comments[multiline_comments.length - 1] + line + "</span>"; - console.log("Mitt i " + line); - } - - return h("tr", [ - first_td, - second_td, - third_td, - h("td", [ - h("code", { innerHTML: line }) - ]) - ]); - } - }); - }) - ]) - ])); - } - else { - commit_patch.push(h("div", { class: "ps-3 pt-3 patch-too-large" }, [ - h("span", "Patch is too large to display.") - ])); - } - - return () => h("div", { class: "commit-file" }, commit_patch); - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/components/HomeHeader.vue b/src/frontend/components/HomeHeader.vue deleted file mode 100644 index 9a0688d..0000000 --- a/src/frontend/components/HomeHeader.vue +++ /dev/null @@ -1,39 +0,0 @@ -<template> - <div class="row mx-0"> - <div id="header" class="col d-flex mt-3 ms-2"> - <div class="d-inline ms-3"> - <span id="title" class="fs-1">{{ title }}</span> - <p id="about" class="mb-3 fs-4"> - {{ about }} - </p> - </div> - </div> - </div> -</template> - -<script> -import { watch, reactive, toRefs } from "vue"; - -export default { - name: "HomeHeader", - setup() - { - const state = reactive({ title: "", about: "" }); - - watch(() => - { - fetch(`${window.location.protocol}//${window.location.host}/api/v1/info`) - .then((res) => res.json()) - .then((data) => - { - state.title = data["data"]["title"], - state.about = data["data"]["about"] - }); - }); - - return { - ... toRefs(state) - }; - } -} -</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryCloneDropdown.vue b/src/frontend/components/RepositoryCloneDropdown.vue deleted file mode 100644 index aaef5ef..0000000 --- a/src/frontend/components/RepositoryCloneDropdown.vue +++ /dev/null @@ -1,71 +0,0 @@ -<template> - <div id="clone" class="d-flex align-items-center"> - <div class="dropdown"> - <button - class="btn btn-primary btn-sm dropdown-toggle" type="button" - id="dropdownMenuButton1" data-bs-toggle="dropdown" - data-bs-auto-close="outside" aria-expanded="false"> - Clone - </button> - <ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" aria-labelledby="dropdownMenuButton1"> - <li class="pt-2"> - <span class="ms-2 fs-5 fw-bold">Clone with HTTP</span> - <label id="clone-url-copy"> - <input - type="text" :value="url" - class="form-control form-control-sm ms-2 me-2" readonly> - <svg - xmlns="http://www.w3.org/2000/svg" height="18px" - viewBox="0 0 24 24" width="18px" - fill="#FFFFFF" @click="copyToClipboard"> - <path d="M0 0h24v24H0z" fill="none" /> - <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" /> - </svg> - </label> - </li> - </ul> - </div> - </div> -</template> - -<script> -import bootstrap from "bootstrap/dist/js/bootstrap.bundle"; - -export default { - name: "RepositoryCloneDropdown", - props: { - repository: { - type: String, - required: true - } - }, - methods: { - copyToClipboard(event) - { - const url_box = document.getElementById("clone").getElementsByTagName("input")[0]; - - url_box.select(); - url_box.setSelectionRange(0, 99999); - document.execCommand("copy"); - - event.stopPropagation(); - - var exampleEl = document.getElementById('clone-url-copy').getElementsByTagName("svg")[0]; - var tooltip = new bootstrap.Tooltip(exampleEl, { boundary: document.body, title: "Copied the URL", trigger: "manual" }); - tooltip.show(); - - const worker = new Worker("../util/worker.js"); - worker.postMessage({ work: "sleep", time: 1700 }); - - worker.onmessage = function() - { - tooltip.hide(); - } - } - }, - setup(props) - { - return { url: `${window.location.protocol}//${window.location.host}/${props.repository}` }; - } -} -</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryHeader.vue b/src/frontend/components/RepositoryHeader.vue deleted file mode 100644 index 39ec00d..0000000 --- a/src/frontend/components/RepositoryHeader.vue +++ /dev/null @@ -1,51 +0,0 @@ -<template> - <div class="row mx-0"> - <div id="header" class="col d-flex mt-3 ms-2"> - <BaseBackButton to="/" /> - <div class="d-inline ms-3"> - <span id="title" class="fs-1">{{ title }}</span> - <p id="about" class="fs-4"> - {{ about }} - </p> - </div> - </div> - </div> -</template> - -<script> -import BaseBackButton from "./BaseBackButton"; - -import { watch, reactive, toRefs } from "vue"; - -export default { - name: "RepositoryHeader", - components: { - BaseBackButton - }, - props: { - repository: { - type: String, - required: true - } - }, - setup(props) - { - const state = reactive({ title: "", about: "" }); - - watch(() => - { - fetch(`${window.location.protocol}//${window.location.host}/api/v1/repos/${props.repository}`) - .then((res) => res.json()) - .then((data) => - { - state.title = data["data"]["name"]; - state.about = data["data"]["description"]; - }); - }); - - return { - ... toRefs(state) - }; - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryNavbar.vue b/src/frontend/components/RepositoryNavbar.vue deleted file mode 100644 index 53e1bfa..0000000 --- a/src/frontend/components/RepositoryNavbar.vue +++ /dev/null @@ -1,54 +0,0 @@ -<template> - <div id="navbar" class="row mx-0"> - <div id="repo-navbar" class="col ms-4 ps-4"> - <nav class="navbar navbar-expand navbar-dark"> - <div class="container-fluid px-0"> - <div class="collapse navbar-collapse"> - <ul class="navbar-nav align-items-center flex-fill"> - <li - v-for="(item, index) in nav_items" :key="index" - class="nav-item"> - <router-link - class="nav-link fs-4" :class="{ active: activePage === item }" - :aria-current="(activePage === item) ? 'page' : ''" :to="'/' + repository + '/' + item"> - {{ item }} - </router-link> - </li> - <li class="nav-item ms-auto me-4"> - <RepositoryCloneDropdown :repository="repository" class="d-block" /> - </li> - </ul> - </div> - </div> - </nav> - </div> - </div> -</template> - -<script> -import RepositoryCloneDropdown from "./RepositoryCloneDropdown"; - -export default { - name: "RepositoryNavbar", - props: { - activePage: { - type: String, - required: true - }, - repository: { - type: String, - required: true - } - }, - components: { - RepositoryCloneDropdown - }, - data() - { - return { - nav_items: [ "log", "refs", "tree" ], - url: `${window.location.protocol}//${window.location.host}/${this.repository}` - }; - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryTreeBlob.vue b/src/frontend/components/RepositoryTreeBlob.vue deleted file mode 100644 index f287f47..0000000 --- a/src/frontend/components/RepositoryTreeBlob.vue +++ /dev/null @@ -1,154 +0,0 @@ -<template> - <BaseBreadcrumb :items="[{ name: 'Tree', path: '/' + repository + '/tree' }]" :active-item="path" /> - <table cellspacing="0px"> - <tbody> - <tr v-for="(line, index) in content_lines" :key="index"> - <td :line="index + 1" /> - <td> - <code v-html="line" /> - </td> - </tr> - </tbody> - </table> -</template> - -<script> -import { ref } from "vue"; -import hljs from "highlight.js"; -import hljs_languages from "../util/hljs-languages"; -import path from "path"; - -export default { - name: "RepositoryTreeBlob", - props: { - repository: { - type: String, - required: true - }, - path: { - type: String, - required: true - }, - content: { - type: String, - required: true - } - }, - watch: { - content() { - this.initHighlightedContent(); - } - }, - mounted() - { - this.initHighlightedContent(); - }, - setup(props) - { - const content_lines = ref([]); - - const initHighlightedContent = async () => - { - const language = hljs_languages.find((lang) => lang["extensions"].some((extension) => path.extname(props.path) === extension)); - let highlighted = language ? hljs.highlight(language["name"], props.content) : hljs.highlightAuto(props.content); - - content_lines.value = highlighted.value.split("\n"); - }; - - return { content_lines, initHighlightedContent }; - - /* - Console.log(props.content); - const content_lines = props.content.split("\n"); - - const language = hljs_languages.find((lang) => lang["extensions"].some((extension) => path.extname(props.path) === extension)); - let highlighted = language ? hljs.highlight(language["name"], props.content) : hljs.highlightAuto(props.content); - console.log(highlighted.value); - Let all_hunks = props.patch["hunks"].map((hunk) => hunk["hunk"]); - - const language = hljs_languages.find((lang) => lang["extensions"].some((extension) => props.patch["to"].endsWith(extension))); - let highlighted = language ? hljs.highlight(language["name"], all_hunks.join("\n")) : hljs.highlightAuto(all_hunks.join("\n")); - console.log(highlighted); - highlighted = highlighted["value"].split("\n"); - - const highlighted_hunks = []; - let hunk_start = 0; - all_hunks.forEach((hunk) => - { - const hunk_row_cnt = hunk.split("\n").length; - highlighted_hunks.push(highlighted.slice(hunk_start, hunk_start + hunk_row_cnt)); - hunk_start = hunk_start + hunk_row_cnt; - }); - - all_hunks = all_hunks.map((hunk) => hunk.split("\n")); - - return h("table", { cellspacing: "0px" }, [ - h("tbody", [ - Props.patch["hunks"].map((hunk, hunk_index) => - { - const multiline_comments = []; - - return highlighted_hunks[hunk_index].map((line, line_index) => - { - else { - let first_td; - let second_td; - let third_td; - - if(hunk['new'].includes(line_index)) { - first_td = h("td", ""); - second_td = h("td", { class: "line-highlight-new" }, Number(hunk["new_start"]) + line_index - new_offset); - third_td = h("td", { class: "line-new" }, "+"); - deleted_offset++; - } - else if(hunk['deleted'].includes(line_index)) { - first_td = h("td", Number(hunk["old_start"]) + line_index - deleted_offset); - second_td = h("td", { class: "line-highlight-deleted" }); - third_td = h("td", { class: "line-deleted" }, "-"); - new_offset++; - } - else { - first_td = h("td", { class: "line-unchanged" }, Number(hunk["old_start"]) + line_index - deleted_offset); - second_td = h("td", { class: "line-unchanged" }, Number(hunk["new_start"]) + line_index - new_offset); - third_td = h("td", ""); - } - - let comment_open = line.match(/<span class="hljs-comment">/g); - const comment_open_cnt = (comment_open !== null) ? comment_open.length : 0; - comment_open = (comment_open !== null) ? comment_open[0] : ""; - - let comment_close = line.match(/<\/span>/g); - const comment_close_cnt = (comment_close !== null) ? comment_close.length : 0; - comment_close = (comment_close !== null) ? comment_close[0] : ""; - - if(comment_open_cnt > comment_close_cnt) { - line = line + "</span>"; - console.log("Öppning " + line); - multiline_comments.push(comment_open); - } - else if(comment_open_cnt < comment_close_cnt && multiline_comments.length !== 0) { - line = multiline_comments[multiline_comments.length - 1] + line; - console.log("Stängning " + line + " " + multiline_comments[multiline_comments.length - 1]); - multiline_comments.pop(); - } - else if(multiline_comments.length !== 0) { - line = multiline_comments[multiline_comments.length - 1] + line + "</span>"; - console.log("Mitt i " + line); - } - - return h("tr", [ - first_td, - second_td, - third_td, - h("td", [ - h("code", { innerHTML: line }) - ]) - ]); - } - }); - }) - ]) - ]);*/ - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/components/RepositoryTreeTree.vue b/src/frontend/components/RepositoryTreeTree.vue deleted file mode 100644 index 70c63eb..0000000 --- a/src/frontend/components/RepositoryTreeTree.vue +++ /dev/null @@ -1,84 +0,0 @@ -<template> - <table id="tree" class="fs-5"> - <thead> - <tr> - <th>Name</th> - <th>Last commit</th> - <th>Last updated</th> - </tr> - </thead> - <tbody> - <tr v-if="path !== ''" @click="$router.go(-1)"> - <td - class="d-flex align-items-center"> - <div class="tree-entry-padding" /> - .. - </td> - <td /> - <td /> - </tr> - <tr - v-for="(entry, entry_name, index) in tree" :key="index" - @click="$router.push(`/${repository}/tree${path ? '/' + path : ''}/${entry_name}`)"> - <td class="d-flex align-items-center"> - <svg - xmlns="http://www.w3.org/2000/svg" height="18px" - viewBox="0 0 24 24" width="18px" - fill="#FFFFFF" v-if="entry['type'] === 'tree'" - preserveAspectRatio="xMidYMin"> - <path d="M0 0h24v24H0z" fill="none" /> - <path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" /> - </svg> - <span v-else class="tree-entry-padding" /> - <a @click="stopClick" :href="`/${repository}/tree${path ? '/' + path : ''}/${entry_name}`">{{ entry_name }}</a> - </td> - <td> - <a @click="routeToCommit(entry.last_commit.id, $event)" :href="`/${repository}/log/${entry.last_commit.id}`"> - {{ entry.last_commit.message }} - </a> - </td> - <td> - {{ getPrettyLastUpdated(entry.last_commit.time) }} - </td> - </tr> - </tbody> - </table> -</template> - -<script> -const { formatDistance } = require('date-fns'); - -export default { - name: "RepositoryTreeTree", - props: { - repository: { - type: String, - required: true - }, - path: { - type: String, - required: true - }, - tree: { - type: Object, - required: true - } - }, - methods: { - stopClick(event) - { - event.preventDefault(); - }, - routeToCommit(commit_id, event) - { - event.stopPropagation(); - event.preventDefault(); - this.$router.push(`/${this.repository}/log/${commit_id}`); - }, - getPrettyLastUpdated(date) - { - return formatDistance(new Date(), new Date(date)); - } - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/router/index.js b/src/frontend/router/index.js deleted file mode 100644 index fc332cd..0000000 --- a/src/frontend/router/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import { createRouter, createWebHistory } from "vue-router"; -import Home from "../views/Home"; -import Repository from "../views/Repository"; -import RepositoryLog from "../views/RepositoryLog"; -import RepositoryCommit from "../views/RepositoryCommit"; -import RepositoryTree from "../views/RepositoryTree"; - -const routes = [ - { - name: "Home", - path: "/", - component: Home - }, - { - name: "Repository", - path: '/:repo([a-zA-Z0-9\\.\\-_]+)', - component: Repository, - props: route => ({ repository: route.params.repo }), - children: [ - { - name: "Repository Log", - path: "log", - component: RepositoryLog - }, - { - name: "Commit", - path: "log/:commit([a-fA-F0-9]{40}$)", - component: RepositoryCommit, - props: route => ({ commit: route.params.commit }) - }, - { - name: "Tree Entry", - path: "tree/:path*", - component: RepositoryTree, - props: route => ({ pathArr: route.params.path ? route.params.path : [] }) - } - ] - } -]; - -const router = createRouter({ - history: createWebHistory(process.env.BASE_URL), - routes -}); - -export default router;
\ No newline at end of file diff --git a/src/frontend/scss/abstracts/_colors.scss b/src/frontend/scss/abstracts/_colors.scss deleted file mode 100644 index 3c05336..0000000 --- a/src/frontend/scss/abstracts/_colors.scss +++ /dev/null @@ -1,10 +0,0 @@ -$primary: #023E8A; -$primary-light: #0077b6; -$secondary: #F48C06; -$success: #40916C; -$new: #06d6a0; -$danger: #D00000; -$text: #ffffff; -$text-gray: #6c757d; -$background: #121212; -$not-selected: #adb5bd;
\ No newline at end of file diff --git a/src/frontend/scss/abstracts/_fonts.scss b/src/frontend/scss/abstracts/_fonts.scss deleted file mode 100644 index 6af5233..0000000 --- a/src/frontend/scss/abstracts/_fonts.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Oxygen:wght@300&display=swap'); - -$title: 'Oxygen', sans-serif; -$primary: 'Open Sans', sans-serif;
\ No newline at end of file diff --git a/src/frontend/scss/style.scss b/src/frontend/scss/style.scss deleted file mode 100644 index 8cca066..0000000 --- a/src/frontend/scss/style.scss +++ /dev/null @@ -1,396 +0,0 @@ -@use "abstracts/colors"; -@use "abstracts/fonts"; - -@import "../../../node_modules/bootstrap/scss/functions"; -@import "../../../node_modules/bootstrap/scss/variables"; -@import "../../../node_modules/bootstrap/scss/mixins"; - -$theme-colors: ( - "primary": colors.$primary, - "secondary": colors.$secondary, - "success": colors.$success, - "info": $info, - "warning": $warning, - "danger": colors.$danger, - "light": $light, - "dark": $dark -); - -$font-size-base: 0.75rem; - -$h1-font-size: $font-size-base * 2.5; -$h2-font-size: $font-size-base * 2; -$h3-font-size: $font-size-base * 1.75; -$h4-font-size: $font-size-base * 1.5; -$h5-font-size: $font-size-base * 1.125; -$h6-font-size: $font-size-base; - -$font-sizes: ( - 1: $h1-font-size, - 2: $h2-font-size, - 3: $h3-font-size, - 4: $h4-font-size, - 5: $h5-font-size, - 6: $h6-font-size -); - -$navbar-nav-link-padding-x: 0.5rem; - -$btn-box-shadow: none; -$btn-active-box-shadow: none; -$btn-focus-box-shadow: none; - -$input-bg: lighten(#000000, 12%); -$input-disabled-bg: lighten(#000000, 12%); -$input-color: colors.$text; -$input-focus-color: colors.$text; -$input-focus-box-shadow: none; -$input-disabled-border-color: lighten(#000000, 50%); -$input-height-sm: auto; - -$dropdown-dark-bg: lighten(#000000, 10%); - -@import "../../../node_modules/bootstrap/scss/breadcrumb"; -@import "../../../node_modules/bootstrap/scss/tooltip"; -@import "../../../node_modules/bootstrap/scss/buttons"; -@import "../../../node_modules/bootstrap/scss/dropdown"; -@import "../../../node_modules/bootstrap/scss/forms"; -@import "../../../node_modules/bootstrap/scss/utilities"; -@import "../../../node_modules/bootstrap/scss/utilities/api"; -@import "../../../node_modules/bootstrap/scss/nav"; -@import "../../../node_modules/bootstrap/scss/navbar"; - -$table-cell-padding-x: 1rem; -$table-cell-padding-y: 0.2rem; - -$table-variants: ( - "primary": shift-color($primary, $table-bg-scale), - "secondary": shift-color($secondary, $table-bg-scale), - "success": shift-color($success, $table-bg-scale), - "info": shift-color($info, $table-bg-scale), - "warning": shift-color($warning, $table-bg-scale), - "danger": shift-color($danger, $table-bg-scale), - "light": $light, - "dark": colors.$background, -); - -@import "../../../node_modules/bootstrap/scss/tables"; - -@import "../../../node_modules/bootstrap/scss/containers"; -@import "../../../node_modules/bootstrap/scss/grid"; - -@import "../../../node_modules/highlight.js/scss/srcery.scss"; - -body { - background-color: colors.$background; - color: colors.$text; - font-family: fonts.$primary; - margin: 0px; -} - -ul { - list-style-type: none; - padding: 0; -} - -li { - div { - h2 { - margin: 0px; - } - } -} - -p { - margin: 0px; -} - -#title { - font-family: fonts.$title; - font-weight: 300; - line-height: 0.6; -} - -#about { - font-weight: 300; - padding-left: 1px; -} - -.form-control { - width: auto; -} - -#clone { - margin-left: auto; - margin-right: 40px; -} - -#clone-url-copy { - position: relative; - height: 30px; - display: block; - text-align: left; - margin: 5px auto; - input { - display: inline-block; - padding-right: 30px; - } - svg { - content: ""; - position: absolute; - right: 12px; - top: 7px; - bottom: 0; - width: 18px; - fill: colors.$not-selected; - &:hover { - fill: colors.$text; - } - } -} - -#projects-search { - align-items: center; - form { - display: flex; - align-items: center; - height: 35px; - input[type=search] { - margin-right: 15px; - } - } -} - -#repos { - margin-top: 25px; - li { - margin-bottom: 25px; - } -} - -.repo-last-updated { - display: block; - font-weight: 300; - font-style: italic; -} - -input[type=submit] { - background-color: colors.$primary; - color: colors.$text; - font-size: 1rem; - border: 0px; - border-radius: 7px; - padding: 8px 15px 8px 15px; -} - -a { - color: colors.$text; - text-decoration: none; - &:hover { - color: colors.$primary-light; - } -} - -.breadcrumb { - li { - margin-bottom: 0.5rem; - } -} - -#commit-info { - margin-bottom: 2rem; - tbody tr { - td { - padding: 0px; - padding-right: 10px; - } - } -} - -.commit-file { - margin-bottom: 50px; - table { - padding-top: 15px; - tbody tr td { - padding: 0px; - padding-left: 8px; - vertical-align: top; - &:nth-child(2) { - padding-right: 7px; - } - &:nth-child(3) { - padding-right: 15px; - } - } - } -} - -.commit-file-add-del { - margin-left: auto; - margin-right: 23px; - span { - margin-right: 10px !important; - font-weight: 700; - &:nth-child(1) { - color: colors.$new; - } - } -} - -.commit-file-pos-change { - color: colors.$text-gray; -} - -.commit-file-no-newline { - color: colors.$text-gray; -} - -.line-new { - color: colors.$new; -} -.line-deleted { - color: colors.$danger; -} - -.line-unchanged { - color: colors.$text-gray; -} - -[patch-line-col-unsel]::before { - content: attr(patch-line-col-unsel); -} - -.line-highlight-new { - border-right: 1px solid colors.$new; -} -.line-highlight-deleted { - border-right: 1px solid colors.$danger; -} - -code { - white-space: pre-wrap; - word-wrap: anywhere; -} - -.commit-file-header { - display: flex; - background-color: lighten(#000000, 14%); - padding: 10px; - span { - margin-right: 30px; - &:nth-child(2) { - color: colors.$danger; - } - } -} - -#back:hover { - fill: colors.$primary-light; -} - -#navbar { - line-height: 0; -} - -th { - text-align: start; -} - -.commit-info-title { - color: colors.$secondary; - padding-right: 30px; - width: 20px; -} - -.patch-too-large { - font-weight: 600; -} - -.dropdown-item { - width: auto !important; -} - -.btn-primary { - color: colors.$text; - &:hover { - background-color: colors.$primary-light; - } -} - - -.btn-check:checked + .btn-primary:focus, -.btn-check:active + .btn-primary:focus, -.btn-primary:active:focus, -.btn-primary.active:focus, -.show > .btn-primary.dropdown-toggle:focus { - box-shadow: none; -} - -.btn-check:focus + .btn-primary, -.btn-primary:focus { - box-shadow: none; -} - -#tree { - border-spacing: 0; - th { - padding-bottom: 5px; - color: colors.$secondary; - } - tbody tr:hover { - background-color: lighten(colors.$background, 10%); - } - td { - padding-top: 5px; - padding-bottom: 5px; - padding-right: 2vw; - &:nth-child(2) a, &:nth-child(3) { - font-weight: 300; - } - } - .tree-entry-padding, svg { - width: 18px; - padding-right: 5px; - } - a { - padding-right: 18px; - } -} - -[line]::before { - content: attr(line); - padding-right: 10px; -} - -@include media-breakpoint-down(sm) { - .commit-file table tbody tr td { - padding-left: 4px; - &:nth-child(2) { - padding-right: 4px; - } - &:nth-child(3) { - padding-right: 5px; - } - } - .table > :not(caption) > * > * { - padding: 0.1rem; - } -} - -@media (max-width: 1200px) { - .fs-1 { - font-size: calc(1.375rem + 0.667vw) !important; - } - .fs-2 { - font-size: calc(1.325rem + 1.584vw) !important; - } - .fs-3 { - font-size: calc(1.3rem + 0.017vw) !important; - } - .fs-4 { - font-size: calc(0.82rem + 0.4vw) !important; - } - .fs-5 { - font-size: calc(0.65rem + 0.25vw) !important; - } -}
\ No newline at end of file diff --git a/src/frontend/util/hljs-languages.js b/src/frontend/util/hljs-languages.js deleted file mode 100644 index c8576e0..0000000 --- a/src/frontend/util/hljs-languages.js +++ /dev/null @@ -1,45 +0,0 @@ -const languages = [ - { "name": "arduino", "extensions": [ ".ino" ]}, - { "name": "actionscript", "extensions": [ ".as" ]}, - { "name": "bash", "extensions": [ ".sh", ".zsh" ]}, - { "name": "csharp", "extensions": [ ".cs" ]}, - { "name": "c", "extensions": [ ".c", ".h" ]}, - { "name": "cpp", "extensions": [ ".cpp", ".hpp" ]}, - { "name": "cmake", "extensions": [ "cmake.in" ]}, - { "name": "css", "extensions": [ ".css" ]}, - { "name": "d", "extensions": [ ".d" ]}, - { "name": "dos", "extensions": [ ".bat", ".cmd" ]}, - { "name": "dockerfile", "extensions": [ "dockerfile", "Dockerfile" ]}, - { "name": "go", "extensions": [ ".go" ]}, - { "name": "gradle", "extensions": [ ".gradle" ]}, - { "name": "xml", "extensions": [ ".xml", ".html", ".xhtml", ".rss", ".atom", ".xjb", ".xsd", ".xsl", ".plist", ".svg" ]}, - { "name": "haskell", "extensions": [ ".hs" ]}, - { "name": "ini", "extensions": [ ".ini", ".toml" ]}, - { "name": "json", "extensions": [ ".json" ]}, - { "name": "java", "extensions": [ ".java", ".jsp" ]}, - { "name": "javascript", "extensions": [ ".js", ".jsx" ]}, - { "name": "kotlin", "extensions": [ ".kt" ]}, - { "name": "lua", "extensions": [ ".lua" ]}, - { "name": "makefile", "extensions": [ "makefile", "Makefile" ]}, - { "name": "markdown", "extensions": [ ".md" ]}, - { "name": "objectivec", "extensions": [ ".m", ".mm", ".M" ]}, - { "name": "php", "extensions": [ ".php" ]}, - { "name": "perl", "extensions": [ ".pl", ".pm" ]}, - { "name": "plaintext", "extensions": [ ".txt" ]}, - { "name": "pgsql", "extensions": [ ".pgsql" ]}, - { "name": "powershell", "extensions": [ ".ps", ".ps1" ]}, - { "name": "python", "extensions": [ ".py" ]}, - { "name": "ruby", "extensions": [ ".rb" ]}, - { "name": "rust", "extensions": [ ".rs" ]}, - { "name": "scss", "extensions": [ ".scss" ]}, - { "name": "sql", "extensions": [ ".sql" ]}, - { "name": "swift", "extensions": [ ".swift" ]}, - { "name": "typescript", "extensions": [ ".ts" ]}, - { "name": "vbnet", "extensions": [ ".vb" ]}, - { "name": "vba", "extensions": [ ".vba" ]}, - { "name": "vbscript", "extensions": [ ".vbs" ]}, - { "name": "vim", "extensions": [ ".vim" ]}, - { "name": "yml", "extensions": [ ".yml" ]} -]; - -export default languages;
\ No newline at end of file diff --git a/src/frontend/util/worker.js b/src/frontend/util/worker.js deleted file mode 100644 index e50743a..0000000 --- a/src/frontend/util/worker.js +++ /dev/null @@ -1,6 +0,0 @@ -onmessage = function(e) -{ - if(e.data.work === "sleep") { - setTimeout(() => postMessage("done"), e.data.time); - } -};
\ No newline at end of file diff --git a/src/frontend/views/Home.vue b/src/frontend/views/Home.vue deleted file mode 100644 index e3746c7..0000000 --- a/src/frontend/views/Home.vue +++ /dev/null @@ -1,62 +0,0 @@ -<template> - <HomeHeader /> - <div class="row mx-0"> - <div id="projects-header" class="col ms-4"> - <p class="fs-1"> - Projects - </p> - </div> - <div id="projects-search" class="col d-flex justify-content-end"> - <form> - <input type="search" name="q"> - <input type="submit" value="Search"> - </form> - </div> - </div> - <div class="row mx-0"> - <div class="col ms-4"> - <ul id="repos"> - <li v-for="(project, project_name, index) in projects" :key="index"> - <div v-if="(search !== null && project_name.includes(search)) || search == null"> - <p class="fs-3"> - <router-link :to="project_name"> - {{ project_name }} - </router-link> - </p> - <span class="repo-last-updated fs-5">Last updated about {{ project["last_updated"] }} ago</span> - <span class="fs-5">{{ project["description"] }}</span> - </div> - </li> - </ul> - </div> - </div> -</template> - -<script> -import HomeHeader from "../components/HomeHeader"; -import { watch, reactive, toRefs } from "vue"; - -export default { - name: "Home", - components: { - HomeHeader - }, - setup() - { - const state = reactive({ projects: {}, search: "" }); - - watch(() => - { - fetch(`${window.location.protocol}//${window.location.host}/api/v1/repos`) - .then((res) => res.json()) - .then((data) => state.projects = data["data"]); - }); - - state.search = (new URLSearchParams(window.location.search)).get("q"); - - return { - ... toRefs(state) - }; - } -} -</script>
\ No newline at end of file diff --git a/src/frontend/views/Repository.vue b/src/frontend/views/Repository.vue deleted file mode 100644 index 8863529..0000000 --- a/src/frontend/views/Repository.vue +++ /dev/null @@ -1,29 +0,0 @@ -<template> - <RepositoryHeader :repository="repository" /> - <router-view :repository="repository" /> -</template> - -<script> -import RepositoryHeader from "../components/RepositoryHeader"; - -export default { - name: "Repository", - components: { - RepositoryHeader - }, - props: { - repository: { - type: String, - required: true - } - }, - created() - { - if(/^\/[a-zA-Z0-9.\-_]+[/]?$/.test(window.location.pathname)) { - this.$router.push({ path: `${window.location.pathname}${( window.location.pathname.endsWith("/") ) ? "log" : "/log"}`, replace: true}); - } - - console.log(this.repository); - } -} -</script>
\ No newline at end of file diff --git a/src/frontend/views/RepositoryCommit.vue b/src/frontend/views/RepositoryCommit.vue deleted file mode 100644 index d3ba174..0000000 --- a/src/frontend/views/RepositoryCommit.vue +++ /dev/null @@ -1,89 +0,0 @@ -<template> - <RepositoryNavbar active-page="log" :repository="repository" /> - <div class="row mx-0"> - <div class="col ms-2 ps-4 ps-sm-5 fs-5 vld-parent"> - <BaseBreadcrumb :items="[{ name: 'Log', path: '/' + repository + '/log' }]" :active-item="commit" /> - <Loading - v-model:active="is_loading" :height="24" - :width="24" color="#ffffff" - :opacity="0" /> - <table id="commit-info" class="table table-dark"> - <tbody> - <tr> - <td class="commit-info-title"> - Author - </td> - <td>{{ commit_data["author"] }}</td> - </tr> - <tr> - <td class="commit-info-title"> - Date - </td> - <td>{{ commit_data["date"] }}</td> - </tr> - <tr> - <td class="commit-info-title"> - Message - </td> - <td>{{ commit_data["message"] }}</td> - </tr> - </tbody> - </table> - - <template - v-for="(patch, index) in commit_data['patches']" :key="index"> - <CommitPatch :patch="patch" /> - </template> - </div> - </div> -</template> - -<script> -import RepositoryNavbar from "../components/RepositoryNavbar"; -import CommitPatch from "../components/CommitPatch"; -import BaseBreadcrumb from "../components/BaseBreadcrumb"; -import Loading from "vue-loading-overlay"; -import 'vue-loading-overlay/dist/vue-loading.css'; -import { watch, reactive, toRefs } from "vue"; -import { format } from "date-fns"; - -export default { - name: "RepositoryCommit", - components: { - RepositoryNavbar, - CommitPatch, - Loading, - BaseBreadcrumb - }, - props: { - repository: { - type: String, - required: true - }, - commit: { - type: String, - required: true - } - }, - setup(props) - { - const state = reactive({ commit_data: {}, is_loading: true }); - - watch(() => - { - fetch(`${window.location.protocol}//${window.location.host}/api/v1/repos/${props.repository}/log/${props.commit}`) - .then((res) => res.json()) - .then((data) => - { - data["data"]["date"] = format(new Date(data["data"]["date"]), "yyyy-MM-dd hh:mm"); - state.commit_data = data["data"]; - state.is_loading = false; - }); - }); - - return { - ... toRefs(state) - }; - } -}; -</script> diff --git a/src/frontend/views/RepositoryLog.vue b/src/frontend/views/RepositoryLog.vue deleted file mode 100644 index 3adb6c2..0000000 --- a/src/frontend/views/RepositoryLog.vue +++ /dev/null @@ -1,91 +0,0 @@ -<template> - <RepositoryNavbar active-page="log" :repository="repository" /> - <div class="row mx-0 vld-parent"> - <Loading - v-model:active="is_loading" :height="24" - :width="24" color="#ffffff" - :opacity="0" /> - <div class="col ms-4 ps-4 ps-sm-5 mt-3"> - <table id="log" class="table table-dark fs-5"> - <thead> - <tr> - <th class="text-secondary"> - Subject - </th> - <th class="text-secondary"> - Author - </th> - <th class="text-secondary"> - Date - </th> - <th class="text-secondary"> - Files - </th> - <th class="text-secondary"> - Del/Add - </th> - </tr> - </thead> - <tbody> - <tr v-for="(commit, index) in commits" :key="index"> - <td> - <router-link :to="'log/' + commit['commit']"> - {{ commit["message"] }} - </router-link> - </td> - <td>{{ commit["author_name"] }}</td> - <td>{{ format(new Date(commit["date"]), "yyyy-MM-dd hh:mm") }}</td> - <td>{{ commit["files_changed"] }}</td> - <td><span class="text-danger">-{{ commit["deletions"] }}</span> / <span class="text-success">+{{ commit["insertions"] }}</span></td> - </tr> - </tbody> - </table> - </div> - </div> -</template> - -<script> -import RepositoryNavbar from "../components/RepositoryNavbar"; -import Loading from "vue-loading-overlay"; -import 'vue-loading-overlay/dist/vue-loading.css'; -import { format } from "date-fns"; -import { watch, reactive, toRefs } from "vue"; - -export default { - name: "RepositoryLog", - components: { - RepositoryNavbar, - Loading - }, - props: { - repository: { - type: String, - required: true - } - }, - data() - { - return { - format: format - }; - }, - setup(props) - { - const state = reactive({ commits: {}, is_loading: true }); - - watch(() => - { - fetch(`${window.location.protocol}//${window.location.host}/api/v1/repos/${props.repository}/log`) - .then((res) => res.json()) - .then((data) => { - state.commits = data["data"]; - state.is_loading = false; - }); - }); - - return { - ... toRefs(state) - }; - } -}; -</script>
\ No newline at end of file diff --git a/src/frontend/views/RepositoryTree.vue b/src/frontend/views/RepositoryTree.vue deleted file mode 100644 index dc2c067..0000000 --- a/src/frontend/views/RepositoryTree.vue +++ /dev/null @@ -1,110 +0,0 @@ -<template> - <RepositoryNavbar active-page="tree" :repository="repository" /> - <div class="row mx-0 vld-parent"> - <Loading - v-model:active="is_loading" :height="24" - :width="24" color="#ffffff" - :opacity="0" /> - <div class="col ms-4 ps-4 ps-sm-5 mt-3 fs-5"> - <BaseBreadcrumb - :items="[{ name: repository, path: '/' + repository + '/tree' }].concat(pathArr.slice(0, -1).map((path_part, index) => - { - return { - name: path_part, - path: '/' + repository + '/tree/' + pathArr.slice(0, index + 1).join('/') - } - }))" :active-item="pathArr[pathArr.length - 1]" /> - <RepositoryTreeTree - :repository="repository" :path="path" - :tree="tree" v-if="type === 'tree'" /> - <RepositoryTreeBlob - :repository="repository" :path="path" - :content="blob_content" v-else /> - </div> - </div> -</template> - -<script> -import RepositoryNavbar from "../components/RepositoryNavbar"; -import RepositoryTreeTree from "../components/RepositoryTreeTree"; -import RepositoryTreeBlob from "../components/RepositoryTreeBlob"; -import BaseBreadcrumb from "../components/BaseBreadcrumb"; -import Loading from "vue-loading-overlay"; -import 'vue-loading-overlay/dist/vue-loading.css'; -import { ref } from "vue"; - -export default { - name: "RepositoryTree", - components: { - RepositoryNavbar, - Loading, - RepositoryTreeTree, - RepositoryTreeBlob, - BaseBreadcrumb - }, - props: { - repository: { - type: String, - required: true - }, - pathArr: { - type: Array, - required: true - } - }, - watch: { - pathArr() - { - this.fetchTree(); - } - }, - setup(props) - { - const type = ref(""); - const tree = ref({}); - const blob_content = ref(""); - const is_loading = ref(true); - const path = ref(""); - - const fetchTree = async () => - { - path.value = props.pathArr ? props.pathArr.join("/") : undefined; - const data = await (await fetch(`${window.location.protocol}//${window.location.host}/api/v1/repos/${props.repository}/tree${path.value ? "?path=" + path.value : ""}`)).json(); - - console.log(path.value); - type.value = data["data"]["type"]; - - if(data["data"]["type"] === "tree") { - const tree_data = data["data"]["tree"]; - - let tree_trees = Object.entries(tree_data).filter((entry) => entry[1].type === "tree"); - tree_trees = tree_trees.sort((a, b) => a[0].localeCompare(b[0])); - - let tree_blobs = Object.entries(tree_data).filter((entry) => entry[1].type === "blob"); - tree_blobs = tree_blobs.sort((a, b) => a[0].localeCompare(b[0])); - - tree.value = Object.fromEntries(tree_trees.concat(tree_blobs)); - } - else { - blob_content.value = data["data"]["content"]; - } - - is_loading.value = false; - }; - - return { - type, - tree, - blob_content, - is_loading, - path, - fetchTree - }; - }, - mounted() - { - console.log("VAFAAAN öaöaöaö"); - this.fetchTree(); - } -}; -</script>
\ No newline at end of file |