feat: deleting article

This commit is contained in:
Sergey Yarkov 2022-03-18 22:43:48 +03:00
parent 9c84c17308
commit 3266939fdc
8 changed files with 91 additions and 10 deletions

View File

@ -0,0 +1,8 @@
# node-docker-example
This app showcases a website built with NodeJS. The site has a set of
articles, each article has a separate page, all data about the article
is stored in the PostgreSQL database. Docker is used to deploy the
application.
## How to deploy

View File

@ -40,8 +40,7 @@ class ArticleController {
/**
* Validate url `id` param
*/
const id = req.params.id;
if (!/^\d+$/.test(id)) {
if (!req.params || !/^\d+$/.test(req.params.id)) {
res.render(
'./views/404.hbs',
{ title: '404 | Article not found.' },
@ -50,7 +49,7 @@ class ArticleController {
return;
}
const article = await ArticleModel.getById(id);
const article = await ArticleModel.getById(req.params.id);
/**
* Not found
@ -94,6 +93,29 @@ class ArticleController {
res.serverError(error);
}
}
/**
* Delete article by `id`
*
* @param {http.ClientRequest} req
* @param {Response} res
*/
static async delete(req, res) {
try {
/**
* Validate url `id` param
*/
if (!req.params || !/^\d+$/.test(req.params.id)) {
res.render('./views/403.hbs', { title: '403 | Bad request.' }, 403);
return;
}
await ArticleModel.delete(req.params.id);
res.redirect(`/`);
} catch (error) {
res.serverError(error);
}
}
}
export default ArticleController;

View File

@ -14,6 +14,7 @@ import db from '#app/db';
class ArticleModel {
/**
* Get list of articles
*
* @returns {Promise<Array<Article>>} List of articles
*/
static async getAll() {
@ -73,6 +74,19 @@ class ArticleModel {
throw error;
}
}
/**
* Delete article by `id`
*
* @param {string} id
*/
static async delete(id) {
try {
await db.client.query(`DELETE FROM "articles" WHERE id = $1`, [id]);
} catch (error) {
throw error;
}
}
}
export default ArticleModel;

View File

@ -17,4 +17,5 @@ import WaitMiddleware from '#middlewares/WaitMiddleware';
router.get('/', ArticleController.index).middleware([WaitMiddleware.handle]);
router.get('/articles/:id', ArticleController.show);
router.post('/articles', ArticleController.create);
router.post('/articles/:id', ArticleController.delete);
router.get('/editor/create', EditorController.create);

View File

@ -55,6 +55,16 @@ textarea {
display: block;
}
.app article .article-heading {
display: flex;
align-items: center;
justify-content: space-between;
}
.app article .article-heading button {
padding: 5px;
}
.app .app-footer {
text-align: center;
}

View File

@ -179,6 +179,16 @@ class Router {
post(url, handler) {
return this.define('POST', url, handler);
}
/**
* DELETE Method route
*
* @param {*} url
* @param {*} handler
*/
delete(url, handler) {
return this.define('DELETE', url, handler);
}
}
export default new Router();

7
views/403.hbs Normal file
View File

@ -0,0 +1,7 @@
<html lang='en'>
{{> head }}
<body>
<h1>{{ title }}</h1>
<a href="/">&lt; return to home</a>
</body>
</html>

View File

@ -23,13 +23,22 @@
{{/unless}}
{{#each articles}}
<article>
<h2>
<a href='articles/{{this.id}}'>
{{ this.title }}
</a>
</h2>
<p>{{ this.description }}</p>
<time>{{ this.created_at }}</time>
<div class="article-heading">
<h2>
<a href='articles/{{ this.id }}'>
{{ this.title }}
</a>
</h2>
<form method='post' action='/articles/{{ this.id }}'>
<button type="submit">&#x2715;</button>
</form>
</div>
<div class="article-description">
<p>{{ this.description }}</p>
</div>
<div class="article-time">
<time>{{ this.created_at }}</time>
</div>
</article>
<hr />
{{/each}}