diff options
| -rw-r--r-- | package-lock.json | 31 | ||||
| -rw-r--r-- | package.json | 2 | ||||
| -rw-r--r-- | public/views/createPost.html | 2 | ||||
| -rw-r--r-- | public/views/post.html | 18 | ||||
| -rw-r--r-- | routes/blog.js | 46 | ||||
| -rw-r--r-- | routes/index.js | 36 | ||||
| -rw-r--r-- | routes/posts.js | 100 |
7 files changed, 158 insertions, 77 deletions
diff --git a/package-lock.json b/package-lock.json index 18f8d9f..5244d24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "ejs": "^3.1.6", "express": "^4.17.3", "firebase-admin": "^10.0.2", + "highlight.js": "^11.5.0", + "marked": "^4.0.12", "node-fetch": "^3.2.3", "shelljs": "^0.8.5" }, @@ -1962,6 +1964,14 @@ "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==", "optional": true }, + "node_modules/highlight.js": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.0.tgz", + "integrity": "sha512-SM6WDj5/C+VfIY8pZ6yW6Xa0Fm1tniYVYWYW1Q/DcMnISZFrC3aQAZZZFAAZtybKNrGId3p/DNbFTtcTXXgYBw==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -2648,6 +2658,17 @@ "semver": "bin/semver.js" } }, + "node_modules/marked": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", + "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -5449,6 +5470,11 @@ "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==", "optional": true }, + "highlight.js": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.5.0.tgz", + "integrity": "sha512-SM6WDj5/C+VfIY8pZ6yW6Xa0Fm1tniYVYWYW1Q/DcMnISZFrC3aQAZZZFAAZtybKNrGId3p/DNbFTtcTXXgYBw==" + }, "htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -6000,6 +6026,11 @@ } } }, + "marked": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", + "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/package.json b/package.json index d18c47e..ab29983 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "ejs": "^3.1.6", "express": "^4.17.3", "firebase-admin": "^10.0.2", + "highlight.js": "^11.5.0", + "marked": "^4.0.12", "node-fetch": "^3.2.3", "shelljs": "^0.8.5" }, diff --git a/public/views/createPost.html b/public/views/createPost.html index fddf13c..976c0e5 100644 --- a/public/views/createPost.html +++ b/public/views/createPost.html @@ -170,7 +170,7 @@ title: title, publishDate: publishDate, tags: tags, - content: marked.parse(content), + content: content, shortText: marked.parse(content.substring(0, 120) + "..."), slug: slug }; diff --git a/public/views/post.html b/public/views/post.html index c0897f6..e2f9bdc 100644 --- a/public/views/post.html +++ b/public/views/post.html @@ -9,7 +9,9 @@ <meta name="author" content=""> <link href="/static/assets/css/bootstrap.css" rel="stylesheet"> <link href="/static/assets/css/bootstrap-responsive.css" rel="stylesheet"> - <!-- <link href="/static/assets/css/docs.css" rel="stylesheet"> --> + <link href="/static/assets/css/docs.css" rel="stylesheet"> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" + integrity="sha384-KiWOvVjnN8qwAZbuQyWDIbfCLFhLXNETzBQjA/92pIowpC0d2O3nppDGQVgwd2nB" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/styles/base16/seti-ui.min.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> @@ -44,10 +46,10 @@ <div class="container"> <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"></button> - <a class="brand" href="./">That Computer Scientist</a> + <a class="brand" href="/">That Computer Scientist</a> <div class="nav-collapse collapse"> <ul class="nav"> - <li class="active"><a href="./">Home</a></li> + <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/repos">Repositories</a></li> <li><a href="https://github.com/luciferreeves" target="_blank">Github</a></li> @@ -69,17 +71,9 @@ <div id="content"></div> </div> <script src="/static/assets/js/jquery.js"></script> - <!-- <script src="/static/assets/js/bootstrap-386.js"></script> --> + <script src="/static/assets/js/bootstrap-386.js"></script> <script src="/static/assets/js/bootstrap-transition.js"></script> <script src="/static/assets/js/bootstrap-collapse.js"></script> - <script src="/static/assets/js/marked.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/highlight.min.js"></script> - <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" - integrity="sha384-0fdwu/T/EQMsQlrHCCHoH10pkPLlKA1jL5dFyUOvB3lfeT2540/2g6YgSi2BL14p" - crossorigin="anonymous"></script> - <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" - integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" - crossorigin="anonymous"></script> </body> </html>
\ No newline at end of file diff --git a/routes/blog.js b/routes/blog.js new file mode 100644 index 0000000..fbdc1f2 --- /dev/null +++ b/routes/blog.js @@ -0,0 +1,46 @@ +const firebase = require("../firebase"); +const express = require("express"); +const router = express.Router(); + +router.get("/posts", (req, res) => { + const store = firebase.firestore(); + const posts = []; + let query = store.collection("posts"); + query = query.select("slug", "tags", "title", "shortText", "publishDate"); + query.get().then(function (querySnapshot) { + querySnapshot.forEach(function (doc) { + posts.push(doc.data()); + }); + }).then(() => { + res.json(posts); + }); +}); + +router.post("/new", (req, res) => { + const { title, content, tags, publishDate, shortText, slug } = req.body; + const store = firebase.firestore(); + const id = store.collection("posts").doc().id; + // convert content to base64 + const base64 = Buffer.from(content).toString("base64"); + const post = { + id, + title, + content: base64, + tags: String(tags).split(",").length > 0 ? String(tags).split(",") : [], + publishDate, + shortText, + slug, + }; + let query = store.collection("posts"); + query + .doc(id) + .set(post) + .then(() => { + res.json({ success: true }); + }) + .catch((err) => { + res.json({ success: false, err }); + }); +}); + +module.exports = router; diff --git a/routes/index.js b/routes/index.js index 0db91d6..df2c9be 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,17 +1,16 @@ // Import express router const express = require("express"); const router = express.Router(); -const firebase = require("../firebase"); -const fs = require("fs"); -const cheerio = require("cheerio"); // Import the routes const admin = require("./admin"); const api = require("./api"); +const blog = require("./blog"); const posts = require("./posts"); // Set the routes router.use("/admin", admin); router.use("/api", api); -router.use("/api/blog", posts); +router.use("/api/blog", blog); +router.use("/", posts); // Create the routes router.get("/", (req, res) => { @@ -24,35 +23,6 @@ router.get("/repos", (req, res) => { res.render("repositories.html"); }); -router.get("/posts/:id", function (req, res) { - const id = req.params.id; - // get single document where slug === id - const store = firebase.firestore(); - let query = store.collection("posts"); - query.select("tags", "title", "content", "publishDate"); - query = query.where("slug", "==", id); - var html = fs.readFileSync(__dirname + "/../public/views/post.html", "utf8"); - var $ = cheerio.load(html); - query.get().then(function (querySnapshot) { - querySnapshot.forEach(function (doc) { - const post = doc.data(); - const changedTitle = - '<script>document.title = "That Computer Scientist | ' + - post.title + - '";</script>'; - $("head").append(changedTitle); - $("#title").text(post.title); - $("#content").html(post.content); - $("#publishDate").text(post.publishDate); - post.tags.forEach((tag) => { - $("#tags").append( - `<a href="/posts/tag/${tag.trim().replace(/ /g, '-')}" class="label label-info" style="margin-right: 10px;">${tag.trim()}</a>` - ); - }); - res.send($.html()); - }); - }); -}); // Export the routes module.exports = router; diff --git a/routes/posts.js b/routes/posts.js index 7e68106..583d38d 100644 --- a/routes/posts.js +++ b/routes/posts.js @@ -1,44 +1,82 @@ const firebase = require("../firebase"); +const fs = require("fs"); +const cheerio = require("cheerio"); +const marked = require("marked"); +const hljs = require("highlight.js"); const express = require("express"); const router = express.Router(); -router.get("/posts", (req, res) => { +router.get("/posts/:id", function (req, res) { + const id = req.params.id; + // get single document where slug === id const store = firebase.firestore(); - const posts = []; let query = store.collection("posts"); - query = query.select("slug", "tags", "title", "shortText", "publishDate"); + query.select("tags", "title", "content", "publishDate"); + query = query.where("slug", "==", id); + var html = fs.readFileSync(__dirname + "/../public/views/post.html", "utf8"); + var $ = cheerio.load(html); query.get().then(function (querySnapshot) { querySnapshot.forEach(function (doc) { - posts.push(doc.data()); + const post = doc.data(); + const changedTitle = + '<script>document.title = "That Computer Scientist | ' + + post.title + + '";</script>'; + $("head").append(changedTitle); + $("#title").text(post.title); + // convert content from base64 to utf8 + const content = Buffer.from(post.content, "base64").toString("utf8"); + // Parse the markdown + const markdown = marked.parse(content); + const markdownWithHighlight = markdown.replace( + /<pre><code class="(.*?)">(.*?)<\/code><\/pre>/g, + (match, p1, p2) => { + return `<pre><code class="hljs ${p1}">${ + hljs.highlight(p1, p2).value + }</code></pre>`; + } + ); + $("#content").html(markdownWithHighlight); + $("#publishDate").text(post.publishDate); + post.tags.forEach((tag) => { + $("#tags").append( + `<a href="/posts/tag/${tag + .trim() + .replace( + / /g, + "-" + )}" class="label label-info" style="margin-right: 10px;">${tag.trim()}</a>` + ); + }); + const katexTags = ` + <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" + integrity="sha384-0fdwu/T/EQMsQlrHCCHoH10pkPLlKA1jL5dFyUOvB3lfeT2540/2g6YgSi2BL14p" + crossorigin="anonymous"></script> + <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" + integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" + crossorigin="anonymous"></script>`; + $("body").append(katexTags); + const autoRenderScript = ` + <script> + document.addEventListener("DOMContentLoaded", function() { + renderMathInElement(document.getElementById("content"), { + // customised options + // • auto-render specific keys, e.g.: + delimiters: [ + {left: '$$', right: '$$', display: true}, + {left: '$', right: '$', display: false}, + {left: '\\(', right: '\\)', display: false}, + {left: '\\[', right: '\\]', display: true} + ], + // • rendering keys, e.g.: + throwOnError : false + }); + }); + </script>`; + $("body").append(autoRenderScript); + res.send($.html()); }); - }).then(() => { - res.json(posts); }); }); -router.post("/new", (req, res) => { - const { title, content, tags, publishDate, shortText, slug } = req.body; - const store = firebase.firestore(); - const id = store.collection("posts").doc().id; - const post = { - id, - title, - content, - tags: String(tags).split(",").length > 0 ? String(tags).split(",") : [], - publishDate, - shortText, - slug, - }; - let query = store.collection("posts"); - query - .doc(id) - .set(post) - .then(() => { - res.json({ success: true }); - }) - .catch((err) => { - res.json({ success: false, err }); - }); -}); - module.exports = router; |
