mirror of
https://github.com/sergeyyarkov/node-docker-example.git
synced 2026-10-11 10:19:41 +03:00
refactor: refactored imports
This commit is contained in:
parent
90d0833d1d
commit
b4838dc19b
92
controllers/ArticleController.js
Normal file
92
controllers/ArticleController.js
Normal file
@ -0,0 +1,92 @@
|
||||
import http from 'node:http';
|
||||
import fs from 'node:fs';
|
||||
import Response from '#app/Response';
|
||||
|
||||
/**
|
||||
* Models
|
||||
*/
|
||||
import ArticleModel from '#models/ArticleModel';
|
||||
|
||||
class ArticleController {
|
||||
/**
|
||||
* Render page with list of articles
|
||||
*
|
||||
* @param {http.ClientRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
static async index(req, res) {
|
||||
try {
|
||||
const info = fs.readFileSync('./package.json', 'utf8');
|
||||
const appInfo = JSON.parse(info.toString());
|
||||
const articles = await ArticleModel.getAll();
|
||||
|
||||
res.render('./views/index.hbs', {
|
||||
title: appInfo.name,
|
||||
articles,
|
||||
});
|
||||
} catch (error) {
|
||||
res.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render page with single article by `id`
|
||||
*
|
||||
* @param {http.ClientRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
static async show(req, res) {
|
||||
try {
|
||||
/**
|
||||
* Validate url `id` param
|
||||
*/
|
||||
const id = req.params.id;
|
||||
if (!/^\d+$/.test(id)) {
|
||||
res.render(
|
||||
'./views/404.hbs',
|
||||
{ title: '404 | Article not found.' },
|
||||
404
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const article = await ArticleModel.getById(id);
|
||||
|
||||
/**
|
||||
* Not found
|
||||
*/
|
||||
if (!article) {
|
||||
res.render(
|
||||
'./views/404.hbs',
|
||||
{ title: '404 | Article not found.' },
|
||||
404
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
res.render('./views/article.hbs', {
|
||||
title: article.title,
|
||||
article,
|
||||
});
|
||||
} catch (error) {
|
||||
res.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new article
|
||||
*
|
||||
* @param {http.ClientRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
static async create(req, res) {
|
||||
try {
|
||||
res.json({ data: `POST ${req.url}` });
|
||||
return;
|
||||
} catch (error) {
|
||||
res.serverError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ArticleController;
|
||||
75
handlers.js
75
handlers.js
@ -1,75 +0,0 @@
|
||||
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) => {
|
||||
try {
|
||||
const info = fs.readFileSync('./package.json', 'utf8');
|
||||
const appInfo = JSON.parse(info.toString());
|
||||
const data = await db.client.query('SELECT * FROM "articles"');
|
||||
|
||||
res.render('./views/index.hbs', {
|
||||
title: appInfo.name,
|
||||
articles: data.rows,
|
||||
});
|
||||
} catch (error) {
|
||||
res.serverError(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* GET - Get article by ID
|
||||
*
|
||||
* @param {http.ClientRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
export const getArticleHandler = async (req, res) => {
|
||||
try {
|
||||
/**
|
||||
* Validate url `id` param
|
||||
*/
|
||||
if (!/^\d+$/.test(req.params.id)) {
|
||||
res.render('./views/404.hbs', { title: '404 | Article not found.' }, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await db.client.query(
|
||||
'SELECT * from "articles" WHERE "id" = $1',
|
||||
[req.params.id]
|
||||
);
|
||||
|
||||
if (data.rows.length === 0) {
|
||||
res.render('./views/404.hbs', { title: '404 | Article not found.' }, 404);
|
||||
return;
|
||||
}
|
||||
|
||||
res.render('./views/article.hbs', {
|
||||
title: data.rows[0].title,
|
||||
article: data.rows[0],
|
||||
});
|
||||
} catch (error) {
|
||||
res.serverError(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* POST - Create article
|
||||
*
|
||||
* @param {http.RequestOptions} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
export const createArticleHandler = async (req, res) => {
|
||||
try {
|
||||
res.json({ data: `POST ${req.url}` });
|
||||
return;
|
||||
} catch (error) {
|
||||
res.serverError(error);
|
||||
}
|
||||
};
|
||||
21
index.js
21
index.js
@ -1,28 +1,9 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import Application from './server.js';
|
||||
import FileHelper from './helpers/file.js';
|
||||
|
||||
const app = new Application();
|
||||
|
||||
app.serve('public');
|
||||
|
||||
{
|
||||
const partials = FileHelper.readDirRecursive(
|
||||
process.cwd() + '/views/partials'
|
||||
);
|
||||
|
||||
/**
|
||||
* Register partials from files
|
||||
*/
|
||||
for (const partial of partials) {
|
||||
const fileName = path.basename(partial);
|
||||
app.hbs.registerPartial(
|
||||
path.parse(fileName).name,
|
||||
fs.readFileSync(partial, 'utf8').toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
app.partials(process.cwd() + '/views/partials');
|
||||
|
||||
app.run().catch((error) => {
|
||||
console.error('[ERR]: Error on startup the application.', error);
|
||||
|
||||
12
jsconfig.json
Normal file
12
jsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"#app/*": ["./*"],
|
||||
"#controllers/*": ["./controllers/*"],
|
||||
"#models/*": ["./models/*"],
|
||||
"#middlewares/*": ["./middlewares/*"],
|
||||
"#helpers/*": ["./helpers/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import http from 'node:http';
|
||||
import Response from './response.js';
|
||||
|
||||
/**
|
||||
* This middleware waits for a few milliseconds
|
||||
*
|
||||
* @param {http.ClientRequest} req
|
||||
* @param {Response} res
|
||||
* @param {function} next
|
||||
*/
|
||||
export const waitMiddleware = async (req, res) => {
|
||||
const wait = (ms = 1000) => {
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => res(ms), ms);
|
||||
});
|
||||
};
|
||||
|
||||
await wait(10);
|
||||
};
|
||||
13
middlewares/WaitMiddleware.js
Normal file
13
middlewares/WaitMiddleware.js
Normal file
@ -0,0 +1,13 @@
|
||||
class WaitMiddleware {
|
||||
static async handle() {
|
||||
const wait = (ms = 1000) => {
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => res(ms), ms);
|
||||
});
|
||||
};
|
||||
|
||||
await wait(10);
|
||||
}
|
||||
}
|
||||
|
||||
export default WaitMiddleware;
|
||||
78
models/ArticleModel.js
Normal file
78
models/ArticleModel.js
Normal file
@ -0,0 +1,78 @@
|
||||
import db from '#app/db';
|
||||
|
||||
/**
|
||||
* ArticleModel typedef
|
||||
* @typedef {Object} Article
|
||||
* @property {number} id
|
||||
* @property {string} title
|
||||
* @property {string} description
|
||||
* @property {string} body
|
||||
* @property {Date} created_at
|
||||
* @property {Date} updated_at
|
||||
*/
|
||||
|
||||
class ArticleModel {
|
||||
/**
|
||||
* Get list of articles
|
||||
* @returns {Promise<Array<Article>>} List of articles
|
||||
*/
|
||||
static async getAll() {
|
||||
try {
|
||||
const data = await db.client.query('SELECT * FROM "articles"');
|
||||
return data.rows;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get article by `id`
|
||||
*
|
||||
* @param {string} id Id of article
|
||||
* @returns {Promise<Article | null>} Founded article
|
||||
*/
|
||||
static async getById(id) {
|
||||
try {
|
||||
const data = await db.client.query(
|
||||
'SELECT * from "articles" WHERE "id" = $1',
|
||||
[id]
|
||||
);
|
||||
|
||||
if (data.rows.length === 0) return null;
|
||||
|
||||
return data.rows[0];
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new article in database
|
||||
*
|
||||
* @param {{ title: string, description: string, body: string }} data Data for creating
|
||||
* @returns {Promise<Article>} Created article
|
||||
*/
|
||||
static async create(data) {
|
||||
try {
|
||||
const count = await db.client.query('SELECT COUNT(*) FROM "articles"');
|
||||
const values = Object.values({
|
||||
id: String(parseInt(count.rows[0].count, 10) + 1),
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
body: data.body,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
});
|
||||
const article = await db.client.query(
|
||||
`INSERT INTO "articles" ("id", "title", "description", "body", "created_at", "updated_at") VALUES($1, $2, $3, $4, $5, $6)`,
|
||||
values
|
||||
);
|
||||
|
||||
return article;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ArticleModel;
|
||||
@ -26,5 +26,12 @@
|
||||
"mime-types": "^2.1.35",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pg": "^8.7.3"
|
||||
},
|
||||
"imports": {
|
||||
"#app/*": "./*.js",
|
||||
"#controllers/*": "./controllers/*.js",
|
||||
"#models/*": "./models/*.js",
|
||||
"#middlewares/*": "./middlewares/*.js",
|
||||
"#helpers/*": "./helpers/*.js"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
import router from '../router.js';
|
||||
/**
|
||||
* Router
|
||||
*/
|
||||
import router from '#app/router';
|
||||
|
||||
/**
|
||||
* Handlers
|
||||
* Controllers
|
||||
*/
|
||||
import {
|
||||
createArticleHandler,
|
||||
getArticleHandler,
|
||||
indexPageHandler,
|
||||
} from '../handlers.js';
|
||||
import ArticleController from '#controllers/ArticleController';
|
||||
|
||||
/**
|
||||
* Middlewares
|
||||
*/
|
||||
import { waitMiddleware } from '../middlewares.js';
|
||||
import WaitMiddleware from '#middlewares/WaitMiddleware';
|
||||
|
||||
router.get('/', indexPageHandler).middleware([waitMiddleware]);
|
||||
router.get('/articles/:id', getArticleHandler).middleware([waitMiddleware]);
|
||||
router.post('/articles', createArticleHandler);
|
||||
router.get('/', ArticleController.index).middleware([WaitMiddleware.handle]);
|
||||
router.get('/articles/:id', ArticleController.show);
|
||||
router.post('/articles', ArticleController.create);
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
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 {
|
||||
/**
|
||||
|
||||
49
server.js
49
server.js
@ -1,13 +1,14 @@
|
||||
import http from 'node:http';
|
||||
import fs from 'node:fs';
|
||||
import qs from 'node:querystring';
|
||||
import path from 'node:path';
|
||||
import mime from 'mime-types';
|
||||
import hbs from 'handlebars';
|
||||
import env from './env.js';
|
||||
import db from './db.js';
|
||||
import router from './router.js';
|
||||
import Response from './response.js';
|
||||
import FileHelper from './helpers/file.js';
|
||||
import env from '#app/env';
|
||||
import db from '#app/db';
|
||||
import router from '#app/router';
|
||||
import Response from '#app/response';
|
||||
import FileHelper from '#helpers/file';
|
||||
import './preloads/index.js';
|
||||
|
||||
class Application {
|
||||
@ -41,9 +42,19 @@ class Application {
|
||||
}
|
||||
|
||||
#handle() {
|
||||
this.server.on('request', (req, res) => {
|
||||
this.server.on('request', async (req, res) => {
|
||||
const response = new Response(req, res);
|
||||
this.router.call(req, response);
|
||||
|
||||
// const buffer = Buffer.alloc(parseInt(req.headers['content-length'], 10));
|
||||
|
||||
// req.on('data', (chunk) => {
|
||||
// if (req.method === 'POST') {
|
||||
// console.log(chunk);
|
||||
// // console.log(buffer.toString());
|
||||
// // const buffer = Buffer.alloc(chunk.length)
|
||||
// }
|
||||
// });
|
||||
});
|
||||
this.server.listen(env.app.port, () =>
|
||||
console.log(`[LOG]: Server is running on port "${env.app.port}"`)
|
||||
@ -53,10 +64,10 @@ class Application {
|
||||
/**
|
||||
* Serve folder with static files
|
||||
*
|
||||
* @param {string} path Path to static files
|
||||
* @param {string} folder Path to static files
|
||||
*/
|
||||
serve(path) {
|
||||
const files = FileHelper.readDirRecursive(path);
|
||||
serve(folder) {
|
||||
const files = FileHelper.readDirRecursive(folder);
|
||||
/**
|
||||
* At the moment, this process is called only once.
|
||||
* It would be nice to watch for directory changes and declare new routes again
|
||||
@ -71,6 +82,26 @@ class Application {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register handlebars partials
|
||||
*
|
||||
* @param {string} folder Path to `partials` folder
|
||||
*/
|
||||
partials(folder) {
|
||||
const partials = FileHelper.readDirRecursive(folder);
|
||||
|
||||
/**
|
||||
* Register partials from files
|
||||
*/
|
||||
for (const partial of partials) {
|
||||
const fileName = path.basename(partial);
|
||||
this.hbs.registerPartial(
|
||||
path.parse(fileName).name,
|
||||
fs.readFileSync(partial, 'utf8').toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Application;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user