diff --git a/controllers/ArticleController.js b/controllers/ArticleController.js index 98ce530..1a78a3e 100644 --- a/controllers/ArticleController.js +++ b/controllers/ArticleController.js @@ -81,8 +81,15 @@ class ArticleController { */ static async create(req, res) { try { - res.json({ data: `POST ${req.url}` }); - return; + const data = req.getPostData(); + if (data && data.title && data.description && data.body) { + const article = await ArticleModel.create({ + title: data.title, + description: data.description, + body: data.body, + }); + res.redirect(`/articles/${article.id}`); + } } catch (error) { res.serverError(error); } diff --git a/controllers/EditorController.js b/controllers/EditorController.js new file mode 100644 index 0000000..3d1ad36 --- /dev/null +++ b/controllers/EditorController.js @@ -0,0 +1,20 @@ +import http from 'node:http'; +import Response from '#app/Response'; + +class EditorController { + /** + * Render editor for creating article + * + * @param {http.ClientRequest} req + * @param {Response} res + */ + static create(req, res) { + try { + res.render('./views/editor/create.hbs', { title: 'Create new article' }); + } catch (error) { + res.serverError(error); + } + } +} + +export default EditorController; diff --git a/db.js b/db.js index 2897c90..e0a119d 100644 --- a/db.js +++ b/db.js @@ -1,5 +1,5 @@ import pg from 'pg'; -import env from './env.js'; +import env from '#app/env'; class Database { constructor() { diff --git a/helpers/db.js b/helpers/db.js index 0e75dd8..56d1804 100644 --- a/helpers/db.js +++ b/helpers/db.js @@ -1,5 +1,5 @@ import fs from 'node:fs'; -import db from '../db.js'; +import db from '#app/db'; class DbHelper { /** diff --git a/index.js b/index.js index 081e40d..a085ed9 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -import Application from './server.js'; +import Application from '#app/server'; const app = new Application(); diff --git a/initSchema.js b/initSchema.js index 816c3ae..6d2b538 100644 --- a/initSchema.js +++ b/initSchema.js @@ -1,3 +1,6 @@ -import DbHelper from './helpers/db.js'; +/** + * Helpers + */ +import DbHelper from '#helpers/db'; DbHelper.runQueryFromFile('sql/schema_init.sql'); diff --git a/models/ArticleModel.js b/models/ArticleModel.js index 4ce5f55..fd82dca 100644 --- a/models/ArticleModel.js +++ b/models/ArticleModel.js @@ -18,8 +18,8 @@ class ArticleModel { */ static async getAll() { try { - const data = await db.client.query('SELECT * FROM "articles"'); - return data.rows; + const result = await db.client.query('SELECT * FROM "articles"'); + return result.rows; } catch (error) { throw error; } @@ -33,14 +33,14 @@ class ArticleModel { */ static async getById(id) { try { - const data = await db.client.query( + const result = await db.client.query( 'SELECT * from "articles" WHERE "id" = $1', [id] ); - if (data.rows.length === 0) return null; + if (result.rows.length === 0) return null; - return data.rows[0]; + return result.rows[0]; } catch (error) { throw error; } @@ -63,12 +63,12 @@ class ArticleModel { 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)`, + const result = await db.client.query( + `INSERT INTO "articles" ("id", "title", "description", "body", "created_at", "updated_at") VALUES($1, $2, $3, $4, $5, $6) RETURNING *`, values ); - return article; + return result.rows[0]; } catch (error) { throw error; } diff --git a/preloads/routes.js b/preloads/routes.js index 6d6e518..8c36a61 100644 --- a/preloads/routes.js +++ b/preloads/routes.js @@ -7,6 +7,7 @@ import router from '#app/router'; * Controllers */ import ArticleController from '#controllers/ArticleController'; +import EditorController from '#app/controllers/EditorController'; /** * Middlewares @@ -16,3 +17,4 @@ import WaitMiddleware from '#middlewares/WaitMiddleware'; router.get('/', ArticleController.index).middleware([WaitMiddleware.handle]); router.get('/articles/:id', ArticleController.show); router.post('/articles', ArticleController.create); +router.get('/editor/create', EditorController.create); diff --git a/public/css/style.css b/public/css/style.css index 8a0490f..bb981f0 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,5 +1,8 @@ -body { +* { font-family: monospace; +} + +body { text-align: justify; margin-left: auto; margin-right: auto; @@ -9,6 +12,28 @@ body { padding: 15px; } +button { + cursor: pointer; + padding: 5px 20px; +} + +input { + padding: 7px 5px; + font-size: 16px; +} + +textarea { + resize: none; + font-size: 16px; + padding: 7px 5px; +} + +.textarea-body { + border: none; + border-bottom: 1px solid; + outline: none; +} + .app .app-heading { display: flex; justify-content: space-between; @@ -25,15 +50,11 @@ body { user-select: none; } -.app .article-heading { +.app .article-heading, +.app .editor-heading { display: block; } -.app .app-body article { - margin-top: 2rem; - line-height: 1.3rem; -} - .app .app-footer { text-align: center; } @@ -69,3 +90,9 @@ body { margin-top: 1rem; text-align: center; } + +.app .app-body .app-body__editor form { + display: flex; + flex-direction: column; + gap: 10px; +} diff --git a/response.js b/response.js index 42830a5..baca7bc 100644 --- a/response.js +++ b/response.js @@ -27,6 +27,11 @@ class Response { this.send(text, 404); } + redirect(location) { + this.res.writeHead(302, { Location: location }); + this.res.end(); + } + serverError(error, text = '500 | Internal server error') { console.error(error); this.render('./views/500.hbs', { title: text }, 500); diff --git a/router.js b/router.js index ae1ab16..d5fd0d6 100644 --- a/router.js +++ b/router.js @@ -1,7 +1,12 @@ import http from 'node:http'; -import Response from './response.js'; +import qs from 'node:querystring'; import { pathToRegexp } from 'path-to-regexp'; -import StringHelper from './helpers/string.js'; +import Response from '#app/response'; + +/** + * Helpers + */ +import StringHelper from '#helpers/string'; /** * Single route type @@ -29,11 +34,13 @@ class Router { * @param {Response} res * @returns */ - call(req, res) { + async call(req, res) { const mask = `${req.method}:${StringHelper.delTrailingSlash(req.url)}`; const keys = Object.keys(this.routes); let i = keys.length; + await this.#collectData(req, res); + while (i--) { let route = this.routes[mask]; @@ -92,6 +99,30 @@ class Router { } } + async #collectData(req, res) { + /** + * Collect POST data + */ + if ( + req.method === 'POST' && + req.headers['content-type'] === 'application/x-www-form-urlencoded' + ) { + const buffers = []; + + for await (const chunk of req) { + if ( + req.method === 'POST' && + req.headers['content-type'] === 'application/x-www-form-urlencoded' + ) { + buffers.push(chunk); + } + } + + const data = qs.parse(Buffer.concat(buffers).toString()); + req['getPostData'] = () => data; + } + } + /** * Add more routes to router store * diff --git a/seed.js b/seed.js index 5b275cd..825e4cd 100644 --- a/seed.js +++ b/seed.js @@ -1,3 +1,6 @@ -import DbHelper from './helpers/db.js'; +/** + * Helpers + */ +import DbHelper from '#helpers/db'; DbHelper.runQueryFromFile('sql/seed.sql'); diff --git a/server.js b/server.js index 6d4230b..36a2d2e 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,5 @@ 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'; @@ -9,7 +8,7 @@ import db from '#app/db'; import router from '#app/router'; import Response from '#app/response'; import FileHelper from '#helpers/file'; -import './preloads/index.js'; +import '#app/preloads/index'; class Application { constructor() { @@ -44,17 +43,7 @@ class Application { #handle() { 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) - // } - // }); + await 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/article.hbs b/views/article.hbs index e3f6bd9..83c0e3b 100644 --- a/views/article.hbs +++ b/views/article.hbs @@ -5,7 +5,7 @@ < return to home

{{ article.title }}

- {{ article.created_at }} +

{{ article.created_at }}

{{ article.description }}

diff --git a/views/editor/create.hbs b/views/editor/create.hbs new file mode 100644 index 0000000..bae9b1a --- /dev/null +++ b/views/editor/create.hbs @@ -0,0 +1,26 @@ + + {{> head }} + +
+ < return to home +
+

Create new article

+

Fill in the required fields to create a new article

+
+
+
+
+
+
+ + + + +
+
+
+
+ {{> footer }} +
+ + \ No newline at end of file diff --git a/views/index.hbs b/views/index.hbs index f4102ad..c31650e 100644 --- a/views/index.hbs +++ b/views/index.hbs @@ -15,6 +15,7 @@
+ {{#unless articles.length }}

No articles found.