Merge pull request #42 from AlexisNP/issues/api-auth

Authentication system has been validated
This commit is contained in:
Joururi
2021-01-19 23:03:58 +01:00
committed by GitHub
32 changed files with 4405 additions and 4094 deletions

View File

@@ -6,7 +6,7 @@ USE auracle;
-- ROLES -- ROLES
CREATE TABLE IF NOT EXISTS `role` ( CREATE TABLE IF NOT EXISTS `role` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(255) NOT NULL, `name` VARCHAR(255) NOT NULL,
`description` VARCHAR(255) NOT NULL, `description` VARCHAR(255) NOT NULL,
PRIMARY KEY(`id`) PRIMARY KEY(`id`)
@@ -14,15 +14,15 @@ CREATE TABLE IF NOT EXISTS `role` (
-- PERMISSIONS -- PERMISSIONS
CREATE TABLE IF NOT EXISTS `permission` ( CREATE TABLE IF NOT EXISTS `permission` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`slug` VARCHAR(255) NOT NULL, `slug` VARCHAR(255) NOT NULL,
PRIMARY KEY(`id`) PRIMARY KEY(`id`)
); );
-- USERS -- USERS
CREATE TABLE IF NOT EXISTS `user` ( CREATE TABLE IF NOT EXISTS `user` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`uuid` VARCHAR(36) NOT NULL, `uuid` VARCHAR(36) NOT NULL UNIQUE,
`name` VARCHAR(255) NOT NULL DEFAULT "Disciple", `name` VARCHAR(255) NOT NULL DEFAULT "Disciple",
`mail` VARCHAR(255) NOT NULL, `mail` VARCHAR(255) NOT NULL,
`avatar` VARCHAR(255), `avatar` VARCHAR(255),
@@ -37,16 +37,25 @@ CREATE TABLE IF NOT EXISTS `user` (
FOREIGN KEY(`role_id`) REFERENCES role(`id`) FOREIGN KEY(`role_id`) REFERENCES role(`id`)
); );
-- API_TOKENS
CREATE TABLE IF NOT EXISTS `api_token` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`value` VARCHAR(255) NOT NULL,
`user_uuid` VARCHAR(36) NOT NULL UNIQUE,
PRIMARY KEY(`id`),
FOREIGN KEY(`user_uuid`) REFERENCES user(`uuid`)
);
-- SPELLS -- SPELLS
CREATE TABLE IF NOT EXISTS `spell` ( CREATE TABLE IF NOT EXISTS `spell` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(255) NOT NULL DEFAULT "Nom du sort", `name` VARCHAR(255) NOT NULL DEFAULT "Nom du sort",
`description` VARCHAR(1000) NOT NULL DEFAULT "Description du sort", `description` VARCHAR(1000) NOT NULL DEFAULT "Description du sort",
`level` INT UNSIGNED DEFAULT 0, `level` INT UNSIGNED DEFAULT 0,
`charge` INT UNSIGNED DEFAULT 0, `charge` INT UNSIGNED DEFAULT 0,
`cost` VARCHAR(255) DEFAULT 0, `cost` VARCHAR(255) DEFAULT 0,
`is_ritual` BOOLEAN DEFAULT false, `is_ritual` BOOLEAN DEFAULT false,
`published` BOOLEAN DEFAULT true, `published` BOOLEAN DEFAULT false,
`public` BOOLEAN DEFAULT true, `public` BOOLEAN DEFAULT true,
`author_id` INT UNSIGNED NOT NULL DEFAULT 1, `author_id` INT UNSIGNED NOT NULL DEFAULT 1,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
@@ -55,7 +64,7 @@ CREATE TABLE IF NOT EXISTS `spell` (
-- META SCHOOLS -- META SCHOOLS
CREATE TABLE IF NOT EXISTS `meta_school` ( CREATE TABLE IF NOT EXISTS `meta_school` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école mère", `name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école mère",
`description` VARCHAR(255) DEFAULT "Description de l'école mère", `description` VARCHAR(255) DEFAULT "Description de l'école mère",
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
@@ -63,9 +72,10 @@ CREATE TABLE IF NOT EXISTS `meta_school` (
-- SCHOOLS -- SCHOOLS
CREATE TABLE IF NOT EXISTS `school` ( CREATE TABLE IF NOT EXISTS `school` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école", `name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école",
`description` VARCHAR(255) DEFAULT "Description de l'école", `description` VARCHAR(255) DEFAULT "Description de l'école",
`published` BOOLEAN DEFAULT false,
`meta_school_id` INT UNSIGNED NOT NULL, `meta_school_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
FOREIGN KEY(`meta_school_id`) REFERENCES meta_school(`id`) FOREIGN KEY(`meta_school_id`) REFERENCES meta_school(`id`)
@@ -73,16 +83,18 @@ CREATE TABLE IF NOT EXISTS `school` (
-- COMMON INGREDIENTS -- COMMON INGREDIENTS
CREATE TABLE IF NOT EXISTS `ingredient` ( CREATE TABLE IF NOT EXISTS `ingredient` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`name` VARCHAR(255) NOT NULL DEFAULT "Langue de salamandre", `name` VARCHAR(255) NOT NULL DEFAULT "Langue de salamandre",
`description` VARCHAR(255) NOT NULL DEFAULT "Une langue de salamandre de feu encore chaude.", `description` VARCHAR(255) NOT NULL DEFAULT "Une langue de salamandre de feu encore chaude.",
`published` BOOLEAN DEFAULT false,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );
-- COMMON VARIABLES -- COMMON VARIABLES
CREATE TABLE IF NOT EXISTS `variable` ( CREATE TABLE IF NOT EXISTS `variable` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
`description` VARCHAR(255) NOT NULL DEFAULT "Nombre de créatures affectées", `description` VARCHAR(255) NOT NULL DEFAULT "Nombre de créatures affectées",
`published` BOOLEAN DEFAULT false,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );
@@ -138,51 +150,43 @@ DELIMITER ;
/* =========== PRIMARY INSERTS =========== */ /* =========== PRIMARY INSERTS =========== */
SET NAMES utf8; SET NAMES utf8;
USE auracle;
-- ROLES -- CSV DATA
INSERT INTO `role` (name, description) VALUES LOAD DATA INFILE 'C:/temp/auracle_data/permission.csv'
("Visiteur", "Utilisateur normal, peut consulter les sorts."), INTO TABLE `permission`
("Scribe", "Gardiens des écrits, les scribes sont capables de soumettre des sortilèges."), FIELDS TERMINATED BY ','
("Arcanologue", "Maîtres de l'arcane, ils ont le pouvoir et la responsabilité de juger les sortilèges récents et de les supprimer, ou valider."), ENCLOSED BY '"'
("Augure", "Régents des grimoires, ils ont droit d'accès à l'intégralité des informations connues, et pouvoir absolu sur le savoir arcanique."); LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
-- PERMISSIONS LOAD DATA INFILE 'C:/temp/auracle_data/role.csv'
INSERT INTO `permission` (slug) VALUES INTO TABLE `role`
("SUBMIT_SPELLS"), FIELDS TERMINATED BY ','
("APPROVE_SPELLS"), ENCLOSED BY '"'
("MODIFY_SPELLS"), LINES TERMINATED BY '\n'
("DELETE_SPELLS"), IGNORE 1 ROWS;
("WARN_USERS"),
("BAN_USERS");
INSERT INTO `role_permission` (role_id, permission_id) VALUES LOAD DATA INFILE 'C:/temp/auracle_data/role_permission.csv'
(2, 1), INTO TABLE `role_permission`
(3, 1), FIELDS TERMINATED BY ','
(3, 2), ENCLOSED BY '"'
(3, 3), LINES TERMINATED BY '\n'
(4, 1), IGNORE 1 ROWS;
(4, 2),
(4, 3),
(4, 4),
(4, 5),
(4, 6);
-- USERS LOAD DATA INFILE 'C:/temp/auracle_data/user.csv'
INSERT INTO `user` (uuid, name, mail, avatar, gender, register_date, password, role_id, verified, banned) VALUES INTO TABLE `user`
("08e5d2cf-3f0b-454f-b03f-504350d6be85", "Izàc", "tymos@ambrose.edu", null, null, "2020-12-27 17:47:02", "$2b$10$8KWGfRmdQ/ya32fROZNrWugXIEOciDaZwLz.3.GzQa5xrJaGF9RP2", 4, 1, 0); FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
-- META SCHOOLS LOAD DATA INFILE 'C:/temp/auracle_data/api_token.csv'
INSERT INTO `meta_school` (name, description) VALUES INTO TABLE `api_token`
("Magies blanches", "Magies disciplinant les arts de soins et de lumières."), FIELDS TERMINATED BY ','
("Magies noires", "Magies disciplinant l'art de la mort et des secrets."), ENCLOSED BY '"'
("Magies élémentaires", "Magies disciplinant les éléments basiques tels que l'eau, la foudre et le feu, pour n'en citer que les plus populaires."), LINES TERMINATED BY '\n'
("Magies spirituelles", "Magies disciplinant l'esprit, tant pour le défendre que l'attaquer."), IGNORE 1 ROWS;
("Magies spatio-temporelles", "Magies régissant le temps et l'espace."),
("Magies affiliées", "Magies rattachées à une forme d'énergie magique particulière."),
("Magies autres", "Magies trop spécifiques et ne rentrant dans aucune autre grande école.");
-- CSV FILES
LOAD DATA INFILE 'C:/temp/auracle_data/spell.csv' LOAD DATA INFILE 'C:/temp/auracle_data/spell.csv'
INTO TABLE `spell` INTO TABLE `spell`
FIELDS TERMINATED BY ',' FIELDS TERMINATED BY ','
@@ -190,6 +194,13 @@ LOAD DATA INFILE 'C:/temp/auracle_data/spell.csv'
LINES TERMINATED BY '\n' LINES TERMINATED BY '\n'
IGNORE 1 ROWS; IGNORE 1 ROWS;
LOAD DATA INFILE 'C:/temp/auracle_data/meta_school.csv'
INTO TABLE `meta_school`
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
LOAD DATA INFILE 'C:/temp/auracle_data/school.csv' LOAD DATA INFILE 'C:/temp/auracle_data/school.csv'
INTO TABLE `school` INTO TABLE `school`
FIELDS TERMINATED BY ',' FIELDS TERMINATED BY ','
@@ -211,41 +222,9 @@ LOAD DATA INFILE 'C:/temp/auracle_data/variable.csv'
LINES TERMINATED BY '\n' LINES TERMINATED BY '\n'
IGNORE 1 ROWS; IGNORE 1 ROWS;
-- Insertions de masses LOAD DATA INFILE 'C:/temp/auracle_data/spell_school.csv'
DELIMITER $$ INTO TABLE `spell_school`
CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN id_school INT) FIELDS TERMINATED BY ','
BEGIN ENCLOSED BY '"'
SET @i = delimiter_start; LINES TERMINATED BY '\n'
WHILE @i <= delimiter_end DO IGNORE 1 ROWS;
INSERT INTO spell_school (spell_id, school_id) VALUES (@i, id_school);
SET @i = @i + 1;
END WHILE;
END$$
DELIMITER ;
CALL insertIntoSchoolRange(1, 33, 1);
CALL insertIntoSchoolRange(34, 67, 2);
CALL insertIntoSchoolRange(68, 90, 3);
CALL insertIntoSchoolRange(91, 119, 4);
CALL insertIntoSchoolRange(120, 135, 5);
CALL insertIntoSchoolRange(136, 139, 6);
CALL insertIntoSchoolRange(140, 165, 7);
CALL insertIntoSchoolRange(166, 195, 8);
CALL insertIntoSchoolRange(196, 222, 9);
CALL insertIntoSchoolRange(223, 247, 10);
CALL insertIntoSchoolRange(248, 252, 11);
CALL insertIntoSchoolRange(253, 274, 12);
CALL insertIntoSchoolRange(275, 292, 13);
CALL insertIntoSchoolRange(293, 312, 14);
CALL insertIntoSchoolRange(313, 323, 15);
CALL insertIntoSchoolRange(324, 343, 16);
CALL insertIntoSchoolRange(344, 364, 17);
CALL insertIntoSchoolRange(365, 366, 18);
CALL insertIntoSchoolRange(367, 383, 19);
CALL insertIntoSchoolRange(384, 385, 20);
CALL insertIntoSchoolRange(386, 403, 21);
CALL insertIntoSchoolRange(404, 435, 22);
CALL insertIntoSchoolRange(436, 438, 23);
CALL insertIntoSchoolRange(439, 443, 24);
CALL insertIntoSchoolRange(444, 455, 25);
CALL insertIntoSchoolRange(456, 462, 26);

View File

@@ -5,11 +5,11 @@ const fs = require('fs');
const knex = require('knex')({ const knex = require('knex')({
client: "mysql", client: "mysql",
connection: { connection: {
host : process.env.DB_HOST, host: process.env.DB_HOST,
user : process.env.DB_USER, user: process.env.DB_USER,
password : process.env.DB_PASSWORD, password: process.env.DB_PASSWORD,
database : process.env.DB_DATABASE, database: process.env.DB_DATABASE,
charset : "utf8" charset: "utf8"
}, },
}); });
const bookshelf = require('bookshelf')(knex); const bookshelf = require('bookshelf')(knex);

View File

@@ -11,9 +11,9 @@ const paramIntCheck = (req, res, next, input) => {
} }
} catch (err) { } catch (err) {
res.status(err.code).send(JSON.stringify({ res.status(err.code).send(JSON.stringify({
"message": "Le paramètre doit être un entier non-nul.", "message": "Le paramètre doit être un entier non-nul.",
"code": 403, "code": 403,
}) })
) )
} }
} }

View File

@@ -8,12 +8,9 @@ const morgan = require('morgan');
const cors = require('cors'); // module to format the json response const cors = require('cors'); // module to format the json response
const dotenv = require('dotenv').config(); const dotenv = require('dotenv').config();
// Creates instances of database connections
const connection = require('./database/bookshelf');
const db = connection.db;
// CONSTANTS // CONSTANTS
const port = 2814; const port = 2814;
const base_url = 'http://localhost:2814/api';
// Import routes // Import routes
const routes = require('./routes'); const routes = require('./routes');
@@ -22,10 +19,10 @@ const routes = require('./routes');
let app = express(); let app = express();
app.use(bodyParser.json({ limit: '10kb' })); app.use(bodyParser.json({ limit: '10kb' }));
app.use(cors({ app.use(cors({
origin: [ origin: [
"http://localhost:8080", "http://localhost:8080",
], ],
credentials: true, credentials: true,
})); }));
app.use(morgan('dev')); app.use(morgan('dev'));
app.use(helmet()); app.use(helmet());
@@ -33,19 +30,13 @@ app.use(helmet());
// Server // Server
app.listen(port, () => console.log(`App listening on port ${port}`)); app.listen(port, () => console.log(`App listening on port ${port}`));
// TEMP Auth guard // Entry route
const authguard = (req, res, next) => { app.use('/api/v1/', routes.auth);
// if (req.headers.auracle_key !== process.env.API_KEY_PUBLIC) {
// return res.status(401).send('The API key is either missing or incorrect.');
// } else {
next();
// }
}
// Routing // Routing
app.use('/api/spells', authguard, routes.spells); app.use('/api/v1/spells', routes.spells);
app.use('/api/schools', authguard, routes.schools); app.use('/api/v1/schools', routes.schools);
app.use('/api/meta_schools', authguard, routes.meta_schools); app.use('/api/v1/meta_schools', routes.meta_schools);
app.use('/api/variables', authguard, routes.variables); app.use('/api/v1/variables', routes.variables);
app.use('/api/ingredients', authguard, routes.ingredients); app.use('/api/v1/ingredients', routes.ingredients);
app.use('/api/users', authguard, routes.users); app.use('/api/v1/users', routes.users);

View File

@@ -0,0 +1,14 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf;
require('./user-model');
let APIToken = bookshelf.Model.extend({
tableName: 'api_token',
hidden: ['id'],
user() {
return this.belongsTo('User', 'user_uuid', 'uuid');
}
})
module.exports = bookshelf.model('APIToken', APIToken);

View File

@@ -6,7 +6,7 @@ require('./spell-model')
let Ingredient = bookshelf.Model.extend({ let Ingredient = bookshelf.Model.extend({
tableName: 'ingredient', tableName: 'ingredient',
spells() { spells() {
return this.belongsToMany( 'Spell', 'spell_ingredient') return this.belongsToMany('Spell', 'spell_ingredient')
} }
}) })

View File

@@ -6,7 +6,7 @@ require('./school-model')
let MetaSchool = bookshelf.Model.extend({ let MetaSchool = bookshelf.Model.extend({
tableName: 'meta_school', tableName: 'meta_school',
schools() { schools() {
return this.hasMany( 'School' ) return this.hasMany('School')
} }
}) })

View File

@@ -4,10 +4,10 @@ require('./role-model')
let Permission = bookshelf.Model.extend({ let Permission = bookshelf.Model.extend({
tableName: 'permission', tableName: 'permission',
hidden: [ 'id' ], hidden: ['id'],
role() { role() {
return this.belongsToMany( 'Role', 'role_permission' ) return this.belongsToMany('Role', 'role_permission')
} }
}) })
module.exports = bookshelf.model( 'Permission', Permission ); module.exports = bookshelf.model('Permission', Permission);

View File

@@ -5,8 +5,8 @@ require('./permission-model')
let Role = bookshelf.Model.extend({ let Role = bookshelf.Model.extend({
tableName: 'role', tableName: 'role',
permissions() { permissions() {
return this.belongsToMany( 'Permission', 'role_permission' ); return this.belongsToMany('Permission', 'role_permission');
} }
}) })
module.exports = bookshelf.model( 'Role', Role ); module.exports = bookshelf.model('Role', Role);

View File

@@ -7,10 +7,10 @@ require('./meta-school-model')
let School = bookshelf.Model.extend({ let School = bookshelf.Model.extend({
tableName: 'school', tableName: 'school',
spells() { spells() {
return this.belongsToMany( 'Spell', 'spell_school' ) return this.belongsToMany('Spell', 'spell_school')
}, },
meta_schools() { meta_schools() {
return this.belongsTo( 'MetaSchool', 'meta_school_id' ) return this.belongsTo('MetaSchool', 'meta_school_id')
} }
}) })

View File

@@ -6,12 +6,12 @@ require('./spell-model');
let User = bookshelf.Model.extend({ let User = bookshelf.Model.extend({
tableName: 'user', tableName: 'user',
hidden: [ 'password' ], hidden: ['password', 'role_id'],
role() { role() {
return this.belongsTo( 'Role' ); return this.belongsTo('Role');
}, },
spells() { spells() {
return this.hasMany( 'Spell', 'author_id' ); return this.hasMany('Spell', 'author_id');
} }
}) })

View File

@@ -6,7 +6,7 @@ require('./spell-model')
let Variable = bookshelf.Model.extend({ let Variable = bookshelf.Model.extend({
tableName: 'variable', tableName: 'variable',
spells() { spells() {
return this.belongsToMany( 'Spell', 'spell_variable') return this.belongsToMany('Spell', 'spell_variable')
} }
}) })

4841
api/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,45 +1,44 @@
{ {
"name": "auracle-api", "name": "auracle-api",
"version": "1.0.0", "version": "1.0.0",
"description": "API for Auracle database", "description": "API for Auracle database",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"run": "node index.js" "start": "node index.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/AlexisNP/spellsaurus.git" "url": "git+https://github.com/AlexisNP/spellsaurus.git"
}, },
"keywords": [ "keywords": [
"api", "api",
"rpg", "rpg",
"spells", "spells",
"magic", "magic",
"database", "database",
"ttrpg", "ttrpg",
"dices", "dices",
"worldbuilding" "worldbuilding"
], ],
"author": "AlexisNP", "author": "AlexisNP",
"license": "ISC", "license": "ISC",
"bugs": { "bugs": {
"url": "https://github.com/AlexisNP/spellsaurus/issues" "url": "https://github.com/AlexisNP/spellsaurus/issues"
}, },
"homepage": "https://github.com/AlexisNP/spellsaurus#readme", "homepage": "https://github.com/AlexisNP/spellsaurus#readme",
"dependencies": { "dependencies": {
"bcrypt": "^5.0.0", "bcrypt": "^5.0.0",
"bookshelf": "^1.1.1", "bookshelf": "^1.1.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"handlebars": "^4.7.6", "handlebars": "^4.7.6",
"helmet": "^3.22.0", "helmet": "^3.22.0",
"jsonschema": "^1.2.6", "jsonschema": "^1.2.6",
"jsonwebtoken": "^8.5.1", "knex": "^0.21.1",
"knex": "^0.21.1", "morgan": "^1.10.0",
"morgan": "^1.10.0", "mysql": "^2.18.1",
"mysql": "^2.18.1", "nodemailer": "^6.4.17",
"nodemailer": "^6.4.17", "uuid": "^8.2.0"
"uuid": "^8.2.0" }
} }
}

View File

@@ -5,9 +5,9 @@ const model = require('../models/ingredient-model')
// Model validation // Model validation
const Validator = require('jsonschema').Validator const Validator = require('jsonschema').Validator
const v = new Validator() const validator = new Validator()
const IngredientValidation = require("../validations/IngredientValidation") const IngredientValidation = require("../validations/IngredientValidation")
v.addSchema(IngredientValidation, "/IngredientValidation") validator.addSchema(IngredientValidation, "/IngredientValidation")
// Validations // Validations
const isXSSAttempt = require('../functions').isXSSAttempt const isXSSAttempt = require('../functions').isXSSAttempt
@@ -21,54 +21,54 @@ class IngredientRepository {
getAll() { getAll() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.fetchAll({ withRelated: ['spells'] }) .fetchAll({ withRelated: ['spells'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "Il n'existe aucun ingrédient disponible.", "message": "Il n'existe aucun ingrédient disponible.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getOne(id) { getOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['spells']}) .fetch({ withRelated: ['spells'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "L'ingrédient en question n'a pas pu être trouvé.", "message": "L'ingrédient en question n'a pas pu être trouvé.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getSpellsFromOne(id) { getSpellsFromOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools']}) .fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "Les sortilèges liés à cet ingrédient n'ont pas pu être récupérés.", "message": "Les sortilèges liés à cet ingrédient n'ont pas pu être récupérés.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
@@ -80,9 +80,9 @@ class IngredientRepository {
"message": "Le corps de la requête ne peut pas être vide.", "message": "Le corps de la requête ne peut pas être vide.",
"code": 403, "code": 403,
}); });
} else if (!v.validate(igr, IngredientValidation).valid) { } else if (!validator.validate(igr, IngredientValidation).valid) {
reject({ reject({
"message": `Le modèle d'ingrédient n'est pas respecté : ${v.validate(s, IngredientValidation).errors}`, "message": `Le modèle d'ingrédient n'est pas respecté : ${validator.validate(s, IngredientValidation).errors}`,
"code": 403, "code": 403,
}); });
} else if (isXSSAttempt(igr.description)) { } else if (isXSSAttempt(igr.description)) {
@@ -98,62 +98,10 @@ class IngredientRepository {
}).save(null, { }).save(null, {
transacting: t transacting: t
}) })
.catch(err => {
throw err
})
})
.then(v => {
return v.load(['spells'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
});
})
}
})
}
updateOne(id, igr) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(igr)) {
reject({
"message": "Le corps de la requête ne peut pas être vide.",
"code": 403,
});
} else if (!v.validate(igr, IngredientValidation).valid) {
reject({
"message": `Le modèle d'ingrédient n'est pas respecté : ${v.validate(s, IngredientValidation).errors}`,
"code": 403,
});
} else if (isXSSAttempt(igr.description)) {
reject({
"message": "Tentative d'injection XSS détectée, requête refusée.",
"code": 403,
});
} else {
new model({id: id})
.fetch({require: true, withRelated: ['spells']})
.then(v => {
bookshelf.transaction(t => {
return v.save({
'name': igr.name,
'description': igr.description,
}, {
method: 'update',
transacting: t
})
.catch(err => { .catch(err => {
console.log(err)
throw err throw err
}) })
}) })
.then(v => { .then(v => {
return v.load(['spells']) return v.load(['spells'])
}) })
@@ -167,6 +115,83 @@ class IngredientRepository {
"code": 500, "code": 500,
}); });
}) })
}
})
}
updateOne(id, igr) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(igr)) {
reject({
"message": "Le corps de la requête ne peut pas être vide.",
"code": 403,
});
} else if (!validator.validate(igr, IngredientValidation).valid) {
reject({
"message": `Le modèle d'ingrédient n'est pas respecté : ${validator.validate(s, IngredientValidation).errors}`,
"code": 403,
});
} else if (isXSSAttempt(igr.description)) {
reject({
"message": "Tentative d'injection XSS détectée, requête refusée.",
"code": 403,
});
} else {
new model({ id: id })
.fetch({ require: true, withRelated: ['spells'] })
.then(v => {
bookshelf.transaction(t => {
return v.save({
'name': igr.name,
'description': igr.description,
}, {
method: 'update',
transacting: t
})
.catch(err => {
console.log(err)
throw err
})
})
.then(v => {
return v.load(['spells'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
});
})
})
.catch(err => {
console.log(err)
reject({
"message": "L'ingrédient en question n'a pas été trouvé.",
"code": 404,
});
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id': id })
.fetch({ require: true, withRelated: ['spells'] })
.then(v => {
v.spells().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'Ingredient with ID ' + id + ' successfully deleted !'
})
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
@@ -175,31 +200,6 @@ class IngredientRepository {
"code": 404, "code": 404,
}); });
}) })
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id' : id })
.fetch({require: true, withRelated: ['spells']})
.then(v => {
v.spells().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'Ingredient with ID ' + id + ' successfully deleted !'
})
})
.catch(err => {
console.log(err)
reject({
"message": "L'ingrédient en question n'a pas été trouvé.",
"code": 404,
});
})
}) })
} }
} }

View File

@@ -5,9 +5,9 @@ const model = require('../models/meta-school-model')
// Model validation // Model validation
const Validator = require('jsonschema').Validator const Validator = require('jsonschema').Validator
const v = new Validator() const validator = new Validator()
const MetaSchoolValidation = require("../validations/MetaSchoolValidation") const MetaSchoolValidation = require("../validations/MetaSchoolValidation")
v.addSchema(MetaSchoolValidation, "/MetaSchoolValidation") validator.addSchema(MetaSchoolValidation, "/MetaSchoolValidation")
// Validations // Validations
const regexXSS = RegExp(/<[^>]*script/) const regexXSS = RegExp(/<[^>]*script/)
@@ -20,35 +20,35 @@ class MetaSchoolRepository {
getAll() { getAll() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.fetchAll({ withRelated: ['schools'] }) .fetchAll({ withRelated: ['schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "Il n'existe aucune grande école disponible.", "message": "Il n'existe aucune grande école disponible.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getOne(id) { getOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['schools']}) .fetch({ withRelated: ['schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "La grande école en question n'a pas pu être trouvée.", "message": "La grande école en question n'a pas pu être trouvée.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
} }

View File

@@ -5,9 +5,9 @@ const model = require('../models/school-model')
// Model validation // Model validation
const Validator = require('jsonschema').Validator const Validator = require('jsonschema').Validator
const v = new Validator() const validator = new Validator()
const SchoolValidation = require("../validations/SchoolValidation") const SchoolValidation = require("../validations/SchoolValidation")
v.addSchema(SchoolValidation, "/SchoolValidation") validator.addSchema(SchoolValidation, "/SchoolValidation")
// Validations // Validations
const isXSSAttempt = require('../functions').isXSSAttempt const isXSSAttempt = require('../functions').isXSSAttempt
@@ -21,53 +21,53 @@ class SchoolRepository {
getAll() { getAll() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.fetchAll({ withRelated: ['meta_schools'] }) .fetchAll({ withRelated: ['meta_schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Il n'existe aucune école disponible.", "message": "Il n'existe aucune école disponible.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getOne(id) { getOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['meta_schools']}) .fetch({ withRelated: ['meta_schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "L'école en question n'a pas pu être trouvée.", "message": "L'école en question n'a pas pu être trouvée.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getSpellsFromOne(id) { getSpellsFromOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools']}) .fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Les sortilèges de cette école n'ont pas pu être récupérés.", "message": "Les sortilèges de cette école n'ont pas pu être récupérés.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
@@ -79,9 +79,9 @@ class SchoolRepository {
"message": "Le corps de la requête ne peut pas être vide.", "message": "Le corps de la requête ne peut pas être vide.",
"code": 403, "code": 403,
}); });
} else if (!v.validate(s, SchoolValidation).valid) { } else if (!validator.validate(s, SchoolValidation).valid) {
reject({ reject({
"message": `Le modèle d'école n'est pas respecté : ${v.validate(s, SchoolValidation).errors}`, "message": `Le modèle d'école n'est pas respecté : ${validator.validate(s, SchoolValidation).errors}`,
"code": 403, "code": 403,
}); });
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) { } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) {
@@ -97,24 +97,24 @@ class SchoolRepository {
'meta_school_id': s.meta_school_id, 'meta_school_id': s.meta_school_id,
}).save(null, { }).save(null, {
transacting: t transacting: t
})
.catch(err => {
throw err
})
})
.then(v => {
return v.load(['meta_schools']);
})
.then(v => {
resolve(this.getOne(v.id));
}) })
.catch(err => { .catch(err => {
throw err console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
})
}) })
})
.then(v => {
return v.load(['meta_schools']);
})
.then(v => {
resolve(this.getOne(v.id));
})
.catch(err => {
console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
})
})
} }
}) })
} }
@@ -127,9 +127,9 @@ class SchoolRepository {
"message": "Le corps de la requête ne peut pas être vide.", "message": "Le corps de la requête ne peut pas être vide.",
"code": 403, "code": 403,
}); });
} else if (!v.validate(s, SchoolValidation).valid) { } else if (!validator.validate(s, SchoolValidation).valid) {
reject({ reject({
"message": `Le modèle d'école n'est pas respecté : ${v.validate(s, SchoolValidation).errors}`, "message": `Le modèle d'école n'est pas respecté : ${validator.validate(s, SchoolValidation).errors}`,
"code": 403, "code": 403,
}); });
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) { } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) {
@@ -138,35 +138,60 @@ class SchoolRepository {
"code": 403, "code": 403,
}); });
} else { } else {
new model({id: id}) new model({ id: id })
.fetch({require: true, withRelated: ['meta_schools']}) .fetch({ require: true, withRelated: ['meta_schools'] })
.then(v => {
bookshelf.transaction(t => {
return v.save({
'name': s.name,
'description': s.description,
'meta_school_id': s.meta_school_id
}, {
method: 'update',
transacting: t
})
.catch(err => {
console.log(err)
throw err
})
})
.then(v => { .then(v => {
return v.load(['meta_schools']) bookshelf.transaction(t => {
}) return v.save({
.then(v => { 'name': s.name,
resolve(this.getOne(v.id)) 'description': s.description,
'meta_school_id': s.meta_school_id
}, {
method: 'update',
transacting: t
})
.catch(err => {
console.log(err)
throw err
})
})
.then(v => {
return v.load(['meta_schools'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
})
})
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err);
reject({ reject({
"message": "Une erreur d'insertion s'est produite.", "message": "L'école en question n'a pas été trouvée.",
"code": 500, "code": 404,
}) });
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id': id })
.fetch({ require: true, withRelated: ['spells', 'meta_schools'] })
.then(v => {
v.spells().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'School with ID ' + id + ' successfully deleted !'
}) })
}) })
.catch(err => { .catch(err => {
@@ -176,31 +201,6 @@ class SchoolRepository {
"code": 404, "code": 404,
}); });
}) })
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id' : id })
.fetch({require: true, withRelated: ['spells', 'meta_schools']})
.then(v => {
v.spells().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'School with ID ' + id + ' successfully deleted !'
})
})
.catch(err => {
console.log(err);
reject({
"message": "L'école en question n'a pas été trouvée.",
"code": 404,
});
})
}) })
} }
} }

View File

@@ -5,9 +5,9 @@ const model = require('../models/spell-model')
// Model validation // Model validation
const Validator = require('jsonschema').Validator const Validator = require('jsonschema').Validator
const v = new Validator() const validator = new Validator()
const SpellValidation = require("../validations/SpellValidation") const SpellValidation = require("../validations/SpellValidation")
v.addSchema(SpellValidation, "/SpellValidation") validator.addSchema(SpellValidation, "/SpellValidation")
// Validations // Validations
const isXSSAttempt = require('../functions').isXSSAttempt const isXSSAttempt = require('../functions').isXSSAttempt
@@ -25,88 +25,88 @@ class SpellRepository {
if (name) { query.where('name', 'like', `%${name}%`) } if (name) { query.where('name', 'like', `%${name}%`) }
if (description) { query.where('description', 'like', `%${description}%`) } if (description) { query.where('description', 'like', `%${description}%`) }
if (level) { query.where({ 'level' : level }) } if (level) { query.where({ 'level': level }) }
if (charge) { query.where({ 'charge' : charge }) } if (charge) { query.where({ 'charge': charge }) }
if (cost) { query.where({ 'cost' : cost }) } if (cost) { query.where({ 'cost': cost }) }
if (ritual) { query.where({ 'is_ritual' : ritual }) } if (ritual) { query.where({ 'is_ritual': ritual }) }
query.fetchAll({ withRelated: [ 'schools.meta_schools', 'variables', 'ingredients', 'author' ] }) query.fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Il n'existe aucun sortilège disponible.", "message": "Il n'existe aucun sortilège disponible.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getAllPublic(name, description, level, charge, cost, ritual) { getAllPublic(name, description, level, charge, cost, ritual) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let query = new model().where({ 'public' : 1 }) let query = new model().where({ 'public': 1 })
if (name) { query.where('name', 'like', `%${name}%`) } if (name) { query.where('name', 'like', `%${name}%`) }
if (description) { query.where('description', 'like', `%${description}%`) } if (description) { query.where('description', 'like', `%${description}%`) }
if (level) { query.where({ 'level' : level }) } if (level) { query.where({ 'level': level }) }
if (charge) { query.where({ 'charge' : charge }) } if (charge) { query.where({ 'charge': charge }) }
if (cost) { query.where({ 'cost' : cost }) } if (cost) { query.where({ 'cost': cost }) }
if (ritual) { query.where({ 'is_ritual' : ritual }) } if (ritual) { query.where({ 'is_ritual': ritual }) }
query.fetchAll({ withRelated: [ 'schools.meta_schools', 'variables', 'ingredients', 'author' ] }) query.fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })); resolve(v.toJSON({ omitPivot: true }));
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Il n'existe aucun sortilège disponible.", "message": "Il n'existe aucun sortilège disponible.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getPage(page) { getPage(page) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'public' : 1 }) .where({ 'public': 1 })
.fetchPage({ .fetchPage({
pageSize: 20, pageSize: 20,
page: page, page: page,
withRelated: [ 'schools.meta_schools', 'variables', 'ingredients', 'author' ], withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'],
}) })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "La page de sortilèges n'a pas pu être chargée", "message": "La page de sortilèges n'a pas pu être chargée",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getOne(id) { getOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: [ 'schools.meta_schools', 'variables', 'ingredients', 'author' ]}) .fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Le sortilège en question n'a pas été trouvé.", "message": "Le sortilège en question n'a pas été trouvé.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
@@ -118,10 +118,10 @@ class SpellRepository {
"message": "Le corps de la requête ne peut pas être vide.", "message": "Le corps de la requête ne peut pas être vide.",
"code": 403, "code": 403,
}); });
} else if (!v.validate(s, SpellValidation).valid) { } else if (!validator.validate(s, SpellValidation).valid) {
reject({ reject({
"message": `Le modèle de sortilège n'est pas respecté : ${v.validate(s, SpellValidation).errors}`, "message": `Le modèle de sortilège n'est pas respecté : ${validator.validate(s, SpellValidation).errors}`,
"code": 403, "code": 403,
}); });
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) {
reject({ reject({
@@ -134,54 +134,54 @@ class SpellRepository {
'name': s.name, 'name': s.name,
'description': s.description, 'description': s.description,
'level': s.level, 'level': s.level,
'charge' : s.charge, 'charge': s.charge,
'cost' : s.cost, 'cost': s.cost,
'is_ritual' : s.is_ritual 'is_ritual': s.is_ritual
}).save(null, { }).save(null, {
transacting: t transacting: t
}) })
.tap(spell => { .tap(spell => {
return spell return spell
.schools() .schools()
.attach(s.schools, { .attach(s.schools, {
transacting: t transacting: t
}); });
})
.tap(spell => {
return spell
.variables()
.attach(s.variables, {
transacting: t
});
})
.tap(spell => {
return spell
.ingredients()
.attach(s.ingredients, {
transacting: t
});
})
.catch(err => {
console.log(err);
reject({
"message": "Un attributs du sortilège a provoqué une erreur d'insertion.",
"code": 500,
});
})
})
.then(v => {
return v.load(['schools.meta_schools', 'variables', 'ingredients', 'author'])
}) })
.tap(spell => { .then(v => {
return spell resolve(this.getOne(v.id))
.variables()
.attach(s.variables, {
transacting: t
});
})
.tap(spell => {
return spell
.ingredients()
.attach(s.ingredients, {
transacting: t
});
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Un attributs du sortilège a provoqué une erreur d'insertion.", "message": "Le sortilège n'a pas pu être ajouté.",
"code": 500, "code": 500,
}); });
}) })
})
.then(v => {
return v.load([ 'schools.meta_schools', 'variables', 'ingredients', 'author' ])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err);
reject({
"message": "Le sortilège n'a pas pu être ajouté.",
"code": 500,
});
})
} }
}) })
} }
@@ -194,9 +194,9 @@ class SpellRepository {
"message": "Le corps de la requête ne peut pas être vide.", "message": "Le corps de la requête ne peut pas être vide.",
"code": 403, "code": 403,
}); });
} else if (!v.validate(s, SpellValidation).valid) { } else if (!validator.validate(s, SpellValidation).valid) {
reject({ reject({
"message": `Le modèle de sortilège n'est pas respecté : ${v.validate(s, SpellValidation).errors}`, "message": `Le modèle de sortilège n'est pas respecté : ${validator.validate(s, SpellValidation).errors}`,
"code": 403, "code": 403,
}); });
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) {
@@ -205,82 +205,110 @@ class SpellRepository {
"code": 403, "code": 403,
}); });
} else { } else {
new model({id: id}) new model({ id: id })
.fetch({require: true, withRelated: [ 'schools.meta_schools', 'variables', 'ingredients', 'author' ]}) .fetch({ require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] })
.then(v => { .then(v => {
bookshelf.transaction(t => { bookshelf.transaction(t => {
return v.save({ return v.save({
'name': s.name, 'name': s.name,
'description': s.description, 'description': s.description,
'level': s.level, 'level': s.level,
'charge' : s.charge, 'charge': s.charge,
'cost' : s.cost, 'cost': s.cost,
'is_ritual' : s.is_ritual 'is_ritual': s.is_ritual
}, { }, {
method: 'update', method: 'update',
transacting: t
})
// Detaches AND attaches pivot tables, dw about it
.tap(spell => {
if (s.schools) {
let schools = spell.related('school');
return spell.schools().detach(schools, { transacting: t});
}
})
.tap(spell => {
if (s.variables) {
let variables = spell.related('variable');
return spell.variables().detach(variables, { transacting: t});
}
})
.tap(spell => {
if (s.ingredients) {
let ingredients = spell.related('ingredient');
return spell.ingredients().detach(ingredients, { transacting: t});
}
})
.tap(spell => {
return spell
.schools()
.attach(s.schools, {
transacting: t transacting: t
});
})
.tap(spell => {
return spell
.variables()
.attach(s.variables, {
transacting: t
});
})
.tap(spell => {
return spell
.ingredients()
.attach(s.ingredients, {
transacting: t
});
})
.catch(err => {
console.log(err);
reject({
"message": "Un attributs du sortilège a provoqué une erreur d'insertion.",
"code": 500,
}) })
// Detaches AND attaches pivot tables, dw about it
.tap(spell => {
if (s.schools) {
let schools = spell.related('school');
return spell.schools().detach(schools, { transacting: t });
}
})
.tap(spell => {
if (s.variables) {
let variables = spell.related('variable');
return spell.variables().detach(variables, { transacting: t });
}
})
.tap(spell => {
if (s.ingredients) {
let ingredients = spell.related('ingredient');
return spell.ingredients().detach(ingredients, { transacting: t });
}
})
.tap(spell => {
return spell
.schools()
.attach(s.schools, {
transacting: t
});
})
.tap(spell => {
return spell
.variables()
.attach(s.variables, {
transacting: t
});
})
.tap(spell => {
return spell
.ingredients()
.attach(s.ingredients, {
transacting: t
});
})
.catch(err => {
console.log(err);
reject({
"message": "Un attributs du sortilège a provoqué une erreur d'insertion.",
"code": 500,
})
})
}) })
}) .then(v => {
.then(v => { return v.load(['schools.meta_schools', 'variables', 'ingredients', 'author']);
return v.load([ 'schools.meta_schools', 'variables', 'ingredients', 'author' ]); })
}) .then(v => {
.then(v => { resolve(this.getOne(v.id));
resolve(this.getOne(v.id)); })
.catch(err => {
console.log(err);
reject({
"message": "Une erreur de chargement est survenue.",
"code": 500,
});
})
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
reject({ reject({
"message": "Une erreur de chargement est survenue.", "message": "Le sortilège en question n'a pas été trouvé.",
"code": 500, "code": 404,
}); });
}) })
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id': id })
.fetch({ require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] })
.then(v => {
v.schools().detach();
v.variables().detach();
v.ingredients().detach();
v.destroy();
})
.then(() => {
resolve({
"message": `Le sortilège #${id} a été supprimé avec succès.`,
"code": 200,
});
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
@@ -289,34 +317,6 @@ class SpellRepository {
"code": 404, "code": 404,
}); });
}) })
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id' : id })
.fetch({require: true, withRelated: [ 'schools.meta_schools', 'variables', 'ingredients', 'author' ]})
.then(v => {
v.schools().detach();
v.variables().detach();
v.ingredients().detach();
v.destroy();
})
.then(() => {
resolve({
"message": `Le sortilège #${id} a été supprimé avec succès.`,
"code": 200,
});
})
.catch(err => {
console.log(err);
reject({
"message": "Le sortilège en question n'a pas été trouvé.",
"code": 404,
});
})
}) })
} }
} }

View File

@@ -1,109 +1,176 @@
'use strict' 'use strict'
// Bookshelf // Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf const bookshelf = require('../database/bookshelf').bookshelf;
const model = require('../models/user-model') const model = require('../models/user-model');
const token_model = require('../models/api-token-model');
// Hashing and passwords // Hashing and passwords
const bcrypt = require('bcrypt') const bcrypt = require('bcrypt');
const { v4: uuidv4 } = require('uuid') const { v4: uuidv4 } = require('uuid');
// Mailing methods // Mailing methods
const mails = require('../smtp/mails') const mails = require('../smtp/mails');
// Model validation // Model validation
const Validator = require('jsonschema').Validator const Validator = require('jsonschema').Validator;
const v = new Validator() const validator = new Validator();
const UserValidation = require("../validations/UserValidation") const UserValidation = require("../validations/UserValidation");
v.addSchema(UserValidation, "/UserValidation") validator.addSchema(UserValidation, "/UserValidation");
// Validations // Validations
const isXSSAttempt = require('../functions').isXSSAttempt const isXSSAttempt = require('../functions').isXSSAttempt;
const isEmptyObject = require('../functions').isEmptyObject const isEmptyObject = require('../functions').isEmptyObject;
class UserRepository { class UserRepository {
constructor() { constructor() {
} }
/**
* Fetches all users in the dabatase.
*
* @returns { Promise }
* Fulfilled data: Array of user objects.
*/
getAll() { getAll() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.fetchAll({ withRelated: [ 'role.permissions' ] }) .fetchAll({ withRelated: ['role.permissions'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })); resolve(v.toJSON({ omitPivot: true }));
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "Erreur de base de données, les utilisateurs n'ont pas pu être récupérés.", "message": "Erreur de base de données, les utilisateurs n'ont pas pu être récupérés.",
"code": 500, "code": 500,
}); });
}) })
}) })
} }
/**
* Fetches a user object associated with the uuid.
*
* @param { string } uuid
* @param { boolean } full
* Whether the password should also be fetched. (should never be true unless you want to log the user)
*
* @returns { Promise }
* Fulfilled data: Queried user object.
*/
getOneByUUID(uuid, full) { getOneByUUID(uuid, full) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model()
.where({ 'uuid' : uuid }) if (!(uuid)) {
.fetch({ withRelated: [ 'role.permissions' ] })
.then(v => {
resolve(v.toJSON({ omitPivot: true, visibility: !full }));
})
.catch(() => {
reject({ reject({
"message": "L'utilisateur avec cet UUID n'a pas été trouvé.", "message": "La requête doit renseigner un uuid.",
"code": 404, "code": 400,
}); })
}) }
new model()
.where({ 'uuid': uuid })
.fetch({ withRelated: ['role.permissions'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true, visibility: !full }));
})
.catch(() => {
reject({
"message": "L'utilisateur avec cet UUID n'a pas été trouvé.",
"code": 404,
});
})
}) })
} }
/**
* Fetches a user object associated with the mail address.
*
* @param { string } mail
* @param { boolean } full
* Whether the password should also be fetched. (should never be true unless you want to log the user)
*
* @returns { Promise }
* Fulfilled data: Queried user object.
*/
getOneByEmail(mail, full) { getOneByEmail(mail, full) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model()
.where({ 'mail': mail }) if (!(mail)) {
.fetch({ withRelated: [ 'role' ] })
.then(v => {
resolve(v.toJSON({ omitPivot: true, visibility: !full }));
})
.catch(() => {
reject({ reject({
"message": "L'utilisateur avec cet email n'a pas été trouvé.", "message": "La requête doit renseigner un email.",
"code": 404, "code": 400,
})
}
new model()
.where({ 'mail': mail })
.fetch({ withRelated: ['role'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true, visibility: !full }));
})
.catch(() => {
reject({
"message": "L'utilisateur avec cet email n'a pas été trouvé.",
"code": 404,
});
}); });
});
}) })
} }
/**
* Fetches all spells linked to a user's uuid
*
* @param { string } uuid
*
* @returns
*/
getSpellsFromOne(uuid) { getSpellsFromOne(uuid) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model()
.where({ 'uuid': uuid }) if (!(uuid)) {
.fetch({ withRelated: [ 'role', 'spells.schools.meta_schools', 'spells.variables', 'spells.ingredients' ] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }));
})
.catch(() => {
reject({ reject({
"message": "Les sorts liés à cet utilisateur n'ont pas été trouvés.", "message": "La requête doit renseigner un uuid.",
"code": 404, "code": 400,
})
}
new model()
.where({ 'uuid': uuid })
.fetch({ withRelated: ['role', 'spells.schools.meta_schools', 'spells.variables', 'spells.ingredients'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }));
})
.catch(() => {
reject({
"message": "Les sorts liés à cet utilisateur n'ont pas été trouvés.",
"code": 404,
});
}); });
});
}) })
} }
/**
* Registers a user based on the model at ./models/user-model.js.
*
* @param { object} u
* User object
*
* @returns { Promise }
* Fulfilled data: Queried user object.
*/
addOne(u) { addOne(u) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't // Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(u)) { if (isEmptyObject(u)) {
reject({ reject({
"message": "Le corps de requête ne peut être vide.", "message": "Le corps de requête ne peut être vide.",
"code": 403, "code": 403,
}) })
} else if (!v.validate(u, UserValidation).valid) { } else if (!validator.validate(u, UserValidation).valid) {
reject({ reject({
"message": "Structure de la requête invalide - " + v.validate(u, UserValidation).errors, "message": "Structure de la requête invalide - " + validator.validate(u, UserValidation).errors,
"code": 403, "code": 403,
}) })
} else if (isXSSAttempt(u.name) || isXSSAttempt(u.password) || isXSSAttempt(u.mail)) { } else if (isXSSAttempt(u.name) || isXSSAttempt(u.password) || isXSSAttempt(u.mail)) {
@@ -112,144 +179,329 @@ class UserRepository {
"code": 403, "code": 403,
}) })
} else { } else {
let hash = await bcrypt.hash(u.password, 10) let hash = await bcrypt.hash(u.password, 10);
let uuid = uuidv4() let uuid = uuidv4();
let verification_token = uuidv4(); let verification_token = uuidv4();
this.checkIfEmailAvailable(u.mail) this.checkIfEmailAvailable(u.mail)
.then(() => {
bookshelf.transaction(t => {
return new model({
'uuid': uuid,
'name': u.name,
'mail': u.mail,
'password': hash,
'verification_token': verification_token,
})
.save(null, {
transacting: t
})
.catch(err => {
console.log(err);
reject({
"message": "Un attribut de l'utilisateur a provoqué une erreur d'insertion.",
"code": 500,
});
})
})
.then(() => { .then(() => {
return this.getOneByUUID(uuid, false) bookshelf.transaction(t => {
}) return new model({
.then(newUser => { 'uuid': uuid,
'name': u.name,
// Send a mail to the new user's email with a link to verification url 'mail': u.mail,
mails.sendRegistrationMail({ 'password': hash,
user: { 'verification_token': verification_token,
name: newUser.name, })
mail: newUser.mail, .save(null, {
token: verification_token, transacting: t
} })
.catch(err => {
console.log(err);
reject({
"message": "Un attribut de l'utilisateur a provoqué une erreur d'insertion.",
"code": 500,
});
})
}) })
.then(() => {
return this.getOneByUUID(uuid, false)
})
.then(newUser => {
// Then resolves the api call // Send a mail to the new user's email with a link to verification url
resolve({ mails.sendRegistrationMail({
"message": `Compte utilisateur #${newUser.id} créé avec succès.`, user: {
"code": 201, name: newUser.name,
"user": newUser, mail: newUser.mail,
}) token: verification_token,
}
});
// Then resolves the api call
resolve({
"message": `Compte utilisateur #${newUser.id} créé avec succès.`,
"code": 201,
"user": newUser,
});
})
.catch(err => {
console.log(err);
resolve({
"message": "Une erreur s'est produite en créant votre compte. Veuillez réessayer ultérieurement ou contactez l'administrateur.",
"code": 500,
})
})
}) })
.catch(err => { .catch(err => {
resolve({ reject(err)
"message": "Une erreur s'est produite en créant votre compte. Veuillez réessayer ultérieurement ou contactez l'administrateur.",
"code": 500,
})
}) })
})
.catch(err => {
reject(err)
})
} }
}) })
} }
/**
* Verifies an account based on a private UUID token
*
* @param { string } token
* A UUID v4 identification token provided at registration and on special demands
*
* @returns Redirects to login page
*/
verifyUser(token) { verifyUser(token) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'verification_token' : token }) .where({ 'verification_token': token })
.fetch() .fetch()
.then(v => { .then(v => {
bookshelf.transaction(t => { bookshelf.transaction(t => {
return v.save({ return v.save({
'verification_token': null, 'verification_token': null,
'verified': 1, 'verified': 1,
}, { }, {
method: 'update', method: 'update',
transacting: t transacting: t
})
})
.then(() => {
resolve({
"message": "Insérez ici une future redirection vers le client.",
"code": 202,
})
})
})
.catch(() => {
reject({
"message": "Le lien de vérification ne semble pas correct.",
"code": 404,
}) })
}) })
.then(v => {
resolve({
"message": "Insérez ici une future redirection vers le client.",
"code": 202,
})
})
})
.catch(() => {
reject({
"message": "Le lien de vérification ne semble pas correct.",
"code": 404,
})
})
}); });
} }
// Log user with an email address and a password /**
* Logs a user by comparing the dual mail/password inputs
*
* @param { string } mail
* @param { string } password
*
* @return { Promise }
* Fulfilled data: Queried user object.
*/
logUser(mail, password) { logUser(mail, password) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.getOneByEmail(mail, true) this.getOneByEmail(mail, true)
.then(async fetchedUser => { .then(async fetchedUser => {
let match = await bcrypt.compare(password, fetchedUser.password) let match = await bcrypt.compare(password, fetchedUser.password);
delete fetchedUser.id
delete fetchedUser.password
if (match) { // Makes sure no hash gets out
if (fetchedUser.banned) { delete fetchedUser.password;
reject({
"message": `L'utilisateur #${fetchedUser.name} a été banni, la connexion est impossible.`, // If you found a user...
"code": 403, if (match) {
}) // If they're banned...
} else if (!fetchedUser.verified) { if (fetchedUser.banned) {
reject({ reject({
"message": `L'utilisateur #${fetchedUser.name} n'as pas été vérifié, le compte doit être activé avant la connexion.`, "message": `L'utilisateur #${fetchedUser.name} a été banni, la connexion est impossible.`,
"code": 401, "code": 403,
}) });
// If they're not verified...
} else if (!fetchedUser.verified) {
reject({
"message": `L'utilisateur #${fetchedUser.name} n'as pas été vérifié, le compte doit être activé avant la connexion.`,
"code": 401,
});
} else {
resolve({
"message": `L'utilisateur #${fetchedUser.name} s'est connecté.`,
"code": 200,
"user": fetchedUser,
});
}
} else { } else {
resolve({ reject({
"message": `L'utilisateur #${fetchedUser.name} s'est connecté.`, "message": "Les informations de connexions sont erronées.",
"code": 200, "code": 400,
"user": fetchedUser, });
})
} }
} else { })
reject({ .catch(err => {
"message": "Les informations de connexions sont erronées.", reject(err);
"code": 400, })
})
}
})
.catch(err => {
reject(err)
})
}) })
} }
// Check if one user already has that email /**
* Generate a token for the user to use the API
* Requires mail and password for verifying the user
*
* @param { string } mail
* @param { string } password
*
* @returns { Promise }
* Fulfilled data: A unique UUID token string.
*/
genAPIToken(mail, password) {
return new Promise((resolve, reject) => {
this.logUser(mail, password)
.then(v => {
let user = v.user;
let new_token = uuidv4();
bookshelf.transaction(t => {
return new token_model({
'value': new_token,
'user_uuid': user.uuid,
})
.save(null, {
transacting: t
})
.catch(err => {
// If the account already has an API key linked...
if (err.errno == 1062) {
this.fetchAPIKey(user.uuid)
.then(old_api_key => {
reject({
"message": "Votre compte a déjà généré une clé d'API.",
"code": 409,
"API_key": old_api_key.value,
});
});
// Default errors
} else {
throw err
}
});
})
.then(api_key => {
resolve({
"message": "La clé d'API a été généré.",
"code": 201,
"API_key": api_key,
})
})
.catch(err => {
console.log(err);
reject({
"message": "La génération de jeton d'API n'a pas pu être conclue.",
"code": 500,
});
})
})
.catch(err => {
reject(err);
})
})
}
checkAPITokenPerms(token, permissions) {
return new Promise(async (resolve, reject) => {
// If the request doesn't have a token, reject it.
if (!token) {
reject({
"message": "Vous devez utiliser un jeton d'API dans l'en-tête de votre requête.",
"code": 401,
})
}
// Fetches user from token
new token_model()
.where({ 'value': token })
.fetch({ withRelated: 'user.role.permissions' })
// Catches not found errors
.catch(err => {
if (err instanceof token_model.NotFoundError) {
reject({
"message": "Ce jeton n'est affilié à aucun compte.",
"code": 404,
});
} else {
reject({
"message": "Le jeton ou l'utilisateur n'a pas pu être récupéré.",
"code": 500,
});
}
})
.then(fullToken => {
token = fullToken.toJSON({ omitPivot: true });
// If the token is associated with a banned user...
if (token.user.banned) {
reject({
"message": "Le jeton est lié à un utilisateur banni, il n'est donc plus utilisable.",
"code": 401,
});
}
// If the token is associated with a non-verified user...
if (!token.user.verified) {
reject({
"message": "Le jeton est lié à un utilisateur non-vérifié, il n'est donc pas utilisable.",
"code": 401,
});
}
let user_permissions = token.user.role.permissions;
// Convert user_perm to array
user_permissions.forEach((user_perm, i) => {
user_permissions[i] = user_perm.slug;
});
// Loops to check if the person has all the permissions required...
permissions.forEach(perm => {
// If not, reject...
if (!user_permissions.includes(perm)) {
reject({
"message": "Permissions insuffisantes.",
"code": 401
})
}
});
// If everything went well, resolve the promise.
resolve({
"message": "Credentials accepted.",
"code": 200
});
})
// Unhandled errors
.catch(err => {
reject({
"message": "Une erreur inconnue est survenue.",
"code": 500,
});
});
})
}
/**
* Check if the email that was input is available for account creation.
*
* @param {string} mail
*
* @returns { Promise }
* Fulfilled: HTTP 200 if email is available.
* Rejected: HTTP 400-409 if email is already used.
*/
checkIfEmailAvailable(mail) { checkIfEmailAvailable(mail) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!this.validateEmail(mail)) { if (!(mail)) {
reject({
"message": "La requête doit renseigner un email.",
"code": 400,
})
}
if (!this.validateMail(mail)) {
reject({ reject({
"message": "La requête n'est pas un email valide.", "message": "La requête n'est pas un email valide.",
"code": 400, "code": 400,
@@ -257,24 +509,53 @@ class UserRepository {
} }
this.getOneByEmail(mail, false) this.getOneByEmail(mail, false)
.then(() => { .then(() => {
reject({ reject({
"message": "Cet email est déjà lié à un compte.", "message": "Cet email est déjà lié à un compte.",
"code": 409, "code": 409,
})
})
.catch(() => {
resolve({
"message": "Cet email est disponible.",
"code": 200,
})
}) })
})
.catch(() => {
resolve({
"message": "Cet email est disponible.",
"code": 200,
})
})
}) })
} }
validateEmail(email) { /**
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; * Fetches the associated api_token from a user uuid.
return re.test(String(email).toLowerCase()); *
* @param { string } uuid
*
* @returns { Promise }
* Fulfilled data: Queried API token object
*/
fetchAPIKey(uuid) {
return new Promise((resolve, reject) => {
new token_model()
.where({ 'user_uuid': uuid })
.fetch()
.then(v => {
resolve(v.toJSON({ omitPivot: true }));
})
.catch(err => {
console.log(err);
})
})
}
/**
* Whether a mail is correctly formed and ripe for receiving, ie: xxx@yyy.zzz.
*
* @param { string } mail
*
* @returns { boolean }
*/
validateMail(mail) {
const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(String(mail).toLowerCase());
} }
} }

View File

@@ -5,9 +5,9 @@ const model = require('../models/variable-model')
// Model validation // Model validation
const Validator = require('jsonschema').Validator const Validator = require('jsonschema').Validator
const v = new Validator() const validator = new Validator()
const VariableValidation = require("../validations/VariableValidation") const VariableValidation = require("../validations/VariableValidation")
v.addSchema(VariableValidation, "/VariableValidation") validator.addSchema(VariableValidation, "/VariableValidation")
// Validations // Validations
const isXSSAttempt = require('../functions').isXSSAttempt const isXSSAttempt = require('../functions').isXSSAttempt
@@ -17,58 +17,58 @@ class VariableRepository {
constructor() { constructor() {
} }
getAll() { getAll() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.fetchAll({ withRelated: ['spells'] }) .fetchAll({ withRelated: ['spells'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "Il n'existe aucune variable disponible.", "message": "Il n'existe aucune variable disponible.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getOne(id) { getOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['spells']}) .fetch({ withRelated: ['spells'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "La variable en question n'a pas pu être trouvée.", "message": "La variable en question n'a pas pu être trouvée.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
getSpellsFromOne(id) { getSpellsFromOne(id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new model() new model()
.where({ 'id' : id }) .where({ 'id': id })
.fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools']}) .fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools'] })
.then(v => { .then(v => {
resolve(v.toJSON({ omitPivot: true })) resolve(v.toJSON({ omitPivot: true }))
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
reject({ reject({
"message": "Les sortilèges liés à cette variable n'ont pas pu être récupérés.", "message": "Les sortilèges liés à cette variable n'ont pas pu être récupérés.",
"code": 404, "code": 404,
}); });
}) })
}) })
} }
@@ -80,9 +80,9 @@ class VariableRepository {
"message": "Le corps de la requête ne peut pas être vide.", "message": "Le corps de la requête ne peut pas être vide.",
"code": 403, "code": 403,
}); });
} else if (!v.validate(vr, VariableValidation).valid) { } else if (!validator.validate(vr, VariableValidation).valid) {
reject({ reject({
"message": `Le modèle de variable n'est pas respecté : ${v.validate(s, VariableValidation).errors}`, "message": `Le modèle de variable n'est pas respecté : ${validator.validate(s, VariableValidation).errors}`,
"code": 403, "code": 403,
}); });
} else if (isXSSAttempt(vr.description)) { } else if (isXSSAttempt(vr.description)) {
@@ -97,61 +97,10 @@ class VariableRepository {
}).save(null, { }).save(null, {
transacting: t transacting: t
}) })
.catch(err => {
throw err
})
})
.then(v => {
return v.load(['spells'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
});
})
}
})
}
updateOne(id, vr) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(vr)) {
reject({
"message": "Le corps de la requête ne peut pas être vide.",
"code": 403,
});
} else if (!v.validate(vr, VariableValidation).valid) {
reject({
"message": `Le modèle de variable n'est pas respecté : ${v.validate(s, VariableValidation).errors}`,
"code": 403,
});
} else if (isXSSAttempt(vr.description)) {
reject({
"message": "Tentative d'injection XSS détectée, requête refusée.",
"code": 403,
});
} else {
new model({id: id})
.fetch({require: true, withRelated: ['spells']})
.then(v => {
bookshelf.transaction(t => {
return v.save({
'description': vr.description,
}, {
method: 'update',
transacting: t
})
.catch(err => { .catch(err => {
console.log(err)
throw err throw err
}) })
}) })
.then(v => { .then(v => {
return v.load(['spells']) return v.load(['spells'])
}) })
@@ -165,6 +114,82 @@ class VariableRepository {
"code": 500, "code": 500,
}); });
}) })
}
})
}
updateOne(id, vr) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(vr)) {
reject({
"message": "Le corps de la requête ne peut pas être vide.",
"code": 403,
});
} else if (!validator.validate(vr, VariableValidation).valid) {
reject({
"message": `Le modèle de variable n'est pas respecté : ${validator.validate(s, VariableValidation).errors}`,
"code": 403,
});
} else if (isXSSAttempt(vr.description)) {
reject({
"message": "Tentative d'injection XSS détectée, requête refusée.",
"code": 403,
});
} else {
new model({ id: id })
.fetch({ require: true, withRelated: ['spells'] })
.then(v => {
bookshelf.transaction(t => {
return v.save({
'description': vr.description,
}, {
method: 'update',
transacting: t
})
.catch(err => {
console.log(err)
throw err
})
})
.then(v => {
return v.load(['spells'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject({
"message": "Une erreur d'insertion s'est produite.",
"code": 500,
});
})
})
.catch(err => {
console.log(err)
reject({
"message": "La variable en question n'a pas été trouvée.",
"code": 404,
});
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id': id })
.fetch({ require: true, withRelated: ['spells'] })
.then(v => {
v.spells().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'Variable with ID ' + id + ' successfully deleted !'
})
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
@@ -173,31 +198,6 @@ class VariableRepository {
"code": 404, "code": 404,
}); });
}) })
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
new model()
.where({ 'id' : id })
.fetch({require: true, withRelated: ['spells']})
.then(v => {
v.spells().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'Variable with ID ' + id + ' successfully deleted !'
})
})
.catch(err => {
console.log(err)
reject({
"message": "La variable en question n'a pas été trouvée.",
"code": 404,
});
})
}) })
} }
} }

29
api/routes/auth.js Normal file
View File

@@ -0,0 +1,29 @@
'use strict'
// Router
const express = require('express');
let router = express.Router();
// Repository
const UserRepository = require('../repositories/user-repository');
const Users = new UserRepository();
// ROUTES
// GEN API TOKEN
const generateAPIToken = (mail, password) => {
return Users.genAPIToken(mail, password)
.catch(err => {
throw err
})
}
router.get('/genToken', async (req, res) => {
generateAPIToken(req.body.mail, req.body.password)
.then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v))
})
.catch(err => {
res.status(err.code).send(JSON.stringify(err))
})
})
module.exports = router;

View File

@@ -1,11 +1,14 @@
const spells = require('./spells') const spells = require('./spells');
const schools = require('./schools') const schools = require('./schools');
const meta_schools = require('./meta_schools') const meta_schools = require('./meta_schools');
const variables = require('./variables') const variables = require('./variables');
const ingredients = require('./ingredients') const ingredients = require('./ingredients');
const users = require('./users') const users = require('./users');
const auth = require('./auth');
module.exports = { module.exports = {
auth,
spells, spells,
schools, schools,
meta_schools, meta_schools,

View File

@@ -1,166 +1,183 @@
'use strict' 'use strict'
// Router // Router
const express = require('express') const express = require('express');
let router = express.Router() let router = express.Router();
// Connection // AuthGuard
const connection = require('../database/bookshelf') const authGuard = require('./middleware/authGuard');
const functions = require('../functions')
// Repository // Repository
const IngredientRepository = require('../repositories/ingredient-repository'); const IngredientRepository = require('../repositories/ingredient-repository');
const Ingredients = new IngredientRepository(); const Ingredients = new IngredientRepository();
// Functions
const functions = require('../functions');
// ROUTES // ROUTES
// GET ALL ------------------ // GET ALL ------------------
const getIngredients = () => { const getIngredients = () => {
return Ingredients.getAll() return Ingredients.getAll()
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/', async (req, res) => { router.get(
getIngredients() '/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getIngredients()
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET ONE ------------------ // GET ONE ------------------
const getIngredient = (id) => { const getIngredient = (id) => {
return Ingredients.getOne(id) return Ingredients.getOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/', async (req, res) => { router.get(
getIngredient(req.params.id) '/:id/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getIngredient(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET SPELLS FROM ONE ------------------ // GET SPELLS FROM ONE ------------------
const getSpellsFromOne = (id) => { const getSpellsFromOne = (id) => {
return Ingredients.getSpellsFromOne(id) return Ingredients.getSpellsFromOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/spells', async (req, res) => { router.get(
getSpellsFromOne(req.params.id) '/:id/spells',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSpellsFromOne(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// CREATE ONE ------------------ // CREATE ONE ------------------
const addIngredient = (igr) => { const addIngredient = (igr) => {
return Ingredients.addOne(igr) return Ingredients.addOne(igr)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.post('/', async (req, res) => { router.post(
addIngredient(req.body) '/',
.then(v => { authGuard(['SUBMIT_INGREDIENTS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) addIngredient(req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// UPDATE ONE ------------------ // UPDATE ONE ------------------
const updateIngredient = (id, igr) => { const updateIngredient = (id, igr) => {
return Ingredients.updateOne(id, igr) return Ingredients.updateOne(id, igr)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.put('/:id/', async (req, res) => { router.put(
updateIngredient(req.params.id, req.body) '/:id/',
.then(v => { authGuard(['SUBMIT_INGREDIENTS', 'MODIFY_INGREDIENTS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) updateIngredient(req.params.id, req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// DELETE ONE ------------------ // DELETE ONE ------------------
const deleteIngredient = (id) => { const deleteIngredient = (id) => {
return Ingredients.deleteOne(id) return Ingredients.deleteOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.delete('/:id/', async (req, res) => { router.delete(
deleteIngredient(req.params.id) '/:id/',
.then(v => { authGuard(['SUBMIT_INGREDIENTS', 'MODIFY_INGREDIENTS', 'DELETE_INGREDIENTS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) deleteIngredient(req.params.id)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// Param validations // Param validations
router.param('id', functions.paramIntCheck) router.param('id', functions.paramIntCheck)

View File

@@ -1,12 +1,11 @@
'use strict' 'use strict'
// Router // Router
const express = require('express') const express = require('express');
let router = express.Router() let router = express.Router();
// Connection // Functions
const connection = require('../database/bookshelf') const functions = require('../functions');
const functions = require('../functions')
// Repository // Repository
const MetaSchoolRepository = require('../repositories/meta-school-repository'); const MetaSchoolRepository = require('../repositories/meta-school-repository');
@@ -16,50 +15,50 @@ const MetaSchools = new MetaSchoolRepository();
// GET ALL ------------------ // GET ALL ------------------
const getMetaSchools = () => { const getMetaSchools = () => {
return MetaSchools.getAll() return MetaSchools.getAll()
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
getMetaSchools() getMetaSchools()
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// GET ONE ------------------ // GET ONE ------------------
const getMetaSchool = (id) => { const getMetaSchool = (id) => {
return MetaSchools.getOne(id) return MetaSchools.getOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/', async (req, res) => { router.get('/:id/', async (req, res) => {
getMetaSchool(req.params.id) getMetaSchool(req.params.id)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// Param validations // Param validations

View File

@@ -0,0 +1,23 @@
// Repository
const UserRepository = require('../../repositories/user-repository');
const Users = new UserRepository();
// AUTHGUARD
const authGuard = (permissions) => {
return async (req, res, next) => {
// Get token from headers
let api_token = req.headers['auracle_key'];
// Uses repo to validate the associated perms with the token
Users.checkAPITokenPerms(api_token, permissions)
.then(v => {
next();
})
.catch(err => {
res.status(err.code).send(JSON.stringify(err))
});
}
}
module.exports = authGuard;

View File

@@ -1,165 +1,182 @@
'use strict' 'use strict'
// Router // Router
const express = require('express') const express = require('express');
let router = express.Router() let router = express.Router();
// Connection // AuthGuard
const connection = require('../database/bookshelf') const authGuard = require('./middleware/authGuard');
const functions = require('../functions')
// Repository // Repository
const SchoolRepository = require('../repositories/school-repository'); const SchoolRepository = require('../repositories/school-repository');
const Schools = new SchoolRepository(); const Schools = new SchoolRepository();
// Functions
const functions = require('../functions');
// ROUTES // ROUTES
// GET ALL ------------------ // GET ALL ------------------
const getSchools = () => { const getSchools = () => {
return Schools.getAll() return Schools.getAll()
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/', async (req, res) => { router.get(
getSchools() '/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSchools()
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET ONE ------------------ // GET ONE ------------------
const getSchool = (id) => { const getSchool = (id) => {
return Schools.getOne(id) return Schools.getOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/', async (req, res) => { router.get(
getSchool(req.params.id) '/:id/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSchool(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET SPELLS FROM ONE ------------------ // GET SPELLS FROM ONE ------------------
const getSpellsFromOne = (id) => { const getSpellsFromOne = (id) => {
return Schools.getSpellsFromOne(id) return Schools.getSpellsFromOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/spells', async (req, res) => { router.get(
getSpellsFromOne(req.params.id) '/:id/spells',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSpellsFromOne(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// CREATE ONE ------------------ // CREATE ONE ------------------
const addSchool = (s) => { const addSchool = (s) => {
return Schools.addOne(s) return Schools.addOne(s)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.post('/', async (req, res) => { router.post(
addSchool(req.body) '/',
.then(v => { authGuard(['SUBMIT_SCHOOL']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) addSchool(req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// UPDATE ONE ------------------ // UPDATE ONE ------------------
const updateSchool = (id, s) => { const updateSchool = (id, s) => {
return Schools.updateOne(id, s) return Schools.updateOne(id, s)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.put('/:id/', async (req, res) => { router.put(
updateSchool(req.params.id, req.body) '/:id/',
.then(v => { authGuard(['SUBMIT_SCHOOLS', 'MODIFY_SCHOOLS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) updateSchool(req.params.id, req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// DELETE ONE ------------------ // DELETE ONE ------------------
const deleteSchool = (id) => { const deleteSchool = (id) => {
return Schools.deleteOne(id) return Schools.deleteOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.delete('/:id/', async (req, res) => { router.delete(
deleteSchool(req.params.id) '/:id/',
.then(v => { authGuard(['SUBMIT_SCHOOLS', 'MODIFY_SCHOOLS', 'DELETE_SCHOOLS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) deleteSchool(req.params.id)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// Param validations // Param validations
router.param('id', functions.paramIntCheck) router.param('id', functions.paramIntCheck)

View File

@@ -1,188 +1,208 @@
'use strict' 'use strict'
// Router // Router
const express = require('express') const express = require('express');
let router = express.Router() let router = express.Router();
// Connection // AuthGuard
const connection = require('../database/bookshelf') const authGuard = require('./middleware/authGuard');
const functions = require('../functions')
// Repository // Repository
const SpellReposity = require('../repositories/spell-repository'); const SpellReposity = require('../repositories/spell-repository');
const Spells = new SpellReposity(); const Spells = new SpellReposity();
// Functions
const functions = require('../functions');
// ROUTES // ROUTES
// GET ALL PUBLIC ------------------ // GET ALL PUBLIC ------------------
const getPublicSpells = (name, description, level, charge, cost, ritual) => { const getPublicSpells = (name, description, level, charge, cost, ritual) => {
return Spells.getAllPublic(name, description, level, charge, cost, ritual) return Spells.getAllPublic(name, description, level, charge, cost, ritual)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('//:name?/:description?/:level?/:charge?/:cost?/:ritual?/', async (req, res) => { router.get(
getPublicSpells(req.query.name, req.query.description, req.query.level, req.query.charge, req.query.cost, req.query.ritual) '//:name?/:description?/:level?/:charge?/:cost?/:ritual?/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getPublicSpells(req.query.name, req.query.description, req.query.level, req.query.charge, req.query.cost, req.query.ritual)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET ALL ------------------ // GET ALL ------------------
const getSpells = (name, description, level, charge, cost, ritual) => { const getSpells = (name, description, level, charge, cost, ritual) => {
return Spells.getAll(name, description, level, charge, cost, ritual) return Spells.getAll(name, description, level, charge, cost, ritual)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/private/:name?/:description?/:level?/:charge?/:cost?/:ritual?/', async (req, res) => { router.get(
getSpells(req.query.name, req.query.description, req.query.level, req.query.charge, req.query.cost, req.query.ritual) '/private/:name?/:description?/:level?/:charge?/:cost?/:ritual?/',
.then(v => { authGuard(['SECRET_SPELLS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.end(JSON.stringify(v)) getSpells(req.query.name, req.query.description, req.query.level, req.query.charge, req.query.cost, req.query.ritual)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.end(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET SOME ------------------ // GET SOME ------------------
const getSomeSpells = (page) => { const getSomeSpells = (page) => {
return Spells.getPage(page) return Spells.getPage(page)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/page/:page', async (req, res) => { router.get(
getSomeSpells(req.params.page) '/page/:page',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSomeSpells(req.params.page)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET ONE ------------------ // GET ONE ------------------
const getSpell = (id) => { const getSpell = (id) => {
return Spells.getOne(id) return Spells.getOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/', async (req, res) => { router.get(
getSpell(req.params.id) '/:id/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSpell(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// CREATE ONE ------------------ // CREATE ONE ------------------
const addSpell = (s) => { const addSpell = (s) => {
return Spells.addOne(s) return Spells.addOne(s)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.post('/', async (req, res) => { router.post(
addSpell(req.body) '/',
.then(v => { authGuard(['SUBMIT_SPELLS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) addSpell(req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// UPDATE ONE ------------------ // UPDATE ONE ------------------
const updateSpell = (id, s) => { const updateSpell = (id, s) => {
return Spells.updateOne(id, s) return Spells.updateOne(id, s)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.put('/:id/', async (req, res) => { router.put(
updateSpell(req.params.id, req.body) '/:id/',
.then(v => { authGuard(['SUBMIT_SPELLS', 'MODIFY_SPELLS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) updateSpell(req.params.id, req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// DELETE ONE ------------------ // DELETE ONE ------------------
const deleteSpell = (id) => { const deleteSpell = (id) => {
return Spells.deleteOne(id) return Spells.deleteOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.delete('/:id/', async (req, res) => { router.delete(
deleteSpell(req.params.id) '/:id/',
.then(v => { authGuard(['SUBMIT_SPELLS', 'MODIFY_SPELLS', 'DELETE_SPELLS']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) deleteSpell(req.params.id)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// Param validations // Param validations
router.param('id', functions.paramIntCheck) router.param('id', functions.paramIntCheck)

View File

@@ -1,12 +1,11 @@
'use strict' 'use strict'
// Router // Router
const express = require('express') const express = require('express');
let router = express.Router() let router = express.Router();
// Connection // AuthGuard
const connection = require('../database/bookshelf') const authGuard = require('./middleware/authGuard');
const db = connection.db
// Repository // Repository
const UserRepository = require('../repositories/user-repository'); const UserRepository = require('../repositories/user-repository');
@@ -16,166 +15,166 @@ const Users = new UserRepository();
// GET ALL ------------------ // GET ALL ------------------
const getUsers = () => { const getUsers = () => {
return Users.getAll() return Users.getAll()
.catch(err => { .catch(err => {
throw err throw err
}) })
} }
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
getUsers() getUsers()
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// GET ONE FROM UUID ------------------ // GET ONE FROM UUID ------------------
const getUserByUUID = (uuid) => { const getUserByUUID = (uuid) => {
return Users.getOneByUUID(uuid) return Users.getOneByUUID(uuid)
.catch(err => { .catch(err => {
throw err throw err
}) })
} }
router.get('/:uuid/', async (req, res) => { router.get('/:uuid/', async (req, res) => {
getUserByUUID(req.params.uuid) getUserByUUID(req.params.uuid)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// GET SPELLS FROM ONE ------------------ // GET SPELLS FROM ONE ------------------
const getSpellsFromUser = (uuid) => { const getSpellsFromUser = (uuid) => {
return Users.getSpellsFromOne(uuid) return Users.getSpellsFromOne(uuid)
.catch(err => { .catch(err => {
throw err throw err
}) })
} }
router.get('/:uuid/spells', async (req, res) => { router.get('/:uuid/spells', async (req, res) => {
getSpellsFromUser(req.params.uuid) getSpellsFromUser(req.params.uuid)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// CHECK IF MAIL IS AVAILABLE ------------------ // CHECK IF MAIL IS AVAILABLE ------------------
const checkIfEmailAvailable = (mail) => { const checkIfEmailAvailable = (mail) => {
return Users.checkIfEmailAvailable(mail) return Users.checkIfEmailAvailable(mail)
.catch(err => { .catch(err => {
throw err throw err
}) })
} }
router.get('/available/:mail/', async (req, res) => { router.get('/available/:mail/', async (req, res) => {
checkIfEmailAvailable(req.params.mail) checkIfEmailAvailable(req.params.mail)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// CREATE ONE ------------------ // CREATE ONE ------------------
const addUser = (u) => { const addUser = (u) => {
return Users.addOne(u) return Users.addOne(u)
.catch(err => { .catch(err => {
throw err throw err
}) })
} }
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
addUser(req.body) addUser(req.body)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.send(JSON.stringify(v)) res.send(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
// VERIFY A USER // VERIFY A USER
const verifyUser = (token) => { const verifyUser = (token) => {
return Users.verifyUser(token) return Users.verifyUser(token)
.catch(err => { .catch(err => {
throw err; throw err;
}) })
} }
router.get('/verification/:token', async (req, res) => { router.get('/verification/:token', async (req, res) => {
verifyUser(req.params.token) verifyUser(req.params.token)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.send(JSON.stringify(v)) res.send(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}); });
// LOG A USER ------------------ // LOG A USER ------------------
const logUser = (mail, password) => { const logUser = (mail, password) => {
return Users.logUser(mail, password) return Users.logUser(mail, password)
.catch(err => { .catch(err => {
throw err throw err
}) })
} }
router.post('/login', async (req, res) => { router.post('/login', async (req, res) => {
logUser(req.body.mail, req.body.password) logUser(req.body.mail, req.body.password)
.then(v => { .then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v)) res.end(JSON.stringify(v))
}) })
.catch(err => { .catch(err => {
res.status(err.code).send(JSON.stringify( res.status(err.code).send(JSON.stringify(
{ {
"error": err.message, "error": err.message,
"code": err.code "code": err.code
}) })
) )
}) })
}) })
module.exports = router module.exports = router

View File

@@ -1,166 +1,183 @@
'use strict' 'use strict'
// Router // Router
const express = require('express') const express = require('express');
let router = express.Router() let router = express.Router();
// Connection // AuthGuard
const connection = require('../database/bookshelf') const authGuard = require('./middleware/authGuard');
const functions = require('../functions')
// Repository // Repository
const VariableRepository = require('../repositories/variable-repository'); const VariableRepository = require('../repositories/variable-repository');
const Variables = new VariableRepository(); const Variables = new VariableRepository();
// Functions
const functions = require('../functions');
// ROUTES // ROUTES
// GET ALL ------------------ // GET ALL ------------------
const getvariables = () => { const getvariables = () => {
return Variables.getAll() return Variables.getAll()
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/', async (req, res) => { router.get(
getvariables() '/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getvariables()
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET ONE ------------------ // GET ONE ------------------
const getVariable = (id) => { const getVariable = (id) => {
return Variables.getOne(id) return Variables.getOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/', async (req, res) => { router.get(
getVariable(req.params.id) '/:id/',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getVariable(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// GET SPELLS FROM ONE ------------------ // GET SPELLS FROM ONE ------------------
const getSpellsFromOne = (id) => { const getSpellsFromOne = (id) => {
return Variables.getSpellsFromOne(id) return Variables.getSpellsFromOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.get('/:id/spells', async (req, res) => { router.get(
getSpellsFromOne(req.params.id) '/:id/spells',
.then(v => { async (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8') getSpellsFromOne(req.params.id)
res.end(JSON.stringify(v)) .then(v => {
}) res.setHeader('Content-Type', 'application/json;charset=utf-8')
.catch(err => { res.end(JSON.stringify(v))
res.status(err.code).send(JSON.stringify( })
{ .catch(err => {
"error": err.message, res.status(err.code).send(JSON.stringify(
"code": err.code {
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// CREATE ONE ------------------ // CREATE ONE ------------------
const addVariable = (vr) => { const addVariable = (vr) => {
return Variables.addOne(vr) return Variables.addOne(vr)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.post('/', async (req, res) => { router.post(
addVariable(req.body) '/',
.then(v => { authGuard(['SUBMIT_VARIABLES']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) addVariable(req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// UPDATE ONE ------------------ // UPDATE ONE ------------------
const updateVariable = (id, vr) => { const updateVariable = (id, vr) => {
return Variables.updateOne(id, vr) return Variables.updateOne(id, vr)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.put('/:id/', async (req, res) => { router.put(
updateVariable(req.params.id, req.body) '/:id/',
.then(v => { authGuard(['SUBMIT_VARIABLES', 'MODIFY_VARIABLES']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) updateVariable(req.params.id, req.body)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// DELETE ONE ------------------ // DELETE ONE ------------------
const deleteVariable = (id) => { const deleteVariable = (id) => {
return Variables.deleteOne(id) return Variables.deleteOne(id)
.catch(err => { .catch(err => {
console.log(err) console.log(err)
throw err throw err
}) })
} }
router.delete('/:id/', async (req, res) => { router.delete(
deleteVariable(req.params.id) '/:id/',
.then(v => { authGuard(['SUBMIT_VARIABLES', 'MODIFY_VARIABLES', 'DELETE_VARIABLES']),
res.setHeader('Content-Type', 'application/json;charset=utf-8') async (req, res) => {
res.send(JSON.stringify(v)) deleteVariable(req.params.id)
}) .then(v => {
.catch(err => { res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.status(err.code).send(JSON.stringify( res.send(JSON.stringify(v))
{ })
"error": err.message, .catch(err => {
"code": err.code res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}) })
)
}) })
})
// Param validations // Param validations
router.param('id', functions.paramIntCheck) router.param('id', functions.paramIntCheck)

View File

@@ -1,12 +1,12 @@
const nodemailer = require('nodemailer'); const nodemailer = require('nodemailer');
const transport = nodemailer.createTransport({ const transport = nodemailer.createTransport({
host: "smtp.mailtrap.io", host: "smtp.mailtrap.io",
port: 2525, port: 2525,
auth: { auth: {
user: process.env.SMTP_USER, user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS, pass: process.env.SMTP_PASS,
} }
}); });
module.exports = { module.exports = {

View File

@@ -8,13 +8,13 @@ const sender = 'tymos@auracle.io';
// Fetches a HTML template file for parsing // Fetches a HTML template file for parsing
const getTemplateFile = (path) => { const getTemplateFile = (path) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.readFile(path, {encoding: 'utf-8'}, (err, html) => { fs.readFile(path, { encoding: 'utf-8' }, (err, html) => {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
resolve(html); resolve(html);
} }
}) })
}) })
}; };
@@ -28,34 +28,34 @@ const getTemplateFile = (path) => {
*/ */
const sendRegistrationMail = (data) => { const sendRegistrationMail = (data) => {
getTemplateFile(__dirname + '/templates/template-sign-up.html') getTemplateFile(__dirname + '/templates/template-sign-up.html')
.then(html => { .then(html => {
let template = handlebars.compile(html); let template = handlebars.compile(html);
let template_parsed = template(data); let template_parsed = template(data);
let message = { let message = {
from: sender, from: sender,
to: data.user.mail, to: data.user.mail,
subject: 'Inscription Auracle.io', subject: 'Inscription Auracle.io',
html: template_parsed, html: template_parsed,
}; };
smtp.transport.sendMail(message, (err, info) => { smtp.transport.sendMail(message, (err, info) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
console.log(info); console.log(info);
} }
});
})
.catch(err => {
console.log(err);
}); });
})
.catch(err => {
console.log(err);
});
} }
const sendBanEmail = (date) => { const sendBanEmail = (date) => {
return null; return null;
} }
module.exports = { module.exports = {
sendRegistrationMail, sendRegistrationMail,
} }

View File

@@ -1,100 +1,114 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<head>
<title>Template Email Auracle Inscription</title> <title>Template Email Auracle Inscription</title>
<style> <style>
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&family=Playfair+Display:wght@700;800;900&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&family=Playfair+Display:wght@700;800;900&display=swap');
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
} }
:root { :root {
--black: 52, 58, 64; --black: 52, 58, 64;
--white: #FFF; --white: #FFF;
--primary: 89, 158, 244; --primary: 89, 158, 244;
} }
body { body {
color: rgb(var(--black)); color: rgb(var(--black));
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
max-width: 100%; max-width: 100%;
} }
.display { .display {
font-family: 'Playfair Display', serif; font-family: 'Playfair Display', serif;
} }
h1 { h1 {
font-size: 32px; font-size: 32px;
} }
p:not(:last-child) { p:not(:last-child) {
margin-bottom: 15px; margin-bottom: 15px;
} }
.italics { .italics {
font-style: italic; font-style: italic;
} }
.wrapper { .wrapper {
position: relative; position: relative;
padding: 10vh 5vw; padding: 10vh 5vw;
background: url('https://i.imgur.com/IxEKKEY.png') center center fixed repeat; background: url('https://i.imgur.com/IxEKKEY.png') center center fixed repeat;
} }
.wrapper:before {
display: block;
content: '';
position: absolute;
top: 0;
height: 120px;
left: 0;
right: 0;
background-color: rgb(var(--primary));
z-index: 0;
}
.container { .wrapper:before {
position: relative; display: block;
background-color: var(--white); content: '';
z-index: 5; position: absolute;
} top: 0;
height: 120px;
left: 0;
right: 0;
background-color: rgb(var(--primary));
z-index: 0;
}
header, footer { .container {
text-align: center; position: relative;
color: var(--white); background-color: var(--white);
background-color: rgb(var(--black)); z-index: 5;
} }
header { header,
padding: 15px; footer {
} text-align: center;
color: var(--white);
background-color: rgb(var(--black));
}
main { header {
padding: 30px; padding: 15px;
} }
main {
padding: 30px;
}
</style> </style>
</head> </head>
<body> <body>
<div class="wrapper"> <div class="wrapper">
<div class="container"> <div class="container">
<header> <header>
<h1 class="display">Bienvenue sur Auracle, {{ user.name }}!</h1> <h1 class="display">Bienvenue sur Auracle, {{ user.name }}!</h1>
</header> </header>
<main> <main>
<p>Votre compte Auracle a bien été enregistré et est rattaché à cette adresse mail.</p> <p>Votre compte Auracle a bien été enregistré et est rattaché à cette adresse mail.</p>
<p>Cependant, afin de garantir la sécurité de vos données, votre compte doit être vérifié avant votre première connexion. Vous pouvez <a href="http://localhost:2814/api/users/verification/{{ user.token }}">suivre ce lien afin de confirmer votre inscription.</a></p> <p>Cependant, afin de garantir la sécurité de vos données, votre compte doit être vérifié avant votre
<p>Si vous rencontrez des problèmes, n'hésitez pas à envoyer un mail à <a href="mailto:support@auracle.io">support@auracle.io</a></p> première connexion. Vous pouvez
<p><strong>Merci de votre soutien !</strong></p> <a href="http: //localhost:2814/api/v1/users/verification/{{ user.token }}">
<p class="italics">Bien sincèrement, Izàc Tymos.</p> suivre ce lien afin de confirmer votre inscription.
</main> </a>
<footer> </p>
<div class="icon"> <p>Si vous rencontrez des problèmes, n'hésitez pas à envoyer un mail à
<a href="mailto:support@auracle.io">
support@auracle.io
</a>
</p>
<p><strong>Merci de votre soutien !</strong></p>
<p class="italics">Bien sincèrement, Izàc Tymos.</p>
</main>
<footer>
<div class="icon">
</div>
</footer>
</div> </div>
</footer>
</div> </div>
</div>
</body> </body>
</html> </html>