Merge branch 'dev'
2
.gitignore
vendored
@@ -21,4 +21,4 @@ yarn-error.log*
|
||||
*.sw?
|
||||
|
||||
# creds
|
||||
/database/credentials.json
|
||||
.env
|
||||
@@ -2,8 +2,9 @@
|
||||
A dictionnary of arcane spells from a homebrew tabletop RPG.
|
||||
|
||||
## Built With
|
||||
* [Vue CLI](http://www.dropwizard.io/1.0.2/docs/) - Web Framework
|
||||
* [Vue CLI](http://www.dropwizard.io/1.0.2/docs/) - Web Framework for the client
|
||||
* [NPM](https://maven.apache.org/) - Package and Dependency Management
|
||||
|
||||
## Authors
|
||||
* **Alexis** - *Designing, Development & Conception* - [Github](https://github.com/AlexisNP)
|
||||
* **Alexis Pelé** - *Designing, Development & Conception* - [Github](https://github.com/AlexisNP)
|
||||
* **Mathieu Hance** - *Designing, Integration*
|
||||
@@ -1,7 +0,0 @@
|
||||
import axios from "axios"
|
||||
|
||||
const url = "http://localhost:2814/api"
|
||||
|
||||
export default axios.create({
|
||||
baseURL: url
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
import spellsRepository from './spellsRepository'
|
||||
|
||||
// List of possible repositories
|
||||
const repositories = {
|
||||
spells: spellsRepository
|
||||
}
|
||||
|
||||
// Usage : RepositoryFactoryInstance.get('reponame');
|
||||
export const RepositoryFactory = {
|
||||
get: name => repositories[name]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
}
|
||||
|
||||
1241
client/package-lock.json
generated
@@ -28,8 +28,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"bootstrap": "^4.5.0",
|
||||
"bootstrap-vue": "^2.15.0",
|
||||
"core-js": "^3.6.4",
|
||||
"vue": "^2.6.11"
|
||||
"jquery": "^3.5.1",
|
||||
"popper.js": "^1.16.1",
|
||||
"v-clipboard": "^2.2.3",
|
||||
"vue": "^2.6.11",
|
||||
"vue-masonry": "^0.11.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.2.0",
|
||||
@@ -38,7 +44,7 @@
|
||||
"babel-eslint": "^10.0.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.1.2",
|
||||
"node-sass": "^4.13.1",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue-router": "^3.1.6",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
client/public/img/bg1.png
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
client/public/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
@@ -4,14 +4,15 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>Spellsaurus</title>
|
||||
<link rel="icon" href="<%= BASE_URL %>img/favicon.ico">
|
||||
<title>Auracle</title>
|
||||
<link rel="stylesheet" href="<%= BASE_URL %>libs/style.css">
|
||||
<script src="<%= BASE_URL %>libs/clipboard.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="srs"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
7
client/public/libs/clipboard.min.js
vendored
Normal file
BIN
client/public/libs/material-design-icons.woff2
Normal file
68
client/public/libs/style.css
Normal file
@@ -0,0 +1,68 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Playfair+Display:400,400i,500,500i,600,600i,700,700i,800,800i,900,900i&subset=cyrillic,latin-ext');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap');
|
||||
|
||||
@font-face {
|
||||
font-family: "stgeorge";
|
||||
src: url("/public/resources/stgeorge.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.word-break{
|
||||
word-wrap: break-word;
|
||||
}
|
||||
/*img.emoji {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin: 0 .05em 0 .1em;
|
||||
vertical-align: -0.1em;
|
||||
}*/
|
||||
.cursor-pointer{
|
||||
cursor: pointer;
|
||||
}
|
||||
.font-display{ font-family: 'Playfair Display', serif; }
|
||||
.font-weight-100{ font-weight: 100; }
|
||||
.font-weight-200{ font-weight: 200; }
|
||||
.font-weight-300{ font-weight: 300; }
|
||||
.font-weight-400{ font-weight: 400; }
|
||||
.font-weight-500{ font-weight: 500; }
|
||||
.font-weight-600{ font-weight: 600; }
|
||||
.font-weight-700{ font-weight: 700; }
|
||||
.font-weight-800{ font-weight: 800; }
|
||||
.font-weight-900{ font-weight: 900; }
|
||||
/*.pfp{
|
||||
background: #f8f9fa;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.pfp-32{ height:32px; width:32px; }
|
||||
.pfp-64{ height:64px; width:64px; }
|
||||
.pfp-128{ height:128px; width:128px; }
|
||||
.pfp-256{ height:256px; width:256px; }*/
|
||||
.max-540{ max-width: 540px; }
|
||||
.max-720{ max-width: 720px; }
|
||||
.max-960{ max-width: 960px; }
|
||||
.max-1140{ max-width: 1140px; }
|
||||
.line-height-100{ line-height: 100%; }
|
||||
@font-face{
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/libs/material-design-icons.woff2') format('woff2');
|
||||
}
|
||||
.mad{
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
transform: translateY(12.5%);
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
body{
|
||||
background: url('/img/bg1.png') center center fixed repeat;
|
||||
}
|
||||
9
client/src/api/api.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import axios from "axios"
|
||||
|
||||
const url = process.env.VUE_APP_API_URL
|
||||
|
||||
axios.defaults.headers.common[process.env.VUE_APP_API_KEY_NAME] = process.env.VUE_APP_API_KEY_VALUE
|
||||
|
||||
export default axios.create({
|
||||
baseURL: url
|
||||
})
|
||||
14
client/src/api/ingredientsRepository.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import api from './api'
|
||||
|
||||
// URL for spells
|
||||
const resource = "/ingredients"
|
||||
|
||||
// CRUD methods for spells
|
||||
export default {
|
||||
getIngredients() {
|
||||
return api.get(`${resource}`)
|
||||
},
|
||||
getIngredient(id) {
|
||||
return api.get(`${resource}/${id}`)
|
||||
},
|
||||
}
|
||||
14
client/src/api/metaschoolsRepository.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import api from './api'
|
||||
|
||||
// URL for spells
|
||||
const resource = "/meta_schools"
|
||||
|
||||
// CRUD methods for spells
|
||||
export default {
|
||||
getMetaSchools() {
|
||||
return api.get(`${resource}`)
|
||||
},
|
||||
getMetaSchool(id) {
|
||||
return api.get(`${resource}/${id}`)
|
||||
},
|
||||
}
|
||||
19
client/src/api/repositories.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import spellsRepository from './spellsRepository'
|
||||
import schoolsRepository from './schoolsRepository'
|
||||
import metaschoolsRepository from './metaschoolsRepository'
|
||||
import variablesRepository from './variablesRepository'
|
||||
import ingredientsRepository from './ingredientsRepository'
|
||||
|
||||
// List of possible repositories
|
||||
const repositories = {
|
||||
spells: spellsRepository,
|
||||
schools: schoolsRepository,
|
||||
metaschools: metaschoolsRepository,
|
||||
variables: variablesRepository,
|
||||
ingredients: ingredientsRepository,
|
||||
}
|
||||
|
||||
// Usage : RepositoryFactoryInstance.get('reponame');
|
||||
export const RepositoryFactory = {
|
||||
get: name => repositories[name]
|
||||
}
|
||||
17
client/src/api/schoolsRepository.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import api from './api'
|
||||
|
||||
// URL for spells
|
||||
const resource = "/schools"
|
||||
|
||||
// CRUD methods for spells
|
||||
export default {
|
||||
getSchools() {
|
||||
return api.get(`${resource}`)
|
||||
},
|
||||
getSchool(id) {
|
||||
return api.get(`${resource}/${id}`)
|
||||
},
|
||||
getSpellsFromSchool(id) {
|
||||
return api.get(`${resource}/${id}/spells`)
|
||||
}
|
||||
}
|
||||
23
client/src/api/spellsRepository.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import api from './api'
|
||||
|
||||
// URL for spells
|
||||
const resource = "/spells"
|
||||
|
||||
// CRUD methods for spells
|
||||
export default {
|
||||
getSpells() {
|
||||
return api.get(`${resource}`)
|
||||
},
|
||||
getSpell(id) {
|
||||
return api.get(`${resource}/${id}`)
|
||||
},
|
||||
addSpell(data) {
|
||||
return api.post(`${resource}`, data)
|
||||
},
|
||||
updateSpell(id, data) {
|
||||
return api.put(`${resource}/${id}`, data)
|
||||
},
|
||||
deleteSpell(id) {
|
||||
return api.delete(`${resource}/${id}`)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import api from './api'
|
||||
|
||||
// URL for spells
|
||||
const resource = "/spells"
|
||||
const resource = "/variables"
|
||||
|
||||
// CRUD methods for spells
|
||||
export default {
|
||||
getSpells() {
|
||||
getVariables() {
|
||||
return api.get(`${resource}`)
|
||||
},
|
||||
getSpell(id) {
|
||||
return api.get(`${resource}?id=${id}`)
|
||||
getVariable(id) {
|
||||
return api.get(`${resource}/${id}`)
|
||||
},
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 125" style="enable-background:new 0 0 100 125;" xml:space="preserve">
|
||||
<path d="M93,49.1c-2.4-0.5-4.5-1.5-5.1-4.4c-0.5-2.9,1.8-4,4.1-5.3c0.7-0.4,1.2-0.8,1.4-1.2c-0.2-0.7-0.4-1.4-0.6-2.1
|
||||
c-0.5-0.3-1.1-0.5-2.1-0.4c-2.5,0.3-4.8,0-6.2-2.6c-1.4-2.6,0.5-4.3,2.2-6.3c0.5-0.6,0.8-1.1,0.9-1.4c-0.4-0.6-0.9-1.3-1.3-1.9
|
||||
c-0.5-0.1-1.2,0-2,0.3c-2.3,1-4.6,1.5-6.7-0.6c-2.1-2-0.9-4.3,0.2-6.7c0.3-0.6,0.4-1.2,0.4-1.5c-0.6-0.5-1.3-1-2-1.5
|
||||
c-0.5,0.1-1,0.4-1.6,0.9c-1.8,1.7-3.9,2.8-6.5,1.5s-2.2-3.8-1.9-6.4c0.1-0.7,0-1.2,0-1.5c-0.8-0.3-1.6-0.6-2.4-0.9
|
||||
c-0.4,0.2-0.8,0.7-1.2,1.3c-1.2,2.1-2.8,3.9-5.7,3.5S53.7,9,53.1,6.5c-0.1-0.6-0.3-1.1-0.5-1.4C51.8,5,50.9,5,50,5l0,0
|
||||
c-0.3,0.4-0.6,0.9-0.7,1.6c-0.5,2.4-1.5,4.5-4.4,5.1c-2.9,0.5-4-1.8-5.3-4.1c-0.3-0.5-0.6-0.9-0.9-1.2c-0.9,0.2-1.7,0.5-2.5,0.7
|
||||
c-0.2,0.4-0.3,1-0.2,1.7c0.3,2.5,0,4.8-2.6,6.2s-4.3-0.5-6.3-2.2c-0.5-0.4-0.9-0.7-1.2-0.8c-0.7,0.5-1.5,1-2.2,1.5
|
||||
c0,0.5,0.1,1,0.4,1.7c1,2.3,1.5,4.6-0.6,6.7c-2,2.1-4.3,0.9-6.7-0.2c-0.6-0.3-1.1-0.4-1.4-0.4c-0.6,0.7-1.1,1.3-1.6,2
|
||||
c0.1,0.5,0.4,1,0.9,1.5c1.7,1.8,2.8,3.9,1.5,6.5s-3.8,2.2-6.4,1.9c-0.7-0.1-1.2,0-1.6,0c-0.3,0.8-0.6,1.5-0.9,2.3
|
||||
C7.6,36.1,8,36.6,8.7,37c2.1,1.2,3.9,2.8,3.5,5.7s-2.9,3.2-5.5,3.8c-0.7,0.2-1.2,0.4-1.6,0.6C5,47.9,5,48.7,5,49.5
|
||||
c0.4,0.4,0.9,0.7,1.8,0.9c2.4,0.5,4.5,1.5,5.1,4.4c0.5,2.9-1.8,4-4.1,5.3c-0.7,0.4-1.1,0.8-1.4,1.1c0.2,0.7,0.4,1.4,0.6,2.1
|
||||
c0.5,0.3,1.1,0.5,2.1,0.3c2.5-0.3,4.8,0,6.2,2.6s-0.5,4.3-2.2,6.3c-0.6,0.7-0.9,1.2-1,1.6c0.4,0.6,0.7,1.1,1.1,1.7
|
||||
c0.5,0.1,1.3,0.1,2.2-0.3c2.3-1,4.6-1.5,6.7,0.6c2.1,2,0.9,4.3-0.2,6.7c-0.4,0.9-0.5,1.5-0.5,2c0.5,0.4,1,0.8,1.5,1.1
|
||||
c0.6,0,1.3-0.2,2.1-1c1.8-1.7,3.9-2.8,6.5-1.5s2.2,3.8,1.9,6.4c-0.1,1.1,0,1.7,0.2,2.1c0.5,0.2,1,0.4,1.6,0.6
|
||||
c0.6-0.1,1.3-0.6,1.9-1.6c1.2-2.1,2.8-3.9,5.7-3.5s3.2,2.9,3.8,5.5c0.2,1.1,0.6,1.7,0.9,2c0.5,0,1,0,1.6,0.1
|
||||
c0.5-0.3,1.1-0.9,1.3-2.1c0.5-2.4,1.5-4.5,4.4-5.1c2.9-0.5,4,1.8,5.3,4.1c0.6,1,1.1,1.4,1.5,1.6c0.5-0.1,1-0.3,1.4-0.4
|
||||
c0.4-0.4,0.8-1.2,0.7-2.5c-0.3-2.5,0-4.8,2.6-6.2c2.6-1.4,4.3,0.5,6.3,2.2c0.8,0.8,1.5,1,1.9,1.1c0.4-0.3,0.8-0.6,1.3-0.9
|
||||
c0.2-0.5,0.4-1.4-0.2-2.5c-1-2.3-1.5-4.6,0.6-6.7c2-2.1,4.3-0.9,6.7,0.2c1,0.4,1.7,0.5,2.1,0.4c0.3-0.4,0.7-0.9,1-1.3
|
||||
c0-0.6-0.1-1.4-1-2.3c-1.7-1.8-2.8-3.9-1.5-6.5s3.8-2.2,6.4-1.9c1,0.1,1.7,0,2.1-0.2c0.2-0.6,0.4-1.1,0.6-1.7
|
||||
c-0.2-0.6-0.6-1.2-1.6-1.8c-2.1-1.2-3.9-2.8-3.5-5.7s2.9-3.2,5.5-3.8c0.9-0.2,1.5-0.5,1.8-0.7c0-0.7,0-1.3,0.1-2
|
||||
C94.7,49.8,94.1,49.3,93,49.1z M27.1,68.5c-1.8,0.3-4.1,0.1-5.6-2.7c-2.7-4.5-4.6-12.7-4.1-15.4s1.9-3.7,4.3-4.1s4.1,1.9,4.6,5.5
|
||||
c0.5,3.4,1.7,7.7,3.3,10.5C31.1,65.1,28.9,68.2,27.1,68.5z M25.2,33.7c-0.8-1.6-1.4-3.8,0.8-6.2c3.4-3.9,10.7-8.3,13.4-8.6
|
||||
c2.7-0.3,4.2,0.6,5.2,2.8c1.1,2.1-0.6,4.5-3.8,6c-3.1,1.5-6.8,4-9,6.4C29.7,36.4,26.1,35.3,25.2,33.7z M60.7,77.5
|
||||
c-0.3,1.8-1.1,3.9-4.3,4.5c-5.1,1.2-13.5,0.4-15.9-0.9s-3-3-2.6-5.3c0.4-2.4,3.1-3.3,6.6-2.6c3.4,0.6,7.8,0.8,11,0.1
|
||||
C58.8,72.7,61,75.7,60.7,77.5z M50,68c-9.9,0-18-8.1-18-18s8.1-18,18-18s18,8.1,18,18S59.9,68,50,68z M68.3,34.2
|
||||
c-2.4-2.5-5.9-5.2-8.8-6.6c-2.9-1.4-3-5.2-1.7-6.5c1.3-1.3,3.2-2.5,6.1-1.1c4.8,2.1,11.2,7.6,12.3,10.1c1.2,2.5,0.7,4.1-1,5.8
|
||||
C73.6,37.6,70.8,36.8,68.3,34.2z M82.7,53.8c-0.5,5.2-3.8,13-5.8,14.9c-2,1.9-3.7,1.9-5.9,0.8c-2.1-1.1-2.2-4-0.5-7.1
|
||||
c1.6-3,3.1-7.2,3.5-10.4s4-4.4,5.6-3.6C81.4,49.1,83.1,50.6,82.7,53.8z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" style="enable-background:new 0 0 100 100;" xml:space="preserve"><path d="M98,53.9v-7.6l-8.3-2c-0.2-1.5-0.4-2.9-0.7-4.3l7.1-5.1l-2.9-7.1l-8.6,1.5c-0.7-1.2-1.5-2.3-2.3-3.4l4.6-7.4 l-5.4-5.4l-7.4,4.6c-1.2-0.9-2.4-1.7-3.6-2.4l1.4-8.6l-7.1-2.9l-5,7.1c-1.3-0.3-2.7-0.4-4.1-0.6l-2-8.3h-7.6l-2,8.3 c-1.5,0.2-2.9,0.4-4.3,0.7l-5.1-7.1l-7.1,2.9l1.5,8.6c-1.2,0.7-2.3,1.5-3.4,2.3l-7.4-4.6l-5.4,5.4l4.6,7.4c-0.9,1.2-1.7,2.4-2.4,3.6 l-8.6-1.4l-2.9,7.1l7.1,5c-0.3,1.3-0.8,2.7-1,4.1l-8.7,2v7.6l8.7,2c0.2,1.5,0.6,2.9,1,4.3l-7,5.1l3,7.1l8.6-1.5 c0.7,1.2,1.5,2.3,2.3,3.4l-4.6,7.4l5.4,5.4l7.4-4.6c1.2,0.9,2.4,1.7,3.6,2.4L28,93.5l7.1,2.9l5-7.1c1.3,0.3,2.7,0.8,4.1,1l2,8.7h7.6 l2-8.7c1.5-0.2,2.9-0.6,4.3-1l5.1,7l7.1-3l-1.5-8.6c1.2-0.7,2.3-1.5,3.4-2.3l7.4,4.6l5.4-5.4l-4.6-7.4c0.9-1.2,1.7-2.4,2.4-3.6 l8.6,1.4l2.9-7.1l-7.1-5c0.3-1.3,0.4-2.7,0.6-4.1L98,53.9z M73.5,55.2c-0.6,3.2-1.9,6.2-3.7,8.9c1.8,1.9,1.8,4.8,0,6.7 c-1.8,1.8-4.8,1.9-6.7,0c-2.6,1.8-5.7,3.1-8.9,3.7c0,2.6-2.1,4.7-4.7,4.7s-4.7-2.1-4.7-4.7c-3.2-0.6-6.2-1.9-8.9-3.7 c-1.9,1.8-4.8,1.8-6.7,0c-1.8-1.8-1.9-4.8,0-6.7c-1.8-2.6-3.1-5.7-3.7-8.9c-2.6,0-4.7-2.1-4.7-4.7s2.1-4.7,4.7-4.7 c0.6-3.2,1.9-6.2,3.7-8.9c-1.8-1.9-1.8-4.8,0-6.7c1.8-1.8,4.8-1.9,6.7,0c2.6-1.8,5.7-3.1,8.9-3.7c0-2.6,2.1-4.7,4.7-4.7 s4.7,2.1,4.7,4.7c3.2,0.6,6.2,1.9,8.9,3.7c1.9-1.8,4.8-1.8,6.7,0c1.8,1.8,1.9,4.8,0,6.7c1.8,2.6,3.1,5.7,3.7,8.9 c2.6,0,4.7,2.1,4.7,4.7S76.1,55.2,73.5,55.2z"/><path d="M68.8,50.5c0-2.3,1.6-4.2,3.8-4.7c-0.6-3-1.8-5.9-3.5-8.3c-1.8,1.2-4.3,1-6-0.6c-1.6-1.6-1.8-4.1-0.6-6 c-2.5-1.7-5.3-2.9-8.3-3.5c-0.4,2.2-2.4,3.8-4.7,3.8s-4.2-1.6-4.7-3.8c-3,0.6-5.9,1.8-8.3,3.5c1.2,1.8,1,4.3-0.6,6 c-1.6,1.6-4.1,1.8-6,0.6c-1.7,2.5-2.9,5.3-3.5,8.3c2.2,0.4,3.8,2.4,3.8,4.7s-1.6,4.2-3.8,4.7c0.6,3,1.8,5.9,3.5,8.3 c1.8-1.2,4.3-1,6,0.6c1.6,1.6,1.8,4.1,0.6,6c2.5,1.7,5.3,2.9,8.3,3.5c0.4-2.2,2.4-3.8,4.7-3.8s4.2,1.6,4.7,3.8 c3-0.6,5.9-1.8,8.3-3.5c-1.2-1.8-1-4.3,0.6-6c1.6-1.6,4.1-1.8,6-0.6c1.7-2.5,2.9-5.3,3.5-8.3C70.4,54.7,68.8,52.8,68.8,50.5z M49.5,62.2c-6.8,0-12.2-5.5-12.2-12.2s5.5-12.2,12.2-12.2S61.8,43.2,61.8,50S56.3,62.2,49.5,62.2z"/><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by jayson lim</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 125" version="1.1" xml:space="preserve" style="" x="0px" y="0px" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.41421"><g transform="matrix(1.9721,0,0,1.9952,-72.6849,-77.2885)"><path d="M68.801,43.391C64.516,42.007 59.904,42.007 55.62,43.391L57.024,47.738C56.163,48.016 55.327,48.363 54.522,48.775L52.441,44.707C48.432,46.759 45.171,50.02 43.12,54.028L47.188,56.109C46.776,56.914 46.429,57.751 46.151,58.611L41.803,57.207C40.42,61.491 40.42,66.103 41.803,70.388L46.151,68.984C46.429,69.844 46.776,70.68 47.188,71.485L43.12,73.567C45.171,77.575 48.432,80.836 52.441,82.887L54.522,78.82C55.327,79.232 56.163,79.578 57.024,79.856L55.62,84.204C59.904,85.588 64.516,85.588 68.801,84.204L67.396,79.856C68.257,79.578 69.093,79.232 69.898,78.82L71.98,82.887C75.988,80.836 79.249,77.575 81.3,73.567L77.233,71.485C77.645,70.68 77.991,69.844 78.269,68.984L82.617,70.388C84.001,66.103 84.001,61.491 82.617,57.207L78.269,58.611C77.991,57.751 77.645,56.914 77.233,56.109L81.3,54.028C79.249,50.02 75.988,46.759 71.98,44.707L69.898,48.775C69.093,48.363 68.257,48.016 67.396,47.738L68.801,43.391ZM62.21,57.799C65.521,57.799 68.209,60.487 68.209,63.797C68.209,67.108 65.521,69.796 62.21,69.796C58.899,69.796 56.212,67.108 56.212,63.797C56.212,60.487 58.899,57.799 62.21,57.799Z" style="" fill="black"/></g><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Veronika Krpciarova</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns:x="http://ns.adobe.com/Extensibility/1.0/" xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/" xmlns:graph="http://ns.adobe.com/Graphs/1.0/" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" style="enable-background:new 0 0 100 100;" xml:space="preserve"><switch><foreignObject requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/" x="0" y="0" width="1" height="1"/><g i:extraneous="self"><path d="M97.5,52.7v-5.4l-6.2-0.6c0-0.2,0-0.4,0-0.7l6-1.7l-0.9-5.3l-6.3,0.5c-0.1-0.2-0.1-0.4-0.2-0.6l5.7-2.7l-1.8-5.1l-6.1,1.6 c-0.1-0.2-0.2-0.4-0.3-0.6l5.1-3.6l-2.7-4.7l-5.7,2.6c-0.1-0.2-0.3-0.3-0.4-0.5l4.4-4.5l-3.5-4.1L79.5,21 c-0.2-0.2-0.3-0.3-0.5-0.5l3.6-5.2l-4.1-3.5L74,16.3c-0.2-0.1-0.3-0.3-0.5-0.4l2.6-5.7l-4.7-2.7l-3.6,5.1 c-0.2-0.1-0.4-0.2-0.6-0.3l1.6-6.1l-5.1-1.8L61,10.1c-0.2-0.1-0.4-0.1-0.6-0.2l0.5-6.3l-5.3-0.9l-1.7,6c-0.2,0-0.4,0-0.7,0 l-0.6-6.2h-5.4l-0.6,6.2c-0.2,0-0.4,0-0.7,0l-1.7-6l-5.3,0.9l0.5,6.3c-0.2,0.1-0.4,0.1-0.6,0.2l-2.7-5.7l-5.1,1.8l1.6,6.1 c-0.2,0.1-0.4,0.2-0.6,0.3l-3.6-5.1l-4.7,2.7l2.6,5.7c-0.2,0.1-0.3,0.3-0.5,0.4l-4.5-4.4l-4.1,3.5l3.6,5.2 c-0.2,0.2-0.3,0.3-0.5,0.5l-5.2-3.6l-3.5,4.1l4.4,4.5c-0.1,0.2-0.3,0.3-0.4,0.5l-5.7-2.6l-2.7,4.7l5.1,3.6 c-0.1,0.2-0.2,0.4-0.3,0.6l-6.1-1.6l-1.8,5.1l5.7,2.7c-0.1,0.2-0.1,0.4-0.2,0.6l-6.3-0.5l-0.9,5.3l6,1.7c0,0.2,0,0.4,0,0.7 l-6.2,0.6v5.4l6.2,0.6c0,0.2,0,0.4,0,0.7l-6,1.7l0.9,5.3l6.3-0.5c0.1,0.2,0.1,0.4,0.2,0.6l-5.7,2.7l1.8,5.1l6.1-1.6 c0.1,0.2,0.2,0.4,0.3,0.6l-5.1,3.6l2.7,4.7l5.7-2.6c0.1,0.2,0.3,0.3,0.4,0.5l-4.4,4.5l3.5,4.1l5.2-3.6c0.2,0.2,0.3,0.3,0.5,0.5 l-3.6,5.2l4.1,3.5l4.5-4.4c0.2,0.1,0.3,0.3,0.5,0.4l-2.6,5.7l4.7,2.7l3.6-5.1c0.2,0.1,0.4,0.2,0.6,0.3l-1.6,6.1l5.1,1.8l2.7-5.7 c0.2,0.1,0.4,0.1,0.6,0.2l-0.5,6.3l5.3,0.9l1.7-6c0.2,0,0.4,0,0.7,0l0.6,6.2h5.4l0.6-6.2c0.2,0,0.4,0,0.7,0l1.7,6l5.3-0.9 l-0.5-6.3c0.2-0.1,0.4-0.1,0.6-0.2l2.7,5.7l5.1-1.8l-1.6-6.1c0.2-0.1,0.4-0.2,0.6-0.3l3.6,5.1l4.7-2.7l-2.6-5.7 c0.2-0.1,0.3-0.3,0.5-0.4l4.5,4.4l4.1-3.5L79,79.5c0.2-0.2,0.3-0.3,0.5-0.5l5.2,3.6l3.5-4.1L83.7,74c0.1-0.2,0.3-0.3,0.4-0.5 l5.7,2.6l2.7-4.7l-5.1-3.6c0.1-0.2,0.2-0.4,0.3-0.6l6.1,1.6l1.8-5.1L89.9,61c0.1-0.2,0.1-0.4,0.2-0.6l6.3,0.5l0.9-5.3l-6-1.7 c0-0.2,0-0.4,0-0.7L97.5,52.7z M50,57.8c-4.3,0-7.8-3.5-7.8-7.8s3.5-7.8,7.8-7.8s7.8,3.5,7.8,7.8S54.3,57.8,50,57.8z M53.4,35.9 V15.7c10.2,1,19,6.3,24.7,14.2L60.5,40C58.6,38,56.2,36.5,53.4,35.9z M46.6,35.9c-2.8,0.7-5.3,2.1-7.2,4.1L21.9,29.9 c5.7-7.9,14.5-13.2,24.7-14.2V35.9z M18.5,35.7l17.6,10.1c-0.4,1.3-0.7,2.7-0.7,4.2c0,1.4,0.3,2.8,0.7,4.1L18.5,64.3 c-2-4.4-3.2-9.2-3.2-14.3C15.3,44.9,16.5,40.1,18.5,35.7z M39.5,60c1.9,2,4.4,3.5,7.2,4.1v20.2c-10.2-1-19.1-6.3-24.7-14.2 L39.5,60z M53.4,64.1c2.8-0.7,5.2-2.1,7.2-4.1l17.6,10.1C72.4,78,63.6,83.3,53.4,84.3V64.1z M81.5,64.3L63.9,54.1 c0.4-1.3,0.7-2.7,0.7-4.1c0-1.5-0.3-2.8-0.7-4.2l17.6-10.1c2,4.4,3.2,9.2,3.2,14.3C84.7,55.1,83.5,59.9,81.5,64.3z"/></g></switch><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Alice Design</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns:x="http://ns.adobe.com/Extensibility/1.0/" xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/" xmlns:graph="http://ns.adobe.com/Graphs/1.0/" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" style="enable-background:new 0 0 100 100;" xml:space="preserve"><switch><foreignObject requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/" x="0" y="0" width="1" height="1"/><g i:extraneous="self"><path d="M97.5,55.5V44.5l-10.3-1c-0.4-2.2-1-4.4-1.7-6.4l8.4-6l-5.5-9.6L79,25.7c-1.4-1.7-3-3.3-4.7-4.7l4.3-9.4L69,6.1l-6,8.4 c-2.1-0.8-4.2-1.3-6.4-1.7l-1-10.3H44.5l-1,10.3c-2.2,0.4-4.4,1-6.4,1.7l-6-8.4l-9.6,5.5l4.3,9.4c-1.7,1.4-3.3,3-4.7,4.7l-9.4-4.3 L6.1,31l8.4,6c-0.8,2.1-1.3,4.2-1.7,6.4l-10.3,1v11.1l10.3,1c0.4,2.2,1,4.4,1.7,6.4l-8.4,6l5.5,9.6l9.4-4.3c1.4,1.7,3,3.3,4.7,4.7 l-4.3,9.4l9.6,5.5l6-8.4c2.1,0.8,4.2,1.3,6.4,1.7l1,10.3h11.1l1-10.3c2.2-0.4,4.4-1,6.4-1.7l6,8.4l9.6-5.5L74.3,79 c1.7-1.4,3.3-3,4.7-4.7l9.4,4.3l5.5-9.6l-8.4-6c0.8-2.1,1.3-4.2,1.7-6.4L97.5,55.5z M78.7,50c0,3.6-0.7,7-2,10.2l-12.1-7 c0.2-1.1,0.4-2.1,0.4-3.3c0-1.1-0.1-2.2-0.4-3.3l12.1-7C78,43,78.7,46.4,78.7,50z M21.3,50c0-3.6,0.7-7.1,2-10.2l12.1,7 c-0.2,1.1-0.4,2.2-0.4,3.3c0,1.1,0.1,2.2,0.4,3.3l-12.1,7C22,57,21.3,53.6,21.3,50z M44,50c0-3.3,2.7-6,6-6c3.3,0,6,2.7,6,6 c0,3.3-2.7,6-6,6C46.7,56,44,53.3,44,50z M60.2,38.9c-1.6-1.5-3.5-2.6-5.6-3.2V21.7c7.1,1.1,13.3,4.9,17.7,10.3L60.2,38.9z M45.4,35.7c-2.1,0.7-4,1.8-5.6,3.2l-12-6.9c4.4-5.3,10.5-9.1,17.6-10.2V35.7z M39.9,61.1c1.6,1.5,3.5,2.5,5.6,3.2v14 c-7.1-1.1-13.3-4.9-17.6-10.2L39.9,61.1z M54.5,64.3c2.1-0.7,4-1.8,5.6-3.2l12,7c-4.4,5.4-10.5,9.1-17.7,10.2V64.3z"/></g></switch><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Alice Design</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 32 34" style="enable-background:new 0 0 32 34;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#000;}
|
||||
.st1{fill:#599EF4;}
|
||||
</style>
|
||||
<g id="layer_cog">
|
||||
<path class="st0" d="M31.6,16L29,15.5h0c-0.1,0-0.3-0.1-0.4-0.1c-0.2,0-0.3-0.2-0.4-0.3c-0.3-1.3-0.8-2.5-1.5-3.6
|
||||
c-0.1-0.2-0.1-0.4,0-0.5c0.5-0.8,1.1-1.6,1.6-2.5c0.2-0.2,0.2-0.6-0.1-0.8c-0.7-0.7-1.4-1.4-2-2c-0.2-0.2-0.5-0.2-0.7,0l-0.9,0.6
|
||||
l-1.8,1.1c-1.2,0.8-0.6,3.2,0,3.8c0.8,0.8,1.5,1.8,2,2.8c0.5,1,0.7,2.1,0.8,3.2c0,0.2,0,0.4,0,0.5s0,0.1,0,0.1c0,0.5,0,1.1-0.1,1.7
|
||||
c-0.2,1.2-0.6,2.4-1.2,3.4c-0.6,1.1-1.5,2-2.5,2.8c-1.7,1.3-3.8,2-5.9,2l0,0c-2.1,0-4.2-0.7-5.9-2c-1-0.8-1.8-1.7-2.5-2.8
|
||||
c-0.6-1-1.1-2.2-1.2-3.4c-0.1-0.5-0.1-1.1-0.1-1.7c0,0,0-0.1,0-0.1s0-0.3,0.1-0.5c0.2-2.3,1.2-4.4,2.8-6c0.6-0.6,1.2-3,0-3.8
|
||||
L7.3,6.2L6.4,5.6c-0.2-0.2-0.5-0.2-0.7,0c-0.7,0.7-1.3,1.4-2,2C3.5,7.8,3.5,8.2,3.7,8.4c0.5,0.8,1.1,1.6,1.6,2.5
|
||||
c0.1,0.2,0.1,0.4,0,0.5c-0.7,1.1-1.2,2.3-1.5,3.6c0,0.2-0.2,0.3-0.4,0.3c-0.1,0-0.3,0.1-0.4,0.1H3L0.4,16C0.2,16,0,16.3,0,16.5v2.9
|
||||
C0,19.7,0.2,20,0.4,20l1.6,0.3l1.4,0.3c0.1,0,0.3,0.1,0.3,0.3c0.3,1.3,0.8,2.5,1.5,3.6c0.1,0.2,0.1,0.4,0,0.5
|
||||
C4.8,25.7,4.4,26.3,4,27l-0.4,0.6c-0.2,0.2-0.1,0.6,0.1,0.7c0.7,0.7,1.4,1.4,2.1,2.1c0.1,0.1,0.2,0.2,0.3,0.1
|
||||
c0.1,0,0.3-0.1,0.4-0.2C6.7,30.2,7,30,7.2,29.9L9,28.7c0.1-0.1,0.3-0.1,0.4,0c1.1,0.7,2.4,1.2,3.6,1.5c0.2,0,0.3,0.2,0.3,0.4
|
||||
c0.2,1,0.4,2,0.6,3c0,0.3,0.3,0.4,0.5,0.4h2.9c0.3,0,0.5-0.2,0.5-0.4c0.2-1,0.4-2,0.6-3c0-0.2,0.2-0.3,0.3-0.4
|
||||
c1.3-0.3,2.5-0.8,3.6-1.5c0.1-0.1,0.3-0.1,0.4,0l1.8,1.2c0.2,0.2,0.5,0.3,0.7,0.5c0.1,0.1,0.3,0.1,0.4,0.2c0.1,0,0.3,0,0.4-0.1
|
||||
c0.7-0.7,1.4-1.4,2.1-2.1c0.2-0.2,0.3-0.5,0.1-0.7L28,26.9c-0.4-0.6-0.8-1.3-1.2-1.9c-0.1-0.2-0.1-0.4,0-0.5
|
||||
c0.7-1.1,1.2-2.4,1.5-3.6c0-0.1,0.1-0.2,0.3-0.3l1.4-0.3l1.6-0.3c0.3,0,0.4-0.3,0.4-0.5v-2.9C32,16.3,31.8,16,31.6,16z"/>
|
||||
</g>
|
||||
<g id="layer_flame">
|
||||
<path class="st1" d="M16.5,23.4c0.6-0.9,0.9-2.1,0.6-3.2c-0.1-0.7-0.4-1.4-0.9-1.9c0.1,0.4,0.1,0.8,0.2,1.2c0,0.3,0,0.6-0.1,0.9
|
||||
c-0.2,0.5-0.7,0.7-1.1,0.5c-0.1-0.1-0.2-0.1-0.3-0.3c-0.3-0.4-0.5-0.9-0.4-1.5c0-0.4,0.1-0.7,0.2-1.2c-0.1,0.1-0.3,0.1-0.4,0.2
|
||||
c-0.9,0.7-1.4,1.7-1.5,2.9c-0.1,1.5,0.2,3,1,4.3l0.1,0.2c-0.1,0-0.1,0-0.2-0.1c-2.8-1.2-4.6-4.1-4.5-7.3c0-1.3,0.4-2.6,1-3.7
|
||||
c0.6-0.7,1.1-1.5,1.5-2.4c0.2-0.6,0.4-1.3,0.5-2c0-0.2,0-0.4,0.1-0.6c0.3,0.6,0.5,1.3,0.6,2c0.1,0.7,0,1.5-0.2,2.2
|
||||
c0.4-0.3,0.8-0.7,1.1-1.1c0.6-0.9,0.9-2,1-3c0-0.8,0-1.5,0-2.3c0-0.8,0.1-1.5,0.2-2.3c0.3-1.6,1.5-2.9,2.9-3.3h0.1c0,0,0,0,0,0.1
|
||||
c-0.6,1.4-0.6,3,0,4.4c0.7,1.5,1.5,3,2.3,4.5c0.8,1.5,1.3,2.1,2,3.5c0.4,1.1,0.7,2.3,0.7,3.5c0,2-0.8,3.9-2.3,5.1
|
||||
c-0.3,0.2-0.6,0.5-0.9,0.6l0.1-0.3c0.5-1.3,0.6-2.7,0.3-4c-0.2-0.7-0.4-1.3-0.8-1.9c-0.3-0.5-0.6-0.9-0.9-1.4
|
||||
c-0.3-0.5-0.5-1.1-0.6-1.7c0-0.5,0-0.9,0-1.4c-0.1,0.1-0.2,0.3-0.3,0.5c-0.5,0.9-0.6,1.9-0.3,2.9c0.2,0.7,0.5,1.5,0.7,2.2
|
||||
c0.2,0.7,0.4,1.4,0.4,2.1C18.4,21.7,17.7,22.8,16.5,23.4L16.5,23.4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 32 34" style="enable-background:new 0 0 32 34;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#599EF4;}
|
||||
</style>
|
||||
<g id="layer_cog">
|
||||
<path class="st0" d="M31.6,16L29,15.5h0c-0.1,0-0.3-0.1-0.4-0.1c-0.2,0-0.3-0.2-0.4-0.3c-0.3-1.3-0.8-2.5-1.5-3.6
|
||||
c-0.1-0.2-0.1-0.4,0-0.5c0.5-0.8,1.1-1.6,1.6-2.5c0.2-0.2,0.2-0.6-0.1-0.8c-0.7-0.7-1.4-1.4-2-2c-0.2-0.2-0.5-0.2-0.7,0l-0.9,0.6
|
||||
l-1.8,1.1c-1.2,0.8-0.6,3.2,0,3.8c0.8,0.8,1.5,1.8,2,2.8c0.5,1,0.7,2.1,0.8,3.2c0,0.2,0,0.4,0,0.5s0,0.1,0,0.1c0,0.5,0,1.1-0.1,1.7
|
||||
c-0.2,1.2-0.6,2.4-1.2,3.4c-0.6,1.1-1.5,2-2.5,2.8c-1.7,1.3-3.8,2-5.9,2l0,0c-2.1,0-4.2-0.7-5.9-2c-1-0.8-1.8-1.7-2.5-2.8
|
||||
c-0.6-1-1.1-2.2-1.2-3.4c-0.1-0.5-0.1-1.1-0.1-1.7c0,0,0-0.1,0-0.1s0-0.3,0.1-0.5c0.2-2.3,1.2-4.4,2.8-6c0.6-0.6,1.2-3,0-3.8
|
||||
L7.3,6.2L6.4,5.6c-0.2-0.2-0.5-0.2-0.7,0c-0.7,0.7-1.3,1.4-2,2C3.5,7.8,3.5,8.2,3.7,8.4c0.5,0.8,1.1,1.6,1.6,2.5
|
||||
c0.1,0.2,0.1,0.4,0,0.5c-0.7,1.1-1.2,2.3-1.5,3.6c0,0.2-0.2,0.3-0.4,0.3c-0.1,0-0.3,0.1-0.4,0.1H3L0.4,16C0.2,16,0,16.3,0,16.5v2.9
|
||||
C0,19.7,0.2,20,0.4,20l1.6,0.3l1.4,0.3c0.1,0,0.3,0.1,0.3,0.3c0.3,1.3,0.8,2.5,1.5,3.6c0.1,0.2,0.1,0.4,0,0.5
|
||||
C4.8,25.7,4.4,26.3,4,27l-0.4,0.6c-0.2,0.2-0.1,0.6,0.1,0.7c0.7,0.7,1.4,1.4,2.1,2.1c0.1,0.1,0.2,0.2,0.3,0.1
|
||||
c0.1,0,0.3-0.1,0.4-0.2C6.7,30.2,7,30,7.2,29.9L9,28.7c0.1-0.1,0.3-0.1,0.4,0c1.1,0.7,2.4,1.2,3.6,1.5c0.2,0,0.3,0.2,0.3,0.4
|
||||
c0.2,1,0.4,2,0.6,3c0,0.3,0.3,0.4,0.5,0.4h2.9c0.3,0,0.5-0.2,0.5-0.4c0.2-1,0.4-2,0.6-3c0-0.2,0.2-0.3,0.3-0.4
|
||||
c1.3-0.3,2.5-0.8,3.6-1.5c0.1-0.1,0.3-0.1,0.4,0l1.8,1.2c0.2,0.2,0.5,0.3,0.7,0.5c0.1,0.1,0.3,0.1,0.4,0.2c0.1,0,0.3,0,0.4-0.1
|
||||
c0.7-0.7,1.4-1.4,2.1-2.1c0.2-0.2,0.3-0.5,0.1-0.7L28,26.9c-0.4-0.6-0.8-1.3-1.2-1.9c-0.1-0.2-0.1-0.4,0-0.5
|
||||
c0.7-1.1,1.2-2.4,1.5-3.6c0-0.1,0.1-0.2,0.3-0.3l1.4-0.3l1.6-0.3c0.3,0,0.4-0.3,0.4-0.5v-2.9C32,16.3,31.8,16,31.6,16z"/>
|
||||
</g>
|
||||
<g id="layer_flame">
|
||||
<path class="st1" d="M16.5,23.4c0.6-0.9,0.9-2.1,0.6-3.2c-0.1-0.7-0.4-1.4-0.9-1.9c0.1,0.4,0.1,0.8,0.2,1.2c0,0.3,0,0.6-0.1,0.9
|
||||
c-0.2,0.5-0.7,0.7-1.1,0.5c-0.1-0.1-0.2-0.1-0.3-0.3c-0.3-0.4-0.5-0.9-0.4-1.5c0-0.4,0.1-0.7,0.2-1.2c-0.1,0.1-0.3,0.1-0.4,0.2
|
||||
c-0.9,0.7-1.4,1.7-1.5,2.9c-0.1,1.5,0.2,3,1,4.3l0.1,0.2c-0.1,0-0.1,0-0.2-0.1c-2.8-1.2-4.6-4.1-4.5-7.3c0-1.3,0.4-2.6,1-3.7
|
||||
c0.6-0.7,1.1-1.5,1.5-2.4c0.2-0.6,0.4-1.3,0.5-2c0-0.2,0-0.4,0.1-0.6c0.3,0.6,0.5,1.3,0.6,2c0.1,0.7,0,1.5-0.2,2.2
|
||||
c0.4-0.3,0.8-0.7,1.1-1.1c0.6-0.9,0.9-2,1-3c0-0.8,0-1.5,0-2.3c0-0.8,0.1-1.5,0.2-2.3c0.3-1.6,1.5-2.9,2.9-3.3h0.1c0,0,0,0,0,0.1
|
||||
c-0.6,1.4-0.6,3,0,4.4c0.7,1.5,1.5,3,2.3,4.5c0.8,1.5,1.3,2.1,2,3.5c0.4,1.1,0.7,2.3,0.7,3.5c0,2-0.8,3.9-2.3,5.1
|
||||
c-0.3,0.2-0.6,0.5-0.9,0.6l0.1-0.3c0.5-1.3,0.6-2.7,0.3-4c-0.2-0.7-0.4-1.3-0.8-1.9c-0.3-0.5-0.6-0.9-0.9-1.4
|
||||
c-0.3-0.5-0.5-1.1-0.6-1.7c0-0.5,0-0.9,0-1.4c-0.1,0.1-0.2,0.3-0.3,0.5c-0.5,0.9-0.6,1.9-0.3,2.9c0.2,0.7,0.5,1.5,0.7,2.2
|
||||
c0.2,0.7,0.4,1.4,0.4,2.1C18.4,21.7,17.7,22.8,16.5,23.4L16.5,23.4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -3,14 +3,10 @@
|
||||
@import '_mixins';
|
||||
@import '_fonts';
|
||||
|
||||
// Theme fonts
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Montserrat:wght@600;700&display=swap');
|
||||
|
||||
// RESETS
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
font-size: $font-size-root;
|
||||
font-family: $font-family;
|
||||
font-family: $font-family !important;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
line-height: 1.15;
|
||||
@@ -48,8 +44,6 @@ var {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// BUTTONS
|
||||
|
||||
// MISC.
|
||||
hr {
|
||||
border: none;
|
||||
|
||||
@@ -1,112 +1,33 @@
|
||||
<template>
|
||||
<nav class="navbar navbar-dark">
|
||||
<ul v-if="links.length != 0">
|
||||
<li v-for="(link, index) in links" :key="index">
|
||||
<router-link v-bind:to="link.url">{{ link.text }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
||||
<router-link :to="'/'" class="navbar-brand font-display font-weight-700">Auracle</router-link>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto" v-if="links.length != 0">
|
||||
<li class="nav-item" v-for="(link, index) in links" :key="index">
|
||||
<router-link :to="link.url" class="nav-link">{{ link.text }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'navbar',
|
||||
data() {
|
||||
return {
|
||||
links: [
|
||||
{
|
||||
id: 0,
|
||||
text: 'Accueil',
|
||||
url: '/',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
text: 'Sortilèges',
|
||||
url: '/spells',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text: 'Monde',
|
||||
url: '/world',
|
||||
},
|
||||
]
|
||||
export default {
|
||||
name: 'navbar',
|
||||
data() {
|
||||
return {
|
||||
links: [
|
||||
{
|
||||
text: 'Sortilèges',
|
||||
url: '/spells',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
padding-left: 2rem;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
li {
|
||||
a {
|
||||
position: relative;
|
||||
margin-right: 1rem;
|
||||
padding: 1.5rem;
|
||||
display: block;
|
||||
font-size: .83rem;
|
||||
font-weight: $font-bold;
|
||||
text-transform: uppercase;
|
||||
text-shadow: none;
|
||||
transition: text-shadow .5s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 3px;
|
||||
content: '';
|
||||
box-shadow: none;
|
||||
transition: width .5s cubic-bezier(0.645, 0.045, 0.355, 1), box-shadow .5s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.router-link-exact-active {
|
||||
@include blue-glow-text(.5);
|
||||
&:after {
|
||||
width: 80%;
|
||||
transition: width .5s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
@include blue-glow-box(.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.navbar-light {
|
||||
ul {
|
||||
li {
|
||||
a {
|
||||
color: $black;
|
||||
&:after {
|
||||
background: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.navbar-dark {
|
||||
ul {
|
||||
li {
|
||||
a {
|
||||
color: $white;
|
||||
&:after {
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
|
||||
166
client/src/components/spells/add-spell-card.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<b-modal
|
||||
ref="add_spell_modal"
|
||||
size="lg"
|
||||
modal-class="b-modal">
|
||||
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<div class="h1 modal-title font-display font-weight-bold" id="spell_show_edit_modal">
|
||||
<div class="line-height-100">
|
||||
<span v-if="!spell.name">Nouveau sort</span>
|
||||
<span v-if="spell.name">{{spell.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @click="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template v-slot:default>
|
||||
<form id="add-spell" @submit="addSpell">
|
||||
<div class="form-group">
|
||||
<label for="spell_name" class="font-weight-bold col-form-label">Nom :</label>
|
||||
<input type="text" class="form-control" name="spell_name" id="spell_name" placeholder="(256 caractères max.)" v-model="spell.name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_description" class="font-weight-bold col-form-label">Description :</label>
|
||||
<textarea class="form-control" name="spell_description" id="spell_description" placeholder="(2048 caractères max.)" v-model="spell.description"></textarea>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="checkbox" class="form-check-input" id="spell_ritual" name="spell_ritual" v-model="spell.is_ritual">
|
||||
<label for="spell_ritual" class="font-weight-bold col-form-label">Rituel ? </label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_level" class="font-weight-bold col-form-label">Niveau :</label>
|
||||
<input type="number" class="form-control" name="spell_level" id="spell_level" min="0" max="100" step="1" placeholder="(Nombre entier de 0 à 100)" v-model="spell.level">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_schools" class="font-weight-bold col-form-label">École(s) :</label>
|
||||
<select class="form-control" id="spell_schools" name="spell_schools" multiple v-model="spell.schools">
|
||||
<option v-for="(school, index) in all_schools" :key="index" :value="'school_' + school.id">{{school.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_charge" class="font-weight-bold col-form-label">Charge :</label>
|
||||
<input type="number" class="form-control" name="spell_charge" id="spell_charge" min="0" max="100" step="1" placeholder="(Nombre entier de 0 à 100)" v-model="spell.charge">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_ingredients" class="font-weight-bold col-form-label">Ingrédient(s) :</label>
|
||||
<select class="form-control" id="spell_ingredients" name="spell_ingredients" multiple v-model="spell.ingredients">
|
||||
<option v-for="(ingredient,index) in all_ingredients" :key="index" :value="'ingredient_' + ingredient.id">{{ingredient.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_cost" class="font-weight-bold col-form-label">Coût :</label>
|
||||
<input type="text" class="form-control" name="spell_cost" id="spell_cost" placeholder="(32 caractères max.)" v-model="spell.cost">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_variables" class="font-weight-bold col-form-label">Variables(s) :</label>
|
||||
<select class="form-control" id="spell_variables" name="spell_variables" multiple v-model="spell.variables">
|
||||
<option v-for="(variable,index) in all_variables" :key="index" :value="'variable_' + variable.id">{{variable.description}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<template v-slot:modal-footer="{ close }">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" @click="close()">Fermer</button>
|
||||
<input type="submit" class="btn btn-primary" value="Enregistrer" form="add-spell">
|
||||
</template>
|
||||
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "~/api/repositories"
|
||||
|
||||
const Spells = RepositoryFactory.get('spells')
|
||||
const Schools = RepositoryFactory.get('schools')
|
||||
const Variables = RepositoryFactory.get('variables')
|
||||
const Ingredients = RepositoryFactory.get('ingredients')
|
||||
|
||||
export default {
|
||||
'name': 'add-spell-card',
|
||||
data() {
|
||||
return {
|
||||
spell: {
|
||||
type: Object,
|
||||
name: "",
|
||||
description: "",
|
||||
is_ritual: false,
|
||||
level: 0,
|
||||
cost: "0",
|
||||
charge: 0,
|
||||
schools: [],
|
||||
variables: [],
|
||||
ingredients: [],
|
||||
},
|
||||
all_schools: [],
|
||||
all_variables: [],
|
||||
all_ingredients: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Gets all relevant info for multiple selects
|
||||
let fetchSchools = Schools.getSchools()
|
||||
let fetchVariables = Variables.getVariables()
|
||||
let fetchIngredients = Ingredients.getIngredients()
|
||||
|
||||
Promise.all([fetchSchools, fetchVariables, fetchIngredients])
|
||||
.then(v => {
|
||||
this.all_schools = v[0].data
|
||||
this.all_variables = v[1].data
|
||||
this.all_ingredients = v[2].data
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.$refs["add_spell_modal"].show()
|
||||
this.$root.$on('bv::modal::hide', () => {
|
||||
this.$emit('cancelAdd')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
addSpell(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let schoolsData = Object.values(this.spell.schools).map(v => { return parseInt(v.slice(7)) })
|
||||
let variablesData = Object.values(this.spell.variables).map(v => { return parseInt(v.slice(9)) })
|
||||
let ingredientsData = Object.values(this.spell.ingredients).map(v => { return parseInt(v.slice(11)) })
|
||||
|
||||
let data = {
|
||||
name: this.spell.name,
|
||||
description: this.spell.description,
|
||||
is_ritual: !!+this.spell.is_ritual,
|
||||
level: parseInt(this.spell.level),
|
||||
cost: this.spell.cost,
|
||||
charge: parseInt(this.spell.charge),
|
||||
schools: schoolsData,
|
||||
variables: variablesData,
|
||||
ingredients: ingredientsData,
|
||||
}
|
||||
|
||||
console.log(data)
|
||||
|
||||
Spells.addSpell(data)
|
||||
.then(v => {
|
||||
this.$emit('addSpell', v.data)
|
||||
this.$refs["add_spell_modal"].hide()
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
textarea, select {
|
||||
min-height: 10rem;
|
||||
max-height: 20rem;
|
||||
}
|
||||
</style>
|
||||
199
client/src/components/spells/edit-spell-card.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<b-modal
|
||||
ref="edit_spell_modal"
|
||||
size="lg"
|
||||
modal-class="b-modal"
|
||||
:spell="computeSpell">
|
||||
|
||||
<template v-slot:modal-header="{ close }">
|
||||
<div class="h1 modal-title font-display font-weight-bold" id="spell_show_edit_modal"><div class="line-height-100">{{spell.name}}#{{spell.id}}</div></div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @click="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template v-slot:default>
|
||||
<form id="update-spell" @submit="updateSpell">
|
||||
<div class="form-group">
|
||||
<label for="spell_name" class="font-weight-bold col-form-label">Nom :</label>
|
||||
<input type="text" class="form-control" name="spell_name" id="spell_name" placeholder="(256 caractères max.)" v-model="spell.name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_description" class="font-weight-bold col-form-label">Description :</label>
|
||||
<textarea class="form-control" name="spell_description" id="spell_description" placeholder="(2048 caractères max.)" v-model="spell.description"></textarea>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input type="checkbox" class="form-check-input" id="spell_ritual" name="spell_ritual" v-model="spell.is_ritual">
|
||||
<label for="spell_ritual" class="font-weight-bold col-form-label">Rituel ? </label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_level" class="font-weight-bold col-form-label">Niveau :</label>
|
||||
<input type="number" class="form-control" name="spell_level" id="spell_level" min="0" max="100" step="1" placeholder="(Nombre entier de 0 à 100)" v-model="spell.level">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_schools" class="font-weight-bold col-form-label">École(s) :</label>
|
||||
<select class="form-control" id="spell_schools" name="spell_schools" multiple v-model="spell.spell_school_ids_value">
|
||||
<option v-for="(school, index) in all_schools" :key="index" :value="'school_' + school.id">{{school.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_charge" class="font-weight-bold col-form-label">Charge :</label>
|
||||
<input type="number" class="form-control" name="spell_charge" id="spell_charge" min="0" max="100" step="1" placeholder="(Nombre entier de 0 à 100)" v-model="spell.charge">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_ingredients" class="font-weight-bold col-form-label">Ingrédient(s) :</label>
|
||||
<select class="form-control" id="spell_ingredients" name="spell_ingredients" multiple v-model="spell.spell_ingredient_ids_value">
|
||||
<option v-for="(ingredient,index) in all_ingredients" :key="index" :value="'ingredient_' + ingredient.id">{{ingredient.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_cost" class="font-weight-bold col-form-label">Coût :</label>
|
||||
<input type="text" class="form-control" name="spell_cost" id="spell_cost" placeholder="(32 caractères max.)" v-model="spell.cost">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="spell_variables" class="font-weight-bold col-form-label">Variables(s) :</label>
|
||||
<select class="form-control" id="spell_variables" name="spell_variables" multiple v-model="spell.spell_variable_ids_value">
|
||||
<option v-for="(variable,index) in all_variables" :key="index" :value="'variable_' + variable.id">{{variable.description}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<template v-slot:modal-footer="{ close }">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" @click="close()">Fermer</button>
|
||||
<input type="submit" class="btn btn-primary" value="Enregistrer" form="update-spell">
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "~/api/repositories"
|
||||
const Spells = RepositoryFactory.get('spells')
|
||||
const Schools = RepositoryFactory.get('schools')
|
||||
const Variables = RepositoryFactory.get('variables')
|
||||
const Ingredients = RepositoryFactory.get('ingredients')
|
||||
|
||||
export default {
|
||||
name: 'edit-spell-card',
|
||||
props: {
|
||||
spell: {
|
||||
type: Object,
|
||||
required: true,
|
||||
id: Number,
|
||||
name: String,
|
||||
description: String,
|
||||
is_ritual: Boolean,
|
||||
schools: Array,
|
||||
variables: Array,
|
||||
ingredients: Array,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
all_schools: [],
|
||||
all_variables: [],
|
||||
all_ingredients: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computeSpell() {
|
||||
let output = this.spell
|
||||
output.spell_school_ids = []
|
||||
output.spell_variable_ids = []
|
||||
output.spell_ingredient_ids = []
|
||||
output.spell_school_ids_value = []
|
||||
output.spell_variable_ids_value = []
|
||||
output.spell_ingredient_ids_value = []
|
||||
|
||||
output.schools.forEach(element => {
|
||||
output.spell_school_ids.push(element['id'])
|
||||
})
|
||||
output.variables.forEach(element => {
|
||||
output.spell_variable_ids.push(element['id'])
|
||||
})
|
||||
output.ingredients.forEach(element => {
|
||||
output.spell_ingredient_ids.push(element['id'])
|
||||
})
|
||||
|
||||
output.spell_school_ids.forEach(element => {
|
||||
output.spell_school_ids_value.push("school_" + element)
|
||||
})
|
||||
output.spell_variable_ids.forEach(element => {
|
||||
output.spell_variable_ids_value.push("variable_" + element)
|
||||
})
|
||||
output.spell_ingredient_ids.forEach(element => {
|
||||
output.spell_ingredient_ids_value.push("ingredient_" + element)
|
||||
})
|
||||
|
||||
return output
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Gets all relevant info for multiple selects
|
||||
let fetchSchools = Schools.getSchools()
|
||||
let fetchVariables = Variables.getVariables()
|
||||
let fetchIngredients = Ingredients.getIngredients()
|
||||
|
||||
Promise.all([fetchSchools, fetchVariables, fetchIngredients])
|
||||
.then(v => {
|
||||
this.all_schools = v[0].data
|
||||
this.all_variables = v[1].data
|
||||
this.all_ingredients = v[2].data
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.$refs["edit_spell_modal"].show()
|
||||
this.$root.$on('bv::modal::hide', () => {
|
||||
this.$emit('editSpell', {})
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
computeSpell: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.$refs["edit_spell_modal"].show()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateSpell(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let schoolsData = Object.values(this.spell.spell_school_ids_value).map(v => { return parseInt(v.slice(7)) })
|
||||
let variablesData = Object.values(this.spell.spell_variable_ids_value).map(v => { return parseInt(v.slice(9)) })
|
||||
let ingredientsData = Object.values(this.spell.spell_ingredient_ids_value).map(v => { return parseInt(v.slice(11)) })
|
||||
|
||||
let data = {
|
||||
name: this.spell.name,
|
||||
description: this.spell.description,
|
||||
is_ritual: !!+this.spell.is_ritual,
|
||||
level: parseInt(this.spell.level),
|
||||
cost: this.spell.cost,
|
||||
charge: parseInt(this.spell.charge),
|
||||
schools: schoolsData,
|
||||
variables: variablesData,
|
||||
ingredients: ingredientsData,
|
||||
}
|
||||
|
||||
Spells.updateSpell(this.spell.id, data)
|
||||
.then(v => {
|
||||
this.$emit('updateSpell', v.data)
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
textarea, select {
|
||||
min-height: 10rem;
|
||||
max-height: 20rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,208 +1,135 @@
|
||||
<template>
|
||||
<article class="spell-card">
|
||||
<div class="content">
|
||||
<header>
|
||||
<h3>{{ name }}</h3>
|
||||
</header>
|
||||
<section class="casting-header">
|
||||
<div class="casting-infos">
|
||||
<div class="type">
|
||||
<div class="level">
|
||||
<div class="label">
|
||||
<span>LV.</span>
|
||||
<br>
|
||||
<span>MIN</span>
|
||||
</div>
|
||||
<var>{{ level }}</var>
|
||||
</div>
|
||||
<ul class="schools">
|
||||
<li v-for="(school, index) in schools" :key="index">
|
||||
{{ school.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cost">
|
||||
<span class="label">Malus</span>
|
||||
<var>{{ cost }}</var>
|
||||
</div>
|
||||
<div class="charge">
|
||||
<span class="label">Charge</span>
|
||||
<var>{{ charge }}</var>
|
||||
</div>
|
||||
<div
|
||||
:class="main_school"
|
||||
class="col-12 col-sm-6 col-lg-4 col-xl-3 mb-4 grid-item grid-sizer">
|
||||
<div class="spellcard bg-white p-4 rounded text-dark shadow" style="border-left-width:4px;border-left-style:solid;">
|
||||
|
||||
<div :title="spell.name" class="h3 font-display font-weight-bold text-wrap word-break" style="line-height:100%;">
|
||||
<a class="text-decoration-none" href="#">{{spell.name}}</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-weight-700 text-muted d-inline-block">Niveau {{spell.level}}</div>
|
||||
<span> · </span>
|
||||
<div class="text-muted d-inline-block">
|
||||
<span v-for="(school,index) in spell.schools" :key="index">
|
||||
<span v-if="index!=0">, </span>
|
||||
<a :href="'/school/'+school.id" class="text-secondary">{{school.name}}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="casting-ingredients">
|
||||
<p class="title">Nécessite :</p>
|
||||
<p class="content">
|
||||
<span v-for="(ingredient, index) in ingredients" :key="index">
|
||||
{{ ingredient.name }}<span v-if="index != Object.keys(ingredients).length - 1">, </span>
|
||||
</div>
|
||||
|
||||
<div v-if="spell.charge!=0" class="small font-weight-bold">
|
||||
<span>Charge {{spell.charge}} tour(s)</span>
|
||||
</div>
|
||||
|
||||
<div v-if="spell.ingredients.length>0" class="small">
|
||||
<span class="font-weight-bold">Nécessite</span>
|
||||
<span v-for="(ingredient,index) in spell.ingredients" :key="index">
|
||||
<span v-if="index!=0">, </span>
|
||||
<span>{{ingredient.name}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-clipboard="spell.description"
|
||||
:id="'spell_description_' + spell.id"
|
||||
v-b-tooltip.click
|
||||
placement="bottom"
|
||||
title="Description copiée !"
|
||||
class="small text-muted font-italic mt-2">
|
||||
<span>{{spell.description}}</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="font-weight-bold d-inline-block"><span>Coût </span>{{spell.cost}}</div>
|
||||
<div v-if="spell.variables.length>0" class="small d-inline-block">, où :</div>
|
||||
<div class=small>
|
||||
<span v-for="(variable,index) in spell.variables" :key="index">
|
||||
<span class="font-weight-bold">
|
||||
<span v-if="index!=0"><br></span>
|
||||
<span>{{String.fromCharCode(120+index)}}</span>
|
||||
</span>
|
||||
</p>
|
||||
<span> = {{variable.description}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<hr>
|
||||
<section class="spell-description">
|
||||
<p>{{ description }}</p>
|
||||
</section>
|
||||
<hr>
|
||||
<footer v-if="variables">
|
||||
<div class="label">Variables</div>
|
||||
<div class="variables">
|
||||
<div v-for="(variable, index) in variables" :key="index" class="xyz">
|
||||
<span>{{ variable.name }}</span>
|
||||
<var>{{ variable.description }}</var>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<a class="h5 text-secondary mr-1">
|
||||
<i class="mad" @click="editSpell(spell)">edit</i>
|
||||
</a>
|
||||
<a class="h5 text-danger">
|
||||
<i class="mad" @click="deleteSpell(spell)">delete</i>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "~/api/repositories"
|
||||
const Spells = RepositoryFactory.get('spells')
|
||||
|
||||
export default {
|
||||
name: 'spell-card',
|
||||
props: {
|
||||
name: String,
|
||||
description: String,
|
||||
level: Number,
|
||||
charge: Number,
|
||||
cost: String,
|
||||
schools: Array,
|
||||
ingredients: Array,
|
||||
variables: Array,
|
||||
spell: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isFlipped: false,
|
||||
main_school: this.spell.schools[0].name,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.main_school = this.main_school.toLowerCase();
|
||||
},
|
||||
methods: {
|
||||
toggleCard: async function() {
|
||||
this.isFlipped = !this.isFlipped;
|
||||
editSpell(spell) {
|
||||
this.$emit('editSpell', spell)
|
||||
},
|
||||
deleteSpell(spell) {
|
||||
Spells.deleteSpell(this.spell.id)
|
||||
.then(() => {
|
||||
this.$emit('deleteSpell', spell)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.spell-card {
|
||||
max-width: 35rem;
|
||||
padding: .5rem;
|
||||
margin: .5rem auto .5rem;
|
||||
background: $spell-card--innerborder;
|
||||
border: solid 1px rgba($primary--base, .5);
|
||||
border-radius: .5rem;
|
||||
box-shadow: .0 .0 .75rem rgba($primary--base, .25);
|
||||
|
||||
> .content {
|
||||
max-width: 35rem;
|
||||
height: 100%;
|
||||
padding: .75rem;
|
||||
background: $white;
|
||||
|
||||
header {
|
||||
h3 {
|
||||
padding: .5rem;
|
||||
text-align: center;
|
||||
color: $white;
|
||||
text-transform: uppercase;
|
||||
background: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.casting-header {
|
||||
display: flex;
|
||||
> * {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.casting-infos {
|
||||
margin: .75rem 0;
|
||||
padding-right: .75rem;
|
||||
border-right: 1px solid rgba($black, .25);
|
||||
.type {
|
||||
margin-bottom: .5rem;
|
||||
display: flex;
|
||||
> * {
|
||||
width: 50%;
|
||||
}
|
||||
.level {
|
||||
.label {
|
||||
margin-right: .25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
var {
|
||||
display: inline-block;
|
||||
font-size: 2.3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cost, .charge {
|
||||
font-size: 0.9rem;
|
||||
> * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.label {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
var {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.casting-ingredients {
|
||||
margin: .75rem 0;
|
||||
padding-left: .75rem;
|
||||
.title {
|
||||
margin-bottom: .25rem;
|
||||
font-size: .9rem;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.content {
|
||||
padding-top: 1rem;
|
||||
font-size: .85rem;
|
||||
text-align: justify;
|
||||
text-align-last: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spell-description {
|
||||
padding: .75rem 0;
|
||||
font-size: .85rem;
|
||||
p {
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: .75rem 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.label {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.variables {
|
||||
.xyz {
|
||||
span {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.spell-card {
|
||||
@mixin colorschool($sname,$scolor) {
|
||||
&.#{$sname} {
|
||||
.spellcard { border-left-color: $scolor; }
|
||||
.h3>a { color: $scolor; }
|
||||
}
|
||||
}
|
||||
|
||||
@include colorschool(lumomancie,#babaa4);
|
||||
@include colorschool(vitamancie,#57ab6e);
|
||||
@include colorschool(obstrumancie,#bd4a66);
|
||||
@include colorschool(tenebromancie,#404842);
|
||||
@include colorschool(necromancie,#5d4777);
|
||||
@include colorschool(morbomancie,#d8733d);
|
||||
@include colorschool(pyromancie,#b6362a);
|
||||
@include colorschool(hydromancie,#3f68c7);
|
||||
@include colorschool(electromancie,#cd9731);
|
||||
@include colorschool(terramancie,#7e5540);
|
||||
@include colorschool(caelomancie,#a8a8a8);
|
||||
@include colorschool(légimancie,#5dbabd);
|
||||
@include colorschool(illusiomancie,#9f63a1);
|
||||
@include colorschool(cruciomancie,#252451);
|
||||
@include colorschool(chronomancie,#79896a);
|
||||
@include colorschool(spatiomancie,#2d4776);
|
||||
@include colorschool(kénomancie,#101010);
|
||||
@include colorschool(lutomancie,#4e2827);
|
||||
@include colorschool(échomancie,#6d9fd1);
|
||||
@include colorschool(protomancie,#4f5751);
|
||||
@include colorschool(rebumancie,#8e7245);
|
||||
@include colorschool(vocamancie,#247864);
|
||||
@include colorschool(somamancie,#976c67);
|
||||
@include colorschool(antimancie,#ad95c1);
|
||||
}
|
||||
</style>
|
||||
@@ -1,26 +1,44 @@
|
||||
<template>
|
||||
<div class="spells-list">
|
||||
<spell-card v-for="(spell, index) in spells" :key="index" v-bind="spell"/>
|
||||
<div class="spell-list-wrapper">
|
||||
<button type="button" class="btn font-display font-weight-bold btn-lg btn-outline-dark btn-block shadow-sm mb-4" @click="showAdd">
|
||||
<i class="mad">add</i>
|
||||
<span>Ajouter un sort</span>
|
||||
</button>
|
||||
<div v-masonry transition-duration=".5s" item-selector=".spell-card" class="row spells-list">
|
||||
<spell-card v-masonry-tile class="spell-card" v-for="(spell) in spells" :key="spell.id" v-bind:spell="spell" @editSpell="editSpell" @deleteSpell="deleteSpell"/>
|
||||
</div>
|
||||
<edit-spell-card v-if="Object.keys(current_edit_spell).length > 0" v-bind:spell="current_edit_spell" @editSpell="editSpell" @updateSpell="updateSpell"/>
|
||||
<add-spell-card v-if="adding_spell" @cancelAdd="cancelAdd" @addSpell="addSpell"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
// Components
|
||||
import spellcard from "./spell-card"
|
||||
import SpellCard from "./spell-card"
|
||||
import EditSpellCard from "./edit-spell-card"
|
||||
import AddSpellCard from "./add-spell-card"
|
||||
|
||||
// API
|
||||
import { RepositoryFactory } from "../../../api/repositories"
|
||||
const spellsRepository = RepositoryFactory.get('spells')
|
||||
import { RepositoryFactory } from "~/api/repositories"
|
||||
const Spells = RepositoryFactory.get('spells')
|
||||
const Schools = RepositoryFactory.get('schools')
|
||||
|
||||
export default {
|
||||
name: 'spellslist',
|
||||
components: {
|
||||
'spell-card': spellcard,
|
||||
'spell-card': SpellCard,
|
||||
'edit-spell-card': EditSpellCard,
|
||||
'add-spell-card': AddSpellCard,
|
||||
},
|
||||
props: {
|
||||
school_id: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
spells: []
|
||||
spells: [],
|
||||
current_edit_spell: {},
|
||||
adding_spell: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -28,20 +46,55 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async fetchSpells() {
|
||||
const { data } = await spellsRepository.getSpells()
|
||||
return data
|
||||
if (!this.school_id) {
|
||||
const { data } = await Spells.getSpells()
|
||||
return data
|
||||
} else {
|
||||
const { data } = await Schools.getSpellsFromSchool(this.school_id)
|
||||
return data.spells
|
||||
}
|
||||
},
|
||||
async computeSpells() {
|
||||
this.loading = true
|
||||
const displaySpells = await this.fetchSpells();
|
||||
const displaySpells = await this.fetchSpells()
|
||||
this.loading = false
|
||||
this.spells = displaySpells.slice(0, 10)
|
||||
this.spells = displaySpells
|
||||
},
|
||||
editSpell(e) {
|
||||
this.current_edit_spell = e;
|
||||
},
|
||||
showAdd() {
|
||||
this.adding_spell = true
|
||||
},
|
||||
cancelAdd() {
|
||||
this.adding_spell = false
|
||||
},
|
||||
addSpell(e) {
|
||||
Spells.getSpell(e.id)
|
||||
.then(v => {
|
||||
this.spells.unshift(v.data)
|
||||
})
|
||||
.then(() => {
|
||||
this.adding_spell = false
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
updateSpell(e) {
|
||||
let oldSpell = this.spells.find(x => x.id === e.id)
|
||||
this.spells.splice(this.spells.indexOf(oldSpell), 1, e)
|
||||
this.current_edit_spell = {}
|
||||
},
|
||||
deleteSpell(e) {
|
||||
this.spells.splice(this.spells.indexOf(e), 1)
|
||||
this.current_edit_spell = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -1,15 +1,54 @@
|
||||
// Core
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import App from './app.vue'
|
||||
|
||||
// Auth
|
||||
require('dotenv').config()
|
||||
|
||||
// Router
|
||||
import routes from './routes'
|
||||
Vue.use(VueRouter)
|
||||
|
||||
// Jquery
|
||||
import jquery from 'jquery'
|
||||
window.$ = jquery
|
||||
window.jquery = jquery
|
||||
|
||||
// Global styles
|
||||
import './assets/scss/_global.scss'
|
||||
|
||||
// Bootstrap
|
||||
import 'bootstrap/dist/css/bootstrap.css'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
import 'bootstrap/dist/js/bootstrap.js'
|
||||
import { BootstrapVue } from 'bootstrap-vue'
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
// Masonry
|
||||
import { VueMasonryPlugin } from 'vue-masonry';
|
||||
Vue.use(VueMasonryPlugin)
|
||||
|
||||
// Clipboard plugin
|
||||
import clipboard from 'v-clipboard'
|
||||
Vue.use(clipboard)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(VueRouter)
|
||||
|
||||
var filter = function(text, length, clamp){
|
||||
clamp = clamp || '...';
|
||||
var node = document.createElement('div');
|
||||
node.innerHTML = text;
|
||||
var content = node.textContent;
|
||||
return content.length > length ? content.slice(0, length) + clamp : content;
|
||||
};
|
||||
Vue.filter('truncate', filter);
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes,
|
||||
linkActiveClass: "",
|
||||
linkExactActiveClass: "active",
|
||||
});
|
||||
|
||||
const app = new Vue({
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
class Spell {
|
||||
|
||||
private id: number
|
||||
private name: string
|
||||
private description: string
|
||||
private cost: number
|
||||
private charge: number
|
||||
private schools: Array<string>
|
||||
private ingredients: Array<string>
|
||||
private variables: Array<string>
|
||||
|
||||
constructor(id: number) {
|
||||
this.id = id
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Spell;
|
||||
@@ -1,15 +1,14 @@
|
||||
<template>
|
||||
<div class="home-page">
|
||||
<section class="landing">
|
||||
<div class="splash-title">
|
||||
<h1>Auracle</h1>
|
||||
<div>
|
||||
<p>Recueil de savoirs arcaniques et ésotériques des contrées lointaines de Léïm</p>
|
||||
<p>Découvrez ses sortilèges et maléfices...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="container-fluid" id="index-container">
|
||||
<section class="fullpage">
|
||||
<div class="px-4">
|
||||
<h1 class="text-dark mb-4 font-display line-height-100 font-weight-900">"C'est magique, ta gueule."</h1>
|
||||
<div class="lead font-display font-weight-bold">— N'importe quel MJ, une fois dans sa vie</div>
|
||||
<hr class="w-50">
|
||||
<div class="lead"><span class="font-display" style="font-weight:700;">Auracle</span> est une base de données publique en ligne pour sortilèges, en soutien au jeu de rôle sur table <span class="font-weight-bold text-secondary">Leïm</span>.</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -19,39 +18,20 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.home-page {
|
||||
.landing {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: $dull--black;
|
||||
|
||||
.splash-title {
|
||||
padding-bottom: 1rem;
|
||||
color: $white;
|
||||
text-align: center;
|
||||
@include blue-glow-text(.8);
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 4rem;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: $font-bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
div {
|
||||
padding-top: 1rem;
|
||||
p {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.2rem;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.fullpage {
|
||||
min-height: calc(100vh - 56px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
h1 {
|
||||
font-size: 5rem;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.fullpage {
|
||||
h1 {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
48
client/src/pages/schools/single-school-page.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="container-fluid p-4" id="spell-container">
|
||||
<h1 class="display-3 font-display mb-3">{{ school.name }}</h1>
|
||||
<p>{{ school.description }}</p>
|
||||
<spell-list :school_id="id"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SpellsList from "~/components/spells/spells-list"
|
||||
|
||||
// API
|
||||
import { RepositoryFactory } from "~/api/repositories"
|
||||
const Schools = RepositoryFactory.get('schools')
|
||||
|
||||
export default {
|
||||
name: 'single-school',
|
||||
components: {
|
||||
'spell-list': SpellsList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
school: {},
|
||||
id: this.$route.params.id
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.computeSchool()
|
||||
},
|
||||
methods: {
|
||||
async fetchSchool(id) {
|
||||
const { data } = await Schools.getSchool(id)
|
||||
return data
|
||||
},
|
||||
async computeSchool() {
|
||||
this.loading = true
|
||||
const displaySchool = await this.fetchSchool(this.id)
|
||||
this.loading = false
|
||||
this.school = displaySchool
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,23 +1,48 @@
|
||||
<template>
|
||||
<section class="spells-page">
|
||||
<spell-list/>
|
||||
</section>
|
||||
<div class="container-fluid p-4" id="spell-container">
|
||||
<h1 class="display-3 font-display mb-3">Sortilèges</h1>
|
||||
<div class="mb-4">
|
||||
<form>
|
||||
<div class="form-group mb-2">
|
||||
<input type="text" class="form-control" name="search_terms" id="search_terms" placeholder="Rechercher un sort">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-check-inline">
|
||||
<input checked class="form-check-input" type="checkbox" id="search_fields_name" value="search_fields_name">
|
||||
<label class="form-check-label" for="search_fields_name">Nom</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input checked class="form-check-input" type="checkbox" id="search_fields_schools" value="search_fields_schools">
|
||||
<label class="form-check-label" for="search_fields_schools">École(s)</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input checked class="form-check-input" type="checkbox" id="search_fields_ingredients" value="search_fields_ingredients">
|
||||
<label class="form-check-label" for="search_fields_ingredients">Ingrédients</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input checked class="form-check-input" type="checkbox" id="search_fields_description" value="search_fields_description">
|
||||
<label class="form-check-label" for="search_fields_description">Description</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input checked class="form-check-input" type="checkbox" id="search_fields_variables" value="search_fields_variables">
|
||||
<label class="form-check-label" for="search_fields_variables">Variables</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<spell-list/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SpellsList from "../components/spells/spells-list"
|
||||
|
||||
// Components
|
||||
import spellslist from "../components/spells/spells-list"
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'spells',
|
||||
components: {
|
||||
'spell-list': spellslist,
|
||||
'spell-list': SpellsList,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<section class="world-page">
|
||||
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'world',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,10 +1,7 @@
|
||||
// App router
|
||||
|
||||
|
||||
// Pages
|
||||
import index from "./pages/index-page";
|
||||
import spells from "./pages/spells-page";
|
||||
import world from "./pages/world-page";
|
||||
import Index from "./pages/index-page";
|
||||
import Spells from "./pages/spells-page";
|
||||
import SchoolSingle from "./pages/schools/single-school-page";
|
||||
|
||||
// Routes
|
||||
const routes = [
|
||||
@@ -12,13 +9,13 @@ const routes = [
|
||||
path: "*", redirect: "/",
|
||||
},
|
||||
{
|
||||
path: '/', component: index,
|
||||
path: '/', component: Index,
|
||||
},
|
||||
{
|
||||
path: '/spells', component: spells,
|
||||
path: '/spells', component: Spells,
|
||||
},
|
||||
{
|
||||
path: '/world', component: world,
|
||||
path: '/school/:id', component: SchoolSingle, props: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
css: {
|
||||
loaderOptions: {
|
||||
sass: {
|
||||
prependData: `
|
||||
@import "@/assets/scss/_global.scss";
|
||||
@import "@/assets/scss/_variables.scss";
|
||||
@import "@/assets/scss/_mixins.scss";
|
||||
@import "@/assets/scss/_fonts.scss";
|
||||
`
|
||||
}
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
"~": path.resolve(__dirname, 'src/')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
DROP DATABASE IF EXISTS spellsaurus;
|
||||
CREATE DATABASE spellsaurus CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
USE spellsaurus;
|
||||
DROP DATABASE IF EXISTS auracle;
|
||||
CREATE DATABASE auracle CHARACTER SET utf8 COLLATE utf8_bin;
|
||||
USE auracle;
|
||||
|
||||
/* ==== PRIMARY TABLES ==== */
|
||||
|
||||
@@ -29,15 +29,16 @@ 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",
|
||||
`id_meta_school` INT UNSIGNED NOT NULL,
|
||||
`meta_school_id` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
FOREIGN KEY(`id_meta_school`) REFERENCES meta_school(`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 qui bouge encore un peu.",
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
@@ -48,35 +49,45 @@ CREATE TABLE IF NOT EXISTS `variable` (
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
/* ==== USER TABLES ==== */
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL DEFAULT "Disciple",
|
||||
`mail` VARCHAR(255) NOT NULL,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`banned` BOOLEAN DEFAULT false,
|
||||
PRIMARY KEY(`id`)
|
||||
);
|
||||
|
||||
/* ==== ASSOCIATION TABLES ==== */
|
||||
|
||||
/* SPELLS' SCHOOLS */
|
||||
/* One spell can have multiple (up to 3) schools */
|
||||
CREATE TABLE IF NOT EXISTS `spells_schools` (
|
||||
`id_spell` INT UNSIGNED NOT NULL,
|
||||
`id_school` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id_spell`, `id_school`),
|
||||
FOREIGN KEY(`id_spell`) REFERENCES spell(`id`),
|
||||
FOREIGN KEY(`id_school`) REFERENCES school(`id`)
|
||||
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 `spells_variables` (
|
||||
`id_spell` INT UNSIGNED NOT NULL,
|
||||
`id_variable` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id_spell`, `id_variable`),
|
||||
FOREIGN KEY(`id_spell`) REFERENCES spell(`id`),
|
||||
FOREIGN KEY(`id_variable`) REFERENCES variable(`id`)
|
||||
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 `spells_ingredients` (
|
||||
`id_spell` INT UNSIGNED NOT NULL,
|
||||
`id_ingredient` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id_spell`, `id_ingredient`),
|
||||
FOREIGN KEY(`id_spell`) REFERENCES spell(`id`),
|
||||
FOREIGN KEY(`id_ingredient`) REFERENCES ingredient(`id`)
|
||||
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`)
|
||||
);
|
||||
@@ -1,5 +1,5 @@
|
||||
SET NAMES utf8;
|
||||
USE spellsaurus;
|
||||
USE auracle;
|
||||
|
||||
-- META SCHOOLS
|
||||
INSERT INTO `meta_school` (name, description) VALUES
|
||||
@@ -12,7 +12,7 @@ INSERT INTO `meta_school` (name, description) VALUES
|
||||
('Magies autres', 'Magies trop spécifiques et ne rentrant dans aucune autre grande école.');
|
||||
|
||||
-- SCHOOLS
|
||||
INSERT INTO `school` (name, description, id_meta_school) VALUES
|
||||
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),
|
||||
@@ -202,10 +202,14 @@ INSERT INTO `spell` (name, description, level, charge, cost, is_ritual) VALUES
|
||||
('Globules corrosives III', 'Une aura verte sombre se dégage des mains du lanceur. Une énorme nuée de globules se manifeste de cette aura et fond sur une cible unique, infligeant 3D10 de dégats de corrosion pendant 3 tours. Le sort ne peut se manifester dans un liquide.', 0, 0, '-50', false),
|
||||
('Vent toxique', 'Le lanceur manifeste un courant d\'air mordant et nocif sur un petit groupe de cibles, infligeant 4D10 dégâts de corrosion pendant 3 tours à ces dernières.', 20, 1, '-70', false);
|
||||
|
||||
-- USERS
|
||||
INSERT INTO `user` (name, mail, password) VALUES
|
||||
('Kevin', '35.alexis.mail@gmail.com', 'sept777');
|
||||
|
||||
-- INGREDIENTS
|
||||
INSERT INTO `ingredient` (name) VALUES
|
||||
('Volonté'),
|
||||
('Geste');
|
||||
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
|
||||
@@ -215,14 +219,15 @@ INSERT INTO `variable` (description) VALUES
|
||||
/*
|
||||
SCHEMA
|
||||
(id_spell, id_school),
|
||||
|
||||
*/
|
||||
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN school_id INT)
|
||||
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 spells_schools (id_spell, id_school) VALUES (@i, school_id);
|
||||
INSERT INTO spell_school (spell_id, school_id) VALUES (@i, id_school);
|
||||
SET @i = @i + 1;
|
||||
END WHILE;
|
||||
END$$
|
||||
18
database/bookshelf.js
Normal 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 }
|
||||
@@ -1,22 +0,0 @@
|
||||
// MODULES
|
||||
const fs = require('fs')
|
||||
const mysql = require('mysql')
|
||||
|
||||
// Fetches and parses credentials file
|
||||
let credentials_data = fs.readFileSync('./database/credentials.json')
|
||||
let credentials = JSON.parse(credentials_data)
|
||||
|
||||
const db = mysql.createConnection({
|
||||
host: credentials.host,
|
||||
user: credentials.user,
|
||||
password: credentials.password,
|
||||
database: credentials.database,
|
||||
})
|
||||
|
||||
db.on('error', err => {
|
||||
if (err.errno == 'ECONNRESET') {
|
||||
console.log("The connection was reset during your request. Please try again later.");
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = { db }
|
||||
@@ -1,7 +0,0 @@
|
||||
USE spellsaurus;
|
||||
|
||||
/* Fetches a specific spell's school(s) */
|
||||
SELECT school.name
|
||||
FROM spells_schools AS sc
|
||||
INNER JOIN school AS school ON sc.id_school = school.id
|
||||
WHERE sc.id_spell = 1;
|
||||
34
index.js
@@ -6,9 +6,10 @@ 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/connection')
|
||||
const connection = require('./database/bookshelf')
|
||||
const db = connection.db
|
||||
|
||||
// CONSTANTS
|
||||
@@ -27,6 +28,33 @@ 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', routes.spells)
|
||||
app.use('/api/schools', routes.schools)
|
||||
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)
|
||||
|
||||
|
||||
13
models/ingredient-model.js
Normal 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)
|
||||
13
models/meta-school-model.js
Normal 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)
|
||||
17
models/school-model.js
Normal 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
models/spell-model.js
Normal 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)
|
||||
8
models/user-model.js
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
const bookshelf = require('../database/bookshelf').bookshelf
|
||||
|
||||
let User = bookshelf.Model.extend({
|
||||
tableName: 'user',
|
||||
})
|
||||
|
||||
module.exports = bookshelf.model('User', User)
|
||||
13
models/variable-model.js
Normal 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)
|
||||
1805
package-lock.json
generated
@@ -27,10 +27,15 @@
|
||||
},
|
||||
"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"
|
||||
}
|
||||
|
||||
188
repositories/ingredient-repository.js
Normal file
@@ -0,0 +1,188 @@
|
||||
'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 regexXSS = RegExp(/<[^>]*script/)
|
||||
|
||||
// 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 (this.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 (this.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 (this.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 (this.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"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Check if object is null
|
||||
isEmptyObject(obj) {
|
||||
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if script injection attempt
|
||||
isXSSAttempt(string) {
|
||||
if (regexXSS.test(string)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IngredientRepository
|
||||
53
repositories/meta-school-repository.js
Normal 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
|
||||
189
repositories/school-repository.js
Normal file
@@ -0,0 +1,189 @@
|
||||
'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 regexXSS = RegExp(/<[^>]*script/)
|
||||
|
||||
// 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 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 (this.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 (this.isXSSAttempt(s.name) || this.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 (this.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 (this.isXSSAttempt(s.name) || this.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"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Check if object is null
|
||||
isEmptyObject(obj) {
|
||||
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if script injection attempt
|
||||
isXSSAttempt(string) {
|
||||
if (regexXSS.test(string)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SchoolRepository
|
||||
245
repositories/spell-repository.js
Normal file
@@ -0,0 +1,245 @@
|
||||
'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 regexXSS = RegExp(/<[^>]*script/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
class SpellRepository {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return new Promise((resolve, reject) => {
|
||||
model.forge()
|
||||
.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"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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 (this.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 (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description) || this.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 (this.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 (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description) || this.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"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Check if object is null
|
||||
isEmptyObject(obj) {
|
||||
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if script injection attempt
|
||||
isXSSAttempt(string) {
|
||||
if (regexXSS.test(string)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SpellRepository
|
||||
56
repositories/user-repository.js
Normal file
@@ -0,0 +1,56 @@
|
||||
'use strict'
|
||||
// Bookshelf
|
||||
const bookshelf = require('../database/bookshelf').bookshelf
|
||||
const model = require('../models/user-model')
|
||||
|
||||
// Hashing and passwords
|
||||
const bcrypt = require('bcrypt')
|
||||
|
||||
// Model validation
|
||||
const Validator = require('jsonschema').Validator
|
||||
const v = new Validator()
|
||||
const UserValidation = require("../validations/UserValidation")
|
||||
v.addSchema(UserValidation, "/UserValidation")
|
||||
|
||||
// Validations
|
||||
const regexXSS = RegExp(/<[^>]*script/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
class UserRepository {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._model.forge()
|
||||
.fetchAll()
|
||||
.then(v => {
|
||||
resolve(v.toJSON({ omitPivot: true }))
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
reject(new HttpError(500, "Couldn't get users"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getOne(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._model.forge()
|
||||
.where({ 'id' : id })
|
||||
.fetch()
|
||||
.then(v => {
|
||||
resolve(v.toJSON({ omitPivot: true }))
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
reject(new HttpError(500, "Couldn't get user"))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserRepository
|
||||
185
repositories/variable-repository.js
Normal file
@@ -0,0 +1,185 @@
|
||||
'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 regexXSS = RegExp(/<[^>]*script/)
|
||||
|
||||
// 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 (this.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 (this.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 (this.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 (this.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"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Check if object is null
|
||||
isEmptyObject(obj) {
|
||||
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if script injection attempt
|
||||
isXSSAttempt(string) {
|
||||
if (regexXSS.test(string)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VariableRepository
|
||||
@@ -1,7 +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,
|
||||
}
|
||||
190
routes/ingredients.js
Normal file
@@ -0,0 +1,190 @@
|
||||
'use strict'
|
||||
|
||||
// Router
|
||||
const express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
// Connection
|
||||
const connection = require('../database/bookshelf')
|
||||
const db = connection.db
|
||||
|
||||
// Repository
|
||||
const IngredientRepository = require('../repositories/ingredient-repository');
|
||||
const Ingredients = new IngredientRepository();
|
||||
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
// 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 validation for ID
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
try {
|
||||
if (regexInt.test(id)) {
|
||||
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
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
91
routes/meta_schools.js
Normal file
@@ -0,0 +1,91 @@
|
||||
'use strict'
|
||||
|
||||
// Router
|
||||
const express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
// Connection
|
||||
const connection = require('../database/bookshelf')
|
||||
const db = connection.db
|
||||
|
||||
// Repository
|
||||
const MetaSchoolRepository = require('../repositories/meta-school-repository');
|
||||
const MetaSchools = new MetaSchoolRepository();
|
||||
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
// 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 validation for ID
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
try {
|
||||
if (regexInt.test(id)) {
|
||||
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
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
@@ -5,132 +5,185 @@ const express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
// Connection
|
||||
const connection = require('../database/connection')
|
||||
const connection = require('../database/bookshelf')
|
||||
const db = connection.db
|
||||
|
||||
// Model validation
|
||||
const Validator = require('jsonschema').Validator
|
||||
const v = new Validator()
|
||||
const School = require("../models/School")
|
||||
v.addSchema(School, "/SchoolModel")
|
||||
// Repository
|
||||
const SchoolRepository = require('../repositories/school-repository');
|
||||
const Schools = new SchoolRepository();
|
||||
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../models/Errors')
|
||||
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
// ROUTES
|
||||
// GET ALL ------------------
|
||||
const getSchools = () => {
|
||||
let getSchoolsPromise = new Promise((resolve, reject) => {
|
||||
|
||||
let query = "SELECT DISTINCT * FROM school"
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Error: Database error'))
|
||||
} else if (result.length == 0) {
|
||||
reject(new HttpError(404, 'Error: No ressource matching this id'))
|
||||
}
|
||||
result = await buildSchool(result[0])
|
||||
resolve(result);
|
||||
})
|
||||
return Schools.getAll()
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
.catch(err => { throw err })
|
||||
|
||||
return getSchoolsPromise
|
||||
}
|
||||
router.get('/', async (req, res, next) => {
|
||||
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(err.message)
|
||||
res.status(err.code).send(JSON.stringify(
|
||||
{
|
||||
"error": err.message,
|
||||
"code": err.code
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// GET ONE ------------------
|
||||
const getSchool = (id) => {
|
||||
let getSchoolPromise = new Promise((resolve, reject) => {
|
||||
|
||||
let query = "SELECT * FROM school WHERE id = " + db.escape(id)
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Error: Database error'))
|
||||
}
|
||||
result = await buildSchool(result[0])
|
||||
resolve(result);
|
||||
})
|
||||
return Schools.getOne(id)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
.catch(err => { throw err })
|
||||
|
||||
return getSchoolPromise
|
||||
}
|
||||
router.get('/:id/', async (req, res, next) => {
|
||||
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(err.message)
|
||||
res.status(err.code).send(JSON.stringify(
|
||||
{
|
||||
"error": err.message,
|
||||
"code": err.code
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Param validation for single school
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
const regex = RegExp(/^[1-9]\d*$/);
|
||||
try {
|
||||
if (regex.test(id)) {
|
||||
next()
|
||||
} else {
|
||||
throw new HttpError(403, 'Provided ID must be an integer')
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(err.code).send(err.message)
|
||||
}
|
||||
})
|
||||
|
||||
// SHARED FUNCTIONS ------------------
|
||||
|
||||
// Builds the associated infos for a given school object
|
||||
const buildSchool = async (school) => {
|
||||
|
||||
// Fetches the school's parent school
|
||||
let fetchMetaSchoolData = new Promise((resolve, reject) => {
|
||||
let query =
|
||||
"SELECT ms.id, ms.name " +
|
||||
"FROM meta_school AS ms " +
|
||||
"WHERE ms.id = " + school.id_meta_school
|
||||
|
||||
db.query(query, (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Error: Database error'))
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
})
|
||||
// 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(403).send(err)
|
||||
res.status(err.code).send(JSON.stringify(
|
||||
{
|
||||
"error": err.message,
|
||||
"code": err.code
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Builds the school and returns it
|
||||
school.meta_school = await fetchMetaSchoolData
|
||||
return school
|
||||
|
||||
// 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
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Check if spell is null
|
||||
const isEmptyObject = (obj) => {
|
||||
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
// 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 validation for ID
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
try {
|
||||
if (regexInt.test(id)) {
|
||||
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
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
558
routes/spells.js
@@ -5,48 +5,24 @@ const express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
// Connection
|
||||
const connection = require('../database/connection')
|
||||
const connection = require('../database/bookshelf')
|
||||
const db = connection.db
|
||||
|
||||
// Validations
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
const regexXSS = RegExp(/<[^>]*script/)
|
||||
// Repository
|
||||
const SpellReposity = require('../repositories/spell-repository');
|
||||
const Spells = new SpellReposity();
|
||||
|
||||
// Model validation
|
||||
const Validator = require('jsonschema').Validator
|
||||
const v = new Validator()
|
||||
const Spell = require("../models/Spell")
|
||||
v.addSchema(Spell, "/SpellModel")
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../models/Errors')
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
// ROUTES
|
||||
// GET ALL ------------------
|
||||
const getSpells = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let query = "SELECT DISTINCT * FROM spell"
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
} else if (result.length == 0) {
|
||||
reject(new HttpError(404, 'No spells were found'))
|
||||
}
|
||||
|
||||
// Loops over the results to fetch the associated tables
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
try {
|
||||
result[i] = await buildSpell(result[i])
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
resolve(result)
|
||||
})
|
||||
})
|
||||
return Spells.getAll()
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
@@ -69,23 +45,9 @@ router.get('/', async (req, res) => {
|
||||
|
||||
// GET ONE ------------------
|
||||
const getSpell = (id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let query = "SELECT * FROM spell WHERE id = " + db.escape(id)
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
}
|
||||
try {
|
||||
result = buildSpell(result[0])
|
||||
resolve(result)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
return Spells.getOne(id)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
@@ -108,136 +70,9 @@ router.get('/:id/', async (req, res) => {
|
||||
|
||||
// CREATE ONE ------------------
|
||||
const addSpell = (s) => {
|
||||
return new Promise(async (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, Spell).valid) {
|
||||
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, Spell).errors))
|
||||
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) {
|
||||
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
|
||||
} else {
|
||||
let query =
|
||||
'INSERT INTO spell (name, description'
|
||||
|
||||
if (s.level != undefined) { query += ', level' }
|
||||
if (s.charge != undefined) { query += ', charge' }
|
||||
if (s.cost != undefined) { query += ', cost' }
|
||||
if (s.is_ritual != undefined) { query += ', is_ritual' }
|
||||
|
||||
query += `) VALUES (${db.escape(s.name)}, ${db.escape(s.description)}`
|
||||
|
||||
if (s.level != undefined) { query += `, ${s.level}` }
|
||||
if (s.charge != undefined) { query += `, ${s.charge}` }
|
||||
if (s.cost != undefined) { query += `, ${db.escape(s.cost)}` }
|
||||
if (s.is_ritual != undefined) { query += `, ${s.is_ritual}` }
|
||||
|
||||
query += ')'
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
}
|
||||
console.log(`Inserted "${s.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`)
|
||||
|
||||
const new_spell_id = result.insertId
|
||||
|
||||
let addSchoolsData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (s.schools != undefined) {
|
||||
if (s.schools.length > 0) {
|
||||
for (let i = 0; i < s.schools.length; i++) {
|
||||
if (!regexInt.test(s.schools[i].id)) {
|
||||
reject(new HttpError(403, 'Query error - School ID should be an integer !'))
|
||||
}
|
||||
|
||||
let insert_schools_query = `INSERT INTO spells_schools (id_spell, id_school) VALUES (${new_spell_id}, ${s.schools[i].id})`
|
||||
|
||||
db.query(insert_schools_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - No schools matching this ID'))
|
||||
} else {
|
||||
console.log(`Associated school ID ${s.schools[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let addVariablesData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (s.variables != undefined) {
|
||||
if (s.variables.length > 0) {
|
||||
for (let i = 0; i < s.variables.length; i++) {
|
||||
if (!regexInt.test(s.variables[i].id)) {
|
||||
reject(new HttpError(403, 'Query error - Variable ID should be an integer !'))
|
||||
}
|
||||
|
||||
let insert_variables_query = `INSERT INTO spells_variables (id_spell, id_variable) VALUES (${new_spell_id}, ${s.variables[i].id})`
|
||||
db.query(insert_variables_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - No variables matching this ID'))
|
||||
} else {
|
||||
console.log(`Associated variable ID ${s.variables[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let addIngredientsData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (s.ingredients != undefined) {
|
||||
if (s.ingredients.length > 0) {
|
||||
for (let i = 0; i < s.ingredients.length; i++) {
|
||||
if (!regexInt.test(s.ingredients[i].id)) {
|
||||
reject(new HttpError(403, 'Query error - Ingredient ID should be an integer !'))
|
||||
}
|
||||
|
||||
let insert_ingredients_query = `INSERT INTO spells_ingredients (id_spell, id_ingredient) VALUES (${new_spell_id}, ${s.ingredients[i].id})`
|
||||
db.query(insert_ingredients_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - No ingredients matching this ID'))
|
||||
} else {
|
||||
console.log(`Associated ingredient ID ${s.ingredients[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Promise.all([addSchoolsData(), addVariablesData(), addIngredientsData()])
|
||||
.then(v => {
|
||||
resolve(getSpell(new_spell_id))
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
return Spells.addOne(s)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
@@ -259,176 +94,15 @@ router.post('/', async (req, res) => {
|
||||
|
||||
|
||||
// UPDATE ONE ------------------
|
||||
const updateSpell = (s, id) => {
|
||||
return new Promise( async (resolve, reject) => {
|
||||
|
||||
// Check if spell exists
|
||||
let old_spell = await getSpell(id)
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
|
||||
// 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, Spell).valid) {
|
||||
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, Spell).errors))
|
||||
} else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) {
|
||||
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
|
||||
} else {
|
||||
let query =
|
||||
'UPDATE spell SET '
|
||||
|
||||
if (s.name != undefined) { query += `name = "${s.name}" ` }
|
||||
if (s.description != undefined) { query += `, description = "${s.description}" ` }
|
||||
if (s.level != undefined) { query += `, level = ${s.level} ` }
|
||||
if (s.charge != undefined) { query += `, charge = ${s.charge} ` }
|
||||
if (s.cost != undefined) { query += `, cost = "${s.cost}" ` }
|
||||
if (s.is_ritual != undefined) { query += `, is_ritual = ${s.is_ritual} ` }
|
||||
|
||||
query += ` WHERE id = ${db.escape(id)}`
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - Spell update failed'))
|
||||
}
|
||||
console.log(`Updated "${s.name}" on ID ${old_spell.id}, affecting ${result.affectedRows} row(s)`)
|
||||
|
||||
let updateSchoolsData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (s.schools != undefined) {
|
||||
if (s.schools.length > 0) {
|
||||
let delete_schools_query =
|
||||
`DELETE FROM spells_schools WHERE id_spell = ${old_spell.id}`
|
||||
|
||||
db.query(delete_schools_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - Spell school deletion failed.'))
|
||||
}
|
||||
})
|
||||
|
||||
for (let i = 0; i < s.schools.length; i++) {
|
||||
if (!regexInt.test(s.schools[i].id)) {
|
||||
reject(new HttpError(403, 'Query error - School ID should be an integer !'))
|
||||
} else {}
|
||||
|
||||
let update_schools_query = `INSERT INTO spells_schools (id_spell, id_school) VALUES (${old_spell.id}, ${s.schools[i].id})`
|
||||
db.query(update_schools_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - No schools matching this ID'))
|
||||
} else {
|
||||
console.log(`Updated association school ID ${s.schools[i].id} to spell ID ${old_spell.id}`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let updateVariablesData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (s.variables != undefined) {
|
||||
if (s.variables.length > 0) {
|
||||
let delete_variables_query =
|
||||
`DELETE FROM spells_variables WHERE id_spell = ${old_spell.id}`
|
||||
|
||||
db.query(delete_variables_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - Spell variable deletion failed.'))
|
||||
}
|
||||
})
|
||||
|
||||
for (let i = 0; i < s.variables.length; i++) {
|
||||
if (!regexInt.test(s.variables[i].id)) {
|
||||
reject(new HttpError(403, 'Query error - Variable ID should be an integer !'))
|
||||
}
|
||||
|
||||
let update_variables_query = `INSERT INTO spells_variables (id_spell, id_variable) VALUES (${old_spell.id}, ${s.variables[i].id})`
|
||||
db.query(update_variables_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - No variables matching this ID'))
|
||||
} else {
|
||||
console.log(`Updated variable ID "${s.variables[i].id}" to spell ID ${old_spell.id}`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let updateIngredientsData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (s.ingredients != undefined) {
|
||||
if (s.ingredients.length > 0) {
|
||||
let delete_ingredients_query =
|
||||
`DELETE FROM spells_ingredients WHERE id_spell = ${old_spell.id}`
|
||||
|
||||
db.query(delete_ingredients_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - Spell ingredients deletion failed.'))
|
||||
}
|
||||
console.log(result)
|
||||
})
|
||||
|
||||
// Loops over ingredients query
|
||||
for (let i = 0; i < s.ingredients.length; i++) {
|
||||
if (!regexInt.test(s.ingredients[i].id)) {
|
||||
reject(new HttpError(403, 'Query error - Ingredient ID should be an integer !'))
|
||||
}
|
||||
|
||||
let update_ingredients_query = `INSERT INTO spells_ingredients (id_spell, id_ingredient) VALUES (${old_spell.id}, ${s.ingredients[i].id})`
|
||||
db.query(update_ingredients_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error - No ingredients matching this ID'))
|
||||
} else {
|
||||
console.log(`Updated ingredient ID "${s.ingredients[i].id}" to spell ID ${old_spell.id}`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const promises = [
|
||||
updateSchoolsData(),
|
||||
updateVariablesData(),
|
||||
updateIngredientsData()
|
||||
]
|
||||
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
resolve(getSpell(old_spell.id))
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
const updateSpell = (id, s) => {
|
||||
return Spells.updateOne(id, s)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
router.put('/:id/', async (req, res) => {
|
||||
updateSpell(req.body, req.params.id)
|
||||
updateSpell(req.params.id, req.body)
|
||||
.then(v => {
|
||||
res.setHeader('Content-Type', 'application/json;charset=utf-8')
|
||||
res.send(JSON.stringify(v))
|
||||
@@ -446,96 +120,9 @@ router.put('/:id/', async (req, res) => {
|
||||
|
||||
// DELETE ONE ------------------
|
||||
const deleteSpell = (id) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
|
||||
// Check if spell exists
|
||||
let old_spell = await getSpell(id)
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
|
||||
if (old_spell == undefined) {
|
||||
reject((new HttpError(404, 'No spells matching this ID')))
|
||||
} else {
|
||||
let deleteSchoolsData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let delete_schools_query = `DELETE FROM spells_schools WHERE id_spell = ${db.escape(id)}`
|
||||
db.query(delete_schools_query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Spell schools deletion failed'))
|
||||
} else {
|
||||
console.log(`Deleted schools associated to spell ID ${db.escape(id)}`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let deleteVariablesData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let delete_variables_query = `DELETE FROM spells_variables WHERE id_spell = ${db.escape(id)}`
|
||||
db.query(delete_variables_query, async (err, result) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(new HttpError(500, 'Spell variables deletion failed'))
|
||||
} else {
|
||||
console.log(`Deleted variables associated to spell ID ${db.escape(id)}`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let deleteIngredientsData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let delete_ingredients_query = `DELETE FROM spells_ingredients WHERE id_spell = ${db.escape(id)}`
|
||||
db.query(delete_ingredients_query, async (err, result) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(new HttpError(500, 'Spell ingredients deletion failed'))
|
||||
} else {
|
||||
console.log(`Deleted ingredients associated to spell ID ${db.escape(id)}`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let deleteSpellData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let delete_spell_query = `DELETE FROM spell WHERE id = ${db.escape(id)}`
|
||||
db.query(delete_spell_query, async (err, result) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(new HttpError(500, 'Spell deletion failed'))
|
||||
} else {
|
||||
console.log(`Deleted spell ID ${db.escape(id)}, affecting ${result.affectedRows} rows`)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const promises = [
|
||||
deleteSchoolsData(),
|
||||
deleteVariablesData(),
|
||||
deleteIngredientsData()
|
||||
]
|
||||
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
deleteSpellData()
|
||||
let response = {
|
||||
message: `Spell ID ${id} was successfully deleted.`
|
||||
}
|
||||
resolve(response)
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
return Spells.deleteOne(id)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
@@ -556,16 +143,17 @@ router.delete('/:id/', async (req, res) => {
|
||||
})
|
||||
|
||||
|
||||
// Param validation for single spell
|
||||
// Param validation for ID
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
try {
|
||||
if (regexInt.test(id)) {
|
||||
next()
|
||||
} else {
|
||||
throw new HttpError(403, 'Provided ID must be an integer and not zero')
|
||||
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,
|
||||
@@ -575,106 +163,4 @@ router.param('id', (req, res, next, id) => {
|
||||
}
|
||||
})
|
||||
|
||||
// SHARED FUNCTIONS ------------------
|
||||
|
||||
// Builds the associated infos for a given spell object
|
||||
const buildSpell = async (spell) => {
|
||||
|
||||
// Fetches the spell's schools
|
||||
let fetchSpellSchoolData = (s) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (s == undefined) { reject(new HttpError(404, "Error: No spells matching this ID"))}
|
||||
|
||||
let query =
|
||||
"SELECT school.id, school.name " +
|
||||
"FROM spells_schools AS sc " +
|
||||
"INNER JOIN school AS school ON sc.id_school = school.id " +
|
||||
"WHERE sc.id_spell = " + s.id
|
||||
|
||||
db.query(query, (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
} else {
|
||||
s.schools = result
|
||||
resolve(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Fetches the spell's variables
|
||||
let fetchSpellVariablesData = (s) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (s == undefined) { reject(new HttpError(404, "Error: No spells matching this ID"))}
|
||||
|
||||
let query =
|
||||
"SELECT variable.id, variable.description " +
|
||||
"FROM spells_variables AS sv " +
|
||||
"INNER JOIN variable AS variable ON sv.id_variable = variable.id " +
|
||||
"WHERE sv.id_spell = " + s.id
|
||||
|
||||
db.query(query, (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
} else {
|
||||
s.variables = result
|
||||
resolve(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Fetches the spell's ingredients
|
||||
let fetchSpellIngredientsData = (s) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (s == undefined) { reject(new HttpError(404, "Error: No spells matching this ID"))}
|
||||
|
||||
let query =
|
||||
"SELECT ingredient.id, ingredient.name " +
|
||||
"FROM spells_ingredients AS si " +
|
||||
"INNER JOIN ingredient AS ingredient ON si.id_ingredient = ingredient.id " +
|
||||
"WHERE si.id_spell = " + s.id
|
||||
|
||||
db.query(query, (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
} else {
|
||||
s.ingredients = result
|
||||
resolve(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Builds the spell and returns it
|
||||
let s = await fetchSpellSchoolData(spell)
|
||||
.then(s => { return fetchSpellVariablesData(s) })
|
||||
.then(s => { return fetchSpellIngredientsData(s) })
|
||||
.catch(err => {
|
||||
throw err
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Check if spell is null
|
||||
const isEmptyObject = (obj) => {
|
||||
if (Object.keys(obj).length === 0 && obj.constructor === Object) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const isXSSAttempt = (string) => {
|
||||
if (regexXSS.test(string)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router
|
||||
|
||||
192
routes/users.js
Normal file
@@ -0,0 +1,192 @@
|
||||
'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();
|
||||
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
// 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 ------------------
|
||||
const getUser = (id) => {
|
||||
return Users.getOne(id)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
router.get('/:id/', async (req, res) => {
|
||||
getUser(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 addUser = (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(new HttpError(403, "Error: User cannot be empty !"))
|
||||
} else if (!v.validate(u, User).valid) {
|
||||
reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(u, User).errors))
|
||||
} else if (isXSSAttempt(u.name) || isXSSAttempt(u.mail) || isXSSAttempt(u.password)) {
|
||||
reject(new HttpError(403, 'Injection attempt detected, aborting the request.'))
|
||||
} else {
|
||||
let query = `INSERT INTO user (name, mail, password) VALUES (${db.escape(u.name)}, ${db.escape(u.mail)}, ${db.escape(u.password)})`
|
||||
|
||||
db.query(query, async (err, result) => {
|
||||
if (err) {
|
||||
reject(new HttpError(500, 'Database error'))
|
||||
} else {
|
||||
console.log(`Inserted "${u.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`)
|
||||
const new_user_id = result.insertId
|
||||
let response = {
|
||||
"message": `User created successfully !`,
|
||||
"inserted_id": `${new_user_id}`
|
||||
}
|
||||
resolve(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
}).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
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// DELETE ONE ------------------
|
||||
const deleteUser = (id) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
|
||||
// Check if user exists
|
||||
let old_user = await getUser(id)
|
||||
.catch(() => {
|
||||
reject((new HttpError(404, 'No user matching this ID')))
|
||||
})
|
||||
|
||||
console.log(old_user)
|
||||
|
||||
let deleteUserData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let delete_user_query = `DELETE FROM user WHERE id = ${db.escape(id)}`
|
||||
db.query(delete_user_query, async (err, result) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(new HttpError(500, 'Spell deletion failed'))
|
||||
} else {
|
||||
let response = {
|
||||
"message": "User delete successully",
|
||||
"deleted_id": `${id}`
|
||||
}
|
||||
resolve(response)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
deleteUserData()
|
||||
.then(v => {
|
||||
resolve(v)
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
throw err
|
||||
})
|
||||
}
|
||||
router.delete('/:id/', async (req, res) => {
|
||||
deleteUser(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 validation for single user
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
try {
|
||||
if (regexInt.test(id)) {
|
||||
next()
|
||||
} else {
|
||||
new Error
|
||||
}
|
||||
} catch (err) {
|
||||
throw new HttpError(403, 'Provided ID must be an integer and not zero')
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
190
routes/variables.js
Normal file
@@ -0,0 +1,190 @@
|
||||
'use strict'
|
||||
|
||||
// Router
|
||||
const express = require('express')
|
||||
let router = express.Router()
|
||||
|
||||
// Connection
|
||||
const connection = require('../database/bookshelf')
|
||||
const db = connection.db
|
||||
|
||||
// Repository
|
||||
const VariableRepository = require('../repositories/variable-repository');
|
||||
const Variables = new VariableRepository();
|
||||
|
||||
const regexInt = RegExp(/^[1-9]\d*$/)
|
||||
|
||||
// Error handling
|
||||
const { HttpError } = require('../validations/Errors')
|
||||
|
||||
// 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 validation for ID
|
||||
// (check if id is int) (could be refactored)
|
||||
router.param('id', (req, res, next, id) => {
|
||||
try {
|
||||
if (regexInt.test(id)) {
|
||||
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
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
11
validations/IngredientValidation.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const Ingredient = {
|
||||
"id": "/IngredientValidation",
|
||||
"type": Object,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"description": { "type": "string" }
|
||||
},
|
||||
"required": ["name", "description"]
|
||||
}
|
||||
|
||||
module.exports = Ingredient
|
||||
@@ -1,12 +1,12 @@
|
||||
const School = {
|
||||
"id": "/SchoolModel",
|
||||
const MetaSchool = {
|
||||
"id": "/MetaSchoolValidation",
|
||||
"type": Object,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"description": { "type": "string" },
|
||||
"meta_school": { "type": "number" },
|
||||
"schools": { "type": "array" }
|
||||
},
|
||||
"required": ["name", "description"]
|
||||
}
|
||||
|
||||
module.exports = School
|
||||
module.exports = MetaSchool
|
||||
12
validations/SchoolValidation.js
Normal 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
|
||||
@@ -1,5 +1,5 @@
|
||||
const Spell = {
|
||||
"id": "/SpellModel",
|
||||
"id": "/SpellValidation",
|
||||
"type": Object,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
13
validations/UserValidation.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const User = {
|
||||
"id": "/UserValidation",
|
||||
"type": Object,
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"mail": { "type": "string" },
|
||||
"password": { "type": "string" },
|
||||
"banned": { "type": "boolean"},
|
||||
},
|
||||
"required": ["name", "password", "mail"]
|
||||
}
|
||||
|
||||
module.exports = User
|
||||
10
validations/VariableValidation.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const Variable = {
|
||||
"id": "/VariableValidation",
|
||||
"type": Object,
|
||||
"properties": {
|
||||
"description": { "type": "string" },
|
||||
},
|
||||
"required": ["description"]
|
||||
}
|
||||
|
||||
module.exports = Variable
|
||||