Skip to content

Commit

Permalink
feat: added blog management
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrilDesch committed Apr 4, 2024
1 parent ec1bc01 commit f9da2f6
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 47 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
node_modules
*.log
public/*
storage/logs
storage/city
storage/*/*
*.xml
apidoc
www
Expand Down
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
"editor.formatOnSave": true,
"prettier.configPath": ".prettierrc.cjs",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.packageManager": "yarn",
"npm.packageManager": "yarn",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["javascript"],
"[javascript]": {
Expand Down
4 changes: 3 additions & 1 deletion app/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ export const db = {
};

export const back = process.env.APP_BACKEND_PUBLIC_URL;
export const apiPath = "/api";
export const apiPath = "api";
export const front = process.env.APP_FRONTEND_PUBLIC_URL;
export const port = process.env.INTERNAL_PORT || 80;
export const docPath = "doc";
export const frontWebhookSecret = process.env.APP_FRONTEND_WEBHOOK_SECRET;

export const mail = {
mailjet_apikey_public: process.env.APP_MAILJET_PUBLIC_KEY,
Expand All @@ -50,3 +51,4 @@ export const cors_origin = process.env.APP_CORS_ORIGIN
// Documents
export const userProfilePicMediaPath = join(storagePath, "user");
export const cityPicMediaPath = join(storagePath, "city");
export const articlePicMediaPath = join(storagePath, "articles");
105 changes: 105 additions & 0 deletions app/controllers/articleController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { join } from "path";
import { articlePicMediaPath } from "../config/config";
import { models } from "../models";
import { reloadPaths } from "../services/nextRenderService";

export async function getAllArticles(req, res, next) {
try {
const articles = await models.Article.findAll();
return res.status(200).send(articles);
} catch (e) {
next(e);
}
}

export async function getArticleBySlug(req, res, next) {
try {
const article = await models.Article.findByPk(req.params.article_slug);

if (!article) {
return res.sendStatus(404);
}

return res.status(200).send(article);
} catch (e) {
next(e);
}
}

export async function createArticle(req, res, next) {
try {
let slug = req.body.title.replace(/\b\w+'(?=[A-Za-zÀ-ÖØ-öø-ÿ])/gi, "");
slug = slug.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
slug = slug.replace(/[^\w\s]/gi, "");
slug = slug.replace(/\s+/g, "-");
slug = slug.toLowerCase();

const article = await models.Article.create(
{ ...req.body, slug },
{
individualHooks: true,
hooks: true,
},
);
await reloadPaths("/blog");
return res.status(200).send(article);
} catch (e) {
next(e);
}
}

export async function deleteArticle(req, res, next) {
try {
await models.Article.destroy({
where: { slug: req.params.article_slug },
individualHooks: true,
hooks: true,
});
await reloadPaths("/blog");
return res.sendStatus(200);
} catch (e) {
next(e);
}
}

export async function updateArticle(req, res, next) {
try {
let slug = req.body.title.replace(/\b\w+'(?=[A-Za-zÀ-ÖØ-öø-ÿ])/gi, "");
slug = slug.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
slug = slug.replace(/[^\w\s]/gi, "");
slug = slug.replace(/\s+/g, "-");
slug = slug.toLowerCase();

await models.Article.update(
{ ...req.body, slug },
{
where: { slug: req.params.article_slug },
individualHooks: true,
hooks: true,
},
);
await reloadPaths("/blog");
next();
} catch (e) {
next(e);
}
}

export async function accessArticlePicture(req, res, next) {
try {
const article = await models.Article.findByPk(req.params.article_slug, {
fields: [req.params.image_type],
});
if (!article || !article[req.params.image_type]) return res.sendStatus(404);

req.file = {
path: join(
articlePicMediaPath,
article.getDataValue(req.params.image_type),
),
};
next();
} catch (error) {
next(error);
}
}
2 changes: 1 addition & 1 deletion app/controllers/userController.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const logout = function (req, res) {
res.status(200).send("Cookie successfully deleted!");
};

// Create endpoint /api/users/:id for GET
// Create endpoint /users/:id for GET
export const getUserById = function (req, res) {
logger.debug("getUserById :" + req.params.user_id);
models.User.findByPk(req.params.user_id, {
Expand Down
6 changes: 3 additions & 3 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ app.use(compression());
app.use(
bodyParser.urlencoded({
extended: true,
limit: "5mb",
limit: "10mb",
}),
);
app.use(bodyParser.json({ limit: "1mb" }));
app.use(bodyParser.json({ limit: "10mb" }));
app.use((req, res, next) => {
if (req.query.relations) {
req.relations = new Proxy(
Expand Down Expand Up @@ -90,7 +90,7 @@ app.use((error, req, res, _next) => {
res.sendStatus(500);
});

//plug our router from routes.js to /api URI
//plug our router from routes.js to / URI
app.use(apiPath, router);

if (app.get("env") === "development") {
Expand Down
101 changes: 101 additions & 0 deletions app/models/article.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { join } from "path";
import sequelize from "sequelize";
const { DataTypes, Model } = sequelize;
import {
ignoreURL,
modelFileDeleter,
modelFileWriter,
} from "../services/fileService";
import { apiPath, back, articlePicMediaPath } from "../config/config";

export default function (sequelize) {
class Article extends Model {}

Article.init(
{
slug: {
type: DataTypes.STRING(220),
allowNull: false,
primaryKey: true,
},
title: {
type: DataTypes.STRING(200),
allowNull: false,
},
description: {
type: DataTypes.STRING(400),
allowNull: false,
},
content: {
type: DataTypes.TEXT,
allowNull: false,
},
readingTime: {
type: DataTypes.INTEGER,
allowNull: false,
},
image: {
type: DataTypes.STRING(60),
allowNull: false,
get() {
const picture = this.getDataValue("image");
return picture
? new URL(
join(
apiPath,
"articles",
String(this.getDataValue("slug")),
"image",
),
back,
)
: picture;
},
},
thumbnail: {
type: DataTypes.STRING(70),
get() {
const picture = this.getDataValue("thumbnail");
return picture
? new URL(
join(
apiPath,
"articles",
String(this.getDataValue("slug")),
"thumbnail",
),
back,
)
: picture;
},
},
},
{
sequelize,
},
);

const pictureWriter = modelFileWriter(
"image",
articlePicMediaPath,
"picture_",
(mime) => {
if (!mime.startsWith("image"))
return Promise.reject(new Error("file not accepted"));
},
{ quality: 70, width: 1500 },
true,
);

Article.beforeCreate(ignoreURL("image", pictureWriter));

Article.beforeUpdate(ignoreURL("image", pictureWriter));
Article.beforeUpdate(ignoreURL("thumbnail"));

Article.beforeDestroy(
modelFileDeleter("image", articlePicMediaPath),
modelFileDeleter("thumbnail", articlePicMediaPath),
);

return Article;
}
30 changes: 30 additions & 0 deletions app/routes/articles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import express from "express";
import {
getAllArticles,
getArticleBySlug,
updateArticle,
deleteArticle,
createArticle,
accessArticlePicture,
} from "../controllers/articleController";
import { downloadFile } from "../services/fileService";

const articlesRouter = express.Router();

articlesRouter.route("/").get(getAllArticles);
articlesRouter.route("/:article_slug").get(getArticleBySlug);

articlesRouter
.route("/its-a-fucking-route-to-manage-article")
.post(createArticle);

articlesRouter
.route("/its-a-fucking-route-to-manage-article/:article_slug")
.put(updateArticle, getArticleBySlug)
.delete(deleteArticle);

articlesRouter
.route("/:article_slug/:image_type")
.get(accessArticlePicture, downloadFile);

export default articlesRouter;
2 changes: 2 additions & 0 deletions app/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { AppError } from "../services/errorService";
import usersRouter from "./users.js";
import citiesRouter from "./cities.js";
import airportsRouter from "./airports";
import articlesRouter from "./articles";

// ROUTES FOR OUR API
// =============================================================================
Expand All @@ -22,6 +23,7 @@ const router = express.Router(); // get an instance of the express Router
router.use("/users", usersRouter);
router.use("/cities", citiesRouter);
router.use("/airports", airportsRouter);
router.use("/articles", articlesRouter);

/**
* @api {get} / Test route
Expand Down
Loading

0 comments on commit f9da2f6

Please sign in to comment.