10 Commits

Author SHA1 Message Date
Rampeur b8edd76454 Ajout du dossier 'middlewares' 2025-08-13 19:02:35 +02:00
Rampeur 9bce9b714e Mise à jour des 'models' 2025-08-13 18:47:41 +02:00
Rampeur e741081966 Mises à jour relatives à la DB 2025-08-13 01:24:52 +02:00
Rampeur 2f78d85a27 Ajout du dossier 'routes' 2025-08-10 09:16:54 +02:00
Rampeur b68dd4c800 Mise à jour de routes et models 2025-08-10 09:15:18 +02:00
Rampeur 61fdac2434 Mise à jour de models 2025-08-08 20:18:39 +02:00
Rampeur e83b2e831f Ajout d'un fichier index 2025-08-08 19:28:29 +02:00
Rampeur dd5dbbb0fe Ajout du package-lock 2025-08-08 19:23:49 +02:00
Rampeur 16b2de084b Ajout du dossier 'routes' 2025-08-08 19:22:30 +02:00
Rampeur d477cf239c Mise à jour mineure 2025-08-08 19:21:36 +02:00
38 changed files with 4055 additions and 452 deletions
+4 -4
View File
@@ -1,14 +1,14 @@
APP_ENV="development" APP_ENV="development"
NODE_ENV="development" NODE_ENV="development"
DEBUG=false DEBUG=false
SERVER_PORT="3200" SERVER_PORT="3201"
SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO" JWT_SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO"
SESSION_LIFETIME="3600" SESSION_LIFETIME="3600"
SENDGRID_API_KEY="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" SENDGRID_API_KEY="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"
SENDGRID_FROM_MAIL="contact@rampeur.fr" SENDGRID_FROM_MAIL="contact@rampeur.fr"
SENDGRID_TO_MAIL="rampeur@gmail.com" SENDGRID_TO_MAIL="rampeur@gmail.com"
MYSQL_DBNAME="c4adastracomdb1" MYSQL_DBNAME="c4adastracomdb2"
MYSQL_DBUSER="c4adastracomu1" MYSQL_DBUSER="c4adastracomu2"
MYSQL_DBPASS="kbrnrt5jPK12" MYSQL_DBPASS="kbrnrt5jPK12"
MYSQL_DBHOST="ns388640.ip-176-31-255.eu" MYSQL_DBHOST="ns388640.ip-176-31-255.eu"
MYSQL_DBPORT="3306" MYSQL_DBPORT="3306"
+4 -4
View File
@@ -1,14 +1,14 @@
APP_ENV="development" APP_ENV="development"
NODE_ENV="development" NODE_ENV="development"
DEBUG=false DEBUG=false
SERVER_PORT="3200" SERVER_PORT="3201"
SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO" JWT_SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO"
SESSION_LIFETIME=3600 SESSION_LIFETIME=3600
SENDGRID_API_KEY="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" SENDGRID_API_KEY="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"
SENDGRID_FROM_MAIL="contact@rampeur.fr" SENDGRID_FROM_MAIL="contact@rampeur.fr"
SENDGRID_TO_MAIL="rampeur@gmail.com" SENDGRID_TO_MAIL="rampeur@gmail.com"
MYSQL_DBNAME="c4adastracomdb1" MYSQL_DBNAME="c4adastracomdb2"
MYSQL_DBUSER="c4adastracomu1" MYSQL_DBUSER="c4adastracomu2"
MYSQL_DBPASS="kbrnrt5jPK12" MYSQL_DBPASS="kbrnrt5jPK12"
MYSQL_DBHOST="ns388640.ip-176-31-255.eu" MYSQL_DBHOST="ns388640.ip-176-31-255.eu"
MYSQL_DBPORT="3306" MYSQL_DBPORT="3306"
+4 -4
View File
@@ -1,14 +1,14 @@
APP_ENV="local" APP_ENV="local"
NODE_ENV="development" NODE_ENV="development"
DEBUG=false DEBUG=false
SERVER_PORT="3200" SERVER_PORT="3201"
SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO" JWT_SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO"
SESSION_LIFETIME=3600 SESSION_LIFETIME=3600
SENDGRID_API_KEY="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" SENDGRID_API_KEY="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"
SENDGRID_FROM_MAIL="contact@rampeur.fr" SENDGRID_FROM_MAIL="contact@rampeur.fr"
SENDGRID_TO_MAIL="rampeur@gmail.com" SENDGRID_TO_MAIL="rampeur@gmail.com"
MYSQL_DBNAME="c4adastracomdb1" MYSQL_DBNAME="c4adastracomdb2"
MYSQL_DBUSER="c4adastracomu1" MYSQL_DBUSER="c4adastracomu2"
MYSQL_DBPASS="kbrnrt5jPK12" MYSQL_DBPASS="kbrnrt5jPK12"
MYSQL_DBHOST="ns388640.ip-176-31-255.eu" MYSQL_DBHOST="ns388640.ip-176-31-255.eu"
MYSQL_DBPORT="3306" MYSQL_DBPORT="3306"
+4 -4
View File
@@ -1,14 +1,14 @@
APP_ENV="production" APP_ENV="production"
NODE_ENV="production" NODE_ENV="production"
DEBUG=false DEBUG=false
SERVER_PORT="3200" SERVER_PORT="3201"
SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO" JWT_SECRET="$2b$10$2.JkQCRUa3fXESy/WyKVWO"
SESSION_LIFETIME=3600 SESSION_LIFETIME=3600
SENDGRID_API_KEY="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" SENDGRID_API_KEY="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"
SENDGRID_FROM_MAIL="contact@rampeur.fr" SENDGRID_FROM_MAIL="contact@rampeur.fr"
SENDGRID_TO_MAIL="rampeur@gmail.com" SENDGRID_TO_MAIL="rampeur@gmail.com"
MYSQL_DBNAME="c4adastracomdb1" MYSQL_DBNAME="c4adastracomdb2"
MYSQL_DBUSER="c4adastracomu1" MYSQL_DBUSER="c4adastracomu2"
MYSQL_DBPASS="kbrnrt5jPK12" MYSQL_DBPASS="kbrnrt5jPK12"
MYSQL_DBHOST="ns388640.ip-176-31-255.eu" MYSQL_DBHOST="ns388640.ip-176-31-255.eu"
MYSQL_DBPORT="3306" MYSQL_DBPORT="3306"
-2
View File
@@ -62,5 +62,3 @@ sonar.properties
**/*.copy.ts **/*.copy.ts
**/*.copy.scss **/*.copy.scss
**/*.copy.html **/*.copy.html
middlewares
+54 -48
View File
@@ -3,72 +3,78 @@ const asyncHandler = require("../middlewares/asyncHandler");
const ErrorResponse = require("../util/errorResponse"); const ErrorResponse = require("../util/errorResponse");
module.exports.getProfile = asyncHandler(async (req, res, next) => { module.exports.getProfile = asyncHandler(async (req, res, next) => {
const username = req.params.username; const username = req.params.username;
const { loggedUser } = req; const { loggedUser } = req;
const user = await User.findOne({ where: { username: username } }); const user = await User.findOne({ where: { username: username } });
if (!user) { if (!user) {
return next(new ErrorResponse(`User not found`, 404)); return next(new ErrorResponse(`User not found`, 404));
} }
let isFollowing = false; let isFollowing = false;
if (loggedUser) { if (loggedUser) {
const followers = await user.getFollowers(); const followers = await user.getFollowers();
isFollowing = followers.some((user) => user.id === loggedUser.id); isFollowing = followers.some((user) => user.id === loggedUser.id);
} }
const profile = { const profile = {
username, username,
bio: user.bio, firstname: user.firstname,
image: user.image, lastname: user.lastname,
following: isFollowing, bio: user.bio,
}; image: user.image,
res.status(200).json({ profile }); following: isFollowing,
};
res.status(200).json({ profile });
}); });
module.exports.followUser = asyncHandler(async (req, res, next) => { module.exports.followUser = asyncHandler(async (req, res, next) => {
const username = req.params.username; const username = req.params.username;
const { loggedUser } = req; const { loggedUser } = req;
const userToFollow = await User.findOne({ where: { username: username } }); const userToFollow = await User.findOne({ where: { username: username } });
if (!userToFollow) { if (!userToFollow) {
return next(new ErrorResponse(`User not found`, 404)); return next(new ErrorResponse(`User not found`, 404));
} }
const currentUser = await User.findByPk(loggedUser.id); const currentUser = await User.findByPk(loggedUser.id);
await userToFollow.addFollower(currentUser); await userToFollow.addFollower(currentUser);
const profile = { const profile = {
username: username, username: username,
bio: userToFollow.dataValues.bio, firstname: userToFollow.dataValues.firstname,
image: userToFollow.dataValues.image, lastname: userToFollow.dataValues.lastname,
following: true, bio: userToFollow.dataValues.bio,
}; image: userToFollow.dataValues.image,
following: true,
};
res.status(200).json({ profile }); res.status(200).json({ profile });
}); });
module.exports.unfollowUser = asyncHandler(async (req, res, next) => { module.exports.unfollowUser = asyncHandler(async (req, res, next) => {
const username = req.params.username; const username = req.params.username;
const { loggedUser } = req; const { loggedUser } = req;
const userToUnfollow = await User.findOne({ where: { username: username } }); const userToUnfollow = await User.findOne({ where: { username: username } });
if (!userToUnfollow) { if (!userToUnfollow) {
return next(new ErrorResponse(`User not found`, 404)); return next(new ErrorResponse(`User not found`, 404));
} }
const currentUser = await User.findByPk(loggedUser.id); const currentUser = await User.findByPk(loggedUser.id);
await userToUnfollow.removeFollower(currentUser); await userToUnfollow.removeFollower(currentUser);
const profile = { const profile = {
username: username, username: username,
bio: userToUnfollow.dataValues.bio, firstname: userToUnfollow.dataValues.firstname,
image: userToUnfollow.dataValues.image, lastname: userToUnfollow.dataValues.lastname,
following: false, bio: userToUnfollow.dataValues.bio,
}; image: userToUnfollow.dataValues.image,
following: false,
};
res.status(200).json({ profile }); res.status(200).json({ profile });
}); });
+58 -54
View File
@@ -4,90 +4,94 @@ const ErrorResponse = require("../util/errorResponse");
const { sign } = require("../util/jwt"); const { sign } = require("../util/jwt");
module.exports.createUser = asyncHandler(async (req, res, next) => { module.exports.createUser = asyncHandler(async (req, res, next) => {
const { email, password, username } = req.body.user; const { email, password, username, firstname, lastname } = req.body.user;
fieldValidation(email, next); fieldValidation(email, next);
fieldValidation(password, next); fieldValidation(password, next);
fieldValidation(username, next); fieldValidation(username, next);
fieldValidation(firstname, next);
fieldValidation(lastname, next);
const user = await User.create({ const user = await User.create({
email: email, email: email,
password: password, password: password,
username: username, username: username,
}); firstname: firstname,
lastname: lastname,
});
if (user.dataValues.password) { if (user.dataValues.password) {
delete user.dataValues.password; delete user.dataValues.password;
} }
user.dataValues.token = await sign(user); user.dataValues.token = await sign(user);
user.dataValues.bio = null; user.dataValues.bio = null;
user.dataValues.image = null; user.dataValues.image = null;
res.status(201).json({ user }); res.status(201).json({ user });
}); });
module.exports.loginUser = asyncHandler(async (req, res, next) => { module.exports.loginUser = asyncHandler(async (req, res, next) => {
const { email, password } = req.body.user; const { email, password } = req.body.user;
fieldValidation(email, next); fieldValidation(email, next);
fieldValidation(password, next); fieldValidation(password, next);
const user = await User.findOne({ const user = await User.findOne({
where: { where: {
email: email, email: email,
}, },
}); });
if (!user) { if (!user) {
return next(new ErrorResponse(`User not found`, 404)); return next(new ErrorResponse(`User not found`, 404));
} }
const isMatch = user.matchPassword(password); const isMatch = user.matchPassword(password);
if (!isMatch) { if (!isMatch) {
return next(new ErrorResponse("Wrong password", 401)); return next(new ErrorResponse("Wrong password", 401));
} }
delete user.dataValues.password; delete user.dataValues.password;
user.dataValues.token = await sign(user); user.dataValues.token = await sign(user);
user.dataValues.bio = null; user.dataValues.bio = null;
user.dataValues.image = null; user.dataValues.image = null;
res.status(200).json({ user }); res.status(200).json({ user });
}); });
module.exports.getCurrentUser = asyncHandler(async (req, res, next) => { module.exports.getCurrentUser = asyncHandler(async (req, res, next) => {
const { loggedUser } = req; const { loggedUser } = req;
const user = await User.findByPk(loggedUser.id); const user = await User.findByPk(loggedUser.id);
if (!user) { if (!user) {
return next(new ErrorResponse(`User not found`, 404)); return next(new ErrorResponse(`User not found`, 404));
} }
user.dataValues.token = req.headers.authorization.split(" ")[1]; user.dataValues.token = req.headers.authorization.split(" ")[1];
res.status(200).json({ user }); res.status(200).json({ user });
}); });
module.exports.updateUser = asyncHandler(async (req, res, next) => { module.exports.updateUser = asyncHandler(async (req, res, next) => {
await User.update(req.body.user, { await User.update(req.body.user, {
where: { where: {
id: req.user.id, id: req.user.id,
}, },
}); });
const user = await User.findByPk(req.user.id); const user = await User.findByPk(req.user.id);
user.dataValues.token = req.headers.authorization.split(" ")[1]; user.dataValues.token = req.headers.authorization.split(" ")[1];
res.status(200).json({ user }); res.status(200).json({ user });
}); });
const fieldValidation = (field, next) => { const fieldValidation = (field, next) => {
if (!field) { if (!field) {
return next(new ErrorResponse(`Missing fields`, 400)); return next(new ErrorResponse(`Missing fields`, 400));
} }
}; };
+9
View File
@@ -0,0 +1,9 @@
import globals from "globals";
import pluginJs from "@eslint/js";
export default [
{files: ["**/*.js"], languageOptions: {sourceType: "commonjs"}},
{languageOptions: { globals: globals.node }},
pluginJs.configs.recommended,
];
+7
View File
@@ -0,0 +1,7 @@
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch((err) => {
next(err);
});
};
module.exports = asyncHandler;
+30
View File
@@ -0,0 +1,30 @@
const { verify } = require("../util/jwt");
const User = require("../models/User");
const ErrorResponse = require("../util/errorResponse");
exports.protect = async (req, res, next) => {
try {
const { headers } = req;
if (!headers.authorization) return next();
const token = headers.authorization.split(" ")[1];
if (!token) throw new SyntaxError("Token missing or malformed");
const userVerified = await verify(token);
if (!userVerified) throw new Error("Invalid Token");
req.loggedUser = await User.findOne({
attributes: { exclude: ["email", "password"] },
where: { email: userVerified.email },
});
if (!req.loggedUser) next(new NotFoundError("User"));
headers.email = userVerified.email;
req.loggedUser.dataValues.token = token;
next();
} catch (error) {
next(error);
}
};
+22
View File
@@ -0,0 +1,22 @@
const ErrorResponse = require("../util/errorResponse");
//REQUESTED PAGE IS NOT FOUND
module.exports.notFound = (req, res, next) => {
const error = new ErrorResponse(`Not Found - ${req.originalUrl}`);
res.status(404);
next(error);
};
module.exports.errorHandler = (err, req, res, next) => {
// Log to console for dev
console.log(err.message.red);
console.log(err.stack.red);
const statusCode = err.statusCode ? err.statusCode : 500;
res.status(statusCode).json({
errors: {
body: [err.message],
},
});
};
-27
View File
@@ -1,27 +0,0 @@
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../util/database");
const slugify = require("slugify");
const Article = sequelize.define("Article", {
slug: {
type: DataTypes.STRING,
allowNull: false,
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
},
body: {
type: DataTypes.TEXT,
allowNull: false,
},
});
Article.beforeValidate((article) => {
article.slug = slugify(article.title, { lower: true });
});
module.exports = Article;
-11
View File
@@ -1,11 +0,0 @@
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../util/database");
const Comment = sequelize.define("Comment", {
body: {
type: DataTypes.TEXT,
allowNull: false,
},
});
module.exports = Comment;
-16
View File
@@ -1,16 +0,0 @@
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../util/database");
const Tag = sequelize.define(
"Tag",
{
name: {
type: DataTypes.STRING,
allowNull: false,
primaryKey: true,
},
},
{ timestamps: false }
);
module.exports = Tag;
-58
View File
@@ -1,58 +0,0 @@
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../util/database");
const bcrypt = require("bcryptjs");
/**
* "email": "jake@jake.jake",
"token": "jwt.token.here",
"username": "jake",
"bio": "I work at statefarm",
"image": null
*/
const User = sequelize.define(
"User",
{
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
bio: {
type: DataTypes.TEXT,
allowNull: true,
},
image: {
type: DataTypes.TEXT,
allowNull: true,
},
},
{ timestamps: false }
);
Model.prototype.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
const DEFAULT_SALT_ROUNDS = 10;
User.addHook("beforeCreate", async (user) => {
const encryptedPassword = await bcrypt.hash(
user.password,
DEFAULT_SALT_ROUNDS
);
user.password = encryptedPassword;
});
module.exports = User;
+34
View File
@@ -0,0 +1,34 @@
const { DataTypes } = require("sequelize");
const sequelize = require("../util/database");
const slugify = require("slugify");
const Article = sequelize.define("Article", {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false },
slug: { type: DataTypes.STRING, allowNull: false },
title: { type: DataTypes.STRING, allowNull: false },
description: { type: DataTypes.TEXT },
body: { type: DataTypes.TEXT, allowNull: false }
}, {
sequelize,
timestamps: true,
modelName: 'Article',
tableName: 'article',
createdAt: true,
updatedAt: true
});
Article.beforeValidate((article) => {
article.slug = slugify(article.title, { lower: true });
});
Article.prototype.toJSONFor = function () {
return {
id: this.id,
slug: this.slug,
title: this.title,
description: this.description,
body: this.body
};
};
module.exports = Article;
+23
View File
@@ -0,0 +1,23 @@
const { DataTypes } = require("sequelize");
const sequelize = require("../util/database");
const Comment = sequelize.define("Comment", {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false },
body: { type: DataTypes.TEXT, allowNull: false }
}, {
sequelize,
timestamps: true,
modelName: 'Comment',
tableName: 'comment',
createdAt: true,
updatedAt: true
});
Comment.prototype.toJSONFor = function () {
return {
id: this.id,
body: this.body
};
};
module.exports = Comment;
+21
View File
@@ -0,0 +1,21 @@
const { DataTypes } = require("sequelize");
const sequelize = require("../util/database");
const Tag = sequelize.define("Tag", {
name: { type: DataTypes.STRING, primaryKey: true, allowNull: false }
}, {
sequelize,
timestamps: true,
modelName: 'Tag',
tableName: 'tag',
createdAt: true,
updatedAt: true
});
Tag.prototype.toJSONFor = function () {
return {
name: this.name
};
};
module.exports = Tag;
+52
View File
@@ -0,0 +1,52 @@
const { DataTypes, Model } = require("sequelize");
const sequelize = require("../util/database");
const bcrypt = require("bcryptjs");
const User = sequelize.define("User", {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false },
email: { type: DataTypes.STRING, allowNull: false, unique: true },
username: { type: DataTypes.STRING, allowNull: false, unique: true },
firstname: { type: DataTypes.STRING },
lastname: { type: DataTypes.STRING },
bio: { type: DataTypes.TEXT, allowNull: true },
image: { type: DataTypes.TEXT, allowNull: true },
role: { type: DataTypes.STRING },
password: { type: DataTypes.STRING, allowNull: false }
}, {
sequelize,
timestamps: true,
modelName: 'User',
tableName: 'user',
createdAt: true,
updatedAt: true
});
Model.prototype.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
const DEFAULT_SALT_ROUNDS = 10;
User.addHook("beforeCreate", async (user) => {
const encryptedPassword = await bcrypt.hash(
user.password,
DEFAULT_SALT_ROUNDS
);
user.password = encryptedPassword;
});
User.prototype.toJSONFor = function () {
return {
id: this.id,
email: this.email,
username: this.username,
firstname: this.firstname,
lastname: this.lastname,
bio: this.bio,
image: this.image,
role: this.role
};
};
module.exports = User;
+4
View File
@@ -0,0 +1,4 @@
exports.User = require('./User');
exports.Article = require('./Article');
exports.Comment = require('./Comment');
exports.Tag = require('./Tag');
+12 -12
View File
@@ -1,5 +1,5 @@
const slug = require("slug"); const slugify = require("slugify");
const { const {
Model Model
@@ -16,11 +16,11 @@ module.exports = (sequelize, DataTypes) => {
} }
} }
Article.init({ Article.init({
article_id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false }, articleId: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false },
article_slug: { type: DataTypes.STRING, allowNull: false }, slug: { type: DataTypes.STRING, allowNull: false },
article_title: { type: DataTypes.STRING, allowNull: false }, title: { type: DataTypes.STRING, allowNull: false },
article_description: { type: DataTypes.TEXT }, description: { type: DataTypes.TEXT },
article_body: { type: DataTypes.TEXT, allowNull: false } body: { type: DataTypes.TEXT, allowNull: false }
}, { }, {
sequelize, sequelize,
timestamps: false, timestamps: false,
@@ -31,16 +31,16 @@ module.exports = (sequelize, DataTypes) => {
}); });
Article.beforeValidate((article) => { Article.beforeValidate((article) => {
article.slug = slug(article.title) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36); article.slug = slugify(article.title, { lower: true });
}); });
Article.prototype.toJSONFor = function () { Article.prototype.toJSONFor = function () {
return { return {
id: this.article_id, articleId: this.articleId,
slug: this.article_slug, slug: this.slug,
title: this.article_title, title: this.title,
description: this.article_description, description: this.description,
body: this.article_body body: this.body
}; };
}; };
+4 -4
View File
@@ -13,8 +13,8 @@ module.exports = (sequelize, DataTypes) => {
} }
} }
Comment.init({ Comment.init({
comment_id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false }, commentId: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false },
comment_body: { type: DataTypes.TEXT, allowNull: false } body: { type: DataTypes.TEXT, allowNull: false }
}, { }, {
sequelize, sequelize,
timestamps: false, timestamps: false,
@@ -26,8 +26,8 @@ module.exports = (sequelize, DataTypes) => {
Comment.prototype.toJSONFor = function () { Comment.prototype.toJSONFor = function () {
return { return {
id: this.comment_id, commentId: this.commentId,
body: this.comment_body body: this.body
}; };
}; };
-57
View File
@@ -1,57 +0,0 @@
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Membre extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
/* define association here */
}
}
Membre.init({
mbr_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
mbr_pseudo: {type: DataTypes.STRING, allowNull: false},
mbr_nom: {type: DataTypes.STRING},
mbr_prenom: {type: DataTypes.STRING},
mbr_email: {type: DataTypes.STRING},
mbr_adresse: {type: DataTypes.STRING},
mbr_ville: {type: DataTypes.STRING},
mbr_region: {type: DataTypes.STRING},
mbr_cp: {type: DataTypes.STRING},
mbr_pays: {type: DataTypes.STRING},
mbr_tel: {type: DataTypes.STRING},
mbr_mobile: {type: DataTypes.STRING},
mbr_statut: {type: DataTypes.INTEGER}
}, {
sequelize,
timestamps: false,
modelName: 'Membre',
tableName: 'membre',
createdAt: false,
updatedAt: false
});
Membre.prototype.toJSONFor = function () {
return {
id: this.mbr_id,
pseudo: this.mbr_pseudo,
nom: this.mbr_nom,
prenom: this.mbr_prenom,
email: this.mbr_email,
adresse: this.mbr_adresse,
ville: this.mbr_ville,
region: this.mbr_region,
cp: this.mbr_cp,
pays: this.mbr_pays,
tel: this.mbr_tel,
mobile: this.mbr_mobile,
statut: this.mbr_statut
};
};
return Membre;
};
+2 -3
View File
@@ -13,7 +13,7 @@ module.exports = (sequelize, DataTypes) => {
} }
} }
Tag.init({ Tag.init({
tag_name: { type: DataTypes.STRING, allowNull: false, primaryKey: true } name: { type: DataTypes.STRING, primaryKey: true, allowNull: false }
}, { }, {
sequelize, sequelize,
timestamps: false, timestamps: false,
@@ -25,8 +25,7 @@ module.exports = (sequelize, DataTypes) => {
Tag.prototype.toJSONFor = function () { Tag.prototype.toJSONFor = function () {
return { return {
id: this.tag_id, name: this.name
name: this.tag_name
}; };
}; };
+18 -27
View File
@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const bcrypt = require('bcrypt'); const bcrypt = require("bcryptjs");
const { const {
Model Model
@@ -17,19 +17,15 @@ module.exports = (sequelize, DataTypes) => {
} }
} }
User.init({ User.init({
user_id: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false}, userId: {type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
user_username: {type: DataTypes.STRING, allowNull: false}, email: {type: DataTypes.STRING, allowNull: false, unique: true},
user_lastname: {type: DataTypes.STRING}, username: {type: DataTypes.STRING, allowNull: false, unique: true},
user_firstname: {type: DataTypes.STRING}, firstname: {type: DataTypes.STRING},
user_email: {type: DataTypes.STRING}, lastname: {type: DataTypes.STRING},
user_adresse: {type: DataTypes.STRING}, bio: {type: DataTypes.TEXT, allowNull: true},
user_ville: {type: DataTypes.STRING}, image: {type: DataTypes.TEXT, allowNull: true},
user_region: {type: DataTypes.STRING}, role: {type: DataTypes.STRING},
user_cp: {type: DataTypes.STRING}, password: {type: DataTypes.STRING, allowNull: false}
user_pays: {type: DataTypes.STRING},
user_tel: {type: DataTypes.STRING},
user_mobile: {type: DataTypes.STRING},
user_role: {type: DataTypes.INTEGER}
}, { }, {
sequelize, sequelize,
timestamps: false, timestamps: false,
@@ -55,19 +51,14 @@ module.exports = (sequelize, DataTypes) => {
User.prototype.toJSONFor = function () { User.prototype.toJSONFor = function () {
return { return {
id: this.mbr_id, userId: this.userId,
username: this.mbr_pseudo, email: this.email,
lastname: this.mbr_nom, username: this.username,
firstname: this.mbr_prenom, firstname: this.firstname,
email: this.mbr_email, lastname: this.lastname,
adresse: this.mbr_adresse, bio: this.bio,
ville: this.mbr_ville, image: this.image,
region: this.mbr_region, role: this.role
cp: this.mbr_cp,
pays: this.mbr_pays,
tel: this.mbr_tel,
mobile: this.mbr_mobile,
statut: this.mbr_statut
}; };
}; };
return User; return User;
+21
View File
@@ -0,0 +1,21 @@
{
"env" : {
"APP_ENV": "local",
"NODE_ENV" : "development",
"DEBUG": false,
"SERVER_PORT": "3201",
"JWT_SECRET": "$2b$10$aEsAUG7Dy8dcTORi1vaQle",
"SESSION_LIFETIME": "3600",
"MONGODB_URI": "mongodb://localhost:27017/adastradb",
"SENDGRID_API_KEY": "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",
"SENDGRID_FROM_MAIL": "contact@rampeur.fr",
"SENDGRID_TO_MAIL": "rampeur@gmail.com",
"AUTHORIZATION_PREFIX": "Api-Key",
"COUCHDB_URL": "http://couchdb.unespace.com",
"COUCHDB_DATABASE": "adastradb",
"COUCHDB_DESIGN": "adastra_app",
"COUCHDB_CREDENTIAL": "cmFtcGV1cjpES3hwMjRQUw==",
"SKYDIVERID_API_URL": "https://skydiver.id/api",
"SKYDIVERID_API_TOKEN": "xmqzgnGYMD.OJgFw1pCXCpJFGmZfSjmWK8lrpqAQNnu"
}
}
+3489
View File
File diff suppressed because it is too large Load Diff
+21 -7
View File
@@ -1,26 +1,40 @@
{ {
"name": "adastra-api", "name": "adastra-api",
"version": "1.0.0", "version": "1.0.0",
"description": "Ad Astra API",
"private": true, "private": true,
"author": "adastra", "license": "UNLICENSED",
"author": "Solide Apps <jgautier.webdev@gmail.com>",
"contributors": [
"Julien Gautier <jgautier.webdev@gmail.com>"
],
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
"dev": "nodemon server.js" "start": "APP_ENV=production node ./server.js",
"dev": "APP_ENV=development node ./server.js",
"local": "APP_ENV=local nodemon ./server.js",
"test": "newman run ./tests/api-tests.postman.json -e ./tests/env-api-tests.postman.json"
},
"engines": {
"node": "^16.20.2 || ^18.19.1 || ^20.11.1"
}, },
"dependencies": { "dependencies": {
"@sendgrid/mail": "^8.1.5",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"colors": "^1.4.0", "colors": "^1.4.0",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"express": "^4.18.2", "express": "^4.18.2",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mysql2": "^2.3.3", "mysql2": "^3.14.3",
"nodemon": "^2.0.20", "nodemon": "^3.1.10",
"sequelize": "^6.26.0", "sequelize": "^6.26.0",
"slugify": "^1.6.5" "slugify": "^1.6.5"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8.29.0", "@eslint/js": "^9.0.0",
"prettier": "^2.8.0" "eslint": "^9.0.0",
"prettier": "^2.8.0",
"sequelize-cli": "^6.6.3"
} }
} }
@@ -9,8 +9,8 @@ const {
deleteFavoriteArticle, deleteFavoriteArticle,
deleteArticle, deleteArticle,
updateArticle, updateArticle,
} = require("../controllers/articles"); } = require("../../controllers/articles");
const { protect } = require("../middlewares/auth"); const { protect } = require("../../middlewares/auth");
router router
.route("/articles") .route("/articles")
@@ -6,9 +6,9 @@ const {
getComments, getComments,
createComment, createComment,
deleteComment, deleteComment,
} = require("../controllers/comments"); } = require("../../controllers/comments");
const { protect } = require("../middlewares/auth"); const { protect } = require("../../middlewares/auth");
router router
.route("/articles/:slug/comments") .route("/articles/:slug/comments")
+22
View File
@@ -0,0 +1,22 @@
var router = require('express').Router();
router.use('/', require('./profiles'));
router.use('/articles', require('./articles'));
router.use('/comments', require('./comments'));
router.use('/tags', require('./tags'));
router.use('/profiles', require('./profiles'));
router.use('/users', require('./users'));
router.use(function (err, req, res, next) {
if (err.name === 'ValidationError') {
return res.status(422).json({
errors: Object.keys(err.errors).reduce(function (errors, key) {
errors[key] = err.errors[key].message;
return errors;
}, {})
});
}
return next(err);
});
module.exports = router;
@@ -4,8 +4,8 @@ const {
getProfile, getProfile,
followUser, followUser,
unfollowUser, unfollowUser,
} = require("../controllers/profiles"); } = require("../../controllers/profiles");
const { protect } = require("../middlewares/auth"); const { protect } = require("../../middlewares/auth");
router.route("/profiles/:username").get(protect, getProfile); router.route("/profiles/:username").get(protect, getProfile);
router router
+1 -3
View File
@@ -1,9 +1,7 @@
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
const { getTags } = require("../controllers/tags"); const { getTags } = require("../../controllers/tags");
const { protect } = require("../middlewares/auth");
router.route("/tags/").get(getTags); router.route("/tags/").get(getTags);
+2 -2
View File
@@ -5,8 +5,8 @@ const {
loginUser, loginUser,
getCurrentUser, getCurrentUser,
updateUser, updateUser,
} = require("../controllers/users"); } = require("../../controllers/users");
const { protect } = require("../middlewares/auth"); const { protect } = require("../../middlewares/auth");
router.post("/users", createUser); router.post("/users", createUser);
router.post("/users/login", loginUser); router.post("/users/login", loginUser);
+4
View File
@@ -0,0 +1,4 @@
var router = require('express').Router();
router.use('/api', require('./api'));
module.exports = router;
+30
View File
@@ -0,0 +1,30 @@
const utils = {
getDateString: () => {
let today = new Date();
let dateNow = today.toLocaleDateString();
return `${dateNow}`;
},
getTimeString: () => {
let today = new Date();
let timeNow = today.toLocaleTimeString();
return `${timeNow}`;
},
getDateTimeString: (delimiter = true) => {
let today = new Date();
let dateString = `${today.toLocaleDateString()} ${today.toLocaleTimeString()}`;
if (delimiter) {
dateString = `[${dateString}]`;
}
return dateString;
},
getDateTimeISOString: (delimiter = true) => {
let today = new Date();
let dateString = today.toISOString();
if (delimiter) {
dateString = `[${dateString}]`;
}
return dateString;
}
};
module.exports = utils;
+92 -98
View File
@@ -2,20 +2,18 @@ var appEnv = process.env.APP_ENV;
if (appEnv == undefined) { if (appEnv == undefined) {
appEnv = 'development'; appEnv = 'development';
} }
require('dotenv').config({ path: `.env.${appEnv}` });
const { promisify } = require('util');
const express = require("express"); const express = require("express");
const sequelize = require("./util/database"); const sequelize = require("./util/database");
const dotenv = require("dotenv"); const chalk = require('chalk');
const morgan = require("morgan"); const morgan = require("morgan");
const colors = require("colors"); const colors = require("colors");
const utils = require('./routes/utils');
const { errorHandler } = require("./middlewares/errorHandler"); const { errorHandler } = require("./middlewares/errorHandler");
// Import Models var isProduction = appEnv === 'production';
const User = require("./models/User");
const Article = require("./models/Article");
const Tag = require("./models/Tag");
const Comment = require("./models/Comment");
dotenv.config({ path: `.env.${appEnv}` });
const app = express(); const app = express();
@@ -23,31 +21,70 @@ const app = express();
app.use(express.json()); app.use(express.json());
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
app.use(morgan("dev")); morgan.token('statusColor', (req, res) => {
let status = (typeof res.headersSent !== 'boolean' ? Boolean(res.header) : res.headersSent)
? res.statusCode
: undefined
return status >= 500 ? chalk.red(status)
: status >= 429 ? chalk.magenta(status)
: status >= 400 ? chalk.yellow(status)
: status >= 300 ? chalk.cyan(status)
: status >= 200 ? chalk.green(status)
: chalk.underline(status);
});
app.use(morgan('[:date[iso]] ":method :url HTTP/:http-version" :statusColor :res[content-length] ":referrer" ":user-agent" :remote-addr'));
//app.use(require('method-override')());
//app.use(express.static(__dirname + '/public'));
} else { } else {
app.use(morgan('[:date[iso]] ":method :url HTTP/:http-version" :statusColor :res[content-length] ":referrer" ":user-agent" :remote-addr')); app.use(morgan("dev"));
} }
// CORS // CORS
app.use((req, res, next) => { app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Origin", "*");
res.header( res.header(
"Access-Control-Allow-Headers", "Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization" "Origin, X-Requested-With, Content-Type, Accept, Authorization"
); );
if (req.method === "OPTIONS") { if (req.method === "OPTIONS") {
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET"); res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET");
return res.status(200).json({}); return res.status(200).json({});
} }
next(); next();
}); });
/*
var db = require("./models");
*/
// Import Models
const User = require("./models/User");
const Article = require("./models/Article");
const Tag = require("./models/Tag");
const Comment = require("./models/Comment");
// Relations
User.belongsToMany(User, { as: "followers", through: "Followers", foreignKey: "userId", timestamps: false });
User.belongsToMany(User, { as: "following", through: "Followers", foreignKey: "followerId", timestamps: false });
User.hasMany(Article, { foreignKey: "authorId", onDelete: "CASCADE" });
Article.belongsTo(User, { as: "author", foreignKey: "authorId" });
User.hasMany(Comment, { foreignKey: "authorId", onDelete: "CASCADE" });
Comment.belongsTo(User, { as: "author", foreignKey: "authorId" });
Article.hasMany(Comment, { foreignKey: "articleId", onDelete: "CASCADE" });
Comment.belongsTo(Article, { foreignKey: "articleId" });
User.belongsToMany(Article, { as: "favorites", through: "Favorites", timestamps: false });
Article.belongsToMany(User, { through: "Favorites", foreignKey: "articleId", timestamps: false });
Article.belongsToMany(Tag, { through: "TagLists", as: "tagLists", foreignKey: "articleId", timestamps: false, onDelete: "CASCADE" });
Tag.belongsToMany(Article, { through: "ArticleTags", uniqueKey: false, timestamps: false });
// Route files // Route files
const users = require("./routes/users"); const users = require("./routes/api/users");
const profiles = require("./routes/profiles"); const profiles = require("./routes/api/profiles");
const articles = require("./routes/articles"); const articles = require("./routes/api/articles");
const comments = require("./routes/comments"); const comments = require("./routes/api/comments");
const tags = require("./routes/tags"); const tags = require("./routes/api/tags");
// Mount routers // Mount routers
app.use(users); app.use(users);
@@ -56,83 +93,40 @@ app.use(articles);
app.use(comments); app.use(comments);
app.use(tags); app.use(tags);
const PORT = process.env.PORT || process.env.SERVER_PORT; //app.use(require('./routes'));
app.use(errorHandler); app.use(errorHandler);
//console.log(`User: ${User}`);
// Relations //Article.sync();
User.belongsToMany(User, { /*
as: "followers",
through: "Followers",
foreignKey: "userId",
timestamps: false,
});
User.belongsToMany(User, {
as: "following",
through: "Followers",
foreignKey: "followerId",
timestamps: false,
});
User.hasMany(Article, {
foreignKey: "authorId",
onDelete: "CASCADE",
});
Article.belongsTo(User, { as: "author", foreignKey: "authorId" });
User.hasMany(Comment, {
foreignKey: "authorId",
onDelete: "CASCADE",
});
Comment.belongsTo(User, { as: "author", foreignKey: "authorId" });
Article.hasMany(Comment, {
foreignKey: "articleId",
onDelete: "CASCADE",
});
Comment.belongsTo(Article, { foreignKey: "articleId" });
User.belongsToMany(Article, {
as: "favorites",
through: "Favorites",
timestamps: false,
});
Article.belongsToMany(User, {
through: "Favorites",
foreignKey: "articleId",
timestamps: false,
});
Article.belongsToMany(Tag, {
through: "TagLists",
as: "tagLists",
foreignKey: "articleId",
timestamps: false,
onDelete: "CASCADE",
});
Tag.belongsToMany(Article, {
through: "ArticleTags",
uniqueKey: false,
timestamps: false,
});
const sync = async () => await sequelize.sync({ force: true }); const sync = async () => await sequelize.sync({ force: true });
sync().then(() => { sync().then(() => {
User.create({ User.create({
email: "test@test.com", email: "jgautier.webdev@gmail.com",
password: "123456", username: "adastra",
username: "neo", firstname: "Julien",
}); lastname: "Gautier",
User.create({ password: "DKxp24PSnr",
email: "test2@test.com", role: "admin"
password: "123456", });
username: "celeb_neo", User.create({
}); email: "rampeur@gmail.com",
username: "solide",
firstname: "Rampeur",
lastname: "Solide",
password: "DKxp24PSnr",
role: "user"
});
}); });
*/
const server = app.listen(
PORT, const startServer = async () => {
console.log( const port = process.env.SERVER_PORT || 3200;
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold await promisify(app.listen).bind(app)(port);
) console.log(`${utils.getDateTimeISOString()}`, chalk.green(`${process.env.APP_ENV} API server listening on port ${port}`));
); }
// finally, let's start our server...
startServer();
+1 -1
View File
@@ -2,7 +2,7 @@ const jwt = require("jsonwebtoken");
module.exports.sign = async (user) => { module.exports.sign = async (user) => {
const token = await jwt.sign( const token = await jwt.sign(
{ id: user.id, username: user.username, email: user.email }, { userId: user.userId, username: user.username, email: user.email },
process.env.JWT_SECRET process.env.JWT_SECRET
); );
return token; return token;