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 0000000..6eecb6b Binary files /dev/null and b/public/images/docker-logo.png differ diff --git a/public/images/nodejs-logo.png b/public/images/nodejs-logo.png new file mode 100644 index 0000000..b29ace9 Binary files /dev/null and b/public/images/nodejs-logo.png differ diff --git a/response.js b/response.js index eef9e68..8565665 100644 --- a/response.js +++ b/response.js @@ -1,4 +1,9 @@ import http from 'node:http'; +import fs from 'node:fs'; +import hbs from 'handlebars'; +import mime from 'mime-types'; +import router from './router.js'; +import FileHelper from './helpers/file.js'; class Response { /** @@ -24,6 +29,42 @@ class Response { notFound(text) { this.send(text, 404); } + + /** + * Response with rendered page + * + * @param {string} src Path to static template file + * @param {object} data Data for the page + */ + render(src, data, status = 200) { + try { + const file = fs.readFileSync(src); + const template = hbs.compile(file.toString()); + const out = template(data); + this.res.writeHead(status, { 'Content-Type': 'text/html' }); + this.res.end(out); + } catch (error) { + throw error; + } + } + + /** + * Response with static files + * + * @param {string} path Path to static files + */ + serve(path) { + const files = FileHelper.readDirRecursive(path); + for (const file of files) { + router.define('GET', `/${file}`, (req, { res }) => { + 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 @@ + +
+ + + +