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 Application from './server.js';
|
||||||
import FileHelper from './helpers/file.js';
|
|
||||||
|
|
||||||
const app = new Application();
|
const app = new Application();
|
||||||
|
|
||||||
app.serve('public');
|
app.serve('public');
|
||||||
|
app.partials(process.cwd() + '/views/partials');
|
||||||
{
|
|
||||||
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.run().catch((error) => {
|
app.run().catch((error) => {
|
||||||
console.error('[ERR]: Error on startup the application.', 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",
|
"mime-types": "^2.1.35",
|
||||||
"path-to-regexp": "^6.2.0",
|
"path-to-regexp": "^6.2.0",
|
||||||
"pg": "^8.7.3"
|
"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 {
|
import ArticleController from '#controllers/ArticleController';
|
||||||
createArticleHandler,
|
|
||||||
getArticleHandler,
|
|
||||||
indexPageHandler,
|
|
||||||
} from '../handlers.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middlewares
|
* Middlewares
|
||||||
*/
|
*/
|
||||||
import { waitMiddleware } from '../middlewares.js';
|
import WaitMiddleware from '#middlewares/WaitMiddleware';
|
||||||
|
|
||||||
router.get('/', indexPageHandler).middleware([waitMiddleware]);
|
router.get('/', ArticleController.index).middleware([WaitMiddleware.handle]);
|
||||||
router.get('/articles/:id', getArticleHandler).middleware([waitMiddleware]);
|
router.get('/articles/:id', ArticleController.show);
|
||||||
router.post('/articles', createArticleHandler);
|
router.post('/articles', ArticleController.create);
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import hbs from 'handlebars';
|
import hbs from 'handlebars';
|
||||||
import mime from 'mime-types';
|
|
||||||
import router from './router.js';
|
|
||||||
import FileHelper from './helpers/file.js';
|
|
||||||
|
|
||||||
class Response {
|
class Response {
|
||||||
/**
|
/**
|
||||||
|
|||||||
49
server.js
49
server.js
@ -1,13 +1,14 @@
|
|||||||
import http from 'node:http';
|
import http from 'node:http';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import qs from 'node:querystring';
|
import qs from 'node:querystring';
|
||||||
|
import path from 'node:path';
|
||||||
import mime from 'mime-types';
|
import mime from 'mime-types';
|
||||||
import hbs from 'handlebars';
|
import hbs from 'handlebars';
|
||||||
import env from './env.js';
|
import env from '#app/env';
|
||||||
import db from './db.js';
|
import db from '#app/db';
|
||||||
import router from './router.js';
|
import router from '#app/router';
|
||||||
import Response from './response.js';
|
import Response from '#app/response';
|
||||||
import FileHelper from './helpers/file.js';
|
import FileHelper from '#helpers/file';
|
||||||
import './preloads/index.js';
|
import './preloads/index.js';
|
||||||
|
|
||||||
class Application {
|
class Application {
|
||||||
@ -41,9 +42,19 @@ class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#handle() {
|
#handle() {
|
||||||
this.server.on('request', (req, res) => {
|
this.server.on('request', async (req, res) => {
|
||||||
const response = new Response(req, res);
|
const response = new Response(req, res);
|
||||||
this.router.call(req, response);
|
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, () =>
|
this.server.listen(env.app.port, () =>
|
||||||
console.log(`[LOG]: Server is running on port "${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
|
* Serve folder with static files
|
||||||
*
|
*
|
||||||
* @param {string} path Path to static files
|
* @param {string} folder Path to static files
|
||||||
*/
|
*/
|
||||||
serve(path) {
|
serve(folder) {
|
||||||
const files = FileHelper.readDirRecursive(path);
|
const files = FileHelper.readDirRecursive(folder);
|
||||||
/**
|
/**
|
||||||
* At the moment, this process is called only once.
|
* At the moment, this process is called only once.
|
||||||
* It would be nice to watch for directory changes and declare new routes again
|
* 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;
|
export default Application;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user