From 15992970bc9d3eec402f6d85e40236e3095f0fc0 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sat, 17 Apr 2021 22:45:12 +0200 Subject: Finished repos api endpoint, created complete repos page & removed dist dir --- api/git.js | 112 +++++++--- api/v1.js | 32 +-- app.js | 4 +- dist/index.01a1c8c0.js | 448 ---------------------------------------- dist/index.01a1c8c0.js.map | 1 - dist/index.8dacaf33.css | 8 - dist/index.8dacaf33.css.map | 1 - dist/index.906f4b28.css | 2 - dist/index.906f4b28.css.map | 1 - dist/index.html | 11 - src/index.html | 27 ++- src/js/app.js | 66 ++++++ src/scss/abstracts/_colors.scss | 3 + src/scss/abstracts/_fonts.scss | 5 + src/scss/style.scss | 103 +++++++++ 15 files changed, 297 insertions(+), 527 deletions(-) delete mode 100644 dist/index.01a1c8c0.js delete mode 100644 dist/index.01a1c8c0.js.map delete mode 100644 dist/index.8dacaf33.css delete mode 100644 dist/index.8dacaf33.css.map delete mode 100644 dist/index.906f4b28.css delete mode 100644 dist/index.906f4b28.css.map delete mode 100644 dist/index.html create mode 100644 src/js/app.js create mode 100644 src/scss/abstracts/_colors.scss create mode 100644 src/scss/abstracts/_fonts.scss diff --git a/api/git.js b/api/git.js index b8d89c3..59f7371 100644 --- a/api/git.js +++ b/api/git.js @@ -1,12 +1,14 @@ const { exec } = require("child_process"); +const { differenceInMilliseconds } = require('date-fns'); +const fs = require('fs'); -const log_format='{"hash": "%H", "author": "%an", "author_email": "%ae", "date": "%at", "subject": "%s"}' +const log_format='{"hash": "%H", "author": "%an", "author_email": "%ae", "date": "%at", "subject": "%s"}'; -function getLog(path) +function execGit(path, action , format, args = "") { return new Promise((resolve) => { - exec(`git -C ${path} log --format=format:'${log_format}'`, (error, stdout, stderr) => + exec(`git -C ${path} ${action} ${args} --format=format:'${format}'`, (error, stdout, stderr) => { if(error) { const no_such_fileor_dir = new RegExp(`cannot change to '${path.replace('/', "\/")}': No such file or directory\\n$`); @@ -23,40 +25,98 @@ function getLog(path) return; } - const log = stdout.split('\n'); - log.forEach((entry, index) => log[index] = JSON.parse(entry)); + resolve({ "data": stdout }); + }); + }); +} + +async function getLog(path) +{ + let log = await execGit(path, "log", log_format); + + if(!log["error"]) { + log["data"] = log["data"].split('\n'); + log["data"].forEach((entry, index) => log["data"][index] = JSON.parse(entry)); + } + + return log; +} + +function getOptimalDateDifference(difference) +{ + const time_values = { + "second": 1000, + "minute": 60000, + "hour": 3600000, + "day": 86400000, + "week": 604800000, + "month": 2629800000, + "year": 31557600000 + }; + + let last; + for(const [key, value] of Object.entries(time_values)) { + if(difference > value) { + last = key; + continue; + } + break; + } + + return `${Math.round(difference / time_values[last])} ${last}s`; +} + +async function getTimeSinceLatestCommit(path) +{ + let commit_timestamp = (await execGit(path, "log", "%at", "-n 1")); + + if(!commit_timestamp["error"]) { + commit_timestamp = commit_timestamp["data"] * 1000; + + const commit_date = new Date(commit_timestamp); + const now_date = new Date(); + + const difference = getOptimalDateDifference(differenceInMilliseconds(now_date, commit_date)); + + return difference; + } + return commit_timestamp; +} - resolve({ "data": log }); +function getRepoFile(base_dir, repo, file) +{ + return new Promise(resolve => + { + fs.readFile(`${base_dir}/${repo}/${file}`, async (err, content) => + { + if(!err) { + resolve(content.toString().replaceAll('\n', '')); + return; + } + resolve(""); }); }); } -function getTimeSinceLatestCommit(path) +function getBasicRepoInfo(base_dir, repo_dirs) { return new Promise((resolve) => { - exec(`git -C ${path} log -n 1 --date=unix'`, (error, stdout, stderr) => + let repos = {}; + repo_dirs.forEach(async (repo, index, arr) => { - if(error) { - const no_such_fileor_dir = new RegExp(`cannot change to '${path.replace('/', "\/")}': No such file or directory\\n$`); + const desc = await getRepoFile(base_dir, repo, "description"); + const owner = await getRepoFile(base_dir, repo, "owner"); + const last_commit_date = await getTimeSinceLatestCommit(`${base_dir}/${repo}`); - if(no_such_fileor_dir.test(error.message)) { - resolve({ "error": 404 }); - return; - } - resolve({ "error": error.message }); - return; - } - if(stderr) { - resolve({ "error": "Failed to communicate with git!" }); - return; - } + let repo_name = ""; + repo_name = repo.slice(0, -4); + repos[repo_name] = { "description": desc, "owner": owner, "last_updated": last_commit_date }; - const - - resolve({ "data": log }); - }); + if(index === 0) resolve(repos); + }); }); } -module.exports.getLog = getLog; \ No newline at end of file +module.exports.getLog = getLog; +module.exports.getBasicRepoInfo = getBasicRepoInfo; \ No newline at end of file diff --git a/api/v1.js b/api/v1.js index 6b96e94..75509ad 100644 --- a/api/v1.js +++ b/api/v1.js @@ -8,40 +8,20 @@ const base_dir="/home/hampus/Projects/" router.get("/repos", async function(req, res) { - fs.readdir(base_dir, (err, files) => + fs.readdir(base_dir, async (err, repo_dirs) => { if(err) { throw err; } + repo_dirs = repo_dirs.filter(repo => repo.endsWith(".git")); - var repos = {}; + console.log("Repo dirs: " + repo_dirs); - files = files.filter(repo => repo.endsWith(".git")); + const repos = await git.getBasicRepoInfo(base_dir, repo_dirs); - let getRepoInfo = new Promise((resolve) => - { - files.forEach((repo, index, arr) => - { - fs.readFile(`${base_dir}/${repo}/description`, (err, content) => - { - let desc = ""; - - if(!err) { - desc = content.toString(); - } + console.log("I v1.js\n" + JSON.stringify(repos) + "\n"); - repos[repo.slice(0, -4)] = { "description": desc }; - - if(index === arr.length -1) resolve(); - }) - }); - }); - - getRepoInfo.then(() => - { - console.log(`Sist: ${JSON.stringify(repos)}`); - res.json({ "data": repos }); - }) + res.json({ "data": repos }); }); }); diff --git a/app.js b/app.js index 9870050..66b7cd8 100644 --- a/app.js +++ b/app.js @@ -1,12 +1,14 @@ const express = require("express"); const api = require("./api/v1"); -const differenceInDays = require('date-fns/differenceInDays') +/* const now = new Date(); const commit = new Date(1611497751 * 1000); console.log(now); console.log(commit) console.log(differenceInDays(now, commit)); +*/ + const app = express(); app.use("/api/v1", api); app.listen(1337); \ No newline at end of file diff --git a/dist/index.01a1c8c0.js b/dist/index.01a1c8c0.js deleted file mode 100644 index d725570..0000000 --- a/dist/index.01a1c8c0.js +++ /dev/null @@ -1,448 +0,0 @@ -// modules are defined as an array -// [ module function, map of requires ] -// -// map of requires is short require name -> numeric require -// -// anything defined in a previous bundle is accessed via the -// orig method which is the require for previous bundles - -(function(modules, entry, mainEntry, parcelRequireName, globalName) { - /* eslint-disable no-undef */ - var globalObject = - typeof globalThis !== 'undefined' - ? globalThis - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : {}; - /* eslint-enable no-undef */ - - // Save the require from previous bundle to this closure if any - var previousRequire = - typeof globalObject[parcelRequireName] === 'function' && - globalObject[parcelRequireName]; - - var cache = previousRequire.cache || {}; - // Do not use `require` to prevent Webpack from trying to bundle this call - var nodeRequire = - typeof module !== 'undefined' && - typeof module.require === 'function' && - module.require.bind(module); - - function newRequire(name, jumped) { - if (!cache[name]) { - if (!modules[name]) { - // if we cannot find the module within our internal map or - // cache jump to the current global require ie. the last bundle - // that was added to the page. - var currentRequire = - typeof globalObject[parcelRequireName] === 'function' && - globalObject[parcelRequireName]; - if (!jumped && currentRequire) { - return currentRequire(name, true); - } - - // If there are other bundles on this page the require from the - // previous one is saved to 'previousRequire'. Repeat this as - // many times as there are bundles until the module is found or - // we exhaust the require chain. - if (previousRequire) { - return previousRequire(name, true); - } - - // Try the node require function if it exists. - if (nodeRequire && typeof name === 'string') { - return nodeRequire(name); - } - - var err = new Error("Cannot find module '" + name + "'"); - err.code = 'MODULE_NOT_FOUND'; - throw err; - } - - localRequire.resolve = resolve; - localRequire.cache = {}; - - var module = (cache[name] = new newRequire.Module(name)); - - modules[name][0].call( - module.exports, - localRequire, - module, - module.exports, - this - ); - } - - return cache[name].exports; - - function localRequire(x) { - return newRequire(localRequire.resolve(x)); - } - - function resolve(x) { - return modules[name][1][x] || x; - } - } - - function Module(moduleName) { - this.id = moduleName; - this.bundle = newRequire; - this.exports = {}; - } - - newRequire.isParcelRequire = true; - newRequire.Module = Module; - newRequire.modules = modules; - newRequire.cache = cache; - newRequire.parent = previousRequire; - newRequire.register = function(id, exports) { - modules[id] = [ - function(require, module) { - module.exports = exports; - }, - {}, - ]; - }; - - Object.defineProperty(newRequire, 'root', { - get: function() { - return globalObject[parcelRequireName]; - }, - }); - - globalObject[parcelRequireName] = newRequire; - - for (var i = 0; i < entry.length; i++) { - newRequire(entry[i]); - } - - if (mainEntry) { - // Expose entry point to Node, AMD or browser globals - // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js - var mainExports = newRequire(mainEntry); - - // CommonJS - if (typeof exports === 'object' && typeof module !== 'undefined') { - module.exports = mainExports; - - // RequireJS - } else if (typeof define === 'function' && define.amd) { - define(function() { - return mainExports; - }); - - // \ No newline at end of file diff --git a/src/index.html b/src/index.html index 1d91a18..0c607b3 100644 --- a/src/index.html +++ b/src/index.html @@ -2,10 +2,33 @@ - + - Hello world! +
+
+ +
+
+
+

Projects

+
+ +
+
+
+
    +
    +
    +
    \ No newline at end of file diff --git a/src/js/app.js b/src/js/app.js new file mode 100644 index 0000000..d39d848 --- /dev/null +++ b/src/js/app.js @@ -0,0 +1,66 @@ +function request(method, source, data = null) +{ + return new Promise(function (resolve, reject){ + let xhr = new XMLHttpRequest(); + xhr.open(method, source, true); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.send(data); + + xhr.onload = function() + { + if(this.status >= 200 && this.status < 300){ + resolve(xhr.response); + } + resolve({ status: this.status, statusText: xhr.statusText }); + }; + xhr.onerror = () => + { + resolve({ status: this.status, statusText: xhr.statusText }); + } + }); +} + + +document.addEventListener("DOMContentLoaded", async function () +{ + const list = document.getElementById("repos"); + const repos = JSON.parse(await request("GET", "http://localhost:1337/api/v1/repos"))["data"]; + + const params = new URLSearchParams(window.location.search); + const search = params.get("q"); + + for(const [key, value] of Object.entries(repos)) { + const li = document.createElement("li"); + const repo_div = document.createElement("div"); + + const repo_title = document.createElement("p"); + const link = document.createElement("a"); + link.setAttribute("href", key); + link.appendChild(document.createTextNode(key)); + repo_title.appendChild(link); + repo_title.classList.add("repo-title"); + + const repo_last_updated = document.createElement("span"); + repo_last_updated.appendChild(document.createTextNode(`Last updated about ${value["last_updated"]} ago`)); + repo_last_updated.classList.add("repo-last-updated"); + + const repo_desc = document.createElement("span"); + repo_desc.appendChild(document.createTextNode(value["description"])); + repo_desc.classList.add("repo-desc"); + + repo_div.appendChild(repo_title) + repo_div.appendChild(repo_last_updated) + repo_div.appendChild(repo_desc) + + li.appendChild(repo_div); + + if(search !== null) { + if(key.indexOf(search) != -1) { + list.appendChild(li); + } + } + else { + list.appendChild(li); + } + } +}); \ No newline at end of file diff --git a/src/scss/abstracts/_colors.scss b/src/scss/abstracts/_colors.scss new file mode 100644 index 0000000..40f08af --- /dev/null +++ b/src/scss/abstracts/_colors.scss @@ -0,0 +1,3 @@ +$primary: #0947ba; +$text: #ffffff; +$background: #121212; \ No newline at end of file diff --git a/src/scss/abstracts/_fonts.scss b/src/scss/abstracts/_fonts.scss new file mode 100644 index 0000000..6af5233 --- /dev/null +++ b/src/scss/abstracts/_fonts.scss @@ -0,0 +1,5 @@ +@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/scss/style.scss b/src/scss/style.scss index e69de29..239194b 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -0,0 +1,103 @@ +@use "abstracts/colors"; +@use "abstracts/fonts"; + +@import "../../node_modules/bootstrap/scss/functions"; +@import "../../node_modules/bootstrap/scss/variables"; +@import "../../node_modules/bootstrap/scss/mixins"; + +$font-sizes: ( + 1: 2.375rem, + 2: 2.125rem, + 3: 2rem, + 4: 1.5rem, + 5: 1.3125rem, + 6: 1rem +); + +@import "../../node_modules/bootstrap/scss/utilities"; +@import "../../node_modules/bootstrap/scss/utilities/api"; + +@import "../../node_modules/bootstrap/scss/containers"; +@import "../../node_modules/bootstrap/scss/grid"; + +body { + background-color: colors.$background; + color: colors.$text; + font-family: fonts.$primary; +} + +ul { + list-style-type: none; + padding: 0; +} + +li { + margin-bottom: 25px; + div { + h2 { + margin: 0px; + } + } +} + +p { + margin: 0px; +} + +#title { + font-family: fonts.$title; + font-weight: 300; +} + +#about { + font-weight: 300; + font-size: 1.125rem; + padding-left: 1px; +} + +#projects-search { + align-items: center; + form { + display: flex; + align-items: center; + height: 35px; + input[type=search] { + margin-right: 15px; + } + } +} + +#repos { + margin-top: 25px; +} + +.repo-title { + font-size: calc(1.0625rem + 0.25vw); +} + +.repo-desc { + font-size: calc(0.875rem + 0.05vw); +} + +.repo-last-updated { + display: block; + font-size: calc(0.875rem + 0.05vw); + font-weight: 300; + font-style: italic; +} + +input[type=submit] { + background-color: colors.$primary; + color: colors.$text; + border: 0px; + border-radius: 7px; + padding: 8px 15px 8px 15px; +} + +a { + color: colors.$text; + text-decoration: none; + &:hover { + color: colors.$primary; + } +} \ No newline at end of file -- cgit v1.2.3-18-g5258