- Moved api files to a relevant subfolder

This commit is contained in:
Alexis
2020-08-09 15:27:22 +02:00
parent 71423a2172
commit ddcc6acc75
33 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
DROP DATABASE IF EXISTS auracle;
CREATE DATABASE auracle CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE auracle;
/* =========== PRIMARY TABLES =========== */
/* PERMISSIONS */
CREATE TABLE IF NOT EXISTS `role` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`description` VARCHAR(255) NOT NULL,
PRIMARY KEY(`id`)
);
/* USERS */
CREATE TABLE IF NOT EXISTS `user` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` VARCHAR(36) NOT NULL,
`name` VARCHAR(255) NOT NULL DEFAULT "Disciple",
`mail` VARCHAR(255) NOT NULL,
`avatar` VARCHAR(255),
`gender` VARCHAR(255),
`password` VARCHAR(255) NOT NULL,
`role_id` INT UNSIGNED NOT NULL DEFAULT 1,
`verified` BOOLEAN DEFAULT false,
`banned` BOOLEAN DEFAULT false,
PRIMARY KEY(`id`),
FOREIGN KEY(`role_id`) REFERENCES role(`id`)
);
/* SPELLS */
CREATE TABLE IF NOT EXISTS `spell` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT "Nom du sort",
`description` VARCHAR(1000) NOT NULL DEFAULT "Description du sort",
`level` INT UNSIGNED DEFAULT 0,
`charge` INT UNSIGNED DEFAULT 0,
`cost` VARCHAR(255) DEFAULT "0",
`is_ritual` BOOLEAN DEFAULT false,
`published` BOOLEAN DEFAULT true,
`public` BOOLEAN DEFAULT true,
`author_id` INT UNSIGNED NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
FOREIGN KEY(`author_id`) REFERENCES user(`id`)
);
/* META SCHOOLS */
CREATE TABLE IF NOT EXISTS `meta_school` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école mère",
`description` VARCHAR(255) DEFAULT "Description de l'école mère",
PRIMARY KEY (`id`)
);
/* SCHOOLS */
CREATE TABLE IF NOT EXISTS `school` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école",
`description` VARCHAR(255) DEFAULT "Description de l'école",
`meta_school_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY(`meta_school_id`) REFERENCES meta_school(`id`)
);
/* COMMON INGREDIENTS */
CREATE TABLE IF NOT EXISTS `ingredient` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT "Langue de salamandre",
`description` VARCHAR(255) NOT NULL DEFAULT "Une langue de salamandre de feu encore chaude.",
PRIMARY KEY (`id`)
);
/* COMMON VARIABLES */
CREATE TABLE IF NOT EXISTS `variable` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`description` VARCHAR(255) NOT NULL DEFAULT "Nombre de créatures affectées",
PRIMARY KEY (`id`)
);
/* ==== ASSOCIATION TABLES ==== */
/* SPELLS' SCHOOLS */
/* One spell can have multiple (up to 3) schools */
CREATE TABLE IF NOT EXISTS `spell_school` (
`spell_id` INT UNSIGNED NOT NULL,
`school_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`spell_id`, `school_id`),
FOREIGN KEY(`spell_id`) REFERENCES spell(`id`),
FOREIGN KEY(`school_id`) REFERENCES school(`id`)
);
/* SPELLS' VARIABLES */
/* One spell can have multiple (up to 2) variables of cost */
CREATE TABLE IF NOT EXISTS `spell_variable` (
`spell_id` INT UNSIGNED NOT NULL,
`variable_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`spell_id`, `variable_id`),
FOREIGN KEY(`spell_id`) REFERENCES spell(`id`),
FOREIGN KEY(`variable_id`) REFERENCES variable(`id`)
);
/* SPELLS' VARIABLES */
/* One spell can have multiple ingredients */
CREATE TABLE IF NOT EXISTS `spell_ingredient` (
`spell_id` INT UNSIGNED NOT NULL,
`ingredient_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`spell_id`, `ingredient_id`),
FOREIGN KEY(`spell_id`) REFERENCES spell(`id`),
FOREIGN KEY(`ingredient_id`) REFERENCES ingredient(`id`)
);
/* Ajout d'une nouvelle ligne avant l'insert de description */
DELIMITER $$
CREATE TRIGGER `multiLine` BEFORE INSERT ON `spell` FOR EACH ROW
BEGIN
SET NEW.description = replace(NEW.description, '<l>', '\n');
END$$
DELIMITER ;
/* =========== PRIMARY INSERTS =========== */
SET NAMES utf8;
USE auracle;
-- PERMISSIONS
INSERT INTO `role` (name, description) VALUES
("Visiteur", "Utilisateur normal, peut consulter les sorts."),
("Scribe", "Gardiens des écrits, les scribes sont capables de modifier et d'ajouter des sortilèges."),
("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."),
("Augure", "Régents des grimoires, ils ont droit d'accès à l'intégralité des informations connues, et pouvoir absolu sur le savoir arcanique.");
-- META SCHOOLS
INSERT INTO `meta_school` (name, description) VALUES
('Magies blanches', 'Magies disciplinant les arts de soins et de lumières.'),
('Magies noires', 'Magies disciplinant l\'art de la mort et des secrets.'),
('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.'),
('Magies spirituelles', 'Magies disciplinant l\'esprit, tant pour le défendre que l\'attaquer.'),
('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.');
-- SCHOOLS
INSERT INTO `school` (name, description, meta_school_id) VALUES
('Lumomancie', 'Discipline arcanique de la lumière.', 1),
('Vitamancie', 'Discipline arcanique de la guérison et de l\'énergie vitale.', 1),
('Obstrumancie', 'Discipline arcanique de la protection et des sceaux.', 1),
('Tenebromancie', 'Discipline arcanique de la lumière.', 2),
('Necromancie', 'Discipline arcanique de la mort.', 2),
('Morbomancie', 'Discipline arcanique des maladies et malédictions.', 2),
('Pyromancie', 'Discipline arcanique du feu.', 3),
('Hydromancie', 'Discipline arcanique de l\'eau.', 3),
('Electromancie', 'Discipline arcanique de la foudre.', 3),
('Terramancie', 'Discipline arcanique de la terre.', 3),
('Sidéromancie', 'Discipline arcanique des métaux rares et précieux.', 3),
('Caelomancie', 'Discipline arcanique de l\'air.', 3),
('Légimancie', 'Discipline arcanique de la lecture et du contrôle spirituel.', 4),
('Illusiomancie', 'Discipline arcanique des illusions.', 4),
('Cruciomancie', 'Discipline arcanique de la destruction spirituelle.', 4),
('Chronomancie', 'Discipline arcanique du temps.', 5),
('Spatiomancie', 'Discipline arcanique de l\'espace.', 5),
('Kénomancie', 'Discipline arcanique du néant.', 6),
('Lutomancie', 'Discipline arcanique des abysses.', 6),
('Échomancie', 'Discipline arcanique de la résolution animique.', 6),
('Protomancie', 'Discipline arcanique de la magie pure.', 7),
('Rebumancie', 'Discipline arcanique de la lumière.', 7),
('Vocamancie', 'Discipline arcanique de la lumière.', 7),
('Somamancie', 'Discipline arcanique de la maîtrise corporelle.', 7),
('Antimancie', 'Discipline arcanique de l\'annulation arcanique.', 7);
-- INGREDIENTS
INSERT INTO `ingredient` (name, description) VALUES
('Volonté', 'La force de volonté du lanceur, concentrée sur un objectif'),
('Geste', 'Un geste précis facilitant la canalisation magique');
-- VARIABLES
INSERT INTO `variable` (description) VALUES
('Nombre de personnes soignées');

View File

@@ -0,0 +1,37 @@
USE auracle;
/* Insertions de masses */
DELIMITER $$
CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN id_school INT)
BEGIN
SET @i = delimiter_start;
WHILE @i <= delimiter_end DO
INSERT INTO spell_school (spell_id, school_id) VALUES (@i, id_school);
SET @i = @i + 1;
END WHILE;
END$$
DELIMITER ;
CALL insertIntoSchoolRange(1, 25, 1);
CALL insertIntoSchoolRange(26, 50, 2);
CALL insertIntoSchoolRange(51, 70, 3);
CALL insertIntoSchoolRange(71, 95, 4);
CALL insertIntoSchoolRange(96, 111, 5);
CALL insertIntoSchoolRange(112, 115, 6);
CALL insertIntoSchoolRange(116, 141, 7);
CALL insertIntoSchoolRange(142, 167, 8);
CALL insertIntoSchoolRange(168, 193, 9);
CALL insertIntoSchoolRange(194, 217, 10);
CALL insertIntoSchoolRange(218, 220, 11);
CALL insertIntoSchoolRange(221, 242, 12);
CALL insertIntoSchoolRange(243, 257, 13);
CALL insertIntoSchoolRange(258, 272, 14);
CALL insertIntoSchoolRange(273, 283, 15);
CALL insertIntoSchoolRange(284, 301, 16);
CALL insertIntoSchoolRange(302, 322, 17);
CALL insertIntoSchoolRange(323, 339, 19);
CALL insertIntoSchoolRange(340, 341, 20);
CALL insertIntoSchoolRange(342, 356, 21);
CALL insertIntoSchoolRange(357, 387, 22);
CALL insertIntoSchoolRange(388, 396, 24);
CALL insertIntoSchoolRange(397, 403, 25);

18
api/database/bookshelf.js Normal file
View File

@@ -0,0 +1,18 @@
// MODULES
const fs = require('fs')
const mysql = require('mysql')
// Setting up the database connection
const knex = require('knex')({
client: "mysql",
connection: {
host : process.env.DB_HOST,
user : process.env.DB_USER,
password : process.env.DB_PASSWORD,
database : "auracle",
charset : "utf8"
},
})
const bookshelf = require('bookshelf')(knex)
module.exports = { bookshelf }

48
api/functions.js Normal file
View File

@@ -0,0 +1,48 @@
// Error handling
const { HttpError } = require('./validations/Errors')
const regexInt = RegExp(/^[1-9]\d*$/)
const regexXSS = RegExp(/<[^>]*script/)
// Check if int for param validation
const paramIntCheck = (req, res, next, input) => {
try {
if (regexInt.test(input)) {
next()
} else {
throw new Error
}
} catch (err) {
err = new HttpError(403, 'Provided ID must be an integer and not zero')
res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
}
}
// Check if script injection attempt
const isXSSAttempt = (string) => {
if (regexXSS.test(string)) {
return true
} else {
return false
}
}
// Check if object is null
const isEmptyObject = (obj) => {
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
return true
} else {
return false
}
}
module.exports = {
paramIntCheck,
isXSSAttempt,
isEmptyObject
}

60
api/index.js Normal file
View File

@@ -0,0 +1,60 @@
'use strict';
// MODULES
const express = require('express')
const bodyParser = require('body-parser')
const helmet = require('helmet')
const morgan = require('morgan')
const cors = require('cors') // module to format the json response
const dotenv = require('dotenv').config()
// Creates instances of database connections
const connection = require('./database/bookshelf')
const db = connection.db
// CONSTANTS
const port = 2814
// Import routes
const routes = require('./routes')
// Builds app w/ express
let app = express()
app.use(bodyParser.json({ limit: '10kb' }))
app.use(cors())
app.use(morgan('tiny'))
app.use(helmet())
// Serves
const server = app.listen( port, () => {console.log(`App listening on port ${port}`)})
// Get credentials
// app.get('/api/login', (req, res, next) => {
// if (req.headers.auracle_key !== process.env.API_KEY_PUBLIC) {
// return res.status(401).send('Credentials error !')
// } else {
// return res.status(200).send(JSON.stringify(
// {
// "secret_key": process.env.API_KEY_PRIVATE,
// })
// )
// }
// })
// Auth guard
const authguard = (req, res, next) => {
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
app.use('/api/spells', authguard,routes.spells)
app.use('/api/schools', authguard, routes.schools)
app.use('/api/meta_schools', authguard, routes.meta_schools)
app.use('/api/variables', authguard, routes.variables)
app.use('/api/ingredients', authguard, routes.ingredients)
app.use('/api/users', authguard, routes.users)

View File

@@ -0,0 +1,13 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf
require('./spell-model')
let Ingredient = bookshelf.Model.extend({
tableName: 'ingredient',
spells() {
return this.belongsToMany( 'Spell', 'spell_ingredient')
}
})
module.exports = bookshelf.model('Ingredient', Ingredient)

View File

@@ -0,0 +1,13 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf
require('./school-model')
let MetaSchool = bookshelf.Model.extend({
tableName: 'meta_school',
schools() {
return this.hasMany( 'School' )
}
})
module.exports = bookshelf.model('MetaSchool', MetaSchool)

View File

@@ -0,0 +1,17 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf
require('./spell-model')
require('./meta-school-model')
let School = bookshelf.Model.extend({
tableName: 'school',
spells() {
return this.belongsToMany( 'Spell', 'spell_school' )
},
meta_schools() {
return this.belongsTo( 'MetaSchool', 'meta_school_id' )
}
})
module.exports = bookshelf.model('School', School)

21
api/models/spell-model.js Normal file
View File

@@ -0,0 +1,21 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf
require('./school-model')
require('./variable-model')
require('./ingredient-model')
let Spell = bookshelf.Model.extend({
tableName: 'spell',
schools() {
return this.belongsToMany( 'School', 'spell_school' )
},
variables() {
return this.belongsToMany( 'Variable', 'spell_variable' )
},
ingredients() {
return this.belongsToMany( 'Ingredient', 'spell_ingredient' )
}
})
module.exports = bookshelf.model('Spell', Spell)

9
api/models/user-model.js Normal file
View File

@@ -0,0 +1,9 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf
let User = bookshelf.Model.extend({
tableName: 'user',
hidden: ['id', 'password']
})
module.exports = bookshelf.model('User', User)

View File

@@ -0,0 +1,13 @@
'use strict'
const bookshelf = require('../database/bookshelf').bookshelf
require('./spell-model')
let Variable = bookshelf.Model.extend({
tableName: 'variable',
spells() {
return this.belongsToMany( 'Spell', 'spell_variable')
}
})
module.exports = bookshelf.model('Variable', Variable)

2433
api/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

44
api/package.json Normal file
View File

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

View File

@@ -0,0 +1,171 @@
'use strict'
// Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf
const model = require('../models/ingredient-model')
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
const IngredientValidation = require("../validations/IngredientValidation")
v.addSchema(IngredientValidation, "/IngredientValidation")
// Validations
const isXSSAttempt = require('../functions').isXSSAttempt
const isEmptyObject = require('../functions').isEmptyObject
// Error handling
const { HttpError } = require('../validations/Errors')
class IngredientRepository {
constructor() {
}
getAll() {
return new Promise((resolve, reject) => {
model.forge()
.fetchAll({ withRelated: ['spells'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get ingredients"))
})
})
}
getOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['spells']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get ingredient"))
})
})
}
getSpellsFromOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get ingredient"))
})
})
}
addOne(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(new HttpError(403, "Error: Ingredient cannot be nothing !"))
} else if (!v.validate(igr, IngredientValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(igr, IngredientValidation).errors))
} else if (isXSSAttempt(igr.description)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
bookshelf.transaction(t => {
return model.forge({
'name': igr.name,
'description': igr.description,
}).save(null, {
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(new HttpError(500, "Insert Ingredient error"))
})
}
})
}
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(new HttpError(403, "Error: Ingredient cannot be nothing !"))
} else if (!v.validate(igr, IngredientValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(igr, IngredientValidation).errors))
} else if (isXSSAttempt(igr.description)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
model.forge({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(new HttpError(500, "Update Ingredient error"))
})
})
.catch(err => {
console.log(err)
reject(new HttpError(404, "Couldn't get ingredient"))
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.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(new HttpError(404, "Couldn't get ingredient"))
})
})
}
}
module.exports = IngredientRepository

View File

@@ -0,0 +1,53 @@
'use strict'
// Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf
const model = require('../models/meta-school-model')
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
const MetaSchoolValidation = require("../validations/MetaSchoolValidation")
v.addSchema(MetaSchoolValidation, "/MetaSchoolValidation")
// Validations
const regexXSS = RegExp(/<[^>]*script/)
// Error handling
const { HttpError } = require('../validations/Errors')
class MetaSchoolRepository {
constructor() {
}
getAll() {
return new Promise((resolve, reject) => {
model.forge()
.fetchAll({ withRelated: ['schools'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get meta schools"))
})
})
}
getOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['schools']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get meta school"))
})
})
}
}
module.exports = MetaSchoolRepository

View File

@@ -0,0 +1,172 @@
'use strict'
// Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf
const model = require('../models/school-model')
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
const SchoolValidation = require("../validations/SchoolValidation")
v.addSchema(SchoolValidation, "/SchoolValidation")
// Validations
const isXSSAttempt = require('../functions').isXSSAttempt
const isEmptyObject = require('../functions').isEmptyObject
// Error handling
const { HttpError } = require('../validations/Errors')
class SchoolRepository {
constructor() {
}
getAll() {
return new Promise((resolve, reject) => {
model.forge()
.fetchAll({ withRelated: ['meta_schools'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get schools"))
})
})
}
getOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['meta_schools']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get school"))
})
})
}
getSpellsFromOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get spells from school"))
})
})
}
addOne(s) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(s)) {
reject(new HttpError(403, "Error: School cannot be nothing !"))
} else if (!v.validate(s, SchoolValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolValidation).errors))
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
bookshelf.transaction(t => {
return model.forge({
'name': s.name,
'description': s.description,
'meta_school_id': s.meta_school_id,
}).save(null, {
transacting: t
})
.catch(err => {
throw err
})
})
.then(v => {
return v.load(['meta_schools'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Insert School error"))
})
}
})
}
updateOne(id, s) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(s)) {
reject(new HttpError(403, "Error: School cannot be nothing !"))
} else if (!v.validate(s, SchoolValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolValidation).errors))
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
model.forge({id: id})
.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 => {
return v.load(['meta_schools'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Update School error"))
})
})
.catch(err => {
console.log(err)
reject(new HttpError(404, "Couldn't get school"))
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.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(new HttpError(404, "Couldn't get school"))
})
})
}
}
module.exports = SchoolRepository

View File

@@ -0,0 +1,280 @@
'use strict'
// Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf
const model = require('../models/spell-model')
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
const SpellValidation = require("../validations/SpellValidation")
v.addSchema(SpellValidation, "/SpellValidation")
// Validations
const isXSSAttempt = require('../functions').isXSSAttempt
const isEmptyObject = require('../functions').isEmptyObject
// Error handling
const { HttpError } = require('../validations/Errors')
class SpellRepository {
constructor() {
}
getAll(name, description, level, charge, cost, ritual) {
return new Promise((resolve, reject) => {
let query = model.forge()
if (name) { query.where('name', 'like', `%${name}%`) }
if (description) { query.where('description', 'like', `%${description}%`) }
if (level) { query.where({ 'level' : level }) }
if (charge) { query.where({ 'charge' : charge }) }
if (cost) { query.where({ 'cost' : cost }) }
if (ritual) { query.where({ 'is_ritual' : ritual }) }
query.fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get spells"))
})
})
}
getAllPublic(name, description, level, charge, cost, ritual) {
return new Promise((resolve, reject) => {
let query = model.forge()
.where({ 'public' : 1 })
if (name) { query.where('name', 'like', `%${name}%`) }
if (description) { query.where('description', 'like', `%${description}%`) }
if (level) { query.where({ 'level' : level }) }
if (charge) { query.where({ 'charge' : charge }) }
if (cost) { query.where({ 'cost' : cost }) }
if (ritual) { query.where({ 'is_ritual' : ritual }) }
query.fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get public spells"))
})
})
}
getPage(page) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'public' : 1 })
.fetchPage({
pageSize: 20,
page: page,
withRelated: ['schools.meta_schools', 'variables', 'ingredients'],
})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get public spells"))
})
})
}
getOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get spell"))
})
})
}
addOne(s) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(s)) {
reject(new HttpError(403, "Error: Spell cannot be nothing !"))
} else if (!v.validate(s, SpellValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellValidation).errors))
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
bookshelf.transaction(t => {
return model.forge({
'name': s.name,
'description': s.description,
'level': s.level,
'charge' : s.charge,
'cost' : s.cost,
'is_ritual' : s.is_ritual
}).save(null, {
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 => {
throw err
})
})
.then(v => {
return v.load(['schools.meta_schools', 'variables', 'ingredients'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Insert Spell error"))
})
}
})
}
updateOne(id, s) {
return new Promise((resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(s)) {
reject(new HttpError(403, "Error: Spell cannot be nothing !"))
} else if (!v.validate(s, SpellValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellValidation).errors))
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
model.forge({id: id})
.fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']})
.then(v => {
bookshelf.transaction(t => {
return v.save({
'name': s.name,
'description': s.description,
'level': s.level,
'charge' : s.charge,
'cost' : s.cost,
'is_ritual' : s.is_ritual
}, {
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});
}
return
})
.tap(spell => {
if (s.variables) {
let variables = spell.related('variable');
return spell.variables().detach(variables, { transacting: t});
}
return
})
.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)
throw err
})
})
.then(v => {
return v.load(['schools.meta_schools', 'variables', 'ingredients'])
})
.then(v => {
resolve(this.getOne(v.id))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Update Spell error"))
})
})
.catch(err => {
console.log(err)
reject(new HttpError(404, "Couldn't get spell"))
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']})
.then(v => {
v.schools().detach()
v.variables().detach()
v.ingredients().detach()
v.destroy()
})
.then(() => {
resolve({
'message': 'Spell with ID ' + id + ' successfully deleted !'
})
})
.catch(err => {
console.log(err)
reject(new HttpError(404, "Couldn't get spell"))
})
})
}
}
module.exports = SpellRepository

View File

@@ -0,0 +1,180 @@
'use strict'
// Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf
const model = require('../models/user-model')
// Hashing and passwords
const bcrypt = require('bcrypt')
const { v4: uuidv4 } = require('uuid')
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
const UserValidation = require("../validations/UserValidation")
v.addSchema(UserValidation, "/UserValidation")
// Validations
const isXSSAttempt = require('../functions').isXSSAttempt
const isEmptyObject = require('../functions').isEmptyObject
class UserRepository {
constructor() {
}
getAll() {
return new Promise((resolve, reject) => {
model.forge()
.fetchAll()
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(() => {
reject({
"message": "Database error, couldn't get all users.",
"code": 500
})
})
})
}
getOneByUUID(uuid, full) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'uuid' : uuid })
.fetch()
.then(v => {
resolve(v.toJSON({ omitPivot: true, visibility: !full }))
})
.catch(err => {
reject({
"message": "User with this UUID was not found.",
"code": 404
})
})
})
}
getOneByEmail(mail, full) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'mail': mail })
.fetch()
.then(v => {
resolve(v.toJSON({ omitPivot: true, visibility: !full }))
})
.catch(() => {
reject({
"message": "User with this email was not found.",
"code": 404
})
})
})
}
addOne(u) {
return new Promise(async (resolve, reject) => {
// Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(u)) {
reject({
"message": "Request body cannot be empty.",
"code": 403
})
} else if (!v.validate(u, UserValidation).valid) {
reject({
"message": "Schema is not valid - " + v.validate(u, UserValidation).errors,
"code": 403
})
} else if (isXSSAttempt(u.name) || isXSSAttempt(u.password) || isXSSAttempt(u.mail)) {
reject({
"message": "Injection attempt detected, aborting the request.",
"code": 403
})
} else {
let hash = await bcrypt.hash(u.password, 10)
let uuid = uuidv4()
this.checkIfEmailAvailable(u.mail)
.then(() => {
bookshelf.transaction(t => {
return model.forge({
'uuid': uuid,
'name': u.name,
'mail': u.mail,
'password': hash,
})
.save(null, {
transacting: t
})
.catch(err => {
throw err
})
})
.then(() => {
return this.getOneByUUID(uuid, false)
})
.then(newUser => {
resolve({
"message": "Account successfully created !",
"user": newUser,
})
})
.catch(err => {
resolve({
"message": "An error has occured while creating your account.",
"code": 500,
})
})
})
.catch(err => {
reject(err)
})
}
})
}
// Log user with an email address and a password
logUser(mail, password) {
return new Promise((resolve, reject) => {
this.getOneByEmail(mail, true)
.then(async fetchedUser => {
let match = await bcrypt.compare(password, fetchedUser.password)
delete fetchedUser.id
delete fetchedUser.password
if (match) {
resolve({
"message": "User successfully logged in !",
"user": fetchedUser,
})
} else {
reject({
"message": "Les informations de connexion sont erronées.",
})
}
})
.catch(err => {
reject(err)
})
})
}
// Check if one user already has that email
checkIfEmailAvailable(mail) {
return new Promise((resolve, reject) => {
this.getOneByEmail(mail, false)
.then(() => {
reject({
"message": "L'email est déjà utilisé par un autre utilisateur.",
"code": 403
})
})
.catch(() => {
resolve(true)
})
})
}
}
module.exports = UserRepository

View File

@@ -0,0 +1,168 @@
'use strict'
// Bookshelf
const bookshelf = require('../database/bookshelf').bookshelf
const model = require('../models/variable-model')
// Model validation
const Validator = require('jsonschema').Validator
const v = new Validator()
const VariableValidation = require("../validations/VariableValidation")
v.addSchema(VariableValidation, "/VariableValidation")
// Validations
const isXSSAttempt = require('../functions').isXSSAttempt
const isEmptyObject = require('../functions').isEmptyObject
// Error handling
const { HttpError } = require('../validations/Errors')
class VariableRepository {
constructor() {
}
getAll() {
return new Promise((resolve, reject) => {
model.forge()
.fetchAll({ withRelated: ['spells'] })
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get variables"))
})
})
}
getOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['spells']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get variable"))
})
})
}
getSpellsFromOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.where({ 'id' : id })
.fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools']})
.then(v => {
resolve(v.toJSON({ omitPivot: true }))
})
.catch(err => {
console.log(err)
reject(new HttpError(500, "Couldn't get variable"))
})
})
}
addOne(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(new HttpError(403, "Error: Variable cannot be nothing !"))
} else if (!v.validate(vr, VariableValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(vr, VariableValidation).errors))
} else if (isXSSAttempt(vr.description)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
bookshelf.transaction(t => {
return model.forge({
'description': vr.description,
}).save(null, {
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(new HttpError(500, "Insert Variable error"))
})
}
})
}
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(new HttpError(403, "Error: Variable cannot be nothing !"))
} else if (!v.validate(vr, VariableValidation).valid) {
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(vr, VariableValidation).errors))
} else if (isXSSAttempt(vr.description)) {
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
} else {
model.forge({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(new HttpError(500, "Update Variable error"))
})
})
.catch(err => {
console.log(err)
reject(new HttpError(404, "Couldn't get variable"))
})
}
})
}
deleteOne(id) {
return new Promise((resolve, reject) => {
model.forge()
.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(new HttpError(404, "Couldn't get variable"))
})
})
}
}
module.exports = VariableRepository

15
api/routes/index.js Normal file
View File

@@ -0,0 +1,15 @@
const spells = require('./spells')
const schools = require('./schools')
const meta_schools = require('./meta_schools')
const variables = require('./variables')
const ingredients = require('./ingredients')
const users = require('./users')
module.exports = {
spells,
schools,
meta_schools,
ingredients,
variables,
users,
}

168
api/routes/ingredients.js Normal file
View File

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

View File

@@ -0,0 +1,68 @@
'use strict'
// Router
const express = require('express')
let router = express.Router()
// Connection
const connection = require('../database/bookshelf')
const functions = require('../functions')
// Repository
const MetaSchoolRepository = require('../repositories/meta-school-repository');
const MetaSchools = new MetaSchoolRepository();
// ROUTES
// GET ALL ------------------
const getMetaSchools = () => {
return MetaSchools.getAll()
.catch(err => {
console.log(err)
throw err
})
}
router.get('/', async (req, res) => {
getMetaSchools()
.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(
{
"error": err.message,
"code": err.code
})
)
})
})
// GET ONE ------------------
const getMetaSchool = (id) => {
return MetaSchools.getOne(id)
.catch(err => {
console.log(err)
throw err
})
}
router.get('/:id/', async (req, res) => {
getMetaSchool(req.params.id)
.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(
{
"error": err.message,
"code": err.code
})
)
})
})
// Param validations
router.param('id', functions.paramIntCheck)
module.exports = router

167
api/routes/schools.js Normal file
View File

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

191
api/routes/spells.js Normal file
View File

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

113
api/routes/users.js Normal file
View File

@@ -0,0 +1,113 @@
'use strict'
// Router
const express = require('express')
let router = express.Router()
// Connection
const connection = require('../database/bookshelf')
const db = connection.db
// Repository
const UserRepository = require('../repositories/user-repository');
const Users = new UserRepository();
// ROUTES
// GET ALL ------------------
const getUsers = () => {
return Users.getAll()
.catch(err => {
console.log(err)
throw err
})
}
router.get('/', async (req, res) => {
getUsers()
.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(
{
"error": err.message,
"code": err.code
})
)
})
})
// GET ONE FORM UUID ------------------
const getUserByUUID = (uuid) => {
return Users.getOneByUUID(uuid)
.catch(err => {
console.log(err)
throw err
})
}
router.get('/:uuid/', async (req, res) => {
getUserByUUID(req.params.uuid)
.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(
{
"error": err.message,
"code": err.code
})
)
})
})
// LOG A USER ------------------
const logUser = (mail, password) => {
return Users.logUser(mail, password)
.catch(err => {
console.log(err)
throw err
})
}
router.post('/login', async (req, res) => {
logUser(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(
{
"error": err.message,
"code": err.code
})
)
})
})
// CREATE ONE ------------------
const addUser = (u) => {
return Users.addOne(u)
.catch(err => {
throw err
})
}
router.post('/', async (req, res) => {
addUser(req.body)
.then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.send(JSON.stringify(v))
})
.catch(err => {
res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
})
})
module.exports = router

168
api/routes/variables.js Normal file
View File

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

13
api/validations/Errors.js Normal file
View File

@@ -0,0 +1,13 @@
// Http error w/ http code
class HttpError extends Error {
constructor(code, message) {
super(message)
this.name = "HttpError"
this.code = code
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = {
HttpError
}

View File

@@ -0,0 +1,11 @@
const Ingredient = {
"id": "/IngredientValidation",
"type": Object,
"properties": {
"name": { "type": "string" },
"description": { "type": "string" }
},
"required": ["name", "description"]
}
module.exports = Ingredient

View File

@@ -0,0 +1,12 @@
const MetaSchool = {
"id": "/MetaSchoolValidation",
"type": Object,
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"schools": { "type": "array" }
},
"required": ["name", "description"]
}
module.exports = MetaSchool

View File

@@ -0,0 +1,12 @@
const School = {
"id": "/SchoolValidation",
"type": Object,
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"meta_school_id": { "type": "number" },
},
"required": ["name", "description", "meta_school_id"]
}
module.exports = School

View File

@@ -0,0 +1,39 @@
const Spell = {
"id": "/SpellValidation",
"type": Object,
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"level": { "type": "number" },
"charge": { "type": "number" },
"cost": { "type": "string" },
"is_ritual": { "type": "boolean" },
"schools": {
"type": { "type": "array" },
"items": {
"properties": {
"id": { "type": "number" },
}
}
},
"variables": {
"type": { "type": "array" },
"items": {
"properties": {
"id": { "type": "number" },
}
}
},
"ingredients": {
"type": { "type": "array" },
"items": {
"properties": {
"id": { "type": "number" },
}
}
}
},
"required": ["name", "description"]
}
module.exports = Spell

View File

@@ -0,0 +1,12 @@
const User = {
"id": "/UserValidation",
"type": Object,
"properties": {
"name": { "type": "string" },
"mail": { "type": "string" },
"password": { "type": "string" },
},
"required": ["name", "password", "mail"]
}
module.exports = User

View File

@@ -0,0 +1,10 @@
const Variable = {
"id": "/VariableValidation",
"type": Object,
"properties": {
"description": { "type": "string" },
},
"required": ["description"]
}
module.exports = Variable