refactor: refactored imports

This commit is contained in:
Sergey Yarkov 2022-03-18 21:10:41 +03:00
parent 90d0833d1d
commit b4838dc19b
11 changed files with 253 additions and 137 deletions

View 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;

View File

@ -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);
}
};

View File

@ -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
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#app/*": ["./*"],
"#controllers/*": ["./controllers/*"],
"#models/*": ["./models/*"],
"#middlewares/*": ["./middlewares/*"],
"#helpers/*": ["./helpers/*"]
}
}
}

View File

@ -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);
};

View 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
View 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;

View File

@ -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"
}
}

View File

@ -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);

View File

@ -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 {
/**

View File

@ -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;