From e19bcdb4da2c61b8fa695855cac1de701ce7eb40 Mon Sep 17 00:00:00 2001 From: fiiv Date: Mon, 29 Apr 2019 11:47:38 +0700 Subject: [PATCH 1/7] fixing an extra string template --- initialisers/http.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initialisers/http.js b/initialisers/http.js index d377929..1f82012 100644 --- a/initialisers/http.js +++ b/initialisers/http.js @@ -10,7 +10,7 @@ const { PORT, APP_NAME } = process.env module.exports = () => { const server = createServer(async ({ url }, response) => { const urlTokens = url.split('.') - const extension = urlTokens.length > 1 ? `${urlTokens[urlTokens.length - 1].toLowerCase().trim()}` : false + const extension = urlTokens.length > 1 ? urlTokens[urlTokens.length - 1].toLowerCase().trim() : false const isRoot = [ '', '/' ].indexOf(url) > -1 const path = isRoot ? '/index.html' : url From 5e70bafe2725d8a5fbf9943948ed72e20fea83c6 Mon Sep 17 00:00:00 2001 From: fiiv Date: Mon, 29 Apr 2019 12:14:11 +0700 Subject: [PATCH 2/7] adding in templates + dynamic routes --- initialisers/http.js | 16 ++- lib/responder.js | 22 +++- package-lock.json | 122 ++++++++++++++++++-- package.json | 4 +- routes/index.js | 17 +++ routes/root.js | 2 + templates/root.hbs | 5 + public/index.html => templates/template.hbs | 8 +- 8 files changed, 170 insertions(+), 26 deletions(-) create mode 100644 routes/index.js create mode 100644 routes/root.js create mode 100644 templates/root.hbs rename public/index.html => templates/template.hbs (87%) diff --git a/initialisers/http.js b/initialisers/http.js index 1f82012..77f4bed 100644 --- a/initialisers/http.js +++ b/initialisers/http.js @@ -2,20 +2,26 @@ if (!process.env.PORT) require('dotenv').config() const { createServer } = require('http') -const serveStaticFile = require('../lib/responder') +const { serveStaticFile, serveRoute } = require('../lib/responder') const errors = require('../config/errors') const { PORT, APP_NAME } = process.env module.exports = () => { - const server = createServer(async ({ url }, response) => { + const server = createServer(async ({ url, method }, response) => { const urlTokens = url.split('.') const extension = urlTokens.length > 1 ? urlTokens[urlTokens.length - 1].toLowerCase().trim() : false - const isRoot = [ '', '/' ].indexOf(url) > -1 - const path = isRoot ? '/index.html' : url + const serveResponse = extension ? serveStaticFile : serveRoute + const responseParams = { path: url } + + if (extension) { + responseParams.extension = extension + } else { + responseParams.method = method + } try { - return await serveStaticFile({ file: path, extension: isRoot ? 'html' : extension }, response) + return await serveResponse(responseParams, response) } catch (error) { console.error(error) const errorData = errors(error) diff --git a/lib/responder.js b/lib/responder.js index 9736b43..376ff79 100644 --- a/lib/responder.js +++ b/lib/responder.js @@ -1,15 +1,19 @@ -const { open } = require('fs').promises +const { readFileSync, promises: { open } } = require('fs') const { lookup } = require('mime-types') +const Handlebars = require('handlebars') const { STATIC_EXTENSIONS } = require('../config/constants') -const serveStaticFile = async ({ file, extension, statusCode }, response) => { +const routes = require('../routes') +const basePage = readFileSync(`./templates/template.hbs`, { encoding: 'utf8' }) + +const serveStaticFile = async ({ path, extension, statusCode }, response) => { if (STATIC_EXTENSIONS.indexOf(extension) === -1) throw new Error('not_found') let fileHandle try { - fileHandle = await open(`./public/${file}`, 'r') + fileHandle = await open(`./public/${path}`, 'r') const staticFile = await fileHandle.readFile() const mime = lookup(extension) @@ -28,4 +32,14 @@ const serveStaticFile = async ({ file, extension, statusCode }, response) => { } } -module.exports = serveStaticFile +const serveRoute = async ({ method, path }, response) => { + const key = `${method}:${path}` + if (!routes[key]) throw new Error('not_found') + + Handlebars.registerPartial('content', routes[key].body) + const hbs = Handlebars.compile(basePage) + + return response.end(hbs()) +} + +module.exports = { serveStaticFile, serveRoute } diff --git a/package-lock.json b/package-lock.json index 0dd75f7..980c6f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,8 +100,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -183,7 +182,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -338,6 +336,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "optional": true + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -347,8 +351,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "configstore": { "version": "3.1.2", @@ -682,6 +685,11 @@ "map-cache": "^0.2.2" } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, "fsevents": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", @@ -1242,6 +1250,19 @@ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -1297,6 +1318,24 @@ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1353,11 +1392,19 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -1670,7 +1717,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1734,6 +1780,11 @@ "to-regex": "^3.0.1" } }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" + }, "nodemon": { "version": "1.18.11", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.11.tgz", @@ -1825,6 +1876,30 @@ "isobject": "^3.0.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + } + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -1858,8 +1933,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -2366,6 +2440,24 @@ "nopt": "~1.0.10" } }, + "uglify-js": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.9.tgz", + "integrity": "sha512-WpT0RqsDtAWPNJK955DEnb6xjymR8Fn0OlK4TT4pS0ASYsVPqr5ELhgwOwLCP5J5vHeJ4xmMmz3DEgdqC10JeQ==", + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, "undefsafe": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", @@ -2545,6 +2637,16 @@ "string-width": "^2.1.1" } }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, "write-file-atomic": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", diff --git a/package.json b/package.json index 4ee6923..d39d034 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "description": "Schedule a reminder for yourself, by email", "main": "app.js", "dependencies": { + "glob": "^7.1.3", + "handlebars": "^4.1.2", "mime-types": "^2.1.24" }, "devDependencies": { @@ -11,7 +13,7 @@ "nodemon": "^1.18.11" }, "scripts": { - "start": "nodemon --inspect -e js,html" + "start": "nodemon --inspect -e js,html,hbs" }, "repository": { "type": "git", diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..946438e --- /dev/null +++ b/routes/index.js @@ -0,0 +1,17 @@ +const { readFileSync } = require('fs') +const glob = require('glob') + +module.exports = glob.sync( + './routes/**/*.js', + { ignore: [ './routes/index.js' ] } +).reduce((routeMap, filename) => { + const route = require(`.${filename}`) + + if (route.template) { + route.body = readFileSync(`./templates/${route.template}.hbs`, { encoding: 'utf8' }) + } + + routeMap[`${route.method || 'GET'}:${route.uri}`] = route + + return routeMap +}, {}) diff --git a/routes/root.js b/routes/root.js new file mode 100644 index 0000000..cd9812d --- /dev/null +++ b/routes/root.js @@ -0,0 +1,2 @@ +exports.uri = '/' +exports.template = 'root' diff --git a/templates/root.hbs b/templates/root.hbs new file mode 100644 index 0000000..8d0b38d --- /dev/null +++ b/templates/root.hbs @@ -0,0 +1,5 @@ +
+ +

+ remind.ist is here +

diff --git a/public/index.html b/templates/template.hbs similarity index 87% rename from public/index.html rename to templates/template.hbs index 5da35e7..080e7be 100644 --- a/public/index.html +++ b/templates/template.hbs @@ -3,7 +3,7 @@ - remind.ist + {{app_name}} @@ -32,10 +32,6 @@ -
- -

- remind.ist is here -

+ {{>content}} From 495c9da61eddf1e38bd6d2af03f73bc1bde3eaff Mon Sep 17 00:00:00 2001 From: fiiv Date: Mon, 29 Apr 2019 12:30:26 +0700 Subject: [PATCH 3/7] adding in a context --- initialisers/http.js | 3 +++ lib/responder.js | 4 ++-- templates/root.hbs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/initialisers/http.js b/initialisers/http.js index 77f4bed..4959363 100644 --- a/initialisers/http.js +++ b/initialisers/http.js @@ -18,6 +18,9 @@ module.exports = () => { responseParams.extension = extension } else { responseParams.method = method + responseParams.context = { + app_name: APP_NAME + } } try { diff --git a/lib/responder.js b/lib/responder.js index 376ff79..db402ac 100644 --- a/lib/responder.js +++ b/lib/responder.js @@ -32,14 +32,14 @@ const serveStaticFile = async ({ path, extension, statusCode }, response) => { } } -const serveRoute = async ({ method, path }, response) => { +const serveRoute = async ({ method, path, context }, response) => { const key = `${method}:${path}` if (!routes[key]) throw new Error('not_found') Handlebars.registerPartial('content', routes[key].body) const hbs = Handlebars.compile(basePage) - return response.end(hbs()) + return response.end(hbs(context)) } module.exports = { serveStaticFile, serveRoute } diff --git a/templates/root.hbs b/templates/root.hbs index 8d0b38d..a70c812 100644 --- a/templates/root.hbs +++ b/templates/root.hbs @@ -1,5 +1,5 @@

- remind.ist is here + {{app_name}} is here

From 192a37a553a2fd3e0b0c731afd0c370a45520a87 Mon Sep 17 00:00:00 2001 From: fiiv Date: Mon, 29 Apr 2019 12:34:13 +0700 Subject: [PATCH 4/7] adding in up to date node version --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d39d034..206015b 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,8 @@ "bugs": { "url": "https://github.com/mtimofiiv/remind.ist/issues" }, - "homepage": "https://github.com/mtimofiiv/remind.ist#readme" + "homepage": "https://github.com/mtimofiiv/remind.ist#readme", + "engines": { + "node": ">=12" + } } From 3a4f826711f555748b46cee5cda77bb7367fd28d Mon Sep 17 00:00:00 2001 From: fiiv Date: Wed, 1 May 2019 09:35:49 +0700 Subject: [PATCH 5/7] fixing error param --- initialisers/http.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initialisers/http.js b/initialisers/http.js index 4959363..4a721e5 100644 --- a/initialisers/http.js +++ b/initialisers/http.js @@ -30,7 +30,7 @@ module.exports = () => { const errorData = errors(error) return await serveStaticFile({ - file: '/error.html', + path: '/error.html', extension: 'html', statusCode: errorData.code }, response) From 72f93b3499f98c70ecf22227406ed9f859f8a338 Mon Sep 17 00:00:00 2001 From: fiiv Date: Wed, 1 May 2019 09:38:25 +0700 Subject: [PATCH 6/7] adding analytics snippet --- templates/template.hbs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/template.hbs b/templates/template.hbs index 080e7be..03126d3 100644 --- a/templates/template.hbs +++ b/templates/template.hbs @@ -33,5 +33,8 @@ {{>content}} + + + From afabedc8b8efb4a1b2a4dee566a56a7e1f36fcd0 Mon Sep 17 00:00:00 2001 From: fiiv Date: Wed, 1 May 2019 10:23:21 +0700 Subject: [PATCH 7/7] formdata now works --- initialisers/http.js | 8 ++++---- lib/formdata.js | 15 +++++++++++++++ lib/responder.js | 15 ++++++++++----- routes/new-reminder.js | 10 ++++++++++ templates/new_reminder.hbs | 7 +++++++ templates/root.hbs | 15 +++++++++++++++ 6 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 lib/formdata.js create mode 100644 routes/new-reminder.js create mode 100644 templates/new_reminder.hbs diff --git a/initialisers/http.js b/initialisers/http.js index 4a721e5..d26d731 100644 --- a/initialisers/http.js +++ b/initialisers/http.js @@ -8,16 +8,16 @@ const errors = require('../config/errors') const { PORT, APP_NAME } = process.env module.exports = () => { - const server = createServer(async ({ url, method }, response) => { - const urlTokens = url.split('.') + const server = createServer(async (request, response) => { + const urlTokens = request.url.split('.') const extension = urlTokens.length > 1 ? urlTokens[urlTokens.length - 1].toLowerCase().trim() : false const serveResponse = extension ? serveStaticFile : serveRoute - const responseParams = { path: url } + const responseParams = { path: request.url } if (extension) { responseParams.extension = extension } else { - responseParams.method = method + responseParams.request = request responseParams.context = { app_name: APP_NAME } diff --git a/lib/formdata.js b/lib/formdata.js new file mode 100644 index 0000000..3875af8 --- /dev/null +++ b/lib/formdata.js @@ -0,0 +1,15 @@ +const { parse: parseFormadata } = require('querystring') + +const getRequestBody = request => new Promise((resolve, reject) => { + let formData = '' + + request.on('data', buffer => formData += buffer.toString()) + request.on('error', reject) + + request.on('end', () => { + const parsedData = parseFormadata(formData) + return resolve(parsedData) + }) +}) + +module.exports = getRequestBody diff --git a/lib/responder.js b/lib/responder.js index db402ac..381938f 100644 --- a/lib/responder.js +++ b/lib/responder.js @@ -32,14 +32,19 @@ const serveStaticFile = async ({ path, extension, statusCode }, response) => { } } -const serveRoute = async ({ method, path, context }, response) => { - const key = `${method}:${path}` - if (!routes[key]) throw new Error('not_found') +const serveRoute = async ({ request, context }, response) => { + const key = `${request.method}:${request.url}` + const route = routes[key] - Handlebars.registerPartial('content', routes[key].body) + if (!route) throw new Error('not_found') + + Handlebars.registerPartial('content', route.body) const hbs = Handlebars.compile(basePage) - return response.end(hbs(context)) + let routeContext = {} + if (route.data) routeContext = await route.data(request) + + return response.end(hbs({ ...context, ...routeContext })) } module.exports = { serveStaticFile, serveRoute } diff --git a/routes/new-reminder.js b/routes/new-reminder.js new file mode 100644 index 0000000..c9e8f92 --- /dev/null +++ b/routes/new-reminder.js @@ -0,0 +1,10 @@ +const getRequestBody = require('../lib/formdata') + +exports.uri = '/new' +exports.template = 'new_reminder' +exports.method = 'POST' + +exports.data = async request => { + const formData = await getRequestBody(request) + return formData +} diff --git a/templates/new_reminder.hbs b/templates/new_reminder.hbs new file mode 100644 index 0000000..b604228 --- /dev/null +++ b/templates/new_reminder.hbs @@ -0,0 +1,7 @@ +

Data received!

+ +

Email: {{email}}

+ +

Message:

+ +

{{message}}

diff --git a/templates/root.hbs b/templates/root.hbs index a70c812..d4162d3 100644 --- a/templates/root.hbs +++ b/templates/root.hbs @@ -3,3 +3,18 @@

{{app_name}} is here

+ +
+
+
+ +
+ +
+ +
+ +
+ +