diff --git a/example.env b/example.env new file mode 100644 index 0000000..035306f --- /dev/null +++ b/example.env @@ -0,0 +1,10 @@ +SITE_OWNER="John Joe" +DOMAIN=example.com + +MAIL_AUTH=true +MAIL_SECURE=true + +MAIL_HOST=smtp.example.com +MAIL_PORT=587 +MAIL_USER=username +MAIL_PASS=password diff --git a/package-lock.json b/package-lock.json index eb4b193..6a73953 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,14 +11,17 @@ "dependencies": { "body-parser": "^2.2.0", "compression": "^1.8.0", + "dotenv": "^16.5.0", "express": "^5.1.0", "express-handlebars": "^8.0.2", "express-rate-limit": "^7.5.0", "gray-matter": "^4.0.3", "hbs": "^4.2.0", "helmet": "^8.1.0", + "js-beautify": "^1.15.4", "marked": "^15.0.11", "morgan": "^1.10.0", + "nodemailer": "^7.0.3", "nodemon": "^3.1.10", "path": "^0.12.7" } @@ -40,6 +43,31 @@ "node": ">=12" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -256,6 +284,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -316,6 +353,16 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -395,6 +442,18 @@ "node": ">= 0.8" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -415,6 +474,39 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -912,6 +1004,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1008,6 +1106,108 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -1199,6 +1399,14 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/nodemailer": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", + "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", @@ -1249,6 +1457,21 @@ "node": "*" } }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1378,6 +1601,12 @@ "node": ">= 0.6.0" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "license": "ISC" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", diff --git a/package.json b/package.json index e1479f7..bed5864 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "nodemon ./src/app.js" + "start": "nodemon ./src/app.js --trace-exit", + "maildev": "maildev" }, "keywords": [], "author": "", @@ -14,14 +15,17 @@ "dependencies": { "body-parser": "^2.2.0", "compression": "^1.8.0", + "dotenv": "^16.5.0", "express": "^5.1.0", "express-handlebars": "^8.0.2", "express-rate-limit": "^7.5.0", "gray-matter": "^4.0.3", "hbs": "^4.2.0", "helmet": "^8.1.0", + "js-beautify": "^1.15.4", "marked": "^15.0.11", "morgan": "^1.10.0", + "nodemailer": "^7.0.3", "nodemon": "^3.1.10", "path": "^0.12.7" } diff --git a/public/css/contact.css b/public/css/contact.css new file mode 100644 index 0000000..cfcd861 --- /dev/null +++ b/public/css/contact.css @@ -0,0 +1,36 @@ +form { + max-width: 400px; + margin: 1em 0; + display: flex; + flex-direction: column; + gap: 0.75em; + } + + label { + font-weight: 600; + } + + input, + textarea { + padding: 0.5em; + border: 1px solid #ccc; + border-radius: 4px; + font-family: inherit; + font-size: 1em; + resize: vertical; + } + + button { + padding: 0.6em 1.2em; + background-color: #007acc; + color: white; + border: none; + border-radius: 4px; + font-weight: 600; + cursor: pointer; + } + + button:hover { + background-color: #005fa3; + } + \ No newline at end of file diff --git a/src/app.js b/src/app.js index dd42755..c39087f 100644 --- a/src/app.js +++ b/src/app.js @@ -1,6 +1,7 @@ // src/app.js const express = require("express"); const exphbs = require("express-handlebars"); +require("dotenv").config(); const setupMiddleware = require("./middleware"); const { registerHelpers } = require("./utils/hbsHelpers"); const app = express(); @@ -10,7 +11,19 @@ layoutsDir: "src/views/layouts", partialsDir: "src/views/partials", defaultLayout: "main", + helpers: { + section: function (name, options) { + if (!this._sections) this._sections = {}; + this._sections[name] = options.fn(this); + return null; + }, + }, + runtimeOptions: { + allowProtoPropertiesByDefault: true, + allowProtoMethodsByDefault: true, + }, }); + registerHelpers(hbs); app.engine("handlebars", hbs.engine); diff --git a/src/middleware/formatHtml.js b/src/middleware/formatHtml.js new file mode 100644 index 0000000..2510eda --- /dev/null +++ b/src/middleware/formatHtml.js @@ -0,0 +1,22 @@ +// src/middleware/formatHtml.js +const beautify = require("js-beautify").html; + +module.exports = function (req, res, next) { + const originalSend = res.send; + + // res.send = function (body) { + // if ( + // typeof body === "string" && + // res.get("Content-Type")?.includes("text/html") + // ) { + // body = beautify(body, { + // indent_size: 2, + // wrap_line_length: 80, + // end_with_newline: true, + // }); + // } + // return originalSend.call(this, body); + // }; + + next(); +}; diff --git a/src/middleware/index.js b/src/middleware/index.js index f7ca64e..77320f7 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -7,6 +7,7 @@ const helmet = require("helmet"); const routes = require("../routes"); +const formatHtml = require("./formatHtml"); const { loggingMiddleware, @@ -26,6 +27,7 @@ // app.use(helmet()); // Sets secure HTTP headers. Prevents common attacks. app.use("/static", express.static("public")); app.use(bodyParser.urlencoded({ extended: true })); + app.use(formatHtml); app.use(routes); app.use((req, res, next) => { const err = new Error("Not Found"); diff --git a/src/routes/contact.js b/src/routes/contact.js new file mode 100644 index 0000000..a70416f --- /dev/null +++ b/src/routes/contact.js @@ -0,0 +1,29 @@ +// src/routes/contact.js +const express = require("express"); +const router = express.Router(); +const sendContactMail = require("../utils/mailer"); +const getBaseContext = require("../utils/baseContext"); + +router.post("/contact", async (req, res, next) => { + try { + const { name, email, message } = req.body; + await sendContactMail({ name, email, message }); + res.redirect("/contact/thankyou"); + } catch (err) { + next(err); + } +}); +router.get("/contact", async (req, res) => { + const context = await getBaseContext({ + title: "Contact", + }); + res.render("pages/contact.handlebars", context); +}); +router.get("/contact/thankyou", async (req, res) => { + const context = await getBaseContext({ + title: "Thank You", + }); + res.render("pages/thankyou.handlebars", context); +}); + +module.exports = router; diff --git a/src/routes/index.js b/src/routes/index.js index ee077b6..d8d076c 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -5,9 +5,11 @@ const fs = require("fs").promises; const path = require("path"); const matter = require("gray-matter"); +const contact = require("./contact"); const getBaseContext = require("../utils/baseContext"); +router.use("/contact", contact); router.get("/post/:year/:month/:name", async (req, res, next) => { const { year, month, name } = req.params; @@ -51,7 +53,6 @@ next(error); } }); - router.get("/", async (req, res) => { const context = await getBaseContext({ title: "Blog Home", diff --git a/src/utils/baseContext.js b/src/utils/baseContext.js index cb96851..092ca8c 100644 --- a/src/utils/baseContext.js +++ b/src/utils/baseContext.js @@ -2,11 +2,12 @@ const path = require("path"); const getPostsMenu = require("../services/postsService"); const { formatMonth } = require("../utils/formatMonth"); + async function getBaseContext(overrides = {}) { const menu = await getPostsMenu(path.join(__dirname, "../../posts")); return Object.assign( { - siteOwner: "Jason Poage", + siteOwner: process.env.SITE_OWNER, navLinks: [ { href: "/", label: "Home" }, { href: "/about", label: "About" }, diff --git a/src/utils/logging.js b/src/utils/logging.js index eeddafe..6c4a814 100644 --- a/src/utils/logging.js +++ b/src/utils/logging.js @@ -78,12 +78,13 @@ // Generic log writer function writeLog(level, stream, consoleFn, ...args) { - const message = args.join(" ") + "\n"; - stream.write(message); - logStreams.session.write(`[${level}] ${message}`); - consoleFn(`[${level}]`, ...args); + const timestamp = new Date().toISOString(); + const message = args.join(" "); + const logLine = `[${timestamp}] [${level}] ${message}\n`; + stream.write(logLine); + logStreams.session.write(logLine); + consoleFn(`[${timestamp}] [${level}]`, ...args); } - function patchConsole() { console.log = (...args) => writeLog("INFO", logStreams.info, originalConsole.log, ...args); diff --git a/src/utils/mailer.js b/src/utils/mailer.js new file mode 100644 index 0000000..47a0299 --- /dev/null +++ b/src/utils/mailer.js @@ -0,0 +1,33 @@ +// src/utils/mailer.js +const nodemailer = require("nodemailer"); +require("dotenv").config(); + +let auth = null; +if (process.env.MAIL_AUTH != "null") { + auth = { + user: process.env.MAIL_USER, + pass: process.env.MAIL_PASS, + }; +} +console.log(process.env.MAIL_PORT); +const transporter = nodemailer.createTransport({ + host: process.env.MAIL_HOST, + port: parseInt(process.env.MAIL_PORT, 10), + secure: process.env.MAIL_SECURE === "true", + auth, +}); + +function sendContactMail({ name, email, message }) { + const { DOMAIN: domain } = process.env; + const data = { + from: `"Contact Form" `, + to: process.env.MAIL_USER, + replyTo: `"${name}" <${email}>`, + subject: "New Contact Form Submission", + text: message, + }; + console.log(data); + return transporter.sendMail(data); +} + +module.exports = sendContactMail; diff --git a/src/views/layouts/main.handlebars b/src/views/layouts/main.handlebars index 0034c38..09d4ad7 100644 --- a/src/views/layouts/main.handlebars +++ b/src/views/layouts/main.handlebars @@ -3,6 +3,7 @@ + {{{_sections.styles}}} {{title}} @@ -16,9 +17,9 @@
{{{body}}}
+ - diff --git a/src/views/pages/contact.handlebars b/src/views/pages/contact.handlebars new file mode 100644 index 0000000..bcff22d --- /dev/null +++ b/src/views/pages/contact.handlebars @@ -0,0 +1,18 @@ +{{#section "styles"}} + +{{/section}} +
+

{{title}}

+
+
+
+ +
+
+ +
+
+ + +
+
diff --git a/src/views/pages/post.handlebars b/src/views/pages/post.handlebars index 5248b4c..bb98b65 100644 --- a/src/views/pages/post.handlebars +++ b/src/views/pages/post.handlebars @@ -1,3 +1,3 @@ -
- {{{content}}} -
+
+ {{{content}}} +
diff --git a/src/views/pages/thankyou.handlebars b/src/views/pages/thankyou.handlebars new file mode 100644 index 0000000..a6b69af --- /dev/null +++ b/src/views/pages/thankyou.handlebars @@ -0,0 +1,4 @@ +
+

Thank You

+

Your message has been sent successfully. We will get back to you shortly.

+
diff --git a/workspace.code-workspace b/workspace.code-workspace index 8f4183e..2470253 100644 --- a/workspace.code-workspace +++ b/workspace.code-workspace @@ -2,6 +2,9 @@ "folders": [ { "path": "." + }, + { + "path": "../../../home/me/.config/nix-config" } ] }