From 9fa90d956b76959c2a636f53cf4e443b7a517bb8 Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Wed, 16 Mar 2022 22:39:18 +0300 Subject: [PATCH] feat: server static files --- handlers.js | 14 ++++++++++++ helpers/file.js | 26 +++++++++++++++++++++ package-lock.json | 13 +++++++++++ package.json | 1 + preloads/routes.js | 8 ++++--- public/css/style.css | 3 +++ public/images/docker-logo.png | Bin 0 -> 24928 bytes public/images/nodejs-logo.png | Bin 0 -> 23561 bytes response.js | 41 ++++++++++++++++++++++++++++++++++ router.js | 15 +++++++------ server.js | 19 ++-------------- views/404.hbs | 11 +++++++++ views/index.hbs | 12 ++++++++++ 13 files changed, 136 insertions(+), 27 deletions(-) create mode 100644 helpers/file.js create mode 100644 public/css/style.css create mode 100644 public/images/docker-logo.png create mode 100644 public/images/nodejs-logo.png create mode 100644 views/404.hbs create mode 100644 views/index.hbs diff --git a/handlers.js b/handlers.js index 2e95d81..eaa70fd 100644 --- a/handlers.js +++ b/handlers.js @@ -1,7 +1,21 @@ import http from 'node:http'; +import fs from 'node:fs'; import Response from './response.js'; import db from './db.js'; +/** + * Render index page + * + * @param {http.ClientRequest} req + * @param {Response} res + */ +export const indexPageHandler = async (req, res) => { + const info = fs.readFileSync('./package.json', 'utf8'); + const data = JSON.parse(info.toString()); + + res.render('./views/index.hbs', { app: { name: data.name } }); +}; + /** * GET - Get list of all artcles from database * diff --git a/helpers/file.js b/helpers/file.js new file mode 100644 index 0000000..61d6126 --- /dev/null +++ b/helpers/file.js @@ -0,0 +1,26 @@ +import fs from 'node:fs'; + +class FileHelper { + static readDirRecursive(dir, files = []) { + const entries = fs.readdirSync(dir); + const result = files; + + for (const entry of entries) { + const path = dir + '/' + entry; + const isDir = fs.statSync(path).isDirectory(); + const isFile = fs.statSync(path).isFile(); + + if (isDir) { + files = this.readDirRecursive(path, files); + } + + if (isFile) { + result.push(path); + } + } + + return result; + } +} + +export default FileHelper; diff --git a/package-lock.json b/package-lock.json index c394ca5..2425fea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -588,6 +588,19 @@ } } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", diff --git a/package.json b/package.json index ebfd283..8c60b8f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "dotenv": "^16.0.0", "handlebars": "^4.7.7", + "mime-types": "^2.1.35", "path-to-regexp": "^6.2.0", "pg": "^8.7.3" } diff --git a/preloads/routes.js b/preloads/routes.js index e2935eb..939b337 100644 --- a/preloads/routes.js +++ b/preloads/routes.js @@ -7,6 +7,7 @@ import { createArticleHandler, getArticleHandler, getArticlesHandler, + indexPageHandler, } from '../handlers.js'; /** @@ -14,6 +15,7 @@ import { */ import { waitMiddleware } from '../middlewares.js'; -router.get('/api/articles', getArticlesHandler).middleware([waitMiddleware]); -router.get('/api/articles/:id', getArticleHandler).middleware([waitMiddleware]); -router.post('/api/article', createArticleHandler); +router.get('/', indexPageHandler); +router.get('/articles', getArticlesHandler).middleware([waitMiddleware]); +router.get('/articles/:id', getArticleHandler).middleware([waitMiddleware]); +router.post('/articles', createArticleHandler); diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..2206be4 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,3 @@ +body { + font-family: monospace; +} diff --git a/public/images/docker-logo.png b/public/images/docker-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6eecb6bde89696b5c08b9ce30d56deeb6ff9300f GIT binary patch literal 24928 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 UP?Gn&!S5zDWvz!r|60BHAHQ9q%m4rY literal 0 HcmV?d00001 diff --git a/public/images/nodejs-logo.png b/public/images/nodejs-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b29ace96da7a0251d004929b25ab50e01a9da25d GIT binary patch literal 23561 zcmc$`WmHsQ|2DdZlm?*j80i#g5C-W^=|*w@2?0SwLAq;5=}uul8l=0VTe{B1 z=l`7bemG~H^Wk0Vg|&t?duHG9+xPv8>$+|}sw&IkVN+rQ0Dvd=TKWwDpeO?X#QFgS zc;y{=-XHJ}nx%x21OWVq#D{^a zJ{n_l7zgVfmgPk4%gqqZ+HZko_JKLOSK%<-!XwQq(iwy}=?6Hr7Fm(bC*8tvbm_-! zpQVSUDI4k<-PIJ{V)vwIalndnh#sklcMHxmwVXvO(qjcE^2YYC57yAxDeleIxeVy1 zzC4?CL6er2##GhPI+sB@?V%f?oD+)67#PfV*i4k1{BUKRdk_H+3p>qAPhUJn=n&91 zAk*fFWA+93_^xr`<=L$I5{YeEY6u2qugl&{>;t>gn&&S6yxXNJTtsi}_ zZ(LzF71oJvxz?$z%ZwN-jEwS@B@g0=fCZF;R}vBu=6-&+8SMNfj*N_qf@Qd{6dUrc zC!>7vk#u6B5i*kh+QHSvL;vR*&3E9tF?m;u z;%MX5-%%C1Hb#c~S!SYAVHnA=l6WEc`emM5^f~@`9oF_SENr6{A5!*r&SSxMZsky~ zb(#L(mzz_bG7i2MPF?U8PL=!b0D{7jIT%0DMq9cW8!r*D6a8mcEA@{VpVJ`U_ww~m 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 oiOGwJwKwPI{nr3j_lJ&l0ssGi!R5 { + const data = fs.readFileSync(file); + const contentType = mime.lookup(file); + + res.writeHead(200, { 'Content-Type': contentType }); + res.end(data); + }); + } + } } export default Response; diff --git a/router.js b/router.js index 7b469da..a6f6613 100644 --- a/router.js +++ b/router.js @@ -1,5 +1,5 @@ import http from 'node:http'; -import util from 'node:util'; +import Response from './response.js'; import { pathToRegexp } from 'path-to-regexp'; import StringHelper from './helpers/string.js'; @@ -22,11 +22,12 @@ class Router { } /** - * This function checks for the existence of a route and calls its handler + * This function checks for the existence of a route + * and calls its middlewares and handler * * @param {http.ClientRequest} req - * @param {http.ServerResponse} res - * @returns {boolean} Is route has been handled + * @param {Response} res + * @returns */ call(req, res) { const mask = `${req.method}:${StringHelper.delTrailingSlash(req.url)}`; @@ -41,7 +42,7 @@ class Router { */ if (this.routes.hasOwnProperty(mask)) { this.#middlewares(route, req, res).then(() => route.handler(req, res)); - return true; + return; } /* Route url is not exact */ @@ -62,12 +63,12 @@ class Router { this.#middlewares(route, req, res).then(() => route.handler(req, res) ); - return true; + return; } } } - return false; + res.notFound('Page not found.'); } /** diff --git a/server.js b/server.js index bc1ffaf..f168f19 100644 --- a/server.js +++ b/server.js @@ -38,23 +38,8 @@ class Application { this.server.on('request', (req, res) => { const response = new Response(req, res); - /** - * Reponse with API data - */ - if (req.url.includes('api')) { - const handled = this.router.call(req, response); - - /** - * Undefined route - */ - if (!handled) response.notFound('Route not found!'); - return; - } - - /** - * Reponse with static files - */ - response.send('Static Page.'); + response.serve('public'); + this.router.call(req, response); }); this.server.listen(env.app.port, () => console.log(`[LOG]: Server is running on port "${env.app.port}"`) diff --git a/views/404.hbs b/views/404.hbs new file mode 100644 index 0000000..94604b2 --- /dev/null +++ b/views/404.hbs @@ -0,0 +1,11 @@ + + + + + + 404 | Not found + + + Page not found. + + \ No newline at end of file diff --git a/views/index.hbs b/views/index.hbs new file mode 100644 index 0000000..12c5e84 --- /dev/null +++ b/views/index.hbs @@ -0,0 +1,12 @@ + + + + + + {{app.name}} + + + + Index Page. + + \ No newline at end of file