diff --git a/.env.development b/.env.development index 3a08757..17d8c14 100644 --- a/.env.development +++ b/.env.development @@ -12,3 +12,5 @@ COUCHDB_URL="http://couchdb.unespace.com" COUCHDB_DATABASE="headupdb" COUCHDB_DESIGN="headup_app" COUCHDB_CREDENTIAL="cmFtcGV1cjpES3hwMjRQUw==" +SKYDIVERID_API_URL="https://skydiver.id/api" +SKYDIVERID_API_TOKEN="xmqzgnGYMD.OJgFw1pCXCpJFGmZfSjmWK8lrpqAQNnu" diff --git a/.env.local b/.env.local index 2611b5f..dc1b1f6 100644 --- a/.env.local +++ b/.env.local @@ -12,3 +12,5 @@ COUCHDB_URL="http://couchdb.unespace.com" COUCHDB_DATABASE="headupdb" COUCHDB_DESIGN="headup_app" COUCHDB_CREDENTIAL="cmFtcGV1cjpES3hwMjRQUw==" +SKYDIVERID_API_URL="https://skydiver.id/api" +SKYDIVERID_API_TOKEN="xmqzgnGYMD.OJgFw1pCXCpJFGmZfSjmWK8lrpqAQNnu" diff --git a/.env.production b/.env.production index fd3f063..52405b2 100644 --- a/.env.production +++ b/.env.production @@ -12,3 +12,5 @@ COUCHDB_URL="http://couchdb.unespace.com" COUCHDB_DATABASE="headupdb" COUCHDB_DESIGN="headup_app" COUCHDB_CREDENTIAL="cmFtcGV1cjpES3hwMjRQUw==" +SKYDIVERID_API_URL="https://skydiver.id/api" +SKYDIVERID_API_TOKEN="xmqzgnGYMD.OJgFw1pCXCpJFGmZfSjmWK8lrpqAQNnu" diff --git a/.gitignore b/.gitignore index c0af70d..b740f61 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,33 @@ node_modules # Optional REPL history .node_repl_history -.idea +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# System files +.DS_Store +Thumbs.db + +# SonarQube +.scannerwork/ +sonar.properties + +**/*.copy.ts +**/*.copy.scss +**/*.copy.html middlewares diff --git a/app.js b/app.js index 7108052..8b7b178 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,8 @@ require('dotenv').config({ path: `.env.${appEnv}` }); const { promisify } = require('util'), bodyParser = require('body-parser'), chalk = require('chalk'), - utils = require('./routes/utils'); + utils = require('./routes/utils'), + exceptionHandler = require('./middlewares/exceptions.handler.js'); var express = require('express'), session = require('express-session'), cors = require('cors'), @@ -23,8 +24,8 @@ app.use(cors()); // Normal express config defaults //app.use(morgan('combined')); -morgan.token('statusColor', (req, res, args) => { - var status = (typeof res.headersSent !== 'boolean' ? Boolean(res.header) : res.headersSent) +morgan.token('statusColor', (req, res) => { + let status = (typeof res.headersSent !== 'boolean' ? Boolean(res.header) : res.headersSent) ? res.statusCode : undefined @@ -60,55 +61,49 @@ if(isProduction){ mongoose.set('debug', process.env.DEBUG); console.log(`${utils.getDateTimeISOString()}`, chalk.red('debug :'), chalk.yellow(process.env.DEBUG)); } -require('./models/mongo/User'); -require('./models/mongo/Application'); -require('./models/mongo/Aeronef'); -require('./models/mongo/Canopy'); -require('./models/mongo/Dropzone'); -require('./models/mongo/Jump'); -require('./models/mongo/QCM'); -/* -require('./models/mongo/Metal'); -require('./models/mongo/Price'); -require('./models/mongo/Product'); -*/ + +require('./models/mongo'); require('./config/passport'); app.use(require('./routes')); -/// catch 404 and forward to error handler +app.use(exceptionHandler.notFoundErrorHandler); +app.use(exceptionHandler.logErrorHandler); +app.use(exceptionHandler.clientErrorHandler); +app.use(exceptionHandler.fianlErrorHandler); + +/* +// catch 404 and forward to error handler app.use(function(req, res, next) { - var err = new Error('Not Found'); + let err = new Error('Not Found'); err.status = 404; next(err); }); -/// error handlers - -// development error handler -// will print stacktrace -if (!isProduction) { - app.use(function(err, req, res, next) { - console.log(`${utils.getDateTimeISOString()}`, chalk.red(`error handler : ${err.message} ${err.err}`)); - res.status(err.status || 500); +// error handler +app.use(function(err, req, res, next) { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`error handler : ${err.message} ${err.err}`)); + if (res.headersSent) { + return next(err) + } + res.status(err.status || 500); + if (!isProduction) { + // development error handler will print stacktrace res.json({errors: { message: err.message, error: err.err, stack: err.stack }}); - }); -} else { - // production error handler - // no stacktraces leaked to user - app.use(function(err, req, res, next) { - res.status(err.status || 500); + } else { + // production error handler no stacktraces leaked to user res.json({errors: { message: err.message, error: {}, stack: {} }}); - }); -} + } +}); +*/ const startServer = async () => { const port = process.env.SERVER_PORT || 3200; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..a75b2a6 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,9 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; + + +export default [ + {files: ["**/*.js"], languageOptions: {sourceType: "commonjs"}}, + {languageOptions: { globals: globals.node }}, + pluginJs.configs.recommended, +]; \ No newline at end of file diff --git a/models/index.js b/models/index.js new file mode 100644 index 0000000..f44ef05 --- /dev/null +++ b/models/index.js @@ -0,0 +1,12 @@ +exports.User = require('./mongo/User'); +exports.Application = require('./mongo/Application'); +exports.Aeronef = require('./mongo/Aeronef'); +exports.Canopy = require('./mongo/Canopy'); +exports.Dropzone = require('./mongo/Dropzone'); +exports.JumpFile = require('./mongo/JumpFile'); +exports.Jump = require('./mongo/Jump'); +exports.Qcm = require('./mongo/Qcm'); +exports.QcmCategory = require('./mongo/QcmCategory'); +exports.QcmQuestion = require('./mongo/QcmQuestion'); +exports.QcmChoice = require('./mongo/QcmChoice'); +exports.X2DataLog = require('./mongo/X2DataLog'); \ No newline at end of file diff --git a/models/mongo/Aeronef.js b/models/mongo/Aeronef.js index 1cec999..7f261b1 100644 --- a/models/mongo/Aeronef.js +++ b/models/mongo/Aeronef.js @@ -1,5 +1,4 @@ var mongoose = require('mongoose'); -var User = mongoose.model('User'); var slug = require('slug'); var AeronefSchema = new mongoose.Schema({ diff --git a/models/mongo/Canopy.js b/models/mongo/Canopy.js index baf1a0e..312fb6c 100644 --- a/models/mongo/Canopy.js +++ b/models/mongo/Canopy.js @@ -1,5 +1,4 @@ var mongoose = require('mongoose'); -var User = mongoose.model('User'); var slug = require('slug'); var CanopySchema = new mongoose.Schema({ diff --git a/models/mongo/Currency.js b/models/mongo/Currency.js deleted file mode 100644 index 327393e..0000000 --- a/models/mongo/Currency.js +++ /dev/null @@ -1,20 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); - -var CurrencySchema = new mongoose.Schema({ - ordering: Number, - code: { type: String, upercase: true, unique: true }, - title: String -}, {timestamps: true, toJSON: {virtuals: true}}); - -CurrencySchema.plugin(uniqueValidator, { message: 'is already taken' }); - -CurrencySchema.methods.toJSONFor = function(){ - return { - ordering: this.ordering, - code: this.code, - title: this.title - }; -}; - -mongoose.model('Currency', CurrencySchema, 'currencies'); \ No newline at end of file diff --git a/models/mongo/Dropzone.js b/models/mongo/Dropzone.js index b7bf076..2591fa2 100644 --- a/models/mongo/Dropzone.js +++ b/models/mongo/Dropzone.js @@ -1,5 +1,4 @@ var mongoose = require('mongoose'); -var User = mongoose.model('User'); var slug = require('slug'); var DropzoneSchema = new mongoose.Schema({ diff --git a/models/mongo/Jump.js b/models/mongo/Jump.js index d403855..714c709 100644 --- a/models/mongo/Jump.js +++ b/models/mongo/Jump.js @@ -1,5 +1,4 @@ var mongoose = require('mongoose'); -var User = mongoose.model('User'); var uniqueValidator = require('mongoose-unique-validator'); var slug = require('slug'); @@ -24,6 +23,8 @@ var JumpSchema = new mongoose.Schema({ zone: String, dossier: String, video: String, + files: [{ type: mongoose.Schema.Types.ObjectId, ref: 'JumpFile' }], + x2data: { type: mongoose.Schema.Types.ObjectId, ref: 'X2DataLog' }, author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } }, {timestamps: true, toJSON: {virtuals: true}}); @@ -40,7 +41,19 @@ JumpSchema.methods.slugify = function () { this.slug = slug(`${this.numero} ${this.lieu}`) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36); }; -JumpSchema.methods.toJSONFor = function(user){ +JumpSchema.methods.addFile = function (id) { + if (this.files.indexOf(id) === -1) { + this.files.push(id); + } + return this.save(); +}; + +JumpSchema.methods.removeFile = function (id) { + this.files.remove(id); + return this.save(); +}; + +JumpSchema.methods.toJSONFor = function(user) { return { slug: this.slug, date: this.date, @@ -62,6 +75,8 @@ JumpSchema.methods.toJSONFor = function(user){ zone: this.zone, dossier: this.dossier, video: this.video, + files: this.files.map(file => file ? file.toJSONForJump() : null), + x2data: this.x2data ? this.x2data.toJSONFor() : null, createdAt: this.createdAt, updatedAt: this.updatedAt, author: this.author.toProfileJSONFor(user) diff --git a/models/mongo/JumpFile.js b/models/mongo/JumpFile.js new file mode 100644 index 0000000..7985a89 --- /dev/null +++ b/models/mongo/JumpFile.js @@ -0,0 +1,51 @@ +var mongoose = require('mongoose'); +var uniqueValidator = require('mongoose-unique-validator'); +var slug = require('slug'); + +var JumpFileSchema = new mongoose.Schema({ + slug: { type: String, lowercase: true, unique: true }, + name: String, + path: String, + type: { + type: String, + enum : ['csv', 'image', 'kml', 'video'], + default: 'video' + }, + author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } +}, {timestamps: true, toJSON: {virtuals: true}}); + +JumpFileSchema.plugin(uniqueValidator, { message: 'is already taken' }); + +JumpFileSchema.pre('validate', function (next) { + if (!this.slug) { + this.slugify(); + } + next(); +}); + +JumpFileSchema.methods.slugify = function () { + this.slug = slug(`${this.type}`) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36) + (Math.random() * Math.pow(36, 6) | 0).toString(36); +}; + +JumpFileSchema.methods.toJSONFor = function(user){ + return { + slug: this.slug, + name: this.name, + path: this.path, + type: this.type, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + author: this.author.toProfileJSONFor(user) + }; +}; + +JumpFileSchema.methods.toJSONForJump = function(){ + return { + slug: this.slug, + name: this.name, + path: this.path, + type: this.type + }; +}; + +mongoose.model('JumpFile', JumpFileSchema); diff --git a/models/mongo/Metal.js b/models/mongo/Metal.js deleted file mode 100644 index e4c9067..0000000 --- a/models/mongo/Metal.js +++ /dev/null @@ -1,24 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); - -var MetalSchema = new mongoose.Schema({ - ordering: Number, - code: { type: String, upercase: true, unique: true }, - title: String, - color: String, - image: String -}, {timestamps: true, toJSON: {virtuals: true}}); - -MetalSchema.plugin(uniqueValidator, { message: 'is already taken' }); - -MetalSchema.methods.toJSONFor = function(){ - return { - ordering: this.ordering, - code: this.code, - title: this.title, - color: this.color, - image: this.image - }; -}; - -mongoose.model('Metal', MetalSchema); \ No newline at end of file diff --git a/models/mongo/Price.js b/models/mongo/Price.js deleted file mode 100644 index 685cd5e..0000000 --- a/models/mongo/Price.js +++ /dev/null @@ -1,87 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); - -var PriceSchema = new mongoose.Schema({ - timestamp: { type: Number, default: 0 }, - metal: { type: String, upercase: true, default: 'ØØØ' }, - currency: { type: String, upercase: true, default: 'ØØØ' }, - exchange: { type: String, upercase: true, default: '' }, - symbol: { type: String, upercase: true, default: '' }, - prev_close_price: { type: Number, default: 0 }, - open_price: { type: Number, default: 0 }, - low_price: { type: Number, default: 0 }, - high_price: { type: Number, default: 0 }, - open_time: { type: Number, default: 0 }, - price: { type: Number, default: 0 }, - ch: { type: Number, default: 0 }, - chp: { type: Number, default: 0 }, - ask: { type: Number, default: 0 }, - bid: { type: Number, default: 0 }, - price_gram_24k: { type: Number, default: 0 }, - price_gram_22k: { type: Number, default: 0 }, - price_gram_21k: { type: Number, default: 0 }, - price_gram_20k: { type: Number, default: 0 }, - price_gram_18k: { type: Number, default: 0 }, - price_gram_16k: { type: Number, default: 0 }, - price_gram_14k: { type: Number, default: 0 }, - price_gram_10k: { type: Number, default: 0 } -}, { timestamps: true, toJSON: { virtuals: true } }); - -PriceSchema.plugin(uniqueValidator, { message: 'is already taken' }); - -PriceSchema.methods.toJSONFor = function () { - /* - let a = { - timestamp: 1689944788, - metal: "XAU", - currency: "EUR", - exchange: "FOREXCOM", - symbol: "FOREXCOM:XAUEUR", - prev_close_price: 0.00, - open_price: 0.00, - low_price: 0.00, - high_price: 0.00, - open_time: 1689897600, - price: 0.00, - ch: 0.00, - chp: 0.00, - ask: 0.00, - bid: 0.00, - price_gram_24k: 0.00, - price_gram_22k: 0.00, - price_gram_21k: 0.00, - price_gram_20k: 0.00, - price_gram_18k: 0.00, - price_gram_16k: 0.00, - price_gram_14k: 0.00, - price_gram_10k: 0.00 - }; - */ - return { - timestamp: this.timestamp, - metal: this.metal, - currency: this.currency, - exchange: this.exchange, - symbol: this.symbol, - prev_close_price: this.prev_close_price, - open_price: this.open_price, - low_price: this.low_price, - high_price: this.high_price, - open_time: this.open_time, - price: this.price, - ch: this.ch, - chp: this.chp, - ask: this.ask, - bid: this.bid, - price_gram_24k: this.price_gram_24k, - price_gram_22k: this.price_gram_22k, - price_gram_21k: this.price_gram_21k, - price_gram_20k: this.price_gram_20k, - price_gram_18k: this.price_gram_18k, - price_gram_16k: this.price_gram_16k, - price_gram_14k: this.price_gram_14k, - price_gram_10k: this.price_gram_10k - }; -}; - -mongoose.model('Price', PriceSchema); \ No newline at end of file diff --git a/models/mongo/Product.js b/models/mongo/Product.js deleted file mode 100644 index 846da2c..0000000 --- a/models/mongo/Product.js +++ /dev/null @@ -1,58 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); -var slug = require('slug'); -var User = mongoose.model('User'); - -var ProductSchema = new mongoose.Schema({ - ordering: Number, - title: String, - slug: { type: String, lowercase: true, unique: true }, - prime_achat: Number, - prime_vente: Number, - metal: { type: mongoose.Schema.Types.ObjectId, ref: 'Metal' }, - poids: Number, - author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, - favoritesCount: { type: Number, default: 0 } -}, {timestamps: true, toJSON: {virtuals: true}}); - -ProductSchema.plugin(uniqueValidator, { message: 'is already taken' }); - -ProductSchema.pre('validate', function (next) { - if (!this.slug) { - this.slugify(); - } - next(); -}); - -ProductSchema.methods.slugify = function () { - this.slug = slug(this.title) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36); -}; - -ProductSchema.methods.updateFavoriteCount = function () { - var product = this; - - return User.count({ favorites: { $in: [product._id] } }).then(function (count) { - product.favoritesCount = count; - - return product.save(); - }); -}; - -ProductSchema.methods.toJSONFor = function(user){ - return { - ordering: this.ordering, - title: this.title, - slug: this.slug, - prime_achat: this.prime_achat, - prime_vente: this.prime_vente, - metal: this.metal.toJSONFor(), - poids: this.poids, - createdAt: this.createdAt, - updatedAt: this.updatedAt, - favorited: user ? user.isFavorite(this._id) : false, - favoritesCount: this.favoritesCount, - author: this.author.toProfileJSONFor(user) - }; -}; - -mongoose.model('Product', ProductSchema); diff --git a/models/mongo/QCM.js b/models/mongo/QCM.js index 084a266..1b413db 100644 --- a/models/mongo/QCM.js +++ b/models/mongo/QCM.js @@ -2,22 +2,45 @@ var mongoose = require('mongoose'); var uniqueValidator = require('mongoose-unique-validator'); var slug = require('slug'); -var QCMSchema = new mongoose.Schema({ +var QcmSchema = new mongoose.Schema({ name: { type: String, lowercase: true, unique: true }, + slug: { type: String, lowercase: true, unique: true }, categories: [ - { type: mongoose.Schema.Types.ObjectId, ref: 'QCMCategory' } + { type: mongoose.Schema.Types.ObjectId, ref: 'QcmCategory' } ] }, {timestamps: true, toJSON: {virtuals: true}}); -QCMSchema.plugin(uniqueValidator, { message: 'is already taken' }); +QcmSchema.plugin(uniqueValidator, { message: 'is already taken' }); -QCMSchema.methods.toJSONFor = function(){ +QcmSchema.pre('validate', function (next) { + if (!this.slug) { + this.slugify(); + } + next(); +}); + +QcmSchema.methods.slugify = function () { +this.slug = slug(`${this.name}-`) + (Math.random() * Math.pow(36, 6) | 0).toString(36); +}; + +QcmSchema.methods.addCategory = function (id) { + if (this.categories.indexOf(id) === -1) { + this.categories.push(id); + } + return this.save(); +}; + +QcmSchema.methods.removeCategory = function (id) { + this.categories.remove(id); + return this.save(); +}; + +QcmSchema.methods.toJSONFor = function() { return { name: this.name, - categories: this.categories, - createdAt: this.createdAt, - updatedAt: this.updatedAt + slug: this.slug, + categories: this.categories.map(category => category.toJSONFor()) }; }; -mongoose.model('QCM', QCMSchema); +mongoose.model('Qcm', QcmSchema); diff --git a/models/mongo/QCMCategories.js b/models/mongo/QCMCategories.js deleted file mode 100644 index e42d836..0000000 --- a/models/mongo/QCMCategories.js +++ /dev/null @@ -1,23 +0,0 @@ -var mongoose = require('mongoose'); -var uniqueValidator = require('mongoose-unique-validator'); -var slug = require('slug'); - -var QCMCategorySchema = new mongoose.Schema({ - num: number, - name: { type: String, lowercase: true, unique: true }, - questions: [] -}, {timestamps: true, toJSON: {virtuals: true}}); - -QCMCategorySchema.plugin(uniqueValidator, { message: 'is already taken' }); - -QCMCategorySchema.methods.toJSONFor = function(){ - return { - num: this.num, - name: this.name, - questions: this.questions, - createdAt: this.createdAt, - updatedAt: this.updatedAt - }; -}; - -mongoose.model('QCMCategory', QCMCategorySchema); \ No newline at end of file diff --git a/models/mongo/QcmCategory.js b/models/mongo/QcmCategory.js new file mode 100644 index 0000000..1a45db4 --- /dev/null +++ b/models/mongo/QcmCategory.js @@ -0,0 +1,48 @@ +var mongoose = require('mongoose'); +var uniqueValidator = require('mongoose-unique-validator'); +var slug = require('slug'); + +var QcmCategorySchema = new mongoose.Schema({ + num: Number, + name: { type: String, unique: true }, + slug: { type: String, lowercase: true, unique: true }, + questions: [ + { type: mongoose.Schema.Types.ObjectId, ref: 'QcmQuestion' } + ] +}, {timestamps: true, toJSON: {virtuals: true}}); + +QcmCategorySchema.plugin(uniqueValidator, { message: 'is already taken' }); + +QcmCategorySchema.pre('validate', function (next) { + if (!this.slug) { + this.slugify(); + } + next(); +}); + +QcmCategorySchema.methods.slugify = function () { +this.slug = slug(`${this.name}-${this.num}-`) + (Math.random() * Math.pow(36, 6) | 0).toString(36); +}; + +QcmCategorySchema.methods.addQuestion = function (id) { + if (this.questions.indexOf(id) === -1) { + this.questions.push(id); + } + return this.save(); +}; + +QcmCategorySchema.methods.removeQuestion = function (id) { + this.questions.remove(id); + return this.save(); +}; + +QcmCategorySchema.methods.toJSONFor = function() { + return { + num: this.num, + name: this.name, + slug: this.slug, + questions: this.questions.map(question => question.toJSONFor()) + }; +}; + +mongoose.model('QcmCategory', QcmCategorySchema); \ No newline at end of file diff --git a/models/mongo/QcmChoice.js b/models/mongo/QcmChoice.js new file mode 100644 index 0000000..38e9c8e --- /dev/null +++ b/models/mongo/QcmChoice.js @@ -0,0 +1,17 @@ +var mongoose = require('mongoose'); + +var QcmChoiceSchema = new mongoose.Schema({ + index: String, + libelle: String, + correct: Boolean +}, {timestamps: true, toJSON: {virtuals: true}}); + +QcmChoiceSchema.methods.toJSONFor = function() { + return { + index: this.index, + libelle: this.libelle, + correct: this.correct + }; +}; + +mongoose.model('QcmChoice', QcmChoiceSchema); \ No newline at end of file diff --git a/models/mongo/QcmQuestion.js b/models/mongo/QcmQuestion.js new file mode 100644 index 0000000..b1a92ab --- /dev/null +++ b/models/mongo/QcmQuestion.js @@ -0,0 +1,31 @@ +var mongoose = require('mongoose'); + +var QcmQuestionSchema = new mongoose.Schema({ + num: Number, + libelle: String, + choices: [ + { type: mongoose.Schema.Types.ObjectId, ref: 'QcmChoice' } + ] +}, {timestamps: true, toJSON: {virtuals: true}}); + +QcmQuestionSchema.methods.addChoice = function (id) { + if (this.choices.indexOf(id) === -1) { + this.choices.push(id); + } + return this.save(); +}; + +QcmQuestionSchema.methods.removeChoice = function (id) { + this.choices.remove(id); + return this.save(); +}; + +QcmQuestionSchema.methods.toJSONFor = function() { + return { + num: this.num, + libelle: this.libelle, + choices: this.choices.map(choice => choice.toJSONFor()) + }; +}; + +mongoose.model('QcmQuestion', QcmQuestionSchema); \ No newline at end of file diff --git a/models/mongo/User.js b/models/mongo/User.js index bf39014..084a64e 100644 --- a/models/mongo/User.js +++ b/models/mongo/User.js @@ -24,7 +24,7 @@ var UserSchema = new mongoose.Schema({ UserSchema.plugin(uniqueValidator, { message: 'is already taken.' }); UserSchema.methods.validPassword = function (password) { - var hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex'); + let hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex'); return this.hash === hash; }; @@ -34,8 +34,8 @@ UserSchema.methods.setPassword = function (password) { }; UserSchema.methods.generateJWT = function () { - var today = new Date(); - var exp = new Date(today); + let today = new Date(); + let exp = new Date(today); exp.setDate(today.getDate() + 60); return jwt.sign({ diff --git a/models/mongo/X2DataLog.js b/models/mongo/X2DataLog.js new file mode 100644 index 0000000..7a626d9 --- /dev/null +++ b/models/mongo/X2DataLog.js @@ -0,0 +1,107 @@ +var mongoose = require('mongoose'); + +var X2DataLogSchema = new mongoose.Schema({ + id: String, + name: String, + type: String, + description: String, + landingZone: String, + speed: { + freeFall: { + vertical: { + max: Number, + avg: Number + }, + horizontal: { + max: Number, + avg: Number + } + }, + underCanopy: { + vertical: { + max: Number, + avg: Number + }, + horizontal: { + max: Number, + avg: Number + } + } + }, + glideRatio: { + freeFall: { + max: Number, + avg: Number + }, + underCanopy: { + max: Number, + avg: Number + } + }, + duration: { + freeFall: Number, + underCanopy: Number + }, + distance: { + exitFromLandingZone: Number, + totalFlying: Number, + totalHorizontal: Number + }, + altitude: { + exit: Number, + deployment: Number + }, + cutaway: String, + malfunctions: [ + { + name: String, + type: String + } + ], + date: String, + createdOn: String, + author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } +}, {timestamps: true, toJSON: {virtuals: true}}); + +X2DataLogSchema.methods.toJSONFor = function() { + return { + id: this.id, + name: this.name, + type: this.type, + description: this.description, + landingZone: this.landingZone, + speed: this.speed, + glideRatio: this.glideRatio, + duration: this.duration, + distance: this.distance, + altitude: this.altitude, + cutaway: this.cutaway, + malfunctions: this.malfunctions, + date: this.date, + createdOn: this.createdOn + }; +}; + +X2DataLogSchema.methods.toJSONForUser = function(user) { + return { + id: this.id, + name: this.name, + type: this.type, + description: this.description, + landingZone: this.landingZone, + speed: this.speed, + glideRatio: this.glideRatio, + duration: this.duration, + distance: this.distance, + altitude: this.altitude, + cutaway: this.cutaway, + malfunctions: this.malfunctions, + date: this.date, + createdOn: this.createdOn, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + author: this.author.toProfileJSONFor(user) + }; +}; + +mongoose.model('X2DataLog', X2DataLogSchema); diff --git a/models/mongo/index.js b/models/mongo/index.js new file mode 100644 index 0000000..ec76188 --- /dev/null +++ b/models/mongo/index.js @@ -0,0 +1,13 @@ +exports.User = require('./User'); +exports.Application = require('./Application'); +exports.Aeronef = require('./Aeronef'); +exports.Canopy = require('./Canopy'); +exports.Dropzone = require('./Dropzone'); +exports.JumpFile = require('./JumpFile'); +exports.Jump = require('./Jump'); +exports.Qcm = require('./Qcm'); +exports.QcmCategory = require('./QcmCategory'); +exports.QcmQuestion = require('./QcmQuestion'); +exports.QcmChoice = require('./QcmChoice'); +exports.X2DataLog = require('./X2DataLog'); + diff --git a/nodemon.json b/nodemon.json index 93455b5..805ea63 100644 --- a/nodemon.json +++ b/nodemon.json @@ -13,6 +13,8 @@ "COUCHDB_URL": "http://couchdb.unespace.com", "COUCHDB_DATABASE": "headupdb", "COUCHDB_DESIGN": "headup_app", - "COUCHDB_CREDENTIAL": "cmFtcGV1cjpES3hwMjRQUw==" + "COUCHDB_CREDENTIAL": "cmFtcGV1cjpES3hwMjRQUw==", + "SKYDIVERID_API_URL": "https://skydiver.id/api", + "SKYDIVERID_API_TOKEN": "xmqzgnGYMD.OJgFw1pCXCpJFGmZfSjmWK8lrpqAQNnu" } } diff --git a/package-lock.json b/package-lock.json index ce35e5d..f97aa65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,11 +35,23 @@ "uuid": "^9.0.1" }, "devDependencies": { + "@eslint/js": "^9.0.0", + "eslint": "^9.0.0", + "globals": "^15.0.0", "newman": "^5.3.2", "nodemon": "^2.0.22" }, "engines": { - "node": "^16.20.1 || ^18.16.1 || ^20.3.1" + "node": "^16.20.2 || ^18.19.1 || ^20.11.1" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/@aws-crypto/crc32": { @@ -719,6 +731,165 @@ "tslib": "^2.3.1" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz", + "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.0.0.tgz", + "integrity": "sha512-RThY/MnKrhubF6+s1JflwUjPEsnCEmYCWwqa/aRISKWNXGZ9epUwft4bUMM35SdKF9xvBrLydAM1RDHd1Z//ZQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.12.3.tgz", + "integrity": "sha512-jsNnTBlMWuTpDkeE3on7+dWJi0D6fdDfeANj/w7MpS8ztROCoLvIO2nG0CcFj+E4k8j4QrSTh4Oryi3i2G669g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -785,6 +956,41 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@postman/form-data": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", @@ -1464,6 +1670,27 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1563,6 +1790,12 @@ "node": ">=10" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -1815,6 +2048,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2021,6 +2263,20 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/csv-parse": { "version": "4.16.3", "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", @@ -2054,6 +2310,12 @@ "ms": "2.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -2217,6 +2479,191 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.0.0.tgz", + "integrity": "sha512-IMryZ5SudxzQvuod6rUdIUz29qFItWx281VhtFVc2Psy/ZhlCeD/5DT6lBIJ4H3G+iamGJoTln1v+QSuPw0p7Q==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^3.0.2", + "@eslint/js": "9.0.0", + "@humanwhocodes/config-array": "^0.12.3", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2387,6 +2834,12 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "node_modules/fast-xml-parser": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", @@ -2409,6 +2862,15 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -2431,6 +2893,18 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/file-type": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", @@ -2505,6 +2979,41 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flat-cache/node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, "node_modules/flatted": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", @@ -2703,6 +3212,18 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.0.0.tgz", + "integrity": "sha512-m/C/yR4mjO6pXDTm9/R/SpYTAIyaUB4EOzcaaMEl7mds7Mshct9GfejiJNQGjHHbdMPey13Kpu4TMbYi9ex1pw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -2714,6 +3235,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -2939,12 +3466,46 @@ } ] }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3038,11 +3599,26 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3071,11 +3647,29 @@ "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==", "dev": true }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -3086,6 +3680,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -3159,6 +3759,28 @@ "node": ">=12.0.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/liquid-json": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", @@ -3168,6 +3790,21 @@ "node": ">=4" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -3542,6 +4179,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3825,6 +4468,65 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", @@ -3887,6 +4589,15 @@ "node": ">= 0.4.0" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3895,6 +4606,15 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -4204,6 +4924,15 @@ "node": ">=10" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -4264,6 +4993,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -4407,6 +5156,25 @@ "uuid": "bin/uuid" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4421,6 +5189,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4548,6 +5339,27 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", @@ -4787,6 +5599,12 @@ "integrity": "sha512-j1llvWVFyEn/6XIFDfX5LAU43DXe0GCt3NfXDwJ8XpRRMkS+i50SAkonAONBy+vxwPFBd50MFU8a2uj8R/ccLg==", "dev": true }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4881,6 +5699,18 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -5035,6 +5865,21 @@ "node": ">=12" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -5076,6 +5921,18 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index a3488b6..89b94e0 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,9 @@ "uuid": "^9.0.1" }, "devDependencies": { + "@eslint/js": "^9.0.0", + "eslint": "^9.0.0", + "globals": "^15.0.0", "newman": "^5.3.2", "nodemon": "^2.0.22" } diff --git a/routes/api/aeronefs.js b/routes/api/aeronefs.js index 36e96bf..07e68d6 100644 --- a/routes/api/aeronefs.js +++ b/routes/api/aeronefs.js @@ -1,14 +1,14 @@ var router = require('express').Router(), mongoose = require('mongoose'), - Aeronef = mongoose.model('Aeronef'), Jump = mongoose.model('Jump'), User = mongoose.model('User'); -const auth = require('../auth'); +const auth = require('../auth'), + queries = require('../queries'); -router.get('/', auth.required, function (req, res, next) { - var limit = 25; - var offset = 0; +router.get('/allByImat', auth.required, function (req, res, next) { + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } @@ -18,43 +18,22 @@ router.get('/', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$group': { - '_id': { - 'aeronef': '$aeronef', - 'imat': '$imat' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': - { - '_id': 1 - }, - } - ]; + let query = queries.getAeronefsByImatQuery(user._id.toString()); Promise.all([ Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() //Aeronef.count(query) ]).then(function (results) { - var aeronefs = results[0]; - //var aeronefsCount = results[1]; + let aeronefs = results[0]; + //let aeronefsCount = results[1]; return res.json({ - aeronefs: aeronefs, + aeronefs: aeronefs.map(function (data) { + return { aeronef: data._id.aeronef, imat: data._id.imat, count: data.count }; + }), aeronefsCount: aeronefs.length }); /* @@ -69,66 +48,96 @@ router.get('/', auth.required, function (req, res, next) { }).catch(next); }); -router.get('/allByDate', auth.required, function (req, res, next) { +router.get('/allByImatByYear', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var limit = 50; - var offset = 0; + let limit = 50; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } if (typeof req.query.offset !== 'undefined') { offset = req.query.offset; } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$project': { - '_id': 0, - 'aeronef': 1, - 'imat': 1, - 'year': { - '$year': '$date' - } - } - }, { - '$group': { - '_id': { - 'aeronef': '$aeronef', - 'imat': '$imat', - 'year': '$year' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': { - '_id': 1 - } - } - ]; + let query = queries.getAeronefsByImatByYearQuery(user._id.toString()); Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var aeronefs = result; - //console.log(aeronefs); + let aeronefs = result.map(function (data) { + return { aeronef: data._id.aeronef, imat: data._id.imat, year: data._id.year, count: data.count }; + }); return res.json({ aeronefs: aeronefs, aeronefsCount: aeronefs.length }); }).catch(next); }).catch(next); + /* + let limit = 50; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] + } + } + }, { + '$project': { + '_id': 0, + 'aeronef': 1, + 'imat': 1, + 'year': { + '$year': '$date' + } + } + }, { + '$group': { + '_id': { + 'aeronef': '$aeronef', + 'imat': '$imat', + 'year': '$year' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + Promise.all([ + req.payload ? User.findById(req.payload.id) : null, + Jump.aggregate(query) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + ]).then(function (results) { + let user = results[0]; + let aeronefs = results[1]; + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + return res.json({ + aeronefs: aeronefs, + aeronefsCount: aeronefs.length + }); + }).catch(next); + */ + }); module.exports = router; diff --git a/routes/api/applications.js b/routes/api/applications.js index dc0bb56..e436b80 100644 --- a/routes/api/applications.js +++ b/routes/api/applications.js @@ -21,9 +21,9 @@ router.param('application', function (req, res, next, slug) { }); router.get('/', auth.required, function (req, res, next) { - var query = {}; - var limit = 25; - var offset = 0; + let query = {}; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; @@ -46,17 +46,17 @@ router.get('/', auth.required, function (req, res, next) { } return Promise.all([ Application.find(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .sort({ createdAt: 'desc' }) .populate('author') .exec(), Application.count(query).exec(), req.payload ? User.findById(req.payload.id) : null ]).then(function (results) { - var applications = results[0]; - var applicationsCount = results[1]; - var user = results[2]; + let applications = results[0]; + let applicationsCount = results[1]; + let user = results[2]; return res.json({ applications: applications.map(function (application) { @@ -73,17 +73,17 @@ router.post('/', auth.required, function (req, res, next) { User.findById(req.payload.id), auth.generateAPIKey() ]).then(function (results) { - var user = results[0]; - var keys = results[1]; + let user = results[0]; + let keys = results[1]; if (!user) { return res.sendStatus(401).json({message: "Unauthorized - You are not allowed to access this resource."}); } - var application = new Application(req.body.application); + let application = new Application(req.body.application); application.author = user; application.apikey = keys.encrypted; application.maskedkey = keys.masked; return application.save().then(function () { - var msg = { + let msg = { to: user.email, from: process.env.SENDGRID_FROM_MAIL, subject: `Api-Key ${application.title}`, @@ -96,7 +96,7 @@ router.post('/', auth.required, function (req, res, next) {
${application.description}
` }; - mailer.send(msg).then(resp => { + mailer.send(msg).then(() => { return res.json({ application: application.toJSONFor(user) }); }) .catch(err => { @@ -112,7 +112,7 @@ router.get('/:application', auth.required, function (req, res, next) { req.payload ? User.findById(req.payload.id) : null, req.application.populate('author') ]).then(function (results) { - var user = results[0]; + let user = results[0]; return res.json({ application: req.application.toJSONFor(user) }); }).catch(next); }); @@ -136,7 +136,7 @@ router.put('/:application', auth.required, function (req, res, next) { return res.json({ application: application.toJSONFor(user) }); }).catch(next); } else { - return res.sendStatus(403); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } }); }); @@ -145,14 +145,14 @@ router.put('/:application', auth.required, function (req, res, next) { router.delete('/:application', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401).json({message: "Unauthorized - You are not allowed to access this resource."}); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } if (user.role == 'Admin' || req.application.author._id.toString() === req.payload.id.toString()) { return req.application.remove().then(function () { return res.sendStatus(204); }); } else { - return res.sendStatus(403).json({message: "Forbidden - You are not allowed to access this resource."}); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } }).catch(next); }); diff --git a/routes/api/calcul.js b/routes/api/calcul.js deleted file mode 100644 index 9886914..0000000 --- a/routes/api/calcul.js +++ /dev/null @@ -1,129 +0,0 @@ -var router = require('express').Router(); -const auth = require('../auth'); -//var ExceptionsHandler = require('../../middlewares/exceptions.handler'); -//var UnknownRoutesHandler = require('../../middlewares/unknownRoutes.handler'); - -router.use('/', function (req, res, next) { - let taxationValues = ['cours_legaux', 'jetons', 'metaux_precieux']; - let errors = []; - if (req.body.taxationType == undefined || taxationValues.indexOf(req.body.taxationType) === -1) { - errors.push('La valeur du paramètre \'taxationType\' est incorrecte.'); - } - if (req.body.purchasePrice == undefined || req.body.purchasePrice <= 0) { - errors.push('La valeur du paramètre \'purchasePrice\' est incorrecte.'); - } - if (req.body.sellPrice == undefined || req.body.sellPrice <= 0) { - errors.push('La valeur du paramètre \'sellPrice\' est incorrecte.'); - } - if (req.body.yearOwnership == undefined || req.body.yearOwnership < 0) { - errors.push('La valeur du paramètre \'yearOwnership\' est incorrecte.'); - } - if (errors.length) { - return res.status(422).json({ - errors: { - name: 'ValidationError', - message: 'La validation des données a échoué.', - messages: errors - } - }); - } - return next(); -}); - -router.post('/', function (req, res, next) { - const config = { - palierDetention: 2, - coeff: { - metaux_precieux: 0.115, - jetons: 0.065, - plus_value: 0.362 - } - }; - const params = { - taxationType: req.body.taxationType ? req.body.taxationType : null, - purchasePrice: req.body.purchasePrice ? req.body.purchasePrice : 0, - sellPrice: req.body.sellPrice ? req.body.sellPrice : 0, - yearOwnership: req.body.yearOwnership ? req.body.yearOwnership : 0 - }; - let results = { - capitalGain: 0, - capitalGainCoeff: 0, - lumpSum: 0, - lumpSumCoeff: 0, - isTaxable: false, - bestChoice: null, // valeurs possibles : 'lumpSum', 'gain' or null - messages: [] - } - if(req.body.purchasePrice >= req.body.sellPrice) { - results.messages.push((req.body.purchasePrice > req.body.sellPrice) ? 'Vous êtes dans le cas d\'une moins-value.' : 'Vous êtes dans le cas d\'une plus-value nulle.'); - } - if(req.body.yearOwnership >= 22) { - results.messages.push('Vous êtes dans le cas d\'une vente non imposable (à partir de 22 ans de détention).'); - } - if (params.sellPrice <= 5000) { - if (params.taxationType == 'cours_legaux') { - results.messages.push('Vous êtes dans le cas d\'une vente non imposable sur le cours légal (inférieure à 5000 €).'); - } else if (params.taxationType == 'jetons') { - results.messages.push('Vous êtes dans le cas d\'une vente non imposable sur les jetons (inférieure à 5000 €).'); - } - } - if (results.messages.length) { - results.messages.push('La déclaration est inutile, il n\'y a pas d\'imposition.'); - return res.json({ - params: params, - results: results - }); - } - - results.isTaxable = true; - results.capitalGainCoeff = config.coeff.plus_value; - if (params.yearOwnership > config.palierDetention) { - /* A partir de la deuxième année, abattement de 5% sur le taux appliqué */ - results.capitalGainCoeff = roundNumber((config.coeff.plus_value - (config.coeff.plus_value * (0.05 * (params.yearOwnership - config.palierDetention)))), 4); - results.capitalGain = Math.floor(results.capitalGainCoeff * (params.sellPrice - params.purchasePrice)); - } else { - results.capitalGain = Math.floor(config.coeff.plus_value * (params.sellPrice - params.purchasePrice)); - } - - let value = roundNumber((results.capitalGainCoeff * 100), 2); - results.messages.push(`Le montant du régime des plus-value (${value}%) est de ${results.capitalGain} €.`); - results.bestChoice = 'gain'; - if (params.taxationType != 'cours_legaux') { - results.lumpSum = Math.floor(params.sellPrice * config.coeff[params.taxationType]); - if (results.lumpSum < results.capitalGain) { - results.bestChoice = 'lumpSum'; - } - results.lumpSumCoeff = config.coeff[params.taxationType]; - value = (config.coeff[params.taxationType] * 100 ); - results.messages.push(`Le montant de la taxe forfaitaire (${value}%) est de ${results.lumpSum} €.`); - results.messages.push(`La meilleure option pour vous est ${(results.bestChoice == 'lumpSum') ? 'la taxe forfaitaire' : 'la taxe sur le régime des plus values'}.`); - if (params.taxationType == 'jetons') { - results.messages.push(`À noter : pour une revente de produits de jetons en dessous du seuil des 5000€, aucune taxe n'est applicable.`); - } - } else { - results.messages.push('La taxe forfaitaire n\'est pas applicable pour les produits à cours légaux. Seule la taxe sur le régime des plus-values est applicable.'); - results.messages.push('À noter : pour une revente de produits à cours légaux en dessous du seuil des 5000 €, aucune taxe n\'est applicable.'); - } - - return res.json({ - params: params, - results: results - }); - -}); - -router.all('*', function (req, res, next) { - return res.status(404).json({ - errors: { - name: 'UnknownRouteError', - message: 'La route API demandée est introuvable.' - } - }); -}); - -function roundNumber(num, dec){ - var num_sign = num >= 0 ? 1 : -1; - return parseFloat((Math.round((num * Math.pow(10, dec)) + (num_sign * 0.0001)) / Math.pow(10, dec)).toFixed(dec)); -} - -module.exports = router; \ No newline at end of file diff --git a/routes/api/canopies.js b/routes/api/canopies.js index 1ac2458..0cff862 100644 --- a/routes/api/canopies.js +++ b/routes/api/canopies.js @@ -1,14 +1,14 @@ var router = require('express').Router(), mongoose = require('mongoose'), - Canopy = mongoose.model('Canopy'), Jump = mongoose.model('Jump'), User = mongoose.model('User'); -const auth = require('../auth'); +const auth = require('../auth'), + queries = require('../queries'); router.get('/', auth.required, function (req, res, next) { - var query = {}; - var limit = 25; - var offset = 0; + let query = {}; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } @@ -21,17 +21,17 @@ router.get('/', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } if (user.role == 'Admin') { - return res.sendStatus(403); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } Jump.find(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var canopies = result; + let canopies = result; return res.json({ canopies: canopies, canopiesCount: canopies.length @@ -41,8 +41,8 @@ router.get('/', auth.required, function (req, res, next) { }); router.get('/allBySize', auth.required, function (req, res, next) { - var limit = 25; - var offset = 0; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } @@ -52,38 +52,17 @@ router.get('/allBySize', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$group': { - '_id': { - 'taille': '$taille' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': - { - '_id': 1 - }, - } - ]; - Promise.all([ - Jump.aggregate(query) - .limit(Number(limit)) - .skip(Number(offset)) - .exec() - ]).then(function (results) { - var canopies = results[0]; + let query = queries.getCanopiesBySizeQuery(user._id.toString()); + Jump.aggregate(query) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + .then(function (result) { + let canopies = result.map(function (data) { + return { taille: data._id.taille, count: data.count }; + }); return res.json({ canopies: canopies, canopiesCount: canopies.length @@ -92,57 +71,28 @@ router.get('/allBySize', auth.required, function (req, res, next) { }).catch(next); }); -router.get('/allByYear', auth.required, function (req, res, next) { +router.get('/allBySizeByYear', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var limit = 25; - var offset = 0; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } if (typeof req.query.offset !== 'undefined') { offset = req.query.offset; } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$project': { - '_id': 0, - 'taille': 1, - 'year': { - '$year': '$date' - } - } - }, { - '$group': { - '_id': { - 'taille': '$taille', - 'year': '$year' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': { - '_id': 1 - } - } - ]; - + let query = queries.getCanopiesBySizeByYearQuery(user._id.toString()); Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var canopies = result; + let canopies = result.map(function (data) { + return { taille: data._id.taille, year: data._id.year, count: data.count }; + }); //console.log(dropzones); return res.json({ canopies: canopies, @@ -152,9 +102,9 @@ router.get('/allByYear', auth.required, function (req, res, next) { }).catch(next); }); -router.get('/allModelBySize', auth.required, function (req, res, next) { - var limit = 25; - var offset = 0; +router.get('/allBySizeByModel', auth.required, function (req, res, next) { + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } @@ -164,39 +114,17 @@ router.get('/allModelBySize', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$group': { - '_id': { - 'taille': '$taille', - 'voile': '$voile' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': - { - '_id': 1 - }, - } - ]; - Promise.all([ - Jump.aggregate(query) - .limit(Number(limit)) - .skip(Number(offset)) - .exec() - ]).then(function (results) { - var canopies = results[0]; + let query = queries.getCanopiesBySizeByModelQuery(user._id.toString()); + Jump.aggregate(query) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + .then(function (result) { + let canopies = result.map(function (data) { + return { taille: data._id.taille, voile: data._id.voile, count: data.count }; + }); return res.json({ canopies: canopies, canopiesCount: canopies.length @@ -205,60 +133,28 @@ router.get('/allModelBySize', auth.required, function (req, res, next) { }).catch(next); }); -router.get('/allModelByYear', auth.required, function (req, res, next) { +router.get('/allBySizeByModelByYear', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var limit = 25; - var offset = 0; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } if (typeof req.query.offset !== 'undefined') { offset = req.query.offset; } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$project': { - '_id': 0, - 'voile': 1, - 'taille': 1, - 'year': { - '$year': '$date' - } - } - }, { - '$group': { - '_id': { - 'taille': '$taille', - 'voile': '$voile', - 'year': '$year' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': { - '_id': 1 - } - } - ]; - + let query = queries.getCanopiesBySizeByModelByYearQuery(user._id.toString()); Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var canopies = result; - //console.log(dropzones); + let canopies = result.map(function (data) { + return { taille: data._id.taille, voile: data._id.voile, year: data._id.year, count: data.count }; + }); return res.json({ canopies: canopies, canopiesCount: canopies.length diff --git a/routes/api/dropzones.js b/routes/api/dropzones.js index 7d27d3c..c51bebc 100644 --- a/routes/api/dropzones.js +++ b/routes/api/dropzones.js @@ -1,54 +1,33 @@ var router = require('express').Router(), mongoose = require('mongoose'), - Dropzone = mongoose.model('Dropzone'), Jump = mongoose.model('Jump'), User = mongoose.model('User'); -const auth = require('../auth'); +const auth = require('../auth'), + queries = require('../queries'); -router.get('/', auth.required, function (req, res, next) { +router.get('/allByOaci', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var limit = 25; - var offset = 0; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } if (typeof req.query.offset !== 'undefined') { offset = req.query.offset; } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$group': { - '_id': { - 'lieu': '$lieu', - 'oaci': '$oaci' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': - { - '_id': 1 - }, - } - ]; + let query = queries.getDropZonesByOaciQuery(user._id.toString()); Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var dropzones = result; + let dropzones = result.map(function (data) { + return { lieu: data._id.lieu, oaci: data._id.oaci, count: data.count }; + }); return res.json({ dropzones: dropzones, dropzonesCount: dropzones.length @@ -57,60 +36,28 @@ router.get('/', auth.required, function (req, res, next) { }).catch(next); }); -router.get('/allByDate', auth.required, function (req, res, next) { +router.get('/allByOaciByYear', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var limit = 25; - var offset = 0; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } if (typeof req.query.offset !== 'undefined') { offset = req.query.offset; } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$project': { - '_id': 0, - 'lieu': 1, - 'oaci': 1, - 'year': { - '$year': '$date' - } - } - }, { - '$group': { - '_id': { - 'lieu': '$lieu', - 'oaci': '$oaci', - 'year': '$year' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': { - '_id': 1 - } - } - ]; - + let query = queries.getDropZonesByOaciByYearQuery(user._id.toString()); Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var dropzones = result; - //console.log(dropzones); + let dropzones = result.map(function (data) { + return { lieu: data._id.lieu, oaci: data._id.oaci, year: data._id.year, count: data.count }; + }); return res.json({ dropzones: dropzones, dropzonesCount: dropzones.length diff --git a/routes/api/index.js b/routes/api/index.js index 70118fe..3feb27b 100644 --- a/routes/api/index.js +++ b/routes/api/index.js @@ -7,13 +7,9 @@ router.use('/aeronefs', require('./aeronefs')); router.use('/canopies', require('./canopies')); router.use('/dropzones', require('./dropzones')); router.use('/jumps', require('./jumps')); +router.use('/pages', require('./pages')); router.use('/qcm', require('./qcm')); -/* -router.use('/products', require('./products')); -router.use('/metals', require('./metals')); -router.use('/prices', require('./prices')); -router.use('/calcul', require('./calcul')); -*/ + router.use(function (err, req, res, next) { if (err.name === 'ValidationError') { return res.status(422).json({ diff --git a/routes/api/jumps.js b/routes/api/jumps.js index b16b88a..ec88766 100644 --- a/routes/api/jumps.js +++ b/routes/api/jumps.js @@ -1,13 +1,20 @@ var router = require('express').Router(), mongoose = require('mongoose'), Jump = mongoose.model('Jump'), - User = mongoose.model('User'); -const auth = require('../auth'); + JumpFile = mongoose.model('JumpFile'), + User = mongoose.model('User'), + X2DataLog = mongoose.model('X2DataLog'); +const auth = require('../auth'), + chalk = require('chalk'), + queries = require('../queries'), + utils = require('../utils'); // Preload jump objects on routes with ':jump' router.param('jump', function (req, res, next, slug) { Jump.findOne({ slug: slug }) .populate('author') + .populate('files') + .populate('x2data') .then(function (jump) { if (!jump) { return res.sendStatus(404); @@ -21,9 +28,9 @@ router.param('jump', function (req, res, next, slug) { * return all jumps */ router.get('/', auth.required, function (req, res, next) { - var query = {}; - var limit = 25; - var offset = 0; + let query = {}; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } @@ -39,31 +46,87 @@ router.get('/', auth.required, function (req, res, next) { ], } } + if (typeof req.query.numeroRangeStart !== 'undefined' && typeof req.query.numeroRangeEnd !== 'undefined') { + query.numero = { + $gte: req.query.numeroRangeStart, + $lte: req.query.numeroRangeEnd + }; + } + if (typeof req.query.tailleRangeStart !== 'undefined' && typeof req.query.tailleRangeEnd !== 'undefined') { + query.taille = { + $gte: req.query.tailleRangeStart, + $lte: req.query.tailleRangeEnd + }; + } + if (typeof req.query.participantsRangeStart !== 'undefined' && typeof req.query.participantsRangeEnd !== 'undefined') { + query.participants = { + $gte: req.query.participantsRangeStart, + $lte: req.query.participantsRangeEnd + }; + } + if (typeof req.query.hauteurRangeStart !== 'undefined' && typeof req.query.hauteurRangeEnd !== 'undefined') { + query.hauteur = { + $gte: req.query.hauteurRangeStart, + $lte: req.query.hauteurRangeEnd + }; + } + if (typeof req.query.yearRangeStart !== 'undefined' && typeof req.query.yearRangeEnd !== 'undefined') { + query.date = { + $gte: new Date(`${req.query.yearRangeStart}-01-01 00:00:00`), + $lte: new Date(`${req.query.yearRangeEnd}-12-31 23:59:59`) + }; + } + if (typeof req.query.dateRangeStart !== 'undefined' && typeof req.query.dateRangeEnd !== 'undefined') { + query.date = { + $gte: new Date(`${req.query.dateRangeStart} 00:00:00`), + $lte: new Date(`${req.query.dateRangeEnd} 23:59:59`) + }; + } Promise.all([ req.query.author ? User.findOne({ username: req.query.author }) : null, req.query.favorited ? User.findOne({ username: req.query.favorited }) : null ]).then(function (users) { - var author = users[0]; + let author = users[0]; if (author) { query.author = author._id; } - return Promise.all([ + Promise.all([ Jump.find(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .sort({ numero: 'desc' }) .populate('author') + .populate('files') + .populate('x2data') .exec(), Jump.count(query).exec(), req.payload ? User.findById(req.payload.id) : null ]).then(function (results) { - var jumps = results[0]; - var jumpsCount = results[1]; - var user = results[2]; - + let jumps = results[0]; + let jumpsCount = results[1]; + let user = results[2]; + /* + Promise.all( + jumps.map(jump => { + if (jump.files[0] !== undefined && jump.files[0].type !== 'kml') { + console.log( chalk.green(`Fichier ${jump.files[0].type}`)) + //console.log( chalk.green(`Suppression du fichier ${jump.files[2]._id}`)) + //return jump.removeFile(jump.files[2]._id); + } + return; + }) + ).then(function () { + return res.json({ + jumps: jumps.map(function (jump) { + return jump.toJSONFor(user); + }), + jumpsCount: jumpsCount + }); + }); + */ return res.json({ jumps: jumps.map(function (jump) { return jump.toJSONFor(user); @@ -74,60 +137,29 @@ router.get('/', auth.required, function (req, res, next) { }).catch(next); }); -router.get('/allByDate', auth.required, function (req, res, next) { +router.get('/allByCategorie', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var limit = 120; - var offset = 0; + let limit = 120; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } if (typeof req.query.offset !== 'undefined') { offset = req.query.offset; } - var query = [ - { - '$match': { - '$expr': { - '$eq': [ '$author' , { '$toObjectId': user._id.toString() } ] - } - } - }, { - '$project': { - '_id': 0, - 'year': { - '$year': '$date' - }, - 'month': { - '$month': '$date' - } - } - }, { - '$group': { - '_id': { - 'year': '$year', - 'month': '$month' - }, - 'count': { - '$sum': 1 - } - } - }, { - '$sort': { - '_id': 1 - } - } - ]; + let query = queries.getJumpsByCategoryQuery(user._id.toString()); Jump.aggregate(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .exec() .then(function (result) { - var jumps = result; - //console.log(jumps); + let jumps = result.map(function (data) { + return { categorie: data._id.categorie, count: data.count }; + }); return res.json({ jumps: jumps, jumpsCount: jumps.length @@ -136,10 +168,133 @@ router.get('/allByDate', auth.required, function (req, res, next) { }).catch(next); }); +router.get('/allByDate', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let limit = 120; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + let query = queries.getJumpsByDateQuery(user._id.toString()); + + Jump.aggregate(query) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + .then(function (result) { + let jumps = result.map(function (data) { + return { year: data._id.year, month: data._id.month, count: data.count }; + }); + return res.json({ + jumps: jumps, + jumpsCount: jumps.length + }); + }).catch(next); + }).catch(next); +}); + +router.get('/allByDay', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let query = {}; + let limit = 50; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + if (typeof req.query.dateRangeStart !== 'undefined' && typeof req.query.dateRangeEnd !== 'undefined') { + query = queries.getJumpsByDayQuery( + user._id.toString(), + req.query.dateRangeStart, + req.query.dateRangeEnd + ); + } else { + query = queries.getJumpsByDayQuery(user._id.toString()); + } + + Jump.aggregate(query) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + .then(function (result) { + let days = result.map(function (data) { + return { jumps: data.jumps, count: data.count }; + }); + return res.json({ + days: days, + daysCount: days.length + }); + }).catch(next); + }).catch(next); +}); + +router.get('/allByModule', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let limit = 120; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + let query = queries.getJumpsByModuleQuery(user._id.toString()); + + Jump.aggregate(query) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + .then(function (result) { + let jumps = result.map(function (data) { + return { categorie: data._id.categorie, module: data._id.module, count: data.count }; + }); + return res.json({ + jumps: jumps, + jumpsCount: jumps.length + }); + }).catch(next); + }).catch(next); +}); + +/** + * return a jump from SkydiverId API + */ +router.get('/allFromSkydiverIdApi', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + utils.fetchSkydiverIdApi('jumps', req.query).then(result => { + if (result.errors === undefined) { + console.log(`${utils.getDateTimeISOString()}`, chalk.green(`Réception des sauts SkydiverId`)); + } else { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`Une erreur ${result.errors.code} '${result.errors.type}' est survenue lors de la réception des sauts SkydiverId : ${result.errors.name} ${result.errors.message}`)); + } + return res.json({ + jumps: result, + jumpsCount: result.items.length + }); + }).catch(next); + }).catch(next); +}); router.get('/feed', auth.required, function (req, res, next) { - var limit = 25; - var offset = 0; + let limit = 25; + let offset = 0; if (typeof req.query.limit !== 'undefined') { limit = req.query.limit; } @@ -149,19 +304,21 @@ router.get('/feed', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var query = { author: { $in: user.following } }; + let query = { author: { $in: user.following } }; Promise.all([ Jump.find(query) - .limit(Number(limit)) .skip(Number(offset)) + .limit(Number(limit)) .populate('author') + .populate('files') + .populate('x2data') .exec(), Jump.count(query) ]).then(function (results) { - var jumps = results[0]; - var jumpsCount = results[1]; + let jumps = results[0]; + let jumpsCount = results[1]; return res.json({ jumps: jumps.map(function (jump) { return jump.toJSONFor(user); @@ -176,10 +333,9 @@ router.get('/feed', auth.required, function (req, res, next) { * return last jump */ router.get('/last', auth.required, function (req, res, next) { - User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } /*Jump.findOne({ slug: slug }) .populate('author') @@ -192,9 +348,10 @@ router.get('/last', auth.required, function (req, res, next) { }).catch(next);*/ Jump.find({}) .limit(1) - .skip(0) .sort({ numero: 'desc' }) .populate('author') + .populate('files') + .populate('x2data') .exec() .then(function (jumps) { if (!jumps) { @@ -202,28 +359,34 @@ router.get('/last', auth.required, function (req, res, next) { } req.lastjump = jumps[0]; if (req.lastjump.author._id.toString() !== req.payload.id.toString()) { - return res.sendStatus(403); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } return res.json({ jump: req.lastjump.toJSONFor(user) }); }).catch(next); }).catch(next); }); + /** * return a jump */ router.get('/:jump', auth.required, function (req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null - /*req.jump.populate('author')*/ - ]).then(function (results) { - var user = results[0]; + User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } if (req.jump.author._id.toString() !== req.payload.id.toString()) { - return res.sendStatus(403); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } - return res.json({ jump: req.jump.toJSONFor(user) }); + Promise.all([ + Jump.findOne({numero: (req.jump.numero - 1)}).exec(), + Jump.findOne({numero: (req.jump.numero + 1)}).exec(), + ]).then(function (results) { + return res.json({ + jump: req.jump.toJSONFor(user), + prevJump: results[0], + nextJump: results[1] + }); + }).catch(next); }).catch(next); }); @@ -231,19 +394,12 @@ router.get('/:jump', auth.required, function (req, res, next) { * save a jump */ router.post('/', auth.required, function (req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null - /*Jump - .find({}).select('numero') - .sort({"numero" : -1}).limit(1) - .exec()*/ - ]).then(function (results) { - var user = results[0]; - //var numero = (results[1][0].numero + 1); + User.findById(req.payload.id).then(function (user) { + //let numero = (results[1][0].numero + 1); if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } - var jump = new Jump(req.body.jump); + let jump = new Jump(req.body.jump); jump.author = user; //jump.numero = numero; return jump.save().then(function () { @@ -252,19 +408,136 @@ router.post('/', auth.required, function (req, res, next) { }).catch(next); }); +/** + * save a jump file + */ +router.post('/data/:jump', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + if (req.jump.author._id.toString() !== req.payload.id.toString()) { + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); + } + /* + console.log(req.jump.files); + return res.json({ jump: req.jump.toJSONFor(user) }); + */ + Object.assign(req.body.file, {_id: new mongoose.Types.ObjectId()}); + let file = new JumpFile(req.body.file); + let files = req.jump.files ? req.jump.files : []; + file.author = user; + files.push(file); + return file.save().then(function () { + Object.assign(req.body.data, {_id: new mongoose.Types.ObjectId()}); + let x2data = new X2DataLog(req.body.data); + x2data.author = user; + return x2data.save().then(function () { + req.jump.files = files; + req.jump.x2data = x2data; + Jump.updateOne({_id: req.jump._id}, {files: files, x2data: x2data._id}).then(function () { + return res.json({ jump: req.jump.toJSONFor(user) }); + }).catch(next); + }).catch(next); + }).catch(next); + }).catch(next); +}); + +/** + * save a jump file + */ +router.post('/file/:jump', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + if (req.jump.author._id.toString() !== req.payload.id.toString()) { + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); + } + Object.assign(req.body.file, {_id: new mongoose.Types.ObjectId()}); + let file = new JumpFile(req.body.file); + let files = req.jump.files ? req.jump.files : []; + file.author = user; + files.push(file); + return file.save().then(function () { + Jump.updateOne({_id: req.jump._id}, {files: files}).then(function () { + return res.json({ file: file.toJSONForJump() }); + }).catch(next); + }).catch(next); + }).catch(next); +}); + +/** + * save a jump file + */ +router.post('/kml/:jump', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + if (req.jump.author._id.toString() !== req.payload.id.toString()) { + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); + } + Object.assign(req.body.file, { + _id: new mongoose.Types.ObjectId(), + name: `${req.jump.x2data.name}.kml`, + path: `/Volumes/Storage/Skydive/X2_Kmls/${req.jump.date.getFullYear()}/`, + type: 'kml' + }); + let file = new JumpFile(req.body.file); + let files = req.jump.files ? req.jump.files : []; + file.author = user; + files.push(file); + //console.log(chalk.yellow(`jump/${req.jump.x2data.id}/kml`)); + return res.json({ file: file.toJSONForJump() }); + /* + utils.fetchSkydiverIdApi(`jump/${req.jump.x2data.id}/kml`, {}).then(result => { + if (result.errors === undefined) { + console.log(`${utils.getDateTimeISOString()}`, chalk.green(`Réception de l'url du fichier kml SkydiverId`)); + } else { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`Une erreur ${result.errors.code} '${result.errors.type}' est survenue lors de la réception de l'url du fichier kml SkydiverId : ${result.errors.name} ${result.errors.message}`)); + } + if (result.url !== undefined) { + console.log(chalk.green(result.url)); + } else { + console.log(chalk.red('result.url is undefined')); + } + return res.json({ file: file.toJSONForJump() }); + }).catch(next); + + + + return file.save().then(function () { + Jump.updateOne({_id: req.jump._id}, {files: files}).then(function () { + utils.fetchSkydiverIdApi(`jump/${req.jump.x2data.id}/kml`, {}).then(result => { + if (result.errors === undefined) { + console.log(`${utils.getDateTimeISOString()}`, chalk.green(`Réception de l'url du fichier kml SkydiverId`)); + } else { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`Une erreur ${result.errors.code} '${result.errors.type}' est survenue lors de la réception de l'url du fichier kml SkydiverId : ${result.errors.name} ${result.errors.message}`)); + } + if (result.url !== undefined) { + console.log(result.url); + } else { + console.log('result.url is undefined'); + } + return res.json({ file: file.toJSONForJump() }); + }).catch(next); + }).catch(next); + }).catch(next); + */ + }).catch(next); +}); + /** * update a jump */ router.put('/:jump', auth.required, function (req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null - ]).then(function (results) { - var user = results[0]; + User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } if (req.jump.author._id.toString() !== req.payload.id.toString()) { - return res.sendStatus(403); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } if (typeof req.body.jump.date !== 'undefined') { req.jump.date = req.body.jump.date; @@ -333,16 +606,16 @@ router.put('/:jump', auth.required, function (req, res, next) { router.delete('/:jump', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } if (req.jump.author._id.toString() === req.payload.id.toString()) { return req.jump.remove().then(function () { return res.sendStatus(204); }); } else { - return res.sendStatus(403); + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); } }).catch(next); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/routes/api/metals.js b/routes/api/metals.js deleted file mode 100644 index 55dae01..0000000 --- a/routes/api/metals.js +++ /dev/null @@ -1,64 +0,0 @@ -var router = require('express').Router(), - mongoose = require('mongoose'), - Metal = mongoose.model('Metal'); -const auth = require('../auth'); - -// Preload metal objects on routes with ':code' -router.param('code', function (req, res, next, code) { - Metal.findOne({ code: code }) - .then(function (metal) { - if (!metal) { - return res.sendStatus(404); - } - req.metal = metal; - return next(); - }).catch(next); -}); - -router.get('/', auth.required, function (req, res, next) { - var query = {}; - var limit = 20; - var offset = 0; - if (typeof req.query.limit !== 'undefined') { - limit = req.query.limit; - } - if (typeof req.query.offset !== 'undefined') { - offset = req.query.offset; - } - if (typeof req.query.code !== 'undefined') { - query.code = req.query.code; - } - if (typeof req.query.color !== 'undefined') { - query.color = req.query.color; - } - if (typeof req.query.ordering !== 'undefined') { - query.ordering = req.query.ordering; - } - if (typeof req.query.title !== 'undefined') { - query.title = req.query.title; - } - Promise.all([ - Metal.find(query) - .limit(Number(limit)) - .skip(Number(offset)) - .exec(), - Metal.count(query).exec() - ]).then(function (results) { - var metals = results[0]; - var metalsCount = results[1]; - - return res.json({ - metals: metals.map(function (metal) { - return metal.toJSONFor(); - }), - metalsCount: metalsCount - }); - }).catch(next); -}); - -// return a metal -router.get('/:code', auth.required, function (req, res, next) { - return res.json({ metal: req.metal.toJSONFor() }); -}); - -module.exports = router; \ No newline at end of file diff --git a/routes/api/pages.js b/routes/api/pages.js new file mode 100644 index 0000000..0d21ab9 --- /dev/null +++ b/routes/api/pages.js @@ -0,0 +1,285 @@ +var router = require('express').Router(), + mongoose = require('mongoose'), + Jump = mongoose.model('Jump'), + User = mongoose.model('User'); +const auth = require('../auth'), + queries = require('../queries'), + utils = require('../utils'); + +router.get('/aeronefs', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let limit = 120; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + Promise.all([ + Jump.find({}) + .limit(1) + .skip(0) + .sort({ numero: 'desc' }) + .populate('author') + .populate('files') + .populate('x2data') + .exec(), + Jump.aggregate(queries.getAeronefsByImatQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getAeronefsByImatByYearQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + ]).then(function (results) { + if (results[0].length) { + let lastjump = results[0][0].toJSONFor(user); + let aeronefsByImat = results[1].map(function (data) { + return { aeronef: data._id.aeronef, imat: data._id.imat, count: data.count }; + }); + let aeronefsByImatByYear = results[2].map(function (data) { + return { + aeronef: data._id.aeronef, + imat: data._id.imat, + year: data._id.year, + count: data.count + }; + }); + return res.json({ + aeronefsByImat: aeronefsByImat, + aeronefsByImatByYear: aeronefsByImatByYear, + lastjump: lastjump + }); + } else { + return res.status(422).json({ errors: { lastjump: "Aucun saut enregistré pour le moment" } }); + } + }).catch(next); + }).catch(next); +}); + +router.get('/canopies', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let limit = 120; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + Promise.all([ + Jump.find({}) + .limit(1) + .skip(0) + .sort({ numero: 'desc' }) + .populate('author') + .populate('files') + .populate('x2data') + .exec(), + Jump.aggregate(queries.getCanopiesBySizeQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getCanopiesBySizeByYearQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getCanopiesBySizeByModelQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getCanopiesBySizeByModelByYearQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + ]).then(function (results) { + if (results[0].length) { + let lastjump = results[0][0].toJSONFor(user); + let canopiesBySize = results[1].map(function (data) { + return { taille: data._id.taille, count: data.count }; + }); + let canopiesBySizeByYear = results[2].map(function (data) { + return { + taille: data._id.taille, + year: data._id.year, + count: data.count + }; + }); + let canopiesBySizeByModel = results[3].map(function (data) { + return { + taille: data._id.taille, + voile: data._id.voile, + count: data.count + }; + }); + let canopiesBySizeByModelByYear = results[4].map(function (data) { + return { + taille: data._id.taille, + voile: data._id.voile, + year: data._id.year, + count: data.count + }; + }); + return res.json({ + canopiesBySize: canopiesBySize, + canopiesBySizeByYear: canopiesBySizeByYear, + canopiesBySizeByModel: canopiesBySizeByModel, + canopiesBySizeByModelByYear: canopiesBySizeByModelByYear, + lastjump: lastjump + }); + } else { + return res.status(422).json({ errors: { lastjump: "Aucun saut enregistré pour le moment" } }); + } + }).catch(next); + }).catch(next); +}); + +router.get('/dropzones', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let limit = 120; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + Promise.all([ + Jump.find({}) + .limit(1) + .skip(0) + .sort({ numero: 'desc' }) + .populate('author') + .populate('files') + .populate('x2data') + .exec(), + Jump.aggregate(queries.getDropZonesByOaciQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getDropZonesByOaciByYearQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + ]).then(function (results) { + if (results[0].length) { + let lastjump = results[0][0].toJSONFor(user); + let dropZonesByOaci = results[1].map(function (data) { + return { lieu: data._id.lieu, oaci: data._id.oaci, count: data.count }; + }); + let dropZonesByOaciByYear = results[2].map(function (data) { + return { + lieu: data._id.lieu, + oaci: data._id.oaci, + year: data._id.year, + count: data.count + }; + }); + return res.json({ + dropZonesByOaci: dropZonesByOaci, + dropZonesByOaciByYear: dropZonesByOaciByYear, + lastjump: lastjump + }); + } else { + return res.status(422).json({ errors: { lastjump: "Aucun saut enregistré pour le moment" } }); + } + }).catch(next); + }).catch(next); +}); + +router.get('/jumps', auth.required, function (req, res, next) { + User.findById(req.payload.id).then(function (user) { + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + let limit = 120; + let offset = 0; + if (typeof req.query.limit !== 'undefined') { + limit = req.query.limit; + } + if (typeof req.query.offset !== 'undefined') { + offset = req.query.offset; + } + Promise.all([ + Jump.find({}) + .limit(1) + .skip(0) + .sort({ numero: 'desc' }) + .populate('author') + .populate('files') + .populate('x2data') + .exec(), + Jump.aggregate(queries.getJumpByYearsQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getJumpsByCategoryQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getJumpsByModuleQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getJumpsByDateQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec(), + Jump.aggregate(queries.getJumpsByModuleByDateQuery(user._id.toString())) + .skip(Number(offset)) + .limit(Number(limit)) + .exec() + //utils.fetchSkydiverIdApi('jumps') + ]).then(function (results) { + if (results[0].length) { + let lastjump = results[0][0].toJSONFor(user); + let jumpsByYears = results[1].map(function (data) { + return { year: data._id.year, count: data.count }; + }); + let jumpsByCategory = results[2].map(function (data) { + return { categorie: data._id.categorie, count: data.count }; + }); + let jumpsByModule = results[3].map(function (data) { + return { categorie: data._id.categorie, module: data._id.module, count: data.count }; + }); + let jumpsByDate = results[4].map(function (data) { + return { year: data._id.year, month: data._id.month, count: data.count }; + }); + let jumpsByDateByModule = results[5].map(function (data) { + return { + categorie: data._id.categorie, + module: data._id.module, + year: data._id.year, + month: data._id.month, + count: data.count + }; + }); + //let jumpsFromSkydiverIdApi = results[6]; + return res.json({ + jumpsByCategory: jumpsByCategory, + jumpsByDate: jumpsByDate, + jumpsByDateByModule: jumpsByDateByModule, + jumpsByModule: jumpsByModule, + jumpsByYears: jumpsByYears, + //jumpsFromSkydiverIdApi: jumpsFromSkydiverIdApi, + lastjump: lastjump + }); + } else { + return res.status(422).json({ errors: { lastjump: "Aucun saut enregistré pour le moment" } }); + } + }).catch(next); + }).catch(next); +}); + +module.exports = router; diff --git a/routes/api/prices.js b/routes/api/prices.js deleted file mode 100644 index 8bfb478..0000000 --- a/routes/api/prices.js +++ /dev/null @@ -1,133 +0,0 @@ -var router = require('express').Router(), - mongoose = require('mongoose'), - Metal = mongoose.model('Metal'), - Price = mongoose.model('Price'), - Currency = mongoose.model('Currency'); -const auth = require('../auth'), - utils = require('../utils'), - chalk = require('chalk'); - -// Preload metal objects on routes with ':metal' -router.param('metal', function (req, res, next, code) { - Metal.findOne({ code: code }) - .then(function (metal) { - if (!metal) { - return res.sendStatus(404); - } - req.metal = metal.code; - return next(); - }).catch(next); -}); -// Preload currency objects on routes with ':currency' -router.param('currency', function (req, res, next, code) { - req.currency = code; - Currency.findOne({ code: code }) - .then(function (currency) { - if (!currency) { - return res.sendStatus(404); - } - req.currency = currency.code; - return next(); - }).catch(next); -}); - -router.get('/', auth.required, function (req, res, next) { - var query = {}; - var limit = 20; - var offset = 0; - const fields = ['metal', 'currency', 'exchange', 'symbol']; - - if (typeof req.query.limit !== 'undefined') { - limit = req.query.limit; - } - if (typeof req.query.offset !== 'undefined') { - offset = req.query.offset; - } - fields.forEach(field => { - if (typeof req.query[field] !== 'undefined') { - query[field] = req.query[field]; - } - }); - - Metal.find({}) - .limit(Number(limit)) - .skip(Number(offset)) - .sort({ ordering: 1 }) - .exec() - .then(metals => { - const currency = 'EUR'; - Promise.all( - metals.map(metal => Price.findOne({ metal: metal.code, currency: currency })) - ).then(results => { - let queries$ = []; - var prices = results.map((price, index) => { - if (!price) { - price = new Price({ metal: metals[index].code, currency: currency }); - } - const now = new Date().getTime(); - const diff = parseInt(((now - price.timestamp) / 1000)); - if (diff >= 50) { - queries$.push(utils.fetchApi(price, res).then(result => { - if (result.errors === undefined) { - price.overwrite(result); - price.save(); - console.log(`${utils.getDateTimeISOString()}`, chalk.green(`${price.metal} | ${price.currency} - Sauvegarde du prix spot`), chalk.magenta(price.price)); - } else { - console.log(`${utils.getDateTimeISOString()}`, chalk.red(`${price.metal} | ${price.currency} - Une erreur ${result.errors.code} '${result.errors.type}' est survenue : ${result.errors.name} ${result.errors.message}`)); - } - return price; - })); - } else { - console.log(`${utils.getDateTimeISOString()}`, chalk.cyan(`${price.metal} | ${price.currency} - Utilisation du prix spot`), chalk.magenta(price.price)); - } - return price; - }); - if (queries$.length) { - Promise.all( - queries$ - ).then(() => { - return res.json({ - prices: prices, - pricesCount: prices.length - }); - }).catch(next); - } else { - return res.json({ - prices: prices, - pricesCount: results[2] - }); - } - }).catch(next); - } - ); -}); - -router.get('/:metal/:currency', auth.required, function (req, res, next) { - Price.findOne({ metal: req.metal, currency: req.currency }) - .then(function (price) { - if (!price) { - price = new Price({ metal: req.metal, currency: req.currency }); - } - const now = new Date().getTime(); - const diff = parseInt(((now - price.timestamp) / 1000)); - if (diff >= 50) { - utils.fetchApi(price, res).then(result => { - if (result.errors === undefined) { - price.overwrite(result); - return price.save().then(function (data) { - console.log(`${utils.getDateTimeISOString()}`, chalk.green(`${data.metal} | ${data.currency} - Enregistrement du prix spot`), chalk.magenta(data.price)); - return res.json({ price: data.toJSONFor() }); - }); - } else { - console.log(`${utils.getDateTimeISOString()}`, chalk.red(`${price.metal} | ${price.currency} - Une erreur ${result.errors.code} '${result.errors.type}' est survenue : `), chalk.red(`${result.errors.name} ${result.errors.message}`)); - return res.json({ price: price }); - } - }); - } else { - console.log(`${utils.getDateTimeISOString()}`, chalk.cyan(`${price.metal} | ${price.currency} - Utilisation du prix stocké`), chalk.magenta(price.price)); - return res.json({ price: price.toJSONFor() }); - } - }).catch(next); -}); - -module.exports = router; \ No newline at end of file diff --git a/routes/api/products.js b/routes/api/products.js deleted file mode 100644 index 92e0f6d..0000000 --- a/routes/api/products.js +++ /dev/null @@ -1,238 +0,0 @@ -var router = require('express').Router(), - mongoose = require('mongoose'), - Product = mongoose.model('Product'), - User = mongoose.model('User'), - Metal = mongoose.model('Metal'); -const auth = require('../auth'); - -// Preload product objects on routes with ':product' -router.param('product', function (req, res, next, slug) { - Product.findOne({ slug: slug }) - .populate('author') - .populate('metal') - .then(function (product) { - if (!product) { - return res.sendStatus(404); - } - req.product = product; - return next(); - }).catch(next); -}); - -router.get('/', auth.required, function (req, res, next) { - var query = {}; - var limit = 20; - var offset = 0; - if (typeof req.query.limit !== 'undefined') { - limit = req.query.limit; - } - if (typeof req.query.offset !== 'undefined') { - offset = req.query.offset; - } - if (typeof req.query.title !== 'undefined') { - const rgx = new RegExp(`.*${req.query.title}.*`); - query = { - $or: [ - { title: { $regex: rgx, $options: "i" } }, - { slug: { $regex: rgx, $options: "i" } }, - ], - } - } - - Promise.all([ - req.query.author ? User.findOne({ username: req.query.author }) : null, - req.query.favorited ? User.findOne({ username: req.query.favorited }) : null - ]).then(function (users) { - var author = users[0]; - var favoriter = users[1]; - - if (author) { - query.author = author._id; - } - - if (favoriter) { - query._id = { $in: favoriter.favorites }; - } else if (req.query.favorited) { - query._id = { $in: [] }; - } - - return Promise.all([ - Product.find(query) - .limit(Number(limit)) - .skip(Number(offset)) - .sort({ ordering: 'asc' }) - .populate('author') - .populate('metal') - .exec(), - Product.count(query).exec(), - req.payload ? User.findById(req.payload.id) : null - ]).then(function (results) { - var products = results[0]; - var productsCount = results[1]; - var user = results[2]; - - return res.json({ - products: products.map(function (product) { - return product.toJSONFor(user); - }), - productsCount: productsCount - }); - }); - }).catch(next); -}); - -router.get('/feed', auth.required, function (req, res, next) { - var limit = 20; - var offset = 0; - if (typeof req.query.limit !== 'undefined') { - limit = req.query.limit; - } - if (typeof req.query.offset !== 'undefined') { - offset = req.query.offset; - } - - User.findById(req.payload.id).then(function (user) { - if (!user) { - return res.sendStatus(401); - } - var query = { author: { $in: user.following } }; - Promise.all([ - Product.find(query) - .limit(Number(limit)) - .skip(Number(offset)) - .populate('author') - .populate('metal') - .exec(), - Product.count(query) - ]).then(function (results) { - var products = results[0]; - var productsCount = results[1]; - return res.json({ - products: products.map(function (product) { - return product.toJSONFor(user); - }), - productsCount: productsCount - }); - }).catch(next); - }); -}); - -router.post('/', auth.required, function (req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null, - Metal.findOne({ code: req.body.product.metal.code }).exec(), - Product - .find({}).select('ordering') - .sort({"ordering" : -1}).limit(1) - .exec() - ]).then(function (results) { - var user = results[0]; - var metal = results[1]; - var ordering = (results[2][0].ordering + 1); - if (!user) { - return res.sendStatus(401); - } - var product = new Product(req.body.product); - product.author = user; - product.metal = metal; - product.ordering = ordering; - return product.save().then(function () { - return res.json({ product: product.toJSONFor(user) }); - }); - }).catch(next); -}); - -// return a product -router.get('/:product', auth.optional, function (req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null, - req.product.populate('author'), - req.product.populate('metal') - ]).then(function (results) { - var user = results[0]; - return res.json({ product: req.product.toJSONFor(user) }); - }).catch(next); -}); - -router.put('/:product', auth.required, function (req, res, next) { - Promise.all([ - req.payload ? User.findById(req.payload.id) : null, - Metal.findOne({ code: req.body.product.metal.code }).exec() - ]).then(function (results) { - var user = results[0]; - var metal = results[1]; - if (!user) { - return res.sendStatus(401); - } - if (req.product.author._id.toString() !== req.payload.id.toString()) { - return res.sendStatus(403); - } - if (typeof req.body.product.title !== 'undefined') { - req.product.title = req.body.product.title; - } - if (typeof req.body.product.prime_achat !== 'undefined') { - req.product.prime_achat = req.body.product.prime_achat; - } - if (typeof req.body.product.prime_vente !== 'undefined') { - req.product.prime_vente = req.body.product.prime_vente; - } - if (typeof req.body.product.poids !== 'undefined') { - req.product.poids = req.body.product.poids; - } - req.product.metal = metal; - - return req.product.save().then(function (product) { - return res.json({ product: product.toJSONFor(user) }); - }); - }).catch(next); -}); - -// delete product -router.delete('/:product', auth.required, function (req, res, next) { - User.findById(req.payload.id).then(function (user) { - if (!user) { - return res.sendStatus(401); - } - if (req.product.author._id.toString() === req.payload.id.toString()) { - return req.product.remove().then(function () { - return res.sendStatus(204); - }); - } else { - return res.sendStatus(403); - } - }).catch(next); -}); - -// Favorite an product -router.post('/:product/favorite', auth.required, function (req, res, next) { - var productId = req.product._id; - User.findById(req.payload.id).then(function (user) { - if (!user) { - return res.sendStatus(401); - } - - return user.favorite(productId).then(function () { - return req.product.updateFavoriteCount().then(function (product) { - return res.json({ product: product.toJSONFor(user) }); - }); - }); - }).catch(next); -}); - -// Unfavorite an product -router.delete('/:product/favorite', auth.required, function (req, res, next) { - var productId = req.product._id; - User.findById(req.payload.id).then(function (user) { - if (!user) { - return res.sendStatus(401); - } - - return user.unfavorite(productId).then(function () { - return req.product.updateFavoriteCount().then(function (product) { - return res.json({ product: product.toJSONFor(user) }); - }); - }); - }).catch(next); -}); - -module.exports = router; \ No newline at end of file diff --git a/routes/api/profiles.js b/routes/api/profiles.js index 078356b..3fb26be 100644 --- a/routes/api/profiles.js +++ b/routes/api/profiles.js @@ -15,7 +15,7 @@ router.param('username', function (req, res, next, username) { }).catch(next); }); -router.get('/:username', auth.optional, function (req, res, next) { +router.get('/:username', auth.optional, function (req, res) { if (req.payload) { User.findById(req.payload.id).then(function (user) { if (!user) { @@ -29,11 +29,11 @@ router.get('/:username', auth.optional, function (req, res, next) { }); router.post('/:username/follow', auth.required, function (req, res, next) { - var profileId = req.profile._id; + let profileId = req.profile._id; User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } return user.follow(profileId).then(function () { return res.json({ profile: req.profile.toProfileJSONFor(user) }); @@ -42,11 +42,11 @@ router.post('/:username/follow', auth.required, function (req, res, next) { }); router.delete('/:username/follow', auth.required, function (req, res, next) { - var profileId = req.profile._id; + let profileId = req.profile._id; User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } return user.unfollow(profileId).then(function () { return res.json({ profile: req.profile.toProfileJSONFor(user) }); diff --git a/routes/api/qcm.js b/routes/api/qcm.js index 64b911d..3c2dad6 100644 --- a/routes/api/qcm.js +++ b/routes/api/qcm.js @@ -1,19 +1,34 @@ var router = require('express').Router(), mongoose = require('mongoose'), - QCM = mongoose.model('QCM'), + Qcm = mongoose.model('Qcm'), + QcmCategory = mongoose.model('QcmCategory'), + QcmQuestion = mongoose.model('QcmQuestion'), + QcmChoice = mongoose.model('QcmChoice'), User = mongoose.model('User'); const auth = require('../auth'); // Preload qcm objects on routes with ':type' router.param('type', function (req, res, next, type) { - QCM.findOne({ name: type }) + Qcm.findOne({ name: type }) .populate('categories') - .then(function (qcm) { + .then(qcm => { if (!qcm) { - return res.sendStatus(404); + return res.status(404).json({ errors: { "Not Found": "Le QCM est introuvable ou inexistant." } }); } req.qcm = qcm; - return next(); + Promise.all( + req.qcm.categories.map(category => { + return category.populate('questions').then(() => { + return Promise.all( + category.questions.map(question => question.populate('choices')) + ).then(() => { + return category; + }).catch(next); + }); + }) + ).then(() => { + return next(); + }).catch(next); }).catch(next); }); @@ -24,9 +39,9 @@ router.get('/', auth.required, function (req, res, next) { Promise.all([ req.payload ? User.findById(req.payload.id) : null ]).then(function (results) { - var user = results[0]; + let user = results[0]; if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } return res.json({ qcm: [] }); }).catch(next); @@ -35,16 +50,108 @@ router.get('/', auth.required, function (req, res, next) { /** * return a qcm */ -router.get('/:type', auth.required, function (req, res, next) { +router.get('/type/:type', auth.required, function (req, res, next) { Promise.all([ req.payload ? User.findById(req.payload.id) : null ]).then(function (results) { - var user = results[0]; + let user = results[0]; if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } return res.json({ qcm: req.qcm.toJSONFor() }); }).catch(next); }); +/** + * save an array of qcm question choice + */ +router.post('/choices/', auth.required, function (req, res, next) { + Promise.all([ + req.payload ? User.findById(req.payload.id) : null, + QcmQuestion + .findOne({num: req.body.question.num}) + .populate('choices') + .exec() + ]).then(async function (results) { + const user = results[0]; + //let numero = (results[1][0].numero + 1); + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + if (user.role !== 'Admin') { + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); + } + const question = results[1]; + if (!question) { + return res.status(422).json({ errors: { question: "question introuvable" } }); + } + let choices = []; + Promise.all( + req.body.question.choices.map(data => { + const choiceId = new mongoose.Types.ObjectId(); + const choice = new QcmChoice({ _id: choiceId, index: data.index, libelle: data.libelle, correct: data.correct}); + choices.push(choiceId); + return choice.save(); + }) + ).then(function () { + question.choices = [...choices]; + question.save().then(function () { + QcmQuestion + .findOne({num: req.body.question.num}) + .populate('choices') + .exec() + .then(function (question) { + return res.json({ question: question.toJSONFor() }); + }).catch(next); + }).catch(next); + }).catch(next); + }).catch(next); +}); + +/** + * save an array of qcm category questions + */ +router.post('/questions/', auth.required, function (req, res, next) { + Promise.all([ + req.payload ? User.findById(req.payload.id) : null, + QcmCategory + .findOne({num: req.body.category.num}) + .populate('questions') + .exec() + ]).then(async function (results) { + const user = results[0]; + //let numero = (results[1][0].numero + 1); + if (!user) { + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); + } + if (user.role !== 'Admin') { + return res.status(403).json({ errors: { "Forbidden": "Vous ne disposez pas des autorisations suffisantes" } }); + } + const category = results[1]; + if (!category) { + return res.status(422).json({ errors: { category: "catégorie introuvable" } }); + } + let questions = []; + Promise.all( + req.body.category.questions.map(data => { + const questionId = new mongoose.Types.ObjectId(); + const question = new QcmQuestion({ _id: questionId, num: data.num, libelle: data.libelle, choices: []}); + questions.push(questionId); + return question.save(); + }) + ).then(function () { + category.questions = [...questions]; + category.save().then(function () { + QcmCategory + .findOne({num: req.body.category.num}) + .populate('questions') + .exec() + .then(function (category) { + return res.json({ category: category.toJSONFor() }); + }).catch(next); + }).catch(next); + }).catch(next); + }).catch(next); +}); + module.exports = router; \ No newline at end of file diff --git a/routes/api/users.js b/routes/api/users.js index d218cda..3355d47 100644 --- a/routes/api/users.js +++ b/routes/api/users.js @@ -7,7 +7,7 @@ const passport = require('passport'), router.get('/user', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } return res.json({ user: user.toAuthJSON() }); }).catch(next); @@ -16,7 +16,7 @@ router.get('/user', auth.required, function (req, res, next) { router.put('/user', auth.required, function (req, res, next) { User.findById(req.payload.id).then(function (user) { if (!user) { - return res.sendStatus(401); + return res.status(401).json({ errors: { "Unauthorized": "autentification requise" } }); } if (typeof req.body.user.username !== 'undefined') { user.username = req.body.user.username; @@ -96,7 +96,7 @@ router.get('/authenticate', function (req, res, next) { })(req, res, next); }); router.post('/users', function (req, res, next) { - var user = new User(); + let user = new User(); if (typeof req.body.user.username !== 'undefined') { user.username = req.body.user.username; } else { diff --git a/routes/queries.js b/routes/queries.js new file mode 100644 index 0000000..1ae0654 --- /dev/null +++ b/routes/queries.js @@ -0,0 +1,507 @@ +const queries = { + getJumpsByCategoryQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$group': { + '_id': { + 'categorie': '$categorie' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getJumpsByDateQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'year': { + '$year': '$date' + }, + 'month': { + '$month': '$date' + } + } + }, { + '$group': { + '_id': { + 'year': '$year', + 'month': '$month' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getJumpsByModuleByDateQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'module': 1, + 'categorie': 1, + 'year': { + '$year': '$date' + }, + 'month': { + '$month': '$date' + } + } + }, { + '$group': { + '_id': { + 'module': '$module', + 'categorie': '$categorie', + 'year': '$year', + 'month': '$month' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + return query; + }, + getJumpsByModuleQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'categorie': 1, + 'module': 1 + } + }, { + '$group': { + '_id': { + 'categorie': '$categorie', + 'module': '$module' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + return query; + }, + getJumpByYearsQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'year': { + '$year': '$date' + } + } + }, { + '$group': { + '_id': { + 'year': '$year' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getAeronefsByImatQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$group': { + '_id': { + 'aeronef': '$aeronef', + 'imat': '$imat' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': + { + 'count': -1 + }, + } + ]; + + return query; + }, + getAeronefsByImatByYearQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'aeronef': 1, + 'imat': 1, + 'year': { + '$year': '$date' + } + } + }, { + '$group': { + '_id': { + 'aeronef': '$aeronef', + 'imat': '$imat', + 'year': '$year' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getCanopiesBySizeQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$group': { + '_id': { + 'taille': '$taille' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': + { + '_id': 1 + }, + } + ]; + + return query; + }, + getCanopiesBySizeByYearQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'taille': 1, + 'year': { + '$year': '$date' + } + } + }, { + '$group': { + '_id': { + 'taille': '$taille', + 'year': '$year' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getCanopiesBySizeByModelQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$group': { + '_id': { + 'taille': '$taille', + 'voile': '$voile' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': + { + '_id': 1 + }, + } + ]; + + return query; + }, + getCanopiesBySizeByModelByYearQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'voile': 1, + 'taille': 1, + 'year': { + '$year': '$date' + } + } + }, { + '$group': { + '_id': { + 'taille': '$taille', + 'voile': '$voile', + 'year': '$year' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getDropZonesByOaciQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$group': { + '_id': { + 'lieu': '$lieu', + 'oaci': '$oaci' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': + { + '_id': 1 + }, + } + ]; + + return query; + }, + getDropZonesByOaciByYearQuery: (userId) => { + let query = [ + { + '$match': { + '$expr': { + '$eq': [ '$author' , { '$toObjectId': userId } ] + } + } + }, { + '$project': { + '_id': 0, + 'lieu': 1, + 'oaci': 1, + 'year': { + '$year': '$date' + } + } + }, { + '$group': { + '_id': { + 'lieu': '$lieu', + 'oaci': '$oaci', + 'year': '$year' + }, + 'count': { + '$sum': 1 + } + } + }, { + '$sort': { + '_id': 1 + } + } + ]; + + return query; + }, + getJumpsByDayQuery: (userId, dateRangeStart = null, dateRangeEnd = null) => { + if (dateRangeStart === null) { + dateRangeStart = '1970-01-01 00:00:00'; + } + if (dateRangeEnd === null) { + const year = new Date().getFullYear(); + dateRangeEnd = `${year}-12-31 23:59:59`; + } + let query = [ + { + $match: { + $expr: { + $and: [ + { + $eq: [ '$author' , { '$toObjectId': userId } ] + }, + { + $gte: ['$date', new Date(dateRangeStart)] + }, + { + $lte: ['$date', new Date(dateRangeEnd)] + } + ] + } + } + }, { + $project: { + _id: 1, + slug: "$slug", + numero: "$numero", + date: "$date", + year: { + $year: "$date" + }, + month: { + $month: "$date" + }, + day: { + $dayOfMonth: "$date" + } + } + }, + { + $group: { + _id: { + year: "$year", + month: "$month", + day: "$day" + }, + jumps: { + $addToSet: { + _id: "$_id", + numero: "$numero", + slug: "$slug", + date: "$date" + } + }, + count: { + $sum: 1 + } + } + }, + { + $sort: { + _id: 1, + } + }, + { + $project: { + _id: 0, + jumps: 1, + count: 1 + } + } + ]; + + return query; + } +}; + +module.exports = queries; diff --git a/routes/utils.js b/routes/utils.js index b9c13f1..caa5f0c 100644 --- a/routes/utils.js +++ b/routes/utils.js @@ -1,16 +1,13 @@ const chalk = require('chalk'); - const utils = { getDateString: () => { let today = new Date(); let dateNow = today.toLocaleDateString(); - return `${dateNow}`; }, getTimeString: () => { let today = new Date(); let timeNow = today.toLocaleTimeString(); - return `${timeNow}`; }, getDateTimeString: (delimiter = true) => { @@ -19,7 +16,6 @@ const utils = { if (delimiter) { dateString = `[${dateString}]`; } - return dateString; }, getDateTimeISOString: (delimiter = true) => { @@ -28,8 +24,60 @@ const utils = { if (delimiter) { dateString = `[${dateString}]`; } - return dateString; + }, + async fetchSkydiverIdApi(path, query) { + let limit = 50; + let offset = 0; + let args = ''; + if (typeof query.limit !== 'undefined') { + limit = query.limit; + } + if (typeof query.offset !== 'undefined') { + offset = ((query.offset/limit)+1); + } + if (offset > 1) { + args += `&page=${offset}`; + } + if (typeof query.dateRangeStart !== 'undefined' && typeof query.dateRangeEnd !== 'undefined') { + args += `&filter[and][0][date][gte]=${query.dateRangeStart}`; + args += `&filter[and][0][date][lte]=${query.dateRangeEnd}`; + } + try { + let url = `${process.env.SKYDIVERID_API_URL}/${path}`; + if (typeof query.limit !== 'undefined') { + url += `?per-page=${limit}${args}`; + } + /* url pour provoquer une erreur en dev : url = `https://www.goldapi.ioppppp/api/${price.metal}/${price.currency}`; */ + let options = { + method: 'GET', + headers: { + 'Authorization': `Bearer ${process.env.SKYDIVERID_API_TOKEN}`, + 'Content-Type': 'application/json' + }, + redirect: 'follow' + }; + /* Pour tester le retour d'erreur : return { errors: { name: 'error name', message: 'error message', type: 'TYPE', code: 'CODE' } }; */ + //const response = await fetch(url, options); + return fetch(url, options).then(function (response) { + if (response.status >= 200 && response.status < 400) { + console.log(`${utils.getDateTimeISOString()}`, chalk.green(`Réception des sauts via SkydiverId API`), `${response.status} ${response.statusText}`); + const data = response.json(); + console.log(data); + return data; + } else { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`Erreur ${response.status} ${response.statusText}`)); + return { errors: { name: response.statusText, message: `Une erreur ${response.status} '${response.statusText}' est survenue lors de la récupération des sauts via SkydiverId API`, type: 'API Error', code: response.status } }; + } + }) + .catch(error => { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`A fetchSkydiverIdApi error occured ${error.code} ${error.type}`)); + return { errors: { name: error.name, message: `A fetchSkydiverIdApi error occured : ${error.message}`, type: error.type, code: error.code } }; + }); + } catch (error) { + console.log(`${utils.getDateTimeISOString()}`, chalk.red(`fetchSkydiverIdApi error`)); + return { errors: { name: error.name, message: error.message, type: error.type, code: error.code } }; + } } };