diff --git a/api/cron-job/cron.js b/api/cron-job/cron.js index 505e78f..b8282fd 100644 --- a/api/cron-job/cron.js +++ b/api/cron-job/cron.js @@ -54,7 +54,7 @@ const createBackup = () => { if (err) { error(err.message); } - logger('\x1b[32m%s\x1b[0m',stderr); + logger('\x1b[32m%s\x1b[0m', stderr); logger('\x1b[35m%s\x1b[0m', new Date().toLocaleString() + ': cron job finished - CREATE BACKUP'); }) diff --git a/api/middleware/permission-check.js b/api/middleware/permission-check.js index f232858..a4990b5 100644 --- a/api/middleware/permission-check.js +++ b/api/middleware/permission-check.js @@ -12,8 +12,16 @@ let check = (requiredPermission, actualPermission, res, next) => { }; module.exports = { - checkSql: (req, res, next) => { check(1, req.user.permission, res, next) }, - checkHl: (req, res, next) => { check(2, req.user.permission, res, next) }, - checkMT: (req, res, next) => { check(3, req.user.permission, res, next) }, - checkAdmin: (req, res, next) => { check(4, req.user.permission, res, next) } + checkSql: (req, res, next) => { + check(1, req.user.permission, res, next) + }, + checkHl: (req, res, next) => { + check(2, req.user.permission, res, next) + }, + checkMT: (req, res, next) => { + check(3, req.user.permission, res, next) + }, + checkAdmin: (req, res, next) => { + check(4, req.user.permission, res, next) + } }; diff --git a/api/middleware/request-checks.js b/api/middleware/request-checks.js index f1ce65e..67109ea 100644 --- a/api/middleware/request-checks.js +++ b/api/middleware/request-checks.js @@ -34,8 +34,8 @@ router.use((req, res, next) => { // request type application/json check router.use((req, res, next) => { if (['POST', 'PUT', 'PATCH'].indexOf(req.method) > -1 && - (!( /multipart\/form-data/.test(req.get('Content-Type'))) && - !( /application\/json/.test(req.get('Content-Type'))))) { + (!(/multipart\/form-data/.test(req.get('Content-Type'))) && + !(/application\/json/.test(req.get('Content-Type'))))) { // send error code 415: unsupported media type res.status(415).send('wrong Content-Type'); // user has SEND the wrong type } else if (!req.accepts('json')) { diff --git a/api/models/logs/transport.js b/api/models/logs/transport.js index 09accf0..24998b3 100644 --- a/api/models/logs/transport.js +++ b/api/models/logs/transport.js @@ -36,6 +36,6 @@ const LogTransportSchema = new Schema({ collection: 'logTransport' }); // optional more indices -LogTransportSchema.index({war: 1}); +LogTransportSchema.index({war: 1, driver: 1}); module.exports = mongoose.model('LogTransport', LogTransportSchema); diff --git a/api/models/logs/vehicle.js b/api/models/logs/vehicle.js new file mode 100644 index 0000000..616f84c --- /dev/null +++ b/api/models/logs/vehicle.js @@ -0,0 +1,34 @@ +"use strict"; + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const LogVehicleKillSchema = new Schema({ + war: { + type: mongoose.Schema.Types.ObjectId, + ref: 'War', + required: true + }, + time: { + type: Date, + required: true + }, + shooter: { + type: String + }, + target: { + type: String, + required: true + }, + fraction: { + type: String, + enum: ['BLUFOR', 'OPFOR', 'NONE'], + required: true + } +}, { + collection: 'logVehicle' +}); +// optional more indices +LogVehicleKillSchema.index({war: 1, shooter: 1, target: 1}); + +module.exports = mongoose.model('LogVehicle', LogVehicleKillSchema); diff --git a/api/models/player.js b/api/models/player.js index 8086056..6741289 100644 --- a/api/models/player.js +++ b/api/models/player.js @@ -24,6 +24,12 @@ const PlayerSchema = new Schema({ set: v => Math.round(v), required: true }, + vehicle: { + type: Number, + get: v => Math.round(v), + set: v => Math.round(v), + required: true + }, death: { type: Number, get: v => Math.round(v), @@ -58,12 +64,17 @@ const PlayerSchema = new Schema({ type: Number, get: v => Math.round(v), set: v => Math.round(v) + }, + steamUUID: { + type: Number, + get: v => Math.round(v), + set: v => Math.round(v) } }, { collection: 'player', timestamps: {createdAt: 'timestamp'} }); // optional more indices -PlayerSchema.index({timestamp: 1}); +PlayerSchema.index({warId: 1}); module.exports = mongoose.model('Player', PlayerSchema); diff --git a/api/models/war.js b/api/models/war.js index d62ec94..e9a293b 100644 --- a/api/models/war.js +++ b/api/models/war.js @@ -11,7 +11,7 @@ const WarSchema = new Schema({ date: { type: Date, }, - endDate : { + endDate: { type: Date, }, ptBlufor: { diff --git a/api/routes/account.js b/api/routes/account.js index 6308e01..016a4a6 100644 --- a/api/routes/account.js +++ b/api/routes/account.js @@ -15,70 +15,72 @@ const AppUserModel = require('../models/app-user'); const account = express.Router(); account.route('/') - .get((req, res, next) => { - AppUserModel.find({}, {}, {sort: {username: 1}}).populate('squad').exec((err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - res.locals.items = items; - res.locals.processed = true; - next(); - }) - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + AppUserModel.find({}, {}, {sort: {username: 1}}).populate('squad').exec((err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + res.locals.items = items; + res.locals.processed = true; + next(); + }) + }) + .all( + routerHandling.httpMethodNotAllowed + ); // routes ********************** account.route('/:id') - .patch((req, res, next) => { - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } + .patch((req, res, next) => { + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - // increment version manually as we do not use .save(.) - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; + // increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - AppUserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}).populate('squad').exec((err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("appUser not found"); - err.status = codes.notfound; - } - else { - res.locals.items = item; - } - next(err); - }) - }) + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to + // reset attributes that are missing. + AppUserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}).populate('squad').exec((err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("appUser not found"); + err.status = codes.notfound; + } + else { + res.locals.items = item; + } + next(err); + }) + }) - .delete((req, res, next) => { - AppUserModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(err); // this works because err is in normal case undefined and that is the same as no parameter - }); - }) + .delete((req, res, next) => { + AppUserModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(err); // this works because err is in normal case undefined and that is the same as no parameter + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/routes/authenticate.js b/api/routes/authenticate.js index 167fe63..c2d59e7 100644 --- a/api/routes/authenticate.js +++ b/api/routes/authenticate.js @@ -21,25 +21,25 @@ const authenticate = express.Router(); // routes ********************** authenticate.route('/') - .post((req, res, next) => { - authCheck(req.body.username, req.body.password, res) - .then((user) => { - if (user) { - // authentication successful - res.send(user); - } else { - // authentication failed - res.status(codes.unauthorized).send('Username or password is incorrect'); - } - }) - .catch((err) => { - res.status(codes.wrongrequest).send(err); - }); - }) + .post((req, res, next) => { + authCheck(req.body.username, req.body.password, res) + .then((user) => { + if (user) { + // authentication successful + res.send(user); + } else { + // authentication failed + res.status(codes.unauthorized).send('Username or password is incorrect'); + } + }) + .catch((err) => { + res.status(codes.wrongrequest).send(err); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); let authCheck = (username, password, res) => { const deferred = Q.defer(); @@ -76,19 +76,19 @@ let authCheck = (username, password, res) => { // ******************************** SIGNUP ************************ authenticate.route('/signup') - .post((req, res, next) => { - create(req.body) - .then(() => { - res.sendStatus(200); - }) - .catch((err) => { - res.status(400).send(err); - }); - }) + .post((req, res, next) => { + create(req.body) + .then(() => { + res.sendStatus(200); + }) + .catch((err) => { + res.status(400).send(err); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); let create = (userParam) => { const deferred = Q.defer(); diff --git a/api/routes/awardings.js b/api/routes/awardings.js index 8658e68..e29fc83 100644 --- a/api/routes/awardings.js +++ b/api/routes/awardings.js @@ -31,127 +31,129 @@ const awarding = express.Router(); // routes ********************** awarding.route('/') - .get((req, res, next) => { - const filter = {}; - if (req.query.userId) { - filter.userId = req.query.userId; - } - if (req.query.inProgress) { - filter.confirmed = 0; - } - if (req.query.simple) { - AwardingModel.find(filter, {}, {sort: {date: 'desc'}}, (err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - // with return before (or after) the next(err) we prevent that the code continues here after next(err) has finished. - // this saves an extra else {..} - } - // if the collection is empty we do not send empty arrays back. - if (items && items.length > 0) { - res.locals.items = items; - } - res.locals.processed = true; - next(); - }); - } else { - AwardingModel.find(filter, {}, {sort: {date: 'desc'}}) - .populate('decorationId').populate('proposer', resultSet).populate('userId').exec((err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - // with return before (or after) the next(err) we prevent that the code continues here after next(err) has finished. - // this saves an extra else {..} - } - let results = []; - if (req.query.fractFilter) { - for (let item of items) { - if (item.decorationId.fraction === req.query.fractFilter) { - results.push(item) - } + .get((req, res, next) => { + const filter = {}; + if (req.query.userId) { + filter.userId = req.query.userId; } - res.locals.items = results; + if (req.query.inProgress) { + filter.confirmed = 0; + } + if (req.query.simple) { + AwardingModel.find(filter, {}, {sort: {date: 'desc'}}, (err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + // with return before (or after) the next(err) we prevent that the code continues here after next(err) + // has finished. this saves an extra else {..} + } + // if the collection is empty we do not send empty arrays back. + if (items && items.length > 0) { + res.locals.items = items; + } + res.locals.processed = true; + next(); + }); + } else { + AwardingModel.find(filter, {}, {sort: {date: 'desc'}}) + .populate('decorationId').populate('proposer', resultSet).populate('userId').exec((err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + // with return before (or after) the next(err) we prevent that the code continues here after next(err) + // has finished. this saves an extra else {..} + } + let results = []; + if (req.query.fractFilter) { + for (let item of items) { + if (item.decorationId.fraction === req.query.fractFilter) { + results.push(item) + } + } + res.locals.items = results; - } else { - res.locals.items = items; - } + } else { + res.locals.items = items; + } - res.locals.processed = true; - next(); - }); - } - }) + res.locals.processed = true; + next(); + }); + } + }) - .post(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - const award = new AwardingModel(req.body); - award.confirmed = 1; - award.proposer = req.user._id; - // timestamp and default are set automatically by Mongoose Schema Validation - award.save((err) => { - if (err) { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - return next(err); - } - res.status(codes.created); - res.locals.items = award; - next(); - }); - }) + .post(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + const award = new AwardingModel(req.body); + award.confirmed = 1; + award.proposer = req.user._id; + // timestamp and default are set automatically by Mongoose Schema Validation + award.save((err) => { + if (err) { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + return next(err); + } + res.status(codes.created); + res.locals.items = award; + next(); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); awarding.route('/:id') - .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } + .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - // optional task 3: increment version manually as we do not use .save(.) - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; + // optional task 3: increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - AwardingModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - else { - res.locals.items = item; - } - next(err); - }) - }) + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to + // reset attributes that are missing. + AwardingModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + else { + res.locals.items = item; + } + next(err); + }) + }) - .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - AwardingModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(err); // this works because err is in normal case undefined and that is the same as no parameter - }); - }) + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + AwardingModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(err); // this works because err is in normal case undefined and that is the same as no parameter + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/routes/campaigns.js b/api/routes/campaigns.js index e37c010..d91df19 100644 --- a/api/routes/campaigns.js +++ b/api/routes/campaigns.js @@ -23,63 +23,63 @@ const campaigns = express.Router(); // routes ********************** campaigns.route('/') - .post(apiAuthenticationMiddleware, checkMT, (req, res, next) => { - const campaign = new CampaignModel(req.body); - // timestamp and default are set automatically by Mongoose Schema Validation - campaign.save((err) => { - if (err) { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - return next(err); - } - res.status(codes.created); - res.locals.items = campaign; - next(); - }); - }) + .post(apiAuthenticationMiddleware, checkMT, (req, res, next) => { + const campaign = new CampaignModel(req.body); + // timestamp and default are set automatically by Mongoose Schema Validation + campaign.save((err) => { + if (err) { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + return next(err); + } + res.status(codes.created); + res.locals.items = campaign; + next(); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); campaigns.route('/:id') - .get(idValidator, (req, res, next) => { - CampaignModel.findById(req.params.id, (err, item) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - res.locals.items = item; - return next(); - }); - }) + .get(idValidator, (req, res, next) => { + CampaignModel.findById(req.params.id, (err, item) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + res.locals.items = item; + return next(); + }); + }) - .delete((req, res, next) => { - CampaignModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - WarModel.find({campaign: req.params.id}).remove().exec(); + .delete((req, res, next) => { + CampaignModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + WarModel.find({campaign: req.params.id}).remove().exec(); - res.locals.processed = true; - next(); - }) - }) + res.locals.processed = true; + next(); + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/routes/command.js b/api/routes/command.js index 4439bbe..11cbef9 100644 --- a/api/routes/command.js +++ b/api/routes/command.js @@ -14,23 +14,23 @@ const createSignature = require('../tools/signature-tool'); const command = express.Router(); command.route('/createSignature') - .post((req, res, next) => { - createAllSignatures(); - }) + .post((req, res, next) => { + createAllSignatures(); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); command.route('/createSignature/:id') - .post((req, res, next) => { - const userId = req.params.id; - createSignature(userId, res, next); - }) + .post((req, res, next) => { + const userId = req.params.id; + createSignature(userId, res, next); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it diff --git a/api/routes/decorations.js b/api/routes/decorations.js index 0278328..53ad131 100644 --- a/api/routes/decorations.js +++ b/api/routes/decorations.js @@ -26,136 +26,145 @@ const decoration = express.Router(); // routes ********************** decoration.route('/') - .get((req, res, next) => { - const filter = {}; - if (req.query.fractFilter) { - filter.fraction = req.query.fractFilter.toUpperCase() - } - if (req.query.q) { - filter.name = {$regex: req.query.q, $options: 'i'} - } - DecorationModel.find(filter, {}, {sort: {fraction: 'asc', isMedal: 'asc', sortingNumber: 'asc', name: 'asc'}}, (err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - if (items && items.length > 0) { - res.locals.items = items; - } else { - res.locals.items = []; - } - res.locals.processed = true; - next(); - }); - }) + .get((req, res, next) => { + const filter = {}; + if (req.query.fractFilter) { + filter.fraction = req.query.fractFilter.toUpperCase() + } + if (req.query.q) { + filter.name = {$regex: req.query.q, $options: 'i'} + } + DecorationModel.find(filter, {}, { + sort: { + fraction: 'asc', + isMedal: 'asc', + sortingNumber: 'asc', + name: 'asc' + } + }, (err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + if (items && items.length > 0) { + res.locals.items = items; + } else { + res.locals.items = []; + } + res.locals.processed = true; + next(); + }); + }) - .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - const decoration = new DecorationModel(req.body); - // timestamp and default are set automatically by Mongoose Schema Validation - decoration.save((err) => { - if (err) { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - return next(err); - } - res.status(codes.created); - res.locals.items = decoration; - fs.appendFile(__dirname + '/../resource/decoration/' + decoration.id + '.png', new Buffer(req.file.buffer), - (err) => { - if (err) next(err); - }); - next(); - }) - }) + .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { + const decoration = new DecorationModel(req.body); + // timestamp and default are set automatically by Mongoose Schema Validation + decoration.save((err) => { + if (err) { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + return next(err); + } + res.status(codes.created); + res.locals.items = decoration; + fs.appendFile(__dirname + '/../resource/decoration/' + decoration.id + '.png', new Buffer(req.file.buffer), + (err) => { + if (err) next(err); + }); + next(); + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); decoration.route('/:id') - .get(idValidator, (req, res, next) => { - DecorationModel.findById(req.params.id, (err, item) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - res.locals.items = item; - next(); - }); - }) + .get(idValidator, (req, res, next) => { + DecorationModel.findById(req.params.id, (err, item) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + res.locals.items = item; + next(); + }); + }) - .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } + .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - // optional task 3: increment version manually as we do not use .save(.) - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; + // optional task 3: increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - if (req.file) { - const file = __dirname + '/../resource/decoration/' + req.body._id + '.png'; - fs.unlink(file, (err) => { - if (err) next(err); - fs.appendFile(file, new Buffer(req.file.buffer), (err) => { - if (err) next(err); - }); - }); - } + if (req.file) { + const file = __dirname + '/../resource/decoration/' + req.body._id + '.png'; + fs.unlink(file, (err) => { + if (err) next(err); + fs.appendFile(file, new Buffer(req.file.buffer), (err) => { + if (err) next(err); + }); + }); + } - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - DecorationModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - else { - res.locals.items = item; - } - next(err); - }) - }) + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need + // to reset attributes that are missing. + DecorationModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + else { + res.locals.items = item; + } + next(err); + }) + }) - .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - DecorationModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + DecorationModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } - // deleted all awardings linked to this decoration - AwardingsModel.find({decorationId: req.params.id}).remove().exec(); + // deleted all awardings linked to this decoration + AwardingsModel.find({decorationId: req.params.id}).remove().exec(); - // delete graphic - fs.unlink(__dirname + '/../resource/decoration/' + req.params.id + '.png', - (err) => { - if (err) next(err); - }); + // delete graphic + fs.unlink(__dirname + '/../resource/decoration/' + req.params.id + '.png', + (err) => { + if (err) next(err); + }); - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(err); // this works because err is in normal case undefined and that is the same as no parameter - }); - }) + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(err); // this works because err is in normal case undefined and that is the same as no parameter + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/routes/logs.js b/api/routes/logs.js index 4891d1b..aafc229 100644 --- a/api/routes/logs.js +++ b/api/routes/logs.js @@ -13,6 +13,7 @@ const LogBudgetModel = require('../models/logs/budget'); const LogRespawnModel = require('../models/logs/respawn'); const LogReviveModel = require('../models/logs/revive'); const LogKillModel = require('../models/logs/kill'); +const LogVehicleModel = require('../models/logs/vehicle'); const LogTransportModel = require('../models/logs/transport'); const LogFlagModel = require('../models/logs/flag'); const LogPointsModel = require('../models/logs/points'); @@ -34,122 +35,124 @@ function processLogRequest(model, filter, res, next) { // routes ********************** logsRouter.route('/:warId') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - const sort = {sort: {time: 1}}; + .get((req, res, next) => { + const filter = {war: req.params.warId}; + const sort = {sort: {time: 1}}; - const pointsObjects = LogPointsModel.find(filter, {}, sort); - const budgetObjects = LogBudgetModel.find(filter, {}, sort); - const respawnObjects = LogRespawnModel.find(filter, {}, sort); - const reviveObjects = LogReviveModel.find(filter, {}, sort); - const killObjects = LogKillModel.find(filter, {}, sort); - const transportObjects = LogTransportModel.find(filter, {}, sort); - const flagObjects = LogFlagModel.find(filter, {}, sort); - const resources = { - points: pointsObjects.exec.bind(pointsObjects), - budget: budgetObjects.exec.bind(budgetObjects), - respawn: respawnObjects.exec.bind(respawnObjects), - revive: reviveObjects.exec.bind(reviveObjects), - kill: killObjects.exec.bind(killObjects), - transport: transportObjects.exec.bind(transportObjects), - flag: flagObjects.exec.bind(flagObjects) - }; + const pointsObjects = LogPointsModel.find(filter, {}, sort); + const budgetObjects = LogBudgetModel.find(filter, {}, sort); + const respawnObjects = LogRespawnModel.find(filter, {}, sort); + const reviveObjects = LogReviveModel.find(filter, {}, sort); + const killObjects = LogKillModel.find(filter, {}, sort); + const vehicleObjects = LogVehicleModel.find(filter, {}, sort); + const transportObjects = LogTransportModel.find(filter, {}, sort); + const flagObjects = LogFlagModel.find(filter, {}, sort); + const resources = { + points: pointsObjects.exec.bind(pointsObjects), + budget: budgetObjects.exec.bind(budgetObjects), + respawn: respawnObjects.exec.bind(respawnObjects), + revive: reviveObjects.exec.bind(reviveObjects), + kill: killObjects.exec.bind(killObjects), + vehicle: killObjects.exec.bind(vehicleObjects), + transport: transportObjects.exec.bind(transportObjects), + flag: flagObjects.exec.bind(flagObjects) + }; - async.parallel(resources, function (error, results){ - if (error) { - res.status(500).send(error); - return; - } - res.locals.items = results; - next(); - }); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + async.parallel(resources, function (error, results) { + if (error) { + res.status(500).send(error); + return; + } + res.locals.items = results; + next(); + }); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/budget') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.fraction) filter['fraction'] = req.query.fraction; - processLogRequest(LogBudgetModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.fraction) filter['fraction'] = req.query.fraction; + processLogRequest(LogBudgetModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/respawn') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.player) filter['player'] = req.query.player; - processLogRequest(LogRespawnModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.player) filter['player'] = req.query.player; + processLogRequest(LogRespawnModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/revive') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.medic) filter['medic'] = req.query.medic; - if (req.query.patient) filter['patient'] = req.query.patient; - if (req.query.stabilized) filter['stabilized'] = true; - if (req.query.revive) filter['stabilized'] = false; - if (req.query.fraction) filter['fraction'] = req.query.fraction; - processLogRequest(LogReviveModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.medic) filter['medic'] = req.query.medic; + if (req.query.patient) filter['patient'] = req.query.patient; + if (req.query.stabilized) filter['stabilized'] = true; + if (req.query.revive) filter['stabilized'] = false; + if (req.query.fraction) filter['fraction'] = req.query.fraction; + processLogRequest(LogReviveModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/kills') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.shooter) filter['shooter'] = req.query.shooter; - if (req.query.target) filter['target'] = req.query.target; - if (req.query.friendlyFire) filter['friendlyFire'] = true; - if (req.query.noFriendlyFire) filter['friendlyFire'] = false; - if (req.query.fraction) filter['fraction'] = req.query.fraction; - processLogRequest(LogKillModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.shooter) filter['shooter'] = req.query.shooter; + if (req.query.target) filter['target'] = req.query.target; + if (req.query.friendlyFire) filter['friendlyFire'] = true; + if (req.query.noFriendlyFire) filter['friendlyFire'] = false; + if (req.query.fraction) filter['fraction'] = req.query.fraction; + processLogRequest(LogKillModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/transport') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.driver) filter['driver'] = req.query.driver; - if (req.query.passenger) filter['passenger'] = req.query.passenger; - if (req.query.fraction) filter['fraction'] = req.query.fraction; - processLogRequest(LogTransportModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.driver) filter['driver'] = req.query.driver; + if (req.query.passenger) filter['passenger'] = req.query.passenger; + if (req.query.fraction) filter['fraction'] = req.query.fraction; + processLogRequest(LogTransportModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/flag') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.player) filter['player'] = req.query.player; - if (req.query.capture) filter['capture'] = true; - if (req.query.defend) filter['capture'] = false; - if (req.query.fraction) filter['fraction'] = req.query.fraction; - processLogRequest(LogFlagModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.player) filter['player'] = req.query.player; + if (req.query.capture) filter['capture'] = true; + if (req.query.defend) filter['capture'] = false; + if (req.query.fraction) filter['fraction'] = req.query.fraction; + processLogRequest(LogFlagModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.route('/:warId/points') - .get((req, res, next) => { - const filter = {war: req.params.warId}; - if (req.query.fraction) filter['fraction'] = req.query.fraction; - processLogRequest(LogPointsModel, filter, res, next); - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .get((req, res, next) => { + const filter = {war: req.params.warId}; + if (req.query.fraction) filter['fraction'] = req.query.fraction; + processLogRequest(LogPointsModel, filter, res, next); + }) + .all( + routerHandling.httpMethodNotAllowed + ); logsRouter.use(routerHandling.emptyResponse); diff --git a/api/routes/overview.js b/api/routes/overview.js index 7d91db2..e41e001 100644 --- a/api/routes/overview.js +++ b/api/routes/overview.js @@ -18,93 +18,93 @@ const overview = express.Router(); // routes ********************** overview.route('/') - .get((req, res, next) => { - let countOpfor = 0; - let countBlufor = 0; - const armyOverview = { - BLUFOR: { - squads: [] - }, - OPFOR: { - squads: [] - } - }; + .get((req, res, next) => { + let countOpfor = 0; + let countBlufor = 0; + const armyOverview = { + BLUFOR: { + squads: [] + }, + OPFOR: { + squads: [] + } + }; - SquadModel.find({}, {'sortingNumber': 0, 'updatedAt': 0, 'timestamp': 0, '__v': 0}, { - sort: { - sortingNumber: 'asc', - name: 'asc' - } - }, (err, squads) => { - if (err) { - return next(err); - } - async.eachSeries(squads, (squad, callback) => { - UserModel.find({squadId: squad._id}, { - 'squadId': 0, - 'updatedAt': 0, - 'timestamp': 0, - '__v': 0 - }, {sort: {rankLvl: 'desc', name: 'asc'}}, (err, users) => { - const squadMembers = []; - async.eachSeries(users, (user, callback) => { - const usr = user.toObject(); - RankModel.findOne({level: user.rankLvl, fraction: squad.fraction}, (err, rank) => { - if (err) { - return next(err); - } - - // not defined if rank was deleted / rankLvl not available for fraction - if (rank) { - usr.rank = rank.name; - } - delete usr.rankLvl; - squadMembers.push(usr) - - callback(); - }); - }, (err) => { + SquadModel.find({}, {'sortingNumber': 0, 'updatedAt': 0, 'timestamp': 0, '__v': 0}, { + sort: { + sortingNumber: 'asc', + name: 'asc' + } + }, (err, squads) => { if (err) { return next(err); } + async.eachSeries(squads, (squad, callback) => { + UserModel.find({squadId: squad._id}, { + 'squadId': 0, + 'updatedAt': 0, + 'timestamp': 0, + '__v': 0 + }, {sort: {rankLvl: 'desc', name: 'asc'}}, (err, users) => { + const squadMembers = []; + async.eachSeries(users, (user, callback) => { + const usr = user.toObject(); + RankModel.findOne({level: user.rankLvl, fraction: squad.fraction}, (err, rank) => { + if (err) { + return next(err); + } - // do not return empty squads - if (squadMembers.length > 0) { - const s = squad.toObject(); - s.members = squadMembers; - s.memberCount = squadMembers.length; - if (s.fraction === 'BLUFOR') { - delete s.fraction; - armyOverview.BLUFOR.squads.push(s); - countBlufor += s.members.length; - } - if (s.fraction === 'OPFOR') { - delete s.fraction; - armyOverview.OPFOR.squads.push(s); - countOpfor += s.members.length; - } - } + // not defined if rank was deleted / rankLvl not available for fraction + if (rank) { + usr.rank = rank.name; + } + delete usr.rankLvl; + squadMembers.push(usr) - callback(); + callback(); + }); + }, (err) => { + if (err) { + return next(err); + } + + // do not return empty squads + if (squadMembers.length > 0) { + const s = squad.toObject(); + s.members = squadMembers; + s.memberCount = squadMembers.length; + if (s.fraction === 'BLUFOR') { + delete s.fraction; + armyOverview.BLUFOR.squads.push(s); + countBlufor += s.members.length; + } + if (s.fraction === 'OPFOR') { + delete s.fraction; + armyOverview.OPFOR.squads.push(s); + countOpfor += s.members.length; + } + } + + callback(); + }); + }); + + }, (err) => { + if (err) { + return next(err); + } + armyOverview.BLUFOR.memberCount = countBlufor; + armyOverview.OPFOR.memberCount = countOpfor; + res.locals.items = armyOverview; + res.locals.processed = true; + next(); + }); }); - }); + }) - }, (err) => { - if (err) { - return next(err); - } - armyOverview.BLUFOR.memberCount = countBlufor; - armyOverview.OPFOR.memberCount = countOpfor; - res.locals.items = armyOverview; - res.locals.processed = true; - next(); - }); - }); - }) - - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); overview.use(routerHandling.emptyResponse); diff --git a/api/routes/players.js b/api/routes/players.js index d9b917f..e6d6527 100644 --- a/api/routes/players.js +++ b/api/routes/players.js @@ -14,99 +14,125 @@ const CampaignModel = require('../models/campaign'); const PlayerModel = require('../models/player'); const WarModel = require('../models/war'); +// Util +const isSteamUUID = require('../tools/util').isSteamUUID; + const campaignPlayer = express.Router(); // routes ********************** campaignPlayer.route('/ranking/:campaignId') - .get((req, res, next) => { - WarModel.find({campaign: req.params.campaignId}, '_id', (err, wars) => { - if (err) return next(err); - const warIds = wars.map((obj) => { - return obj._id; - }); - PlayerModel.find({warId: {"$in": warIds}}, (err, items) => { - if (err) return next(err); - if (!items || items.length === 0) { - const err = new Error('No players for given campaignId'); - err.status = codes.notfound; - return next(err) - } + .get((req, res, next) => { + WarModel.find({campaign: req.params.campaignId}, '_id', (err, wars) => { + if (err) return next(err); + const warIds = wars.map((obj) => { + return obj._id; + }); + PlayerModel.find({warId: {"$in": warIds}}, (err, items) => { + if (err) return next(err); + if (!items || items.length === 0) { + const err = new Error('No players for given campaignId'); + err.status = codes.notfound; + return next(err) + } - const rankingItems = []; - new Set(items.map(x => x.name)).forEach(playerName => { - const playerInstances = items.filter(p => p.name === playerName); - const resItem = {name: playerName, kill: 0, death: 0, friendlyFire: 0, revive: 0, respawn: 0, flagTouch: 0}; - for (let i = 0; i < playerInstances.length; i++) { - resItem.kill += playerInstances[i].kill; - resItem.death += playerInstances[i].death; - resItem.friendlyFire += playerInstances[i].friendlyFire; - resItem.revive += playerInstances[i].revive; - resItem.respawn += playerInstances[i].respawn; - resItem.flagTouch += playerInstances[i].flagTouch; - } - resItem.fraction = playerInstances[playerInstances.length - 1].fraction; - rankingItems.push(resItem); - }); + const rankingItems = []; - function getSortedField(fieldName) { - let num = 1; - rankingItems.sort((a, b) => b[fieldName] - a[fieldName]) - const res = JSON.parse(JSON.stringify(rankingItems)); - for (const entity of res) { - entity.num = num++; - } - return res; - } + // check only first player to have valid steamUUID - then decide if tracked by name or by ID + const usesUUID = isSteamUUID(items[0].steamUUID); - res.locals.items = { - kill: getSortedField('kill'), - death: getSortedField('death'), - friendlyFire: getSortedField('friendlyFire'), - revive: getSortedField('revive'), - respawn: getSortedField('respawn'), - flagTouch: getSortedField('flagTouch') - }; - next(); - }) - }) - }) + new Set(items.map(usesUUID ? x => x.steamUUID : x => x.name)) + .forEach(player => { + const playerInstances = items.filter(usesUUID ? p => p.steamUUID === player : p => p.name === player); + const resItem = { + name: usesUUID ? playerInstances[playerInstances.length - 1].name : player, + kill: 0, + vehicle: 0, + death: 0, + friendlyFire: 0, + revive: 0, + respawn: 0, + flagTouch: 0 + }; + for (let i = 0; i < playerInstances.length; i++) { + resItem.kill += playerInstances[i].kill; + resItem.death += playerInstances[i].death; + resItem.friendlyFire += playerInstances[i].friendlyFire; + resItem.vehicle += playerInstances[i].vehicle; + resItem.revive += playerInstances[i].revive; + resItem.respawn += playerInstances[i].respawn; + resItem.flagTouch += playerInstances[i].flagTouch; + } + resItem.fraction = playerInstances[playerInstances.length - 1].fraction; + rankingItems.push(resItem); + }); - .all( - routerHandling.httpMethodNotAllowed - ); + function getSortedField(fieldName) { + let num = 1; + rankingItems.sort((a, b) => b[fieldName] - a[fieldName]) + const res = JSON.parse(JSON.stringify(rankingItems)); + for (const entity of res) { + entity.num = num++; + } + return res; + } -campaignPlayer.route('/single/:campaignId/:playerName') - .get((req, res, next) => { - CampaignModel.findById(req.params.campaignId, (err, campaign) => { - if (err) return next(err); - WarModel.find({campaign: req.params.campaignId}, '_id', (err, wars) => { - if (err) return next(err); - const warIds = wars.map((obj) => { - return obj._id; - }); - PlayerModel.find({name: req.params.playerName, warId: {"$in": warIds}}) - .populate('warId') - .exec((err, items) => { - if (err) return next(err); - if (!items || items.length === 0) { - const err = new Error('Unknown player name'); - err.status = codes.notfound; - return next(err) - } - res.locals.items = { - name: req.params.playerName, - campaign: campaign, - players: items - }; - next(); - }) - }) - }) - }) + res.locals.items = { + kill: getSortedField('kill'), + death: getSortedField('death'), + friendlyFire: getSortedField('friendlyFire'), + vehicle: getSortedField('vehicle'), + revive: getSortedField('revive'), + respawn: getSortedField('respawn'), + flagTouch: getSortedField('flagTouch') + }; + next(); + }) + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); + +campaignPlayer.route('/single/:campaignId/:playerId') + .get((req, res, next) => { + CampaignModel.findById(req.params.campaignId, (err, campaign) => { + if (err) return next(err); + WarModel.find({campaign: req.params.campaignId}, '_id', (err, wars) => { + if (err) return next(err); + const warIds = wars.map((obj) => { + return obj._id; + }); + + // find by player name until v1.6.12, afterwards by SteamUUID + const playerId = req.params.playerId; + const filter = {}; + filter[isSteamUUID(playerId) ? 'steamUUID' : 'name'] = playerId; + filter['warId'] = {"$in": warIds}; + + PlayerModel.find(filter) + .populate('warId') + .exec((err, items) => { + if (err) return next(err); + if (!items || items.length === 0) { + const err = new Error('Unknown player id'); + err.status = codes.notfound; + return next(err) + } + res.locals.items = { + name: items[items.length - 1].name, + campaign: campaign, + players: items + }; + next(); + }) + }) + }) + }) + + .all( + routerHandling.httpMethodNotAllowed + ); campaignPlayer.use(routerHandling.emptyResponse); diff --git a/api/routes/ranks.js b/api/routes/ranks.js index e0444a3..d5abe3c 100644 --- a/api/routes/ranks.js +++ b/api/routes/ranks.js @@ -24,130 +24,132 @@ const ranks = express.Router(); // routes ********************** ranks.route('/') - .get((req, res, next) => { - const filter = {}; - if (req.query.fractFilter) { - filter.fraction = req.query.fractFilter.toUpperCase() - } - if (req.query.q) { - filter.name = {$regex: req.query.q, $options: 'i'} - } + .get((req, res, next) => { + const filter = {}; + if (req.query.fractFilter) { + filter.fraction = req.query.fractFilter.toUpperCase() + } + if (req.query.q) { + filter.name = {$regex: req.query.q, $options: 'i'} + } - RankModel.find(filter, {}, {sort: {fraction: 'asc', level: 'asc'}}, (err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } + RankModel.find(filter, {}, {sort: {fraction: 'asc', level: 'asc'}}, (err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + } - if (items && items.length > 0) { - res.locals.items = items; - } else { - res.locals.items = []; - } - res.locals.processed = true; - next(); - }); - }) + if (items && items.length > 0) { + res.locals.items = items; + } else { + res.locals.items = []; + } + res.locals.processed = true; + next(); + }); + }) - .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - const rank = new RankModel(req.body); - // timestamp and default are set automatically by Mongoose Schema Validation - rank.save((err) => { - if (err) { - err.status = codes.wrongrequest; - next(err); - } - res.status(codes.created); - res.locals.items = rank; - fs.appendFile(__dirname + '/../resource/rank/' + rank.id + '.png', new Buffer(req.file.buffer), - (err) => { - if (err) next(err); - }); - next(); - }); - }) + .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { + const rank = new RankModel(req.body); + // timestamp and default are set automatically by Mongoose Schema Validation + rank.save((err) => { + if (err) { + err.status = codes.wrongrequest; + next(err); + } + res.status(codes.created); + res.locals.items = rank; + fs.appendFile(__dirname + '/../resource/rank/' + rank.id + '.png', new Buffer(req.file.buffer), + (err) => { + if (err) next(err); + }); + next(); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); ranks.route('/:id') - .get(idValidator, (req, res, next) => { - RankModel.findById(req.params.id, (err, item) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - res.locals.items = item; - next(); - }); - }) + .get(idValidator, (req, res, next) => { + RankModel.findById(req.params.id, (err, item) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + res.locals.items = item; + next(); + }); + }) - .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { + .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - // optional task 3: increment version manually as we do not use .save(.) - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; + // optional task 3: increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - if (req.file) { - const file = __dirname + '/../resource/rank/' + req.body._id + '.png'; - fs.unlink(file, (err) => { - if (err) next(err); - fs.appendFile(file, new Buffer(req.file.buffer), - (err) => { - if (err) next(err); - }); - }); - } + if (req.file) { + const file = __dirname + '/../resource/rank/' + req.body._id + '.png'; + fs.unlink(file, (err) => { + if (err) next(err); + fs.appendFile(file, new Buffer(req.file.buffer), + (err) => { + if (err) next(err); + }); + }); + } - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - RankModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - else { - res.locals.items = item; - } - next(err); - }) - }) + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to + // reset attributes that are missing. + RankModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + else { + res.locals.items = item; + } + next(err); + }) + }) - .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - RankModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(err); // this works because err is in normal case undefined and that is the same as no parameter - }) - }) + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + RankModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(err); // this works because err is in normal case undefined and that is the same as no parameter + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it diff --git a/api/routes/request.js b/api/routes/request.js index e0f6b5f..8a111cb 100644 --- a/api/routes/request.js +++ b/api/routes/request.js @@ -35,145 +35,146 @@ const request = express.Router(); // routes ********************** request.route('/award') - .post(apiAuthenticationMiddleware, checkSql, (req, res, next) => { - const award = new AwardingModel(req.body); - award.confirmed = 0; - award.proposer = req.user._id; - // timestamp and default are set automatically by Mongoose Schema Validation - award.save((err) => { - if (err) { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - return next(err); - } - res.status(codes.created); - res.locals.items = award; - next(); - }); - }) + .post(apiAuthenticationMiddleware, checkSql, (req, res, next) => { + const award = new AwardingModel(req.body); + award.confirmed = 0; + award.proposer = req.user._id; + // timestamp and default are set automatically by Mongoose Schema Validation + award.save((err) => { + if (err) { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + return next(err); + } + res.status(codes.created); + res.locals.items = award; + next(); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); request.route('/promotion') - .get((req, res, next) => { - // TODO: add SQL authentication - const squadFilter = req.query.squadId; - const fractFilter = req.query.fractFilter; - const progressFilter = req.query.inProgress; - let filter; - if (squadFilter) { - filter = {squadId: squadFilter}; - } - let userIds = []; - UserModel.find(filter).populate('squadId').exec((err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } + .get((req, res, next) => { + // TODO: add SQL authentication + const squadFilter = req.query.squadId; + const fractFilter = req.query.fractFilter; + const progressFilter = req.query.inProgress; + let filter; + if (squadFilter) { + filter = {squadId: squadFilter}; + } + let userIds = []; + UserModel.find(filter).populate('squadId').exec((err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + } - for (let item of items) { - if (!fractFilter || (fractFilter && item.squadId && item.squadId.fraction === fractFilter)) { - userIds.push(item._id); - } - } + for (let item of items) { + if (!fractFilter || (fractFilter && item.squadId && item.squadId.fraction === fractFilter)) { + userIds.push(item._id); + } + } - let promotionFilter = { - userId: {"$in": userIds} - }; - if (progressFilter) { - promotionFilter.confirmed = 0; - } + let promotionFilter = { + userId: {"$in": userIds} + }; + if (progressFilter) { + promotionFilter.confirmed = 0; + } - PromotionModel.find(promotionFilter, {}, {sort: {timestamp: 'desc'}}) - .populate('userId').populate('proposer', resultSet).exec((err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } + PromotionModel.find(promotionFilter, {}, {sort: {timestamp: 'desc'}}) + .populate('userId').populate('proposer', resultSet).exec((err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + } - if (items && items.length > 0) { - res.locals.items = items; - } else { - res.locals.items = []; - } - res.locals.processed = true; - next(); - }) - }); + if (items && items.length > 0) { + res.locals.items = items; + } else { + res.locals.items = []; + } + res.locals.processed = true; + next(); + }) + }); - }) + }) - .post(apiAuthenticationMiddleware, checkSql, (req, res, next) => { - const promotion = new PromotionModel(req.body); - promotion.confirmed = 0; - promotion.proposer = req.user._id; - // timestamp and default are set automatically by Mongoose Schema Validation - promotion.save((err) => { - if (err) { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - return next(err); - } - res.status(codes.created); - res.locals.items = promotion; - next(); - }); - }) + .post(apiAuthenticationMiddleware, checkSql, (req, res, next) => { + const promotion = new PromotionModel(req.body); + promotion.confirmed = 0; + promotion.proposer = req.user._id; + // timestamp and default are set automatically by Mongoose Schema Validation + promotion.save((err) => { + if (err) { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + return next(err); + } + res.status(codes.created); + res.locals.items = promotion; + next(); + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); request.route('/promotion/:id') - .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } + .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - PromotionModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - else { - if (item.confirmed === 1) { - let updateUser = { - _id: item.userId, - rankLvl: item.newRankLvl - }; - UserModel.findByIdAndUpdate(updateUser._id, updateUser, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("user not found"); - err.status = codes.notfound; - } - }); - } - res.locals.items = item; - } - next(err); - }) - }) - .all( - routerHandling.httpMethodNotAllowed - ); + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to + // reset attributes that are missing. + PromotionModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + else { + if (item.confirmed === 1) { + let updateUser = { + _id: item.userId, + rankLvl: item.newRankLvl + }; + UserModel.findByIdAndUpdate(updateUser._id, updateUser, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("user not found"); + err.status = codes.notfound; + } + }); + } + res.locals.items = item; + } + next(err); + }) + }) + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/routes/signatures.js b/api/routes/signatures.js index 9f2f987..fc23c97 100644 --- a/api/routes/signatures.js +++ b/api/routes/signatures.js @@ -16,38 +16,38 @@ const signatures = express.Router(); // routes ********************** signatures.route('/:id') - // does not use idValidator since it works by username - .get((req, res, next) => { - // decode UTF8-escape sequences (special characters) - const uri = decodeURIComponent(req.params.id); - UserModel.findOne({username: uri}, (err, user) => { + // does not use idValidator since it works by username + .get((req, res, next) => { + // decode UTF8-escape sequences (special characters) + const uri = decodeURIComponent(req.params.id); + UserModel.findOne({username: uri}, (err, user) => { - const emptyFile = 'resource/signature/0.png'; - if (user === null) { - res.sendFile(emptyFile, {root: __dirname + '/../'}); - } else { - const file = 'resource/signature/' + user._id + '.png'; + const emptyFile = 'resource/signature/0.png'; + if (user === null) { + res.sendFile(emptyFile, {root: __dirname + '/../'}); + } else { + const file = 'resource/signature/' + user._id + '.png'; - fs.stat(__dirname + '/../' + file, (err, stat) => { - if (err === null) { - res.sendFile(file, {root: __dirname + '/../'}); - } else if (err.code === 'ENOENT') { - res.sendFile(emptyFile, {root: __dirname + '/../'}); - } else { - err = new Error("Internal server error"); - err.status = codes.servererror; - return next(err); - } - }); + fs.stat(__dirname + '/../' + file, (err, stat) => { + if (err === null) { + res.sendFile(file, {root: __dirname + '/../'}); + } else if (err.code === 'ENOENT') { + res.sendFile(emptyFile, {root: __dirname + '/../'}); + } else { + err = new Error("Internal server error"); + err.status = codes.servererror; + return next(err); + } + }); - } + } - }) - }) + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it diff --git a/api/routes/squads.js b/api/routes/squads.js index 7f5ac5a..9a77027 100644 --- a/api/routes/squads.js +++ b/api/routes/squads.js @@ -24,138 +24,140 @@ const squads = express.Router(); // routes ********************** squads.route('/') - .get((req, res, next) => { - const filter = {}; - if (req.query.fractFilter) { - filter.fraction = req.query.fractFilter.toUpperCase() - } - if (req.query.q) { - filter.name = {$regex: req.query.q, $options: 'i'} - } - SquadModel.find(filter, {}, {sort: {fraction: 'asc', sortingNumber: 'asc'}}, (err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - if (items) { - res.locals.items = items; - } else { - res.locals.items = []; - } - res.locals.processed = true; - next(); - }); - }) - - .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - const squad = new SquadModel(req.body); - // timestamp and default are set automatically by Mongoose Schema Validation - if (req.file) { - squad.save((err) => { - if (err) { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - return next(err); + .get((req, res, next) => { + const filter = {}; + if (req.query.fractFilter) { + filter.fraction = req.query.fractFilter.toUpperCase() } - res.status(codes.created); - res.locals.items = squad; - fs.appendFile(__dirname + '/../resource/squad/' + squad.id + '.png', new Buffer(req.file.buffer), (err) => { - if (err) next(err); + if (req.query.q) { + filter.name = {$regex: req.query.q, $options: 'i'} + } + SquadModel.find(filter, {}, {sort: {fraction: 'asc', sortingNumber: 'asc'}}, (err, items) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + if (items) { + res.locals.items = items; + } else { + res.locals.items = []; + } + res.locals.processed = true; + next(); }); - next(); }) - } else { - const err = new Error('no image file provided'); - err.status = codes.wrongmediasend; - next(err); - } - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { + const squad = new SquadModel(req.body); + // timestamp and default are set automatically by Mongoose Schema Validation + if (req.file) { + squad.save((err) => { + if (err) { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + return next(err); + } + res.status(codes.created); + res.locals.items = squad; + fs.appendFile(__dirname + '/../resource/squad/' + squad.id + '.png', new Buffer(req.file.buffer), (err) => { + if (err) next(err); + }); + next(); + }) + } else { + const err = new Error('no image file provided'); + err.status = codes.wrongmediasend; + next(err); + } + }) + + .all( + routerHandling.httpMethodNotAllowed + ); squads.route('/:id') - .get(idValidator, (req, res, next) => { - SquadModel.findById(req.params.id, (err, item) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - res.locals.items = item; - next(); - }); - }) - - .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } - - // increment version manually as we do not use .save(.) - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; - - if (req.file) { - const file = __dirname + '/../resource/squad/' + req.body._id + '.png'; - fs.unlink(file, (err) => { - if (err) next(err); - fs.appendFile(file, new Buffer(req.file.buffer), (err) => { - if (err) next(err); + .get(idValidator, (req, res, next) => { + SquadModel.findById(req.params.id, (err, item) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + res.locals.items = item; + next(); }); - }); - } + }) - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - SquadModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - else { - res.locals.items = item; - } - next(err); - }) - }) + .patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => { - .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - SquadModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - // delete graphic - fs.unlink(__dirname + '/../resource/squad/' + req.params.id + '.png', (err) => { - if (err) next(err); - }); + // increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(err); // this works because err is in normal case undefined and that is the same as no parameter - }); - }) + if (req.file) { + const file = __dirname + '/../resource/squad/' + req.body._id + '.png'; + fs.unlink(file, (err) => { + if (err) next(err); + fs.appendFile(file, new Buffer(req.file.buffer), (err) => { + if (err) next(err); + }); + }); + } - .all( - routerHandling.httpMethodNotAllowed - ); + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to + // reset attributes that are missing. + SquadModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + else { + res.locals.items = item; + } + next(err); + }) + }) + + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + SquadModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + + // delete graphic + fs.unlink(__dirname + '/../resource/squad/' + req.params.id + '.png', (err) => { + if (err) next(err); + }); + + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(err); // this works because err is in normal case undefined and that is the same as no parameter + }); + }) + + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/routes/users.js b/api/routes/users.js index 501a8a1..2bde05b 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -28,212 +28,215 @@ users.get('/', offsetlimitMiddleware); // routes ********************** users.route('/') - .get((req, res, next) => { - const userQuery = () => { - UserModel.find(dbFilter, res.locals.filter, res.locals.limitskip) - .populate('squadId') - .collation({locale: "en", strength: 2}) // case insensitive order - .sort('username').exec((err, users) => { - if (err) return next(err); - if (users.length === 0) { - res.locals.items = users; - res.locals.processed = true; - return next(); - } - UserModel.count(dbFilter, (err, totalCount) => { - res.set('x-total-count', totalCount); - res.locals.items = users; - res.locals.processed = true; - return next(); - }) - }) - }; + .get((req, res, next) => { + const userQuery = () => { + UserModel.find(dbFilter, res.locals.filter, res.locals.limitskip) + .populate('squadId') + .collation({locale: "en", strength: 2}) // case insensitive order + .sort('username').exec((err, users) => { + if (err) return next(err); + if (users.length === 0) { + res.locals.items = users; + res.locals.processed = true; + return next(); + } + UserModel.count(dbFilter, (err, totalCount) => { + res.set('x-total-count', totalCount); + res.locals.items = users; + res.locals.processed = true; + return next(); + }) + }) + }; - if (!req.query.q) req.query.q = ''; - const dbFilter = {username: {"$regex": req.query.q, "$options": "i"}}; - if (req.query.squadId) dbFilter["squadId"] = {"$eq": req.query.squadId}; - // squad / fraction filter setup - if (req.query.fractFilter && req.query.fractFilter !== 'UNASSIGNED' && !req.query.squadId) { - SquadModel.find({'fraction': req.query.fractFilter}, {_id: 1}, (err, squads) => { - dbFilter['squadId'] = {$in: squads.map(squad => squad.id)}; - userQuery(); - }) - } else { - if (req.query.fractFilter === 'UNASSIGNED') { - dbFilter['squadId'] = {$eq: null}; - } - userQuery(); - } - }) + if (!req.query.q) req.query.q = ''; + const dbFilter = {username: {"$regex": req.query.q, "$options": "i"}}; + if (req.query.squadId) dbFilter["squadId"] = {"$eq": req.query.squadId}; + // squad / fraction filter setup + if (req.query.fractFilter && req.query.fractFilter !== 'UNASSIGNED' && !req.query.squadId) { + SquadModel.find({'fraction': req.query.fractFilter}, {_id: 1}, (err, squads) => { + dbFilter['squadId'] = {$in: squads.map(squad => squad.id)}; + userQuery(); + }) + } else { + if (req.query.fractFilter === 'UNASSIGNED') { + dbFilter['squadId'] = {$eq: null}; + } + userQuery(); + } + }) - .post(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - const user = new UserModel(req.body); - // timestamp and default are set automatically by Mongoose Schema Validation - user.save((err) => { - if (err) { - err.status = codes.wrongrequest; - return next(err); - } - res.status(codes.created); + .post(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + const user = new UserModel(req.body); + // timestamp and default are set automatically by Mongoose Schema Validation + user.save((err) => { + if (err) { + err.status = codes.wrongrequest; + return next(err); + } + res.status(codes.created); - UserModel.populate(user, {path: 'squadId'}, (err, extUser) => { - res.locals.items = extUser; - res.locals.processed = true; - return next(); - }) - }); - }) + UserModel.populate(user, {path: 'squadId'}, (err, extUser) => { + res.locals.items = extUser; + res.locals.processed = true; + return next(); + }) + }); + }) - .all(routerHandling.httpMethodNotAllowed); + .all(routerHandling.httpMethodNotAllowed); users.route('/:id') - .get(idValidator, (req, res, next) => { - UserModel.findById(req.params.id).populate('squadId').exec((err, user) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - else if (!user) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - res.locals.items = user; - res.locals.processed = true; - return next(); - }); - }) + .get(idValidator, (req, res, next) => { + UserModel.findById(req.params.id).populate('squadId').exec((err, user) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + else if (!user) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + res.locals.items = user; + res.locals.processed = true; + return next(); + }); + }) - .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - if (!req.body || (req.body._id && req.body._id !== req.params.id)) { - // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match - const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } + .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + if (!req.body || (req.body._id && req.body._id !== req.params.id)) { + // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match + const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } - // optional task 3: increment version manually as we do not use .save(.) - req.body.updatedAt = new Date(); - req.body.$inc = {__v: 1}; + // optional task 3: increment version manually as we do not use .save(.) + req.body.updatedAt = new Date(); + req.body.$inc = {__v: 1}; - // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing. - UserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } - UserModel.populate(item, {path: 'squadId'}, (err, extUser) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - if (!user) { - res.locals.items = {}; - res.locals.processed = true; - return next(); - } - res.locals.items = extUser; - res.locals.processed = true; - return next(); - }) - }) - }) + // PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to + // reset attributes that are missing. + UserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } + UserModel.populate(item, {path: 'squadId'}, (err, extUser) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + if (!user) { + res.locals.items = {}; + res.locals.processed = true; + return next(); + } + res.locals.items = extUser; + res.locals.processed = true; + return next(); + }) + }) + }) - .put(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - // first check that the given element id is the same as the URL id - if (!req.body || req.body._id !== req.params.id) { - // the URL does not fit the given element - var err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); - err.status = codes.notfound; - next(err); - return; // prevent node to process this function further after next() has finished. - } - // main difference of PUT and PATCH is that PUT expects all data in request: checked by using the schema - const user = new UserModel(req.body); - UserModel.findById(req.params.id, req.body, {new: true}, function (err, item) { - // with parameter {new: true} the TweetNModel will return the new and changed object from the DB and not the old one. - if (err) { - err.status = codes.wrongrequest; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - // check that version is still accurate - else if (user.__v !== item.__v) { - err = new Error("version outdated. Meanwhile update on item happened. Please GET resource again") - err.status = codes.conflict; - return next(err); - } - // now update all fields in DB item with body data in variable video - for (var field in UserModel.schema.paths) { - if ((field !== '_id') && (field !== '__v')) { - // this includes undefined. is important to reset attributes that are missing in req.body - item.set(field, user[field]); - } - } + .put(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + // first check that the given element id is the same as the URL id + if (!req.body || req.body._id !== req.params.id) { + // the URL does not fit the given element + var err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id); + err.status = codes.notfound; + next(err); + return; // prevent node to process this function further after next() has finished. + } + // main difference of PUT and PATCH is that PUT expects all data in request: checked by using the schema + const user = new UserModel(req.body); + UserModel.findById(req.params.id, req.body, {new: true}, function (err, item) { + // with parameter {new: true} the TweetNModel will return the new and changed object from the DB and not the + // old one. + if (err) { + err.status = codes.wrongrequest; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + // check that version is still accurate + else if (user.__v !== item.__v) { + err = new Error("version outdated. Meanwhile update on item happened. Please GET resource again") + err.status = codes.conflict; + return next(err); + } + // now update all fields in DB item with body data in variable video + for (var field in UserModel.schema.paths) { + if ((field !== '_id') && (field !== '__v')) { + // this includes undefined. is important to reset attributes that are missing in req.body + item.set(field, user[field]); + } + } - // update updatedAt and increase version - item.updatedAt = new Date(); - item.increment(); // this sets __v++ - item.save(function (err) { - if (!err) { - res.locals.items = item; - } else { - err.status = codes.wrongrequest; - err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); - } + // update updatedAt and increase version + item.updatedAt = new Date(); + item.increment(); // this sets __v++ + item.save(function (err) { + if (!err) { + res.locals.items = item; + } else { + err.status = codes.wrongrequest; + err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); + } - UserModel.populate(item, {path: 'squadId'}, (err, extUser) => { - res.locals.items = extUser; - res.locals.processed = true; - return next(); - }) - }); - }) - }) + UserModel.populate(item, {path: 'squadId'}, (err, extUser) => { + res.locals.items = extUser; + res.locals.processed = true; + return next(); + }) + }); + }) + }) - .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { - UserModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - } + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + UserModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + } - // deleted all awardings linked to this user - AwardingModel.find({userId: req.params.id}).remove().exec(); + // deleted all awardings linked to this user + AwardingModel.find({userId: req.params.id}).remove().exec(); - // check if signature exists and delete compressed and uncompressed file - const fileMinified = __dirname + '/../resource/signature/' + req.params.id + '.png'; - if (fs.existsSync(fileMinified)) { - fs.unlink(fileMinified, (err) => { - }); - } - const file = __dirname + '/../resource/signature/big/' + req.params.id + '.png'; - if (fs.existsSync(file)) { - fs.unlink(file, (err) => { - }); - } + // check if signature exists and delete compressed and uncompressed file + const fileMinified = __dirname + '/../resource/signature/' + req.params.id + '.png'; + if (fs.existsSync(fileMinified)) { + fs.unlink(fileMinified, (err) => { + }); + } + const file = __dirname + '/../resource/signature/big/' + req.params.id + '.png'; + if (fs.existsSync(file)) { + fs.unlink(file, (err) => { + }); + } - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(err); // this works because err is in normal case undefined and that is the same as no parameter - }); - }) + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(err); // this works because err is in normal case undefined and that is the same as no parameter + }); + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it diff --git a/api/routes/wars.js b/api/routes/wars.js index f2f90bd..406312f 100644 --- a/api/routes/wars.js +++ b/api/routes/wars.js @@ -27,6 +27,7 @@ const CampaignModel = require('../models/campaign'); const WarModel = require('../models/war'); const PlayerModel = require('../models/player'); const LogKillModel = require('../models/logs/kill'); +const LogVehicleKillModel = require('../models/logs/vehicle'); const LogRespawnModel = require('../models/logs/respawn'); const LogReviveModel = require('../models/logs/revive'); const LogTransportModel = require('../models/logs/transport'); @@ -38,87 +39,91 @@ const wars = express.Router(); // routes ********************** wars.route('/') - .get((req, res, next) => { - let result = []; - CampaignModel.find({}, {}, {sort: {timestamp: 'desc'}}, (err, campaigns) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - if (campaigns) { - WarModel.find({}, {}, {sort: {date: 'desc'}}, (err, wars) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - if (wars) { - campaigns.forEach(campaign => { - let entry = {_id: campaign._id, title: campaign.title, wars: []}; - wars.forEach((war) => { - if (String(campaign._id) === String(war.campaign)) { - entry.wars.push(war); - } - }); - result.push(entry); - }); - res.locals.items = result; - } - res.locals.processed = true; - next(); - } - ) - ; - } - }) - }) - - .post(apiAuthenticationMiddleware, checkMT, upload.single('log'), (req, res, next) => { - const body = req.body; - const warBody = new WarModel(body); - - if (req.file) { - fs.readFile(req.file.buffer, (file, err) => { + .get((req, res, next) => { + let result = []; + CampaignModel.find({}, {}, {sort: {timestamp: 'desc'}}, (err, campaigns) => { if (err) { + err.status = codes.servererror; return next(err); } - const lineArray = file.toString().split("\n"); - const statsResult = parseWarLog(lineArray, warBody); - statsResult.war.save((err, war) => { + if (campaigns) { + WarModel.find({}, {}, {sort: {date: 'desc'}}, (err, wars) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + if (wars) { + campaigns.forEach(campaign => { + let entry = {_id: campaign._id, title: campaign.title, wars: []}; + wars.forEach((war) => { + if (String(campaign._id) === String(war.campaign)) { + entry.wars.push(war); + } + }); + result.push(entry); + }); + res.locals.items = result; + } + res.locals.processed = true; + next(); + } + ) + ; + } + }) + }) + + .post(apiAuthenticationMiddleware, checkMT, upload.single('log'), (req, res, next) => { + const body = req.body; + const warBody = new WarModel(body); + + if (req.file) { + fs.readFile(req.file.buffer, (file, err) => { if (err) { return next(err); } - PlayerModel.create(statsResult.players, function (err) { + const lineArray = file.toString().split("\n"); + const statsResult = parseWarLog(lineArray, warBody); + statsResult.war.save((err, war) => { if (err) { return next(err); } - LogKillModel.create(statsResult.kills, function () { - LogRespawnModel.create(statsResult.respawn, function () { - LogReviveModel.create(statsResult.revive, function () { - LogFlagModel.create(statsResult.flag, function () { - LogBudgetModel.create(statsResult.budget, function () { - LogTransportModel.create(statsResult.transport, function () { - LogPointsModel.create(statsResult.points, function () { - const folderName = __dirname + '/../resource/logs/' + war._id; - mkdirp(folderName, function (err) { - if (err) return next(err); + PlayerModel.create(statsResult.players, function (err) { + if (err) { + return next(err); + } + LogKillModel.create(statsResult.kills, function () { + LogVehicleKillModel.create(statsResult.vehicles, function () { - // save clean log file - const cleanFile = fs.createWriteStream(folderName + '/clean.log'); - statsResult.clean.forEach(cleanLine => { - cleanFile.write(cleanLine + '\n\n') - }); - cleanFile.end(); + LogRespawnModel.create(statsResult.respawn, function () { + LogReviveModel.create(statsResult.revive, function () { + LogFlagModel.create(statsResult.flag, function () { + LogBudgetModel.create(statsResult.budget, function () { + LogTransportModel.create(statsResult.transport, function () { + LogPointsModel.create(statsResult.points, function () { + const folderName = __dirname + '/../resource/logs/' + war._id; + mkdirp(folderName, function (err) { + if (err) return next(err); - // save raw log file - const rawFile = fs.createWriteStream(folderName + '/war.log'); - lineArray.forEach(rawLine => { - rawFile.write(rawLine + '\n') - }); - rawFile.end(); + // save clean log file + const cleanFile = fs.createWriteStream(folderName + '/clean.log'); + statsResult.clean.forEach(cleanLine => { + cleanFile.write(cleanLine + '\n\n') + }); + cleanFile.end(); - res.status(codes.created); - res.locals.items = war; - next(); + // save raw log file + const rawFile = fs.createWriteStream(folderName + '/war.log'); + lineArray.forEach(rawLine => { + rawFile.write(rawLine + '\n') + }); + rawFile.end(); + + res.status(codes.created); + res.locals.items = war; + next(); + }) + }) }) }) }) @@ -128,95 +133,95 @@ wars.route('/') }) }) }) - }) - }); + }); - } else { - const err = new Error('no Logfile provided'); - err.status = codes.wrongmediasend; - next(err); - } - }) + } else { + const err = new Error('no Logfile provided'); + err.status = codes.wrongmediasend; + next(err); + } + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); wars.route('/:id') - .get(idValidator, (req, res, next) => { - WarModel.findById(req.params.id, (err, item) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - PlayerModel.find({warId: item._id}, {}, {sort: {kill: 'desc'}}, (err, items) => { + .get(idValidator, (req, res, next) => { + WarModel.findById(req.params.id, (err, item) => { if (err) { + err.status = codes.servererror; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; + return next(err); + } + PlayerModel.find({warId: item._id}, {}, {sort: {kill: 'desc'}}, (err, items) => { + if (err) { + return next(err); + } + + const responseObj = item.toObject(); + responseObj.players = items; + res.locals.items = responseObj; + return next(); + }); + + }); + }) + + .delete(apiAuthenticationMiddleware, checkMT, (req, res, next) => { + WarModel.findByIdAndRemove(req.params.id, (err, item) => { + if (err) { + err.status = codes.wrongrequest; + return next(err); + } + else if (!item) { + err = new Error("item not found"); + err.status = codes.notfound; return next(err); } - const responseObj = item.toObject(); - responseObj.players = items; - res.locals.items = responseObj; - return next(); - }); + // delete linked appearances + PlayerModel.find({warId: item._id}).remove().exec(); + LogKillModel.find({war: item._id}).remove().exec(); + LogRespawnModel.find({war: item._id}).remove().exec(); + LogReviveModel.find({war: item._id}).remove().exec(); + LogFlagModel.find({war: item._id}).remove().exec(); + LogBudgetModel.find({war: item._id}).remove().exec(); + LogTransportModel.find({war: item._id}).remove().exec(); + LogPointsModel.find({war: item._id}).remove().exec(); - }); - }) + // check if logfiles exist and delete from fs + const warDir = __dirname + '/../resource/logs/' + req.params.id; - .delete(apiAuthenticationMiddleware, checkMT, (req, res, next) => { - WarModel.findByIdAndRemove(req.params.id, (err, item) => { - if (err) { - err.status = codes.wrongrequest; - return next(err); - } - else if (!item) { - err = new Error("item not found"); - err.status = codes.notfound; - return next(err); - } - - // delete linked appearances - PlayerModel.find({warId: item._id}).remove().exec(); - LogKillModel.find({war: item._id}).remove().exec(); - LogRespawnModel.find({war: item._id}).remove().exec(); - LogReviveModel.find({war: item._id}).remove().exec(); - LogFlagModel.find({war: item._id}).remove().exec(); - LogBudgetModel.find({war: item._id}).remove().exec(); - LogTransportModel.find({war: item._id}).remove().exec(); - LogPointsModel.find({war: item._id}).remove().exec(); - - // check if logfiles exist and delete from fs - const warDir = __dirname + '/../resource/logs/' + req.params.id; - - if (fs.existsSync(warDir)) { - const cleanLog = warDir + '/clean.log'; - if (fs.existsSync(cleanLog)) { - fs.unlink(cleanLog, (err) => { + if (fs.existsSync(warDir)) { + const cleanLog = warDir + '/clean.log'; + if (fs.existsSync(cleanLog)) { + fs.unlink(cleanLog, (err) => { + }); + } + const sourceLog = warDir + '/war.log'; + if (fs.existsSync(sourceLog)) { + fs.unlink(sourceLog, (err) => { + }); + } + fs.rmdir(warDir, (err) => { }); } - const sourceLog = warDir + '/war.log'; - if (fs.existsSync(sourceLog)) { - fs.unlink(sourceLog, (err) => { - }); - } - fs.rmdir(warDir, (err) => { - }); - } - // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..) - res.locals.processed = true; - next(); + // we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler + // user.use(..) + res.locals.processed = true; + next(); + }) }) - }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); // this middleware function can be used, if you like or remove it // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json diff --git a/api/test/awardings.spec.js b/api/test/awardings.spec.js index 6961008..7fa81c2 100644 --- a/api/test/awardings.spec.js +++ b/api/test/awardings.spec.js @@ -24,13 +24,13 @@ describe('Awardings', () => { describe('/GET awardings', () => { it('it should GET all awardings', (done) => { chai.request(server) - .get(urls.awards) - .end((err, res) => { - res.should.have.status(codes.success); - res.body.should.be.a('array'); - res.body.length.should.be.eql(0); - done(); - }); + .get(urls.awards) + .end((err, res) => { + res.should.have.status(codes.success); + res.body.should.be.a('array'); + res.body.length.should.be.eql(0); + done(); + }); }); }); @@ -41,15 +41,15 @@ describe('Awardings', () => { it('it should not POST an awarding without auth-token provided', (done) => { chai.request(server) - .post(urls.awards) - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.awards) + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -60,15 +60,15 @@ describe('Awardings', () => { it('it should not PATCH an awarding without auth-token provided', (done) => { chai.request(server) - .patch(urls.awards + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .patch(urls.awards + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -81,28 +81,28 @@ describe('Awardings', () => { it('it should not accept DELETE method without id in url', (done) => { chai.request(server) - .delete(urls.awards) - .send({}) - .end((err, res) => { - res.should.have.status(codes.wrongmethod); - res.body.should.be.a('object'); - res.body.should.have.property('error').property('message') - .eql('this method is not allowed at ' + urls.awards); - done(); - }); + .delete(urls.awards) + .send({}) + .end((err, res) => { + res.should.have.status(codes.wrongmethod); + res.body.should.be.a('object'); + res.body.should.have.property('error').property('message') + .eql('this method is not allowed at ' + urls.awards); + done(); + }); }); it('it should not DELETE an awarding without auth-token provided', (done) => { chai.request(server) - .delete(urls.awards + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .delete(urls.awards + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/test/command.spec.js b/api/test/command.spec.js index bff2b18..18a46c9 100644 --- a/api/test/command.spec.js +++ b/api/test/command.spec.js @@ -24,15 +24,15 @@ describe('Command', () => { describe('/POST command to create signature', () => { it('it should not succeed without auth-token provided', (done) => { chai.request(server) - .post(urls.cmdCreateSig + "/someId") - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.cmdCreateSig + "/someId") + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/test/decorations.spec.js b/api/test/decorations.spec.js index a140255..e191bd0 100644 --- a/api/test/decorations.spec.js +++ b/api/test/decorations.spec.js @@ -24,13 +24,13 @@ describe('Decorations', () => { describe('/GET decorations', () => { it('it should GET all the decorations', (done) => { chai.request(server) - .get(urls.decorations) - .end((err, res) => { - res.should.have.status(codes.success); - res.body.should.be.a('array'); - res.body.length.should.be.eql(0); - done(); - }); + .get(urls.decorations) + .end((err, res) => { + res.should.have.status(codes.success); + res.body.should.be.a('array'); + res.body.length.should.be.eql(0); + done(); + }); }); }); @@ -41,15 +41,15 @@ describe('Decorations', () => { it('it should not POST a decoration without auth-token provided', (done) => { chai.request(server) - .post(urls.decorations) - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.decorations) + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -60,15 +60,15 @@ describe('Decorations', () => { it('it should not PATCH a decoration without auth-token provided', (done) => { chai.request(server) - .patch(urls.decorations + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .patch(urls.decorations + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -80,28 +80,28 @@ describe('Decorations', () => { describe('/DELETE decorations', () => { it('it should not accept DELETE method without id in url', (done) => { chai.request(server) - .delete(urls.decorations) - .send({}) - .end((err, res) => { - res.should.have.status(codes.wrongmethod); - res.body.should.be.a('object'); - res.body.should.have.property('error').property('message') - .eql('this method is not allowed at ' + urls.decorations); - done(); - }); + .delete(urls.decorations) + .send({}) + .end((err, res) => { + res.should.have.status(codes.wrongmethod); + res.body.should.be.a('object'); + res.body.should.have.property('error').property('message') + .eql('this method is not allowed at ' + urls.decorations); + done(); + }); }); it('it should not DELETE a decoration without auth-token provided', (done) => { chai.request(server) - .delete(urls.decorations + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .delete(urls.decorations + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/test/ranks.spec.js b/api/test/ranks.spec.js index a3236b3..edd0111 100644 --- a/api/test/ranks.spec.js +++ b/api/test/ranks.spec.js @@ -24,13 +24,13 @@ describe('Ranks', () => { describe('/GET ranks', () => { it('it should GET all the ranks', (done) => { chai.request(server) - .get(urls.ranks) - .end((err, res) => { - res.should.have.status(codes.success); - res.body.should.be.a('array'); - res.body.length.should.be.eql(0); - done(); - }); + .get(urls.ranks) + .end((err, res) => { + res.should.have.status(codes.success); + res.body.should.be.a('array'); + res.body.length.should.be.eql(0); + done(); + }); }); }); @@ -41,15 +41,15 @@ describe('Ranks', () => { it('it should not POST a rank without auth-token provided', (done) => { chai.request(server) - .post(urls.ranks) - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.ranks) + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }) @@ -60,15 +60,15 @@ describe('Ranks', () => { it('it should not PATCH a rank without auth-token provided', (done) => { chai.request(server) - .patch(urls.ranks + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .patch(urls.ranks + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -80,29 +80,29 @@ describe('Ranks', () => { describe('/DELETE ranks', () => { it('it should not accept DELETE method without id in url', (done) => { chai.request(server) - .delete(urls.ranks) - .send({}) - .end((err, res) => { - res.should.have.status(codes.wrongmethod); - res.body.should.be.a('object'); - res.body.should.have.property('error').property('message') - .eql('this method is not allowed at ' + urls.ranks); - done(); - }); + .delete(urls.ranks) + .send({}) + .end((err, res) => { + res.should.have.status(codes.wrongmethod); + res.body.should.be.a('object'); + res.body.should.have.property('error').property('message') + .eql('this method is not allowed at ' + urls.ranks); + done(); + }); }); it('it should not DELETE a rank without auth-token provided', (done) => { chai.request(server) - .delete(urls.ranks + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .delete(urls.ranks + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/test/squads.spec.js b/api/test/squads.spec.js index 82c8f47..ebc149a 100644 --- a/api/test/squads.spec.js +++ b/api/test/squads.spec.js @@ -24,13 +24,13 @@ describe('Squads', () => { describe('/GET squads', () => { it('it should GET all the squads', (done) => { chai.request(server) - .get(urls.squads) - .end((err, res) => { - res.should.have.status(codes.success); - res.body.should.be.a('array'); - res.body.length.should.be.eql(0); - done(); - }); + .get(urls.squads) + .end((err, res) => { + res.should.have.status(codes.success); + res.body.should.be.a('array'); + res.body.length.should.be.eql(0); + done(); + }); }); }); @@ -41,15 +41,15 @@ describe('Squads', () => { it('it should not POST a squad without auth-token provided', (done) => { chai.request(server) - .post(urls.squads) - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.squads) + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -60,15 +60,15 @@ describe('Squads', () => { it('it should not PATCH a squad without auth-token provided', (done) => { chai.request(server) - .patch(urls.squads + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .patch(urls.squads + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -80,28 +80,28 @@ describe('Squads', () => { describe('/DELETE squads', () => { it('it should not accept DELETE method without id in url', (done) => { chai.request(server) - .delete(urls.squads) - .send({}) - .end((err, res) => { - res.should.have.status(codes.wrongmethod); - res.body.should.be.a('object'); - res.body.should.have.property('error').property('message') - .eql('this method is not allowed at ' + urls.squads); - done(); - }); + .delete(urls.squads) + .send({}) + .end((err, res) => { + res.should.have.status(codes.wrongmethod); + res.body.should.be.a('object'); + res.body.should.have.property('error').property('message') + .eql('this method is not allowed at ' + urls.squads); + done(); + }); }); it('it should not DELETE a squad without auth-token provided', (done) => { chai.request(server) - .delete(urls.squads + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .delete(urls.squads + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/test/users.spec.js b/api/test/users.spec.js index 375f517..bfa5385 100644 --- a/api/test/users.spec.js +++ b/api/test/users.spec.js @@ -25,13 +25,13 @@ describe('Users', () => { describe('/GET users', () => { it('it should GET all the users', (done) => { chai.request(server) - .get(urls.users) - .end((err, res) => { - res.should.have.status(codes.success); - res.body.should.be.a('array'); - res.body.length.should.be.eql(0); - done(); - }); + .get(urls.users) + .end((err, res) => { + res.should.have.status(codes.success); + res.body.should.be.a('array'); + res.body.length.should.be.eql(0); + done(); + }); }); }); @@ -73,15 +73,15 @@ describe('Users', () => { it('it should not POST a user without auth-token provided', (done) => { chai.request(server) - .post(urls.users) - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.users) + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); // it('it should POST a user with provided username', (done) => { @@ -109,15 +109,15 @@ describe('Users', () => { it('it should not PATCH a user without auth-token provided', (done) => { chai.request(server) - .patch(urls.users + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .patch(urls.users + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -129,28 +129,28 @@ describe('Users', () => { describe('/DELETE users', () => { it('it should not accept DELETE method without id in url', (done) => { chai.request(server) - .delete(urls.users) - .send({}) - .end((err, res) => { - res.should.have.status(codes.wrongmethod); - res.body.should.be.a('object'); - res.body.should.have.property('error').property('message') - .eql('this method is not allowed at ' + urls.users); - done(); - }); + .delete(urls.users) + .send({}) + .end((err, res) => { + res.should.have.status(codes.wrongmethod); + res.body.should.be.a('object'); + res.body.should.have.property('error').property('message') + .eql('this method is not allowed at ' + urls.users); + done(); + }); }); it('it should not DELETE a user without auth-token provided', (done) => { chai.request(server) - .delete(urls.users + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .delete(urls.users + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/test/wars.spec.js b/api/test/wars.spec.js index 62818ba..3b9a852 100644 --- a/api/test/wars.spec.js +++ b/api/test/wars.spec.js @@ -20,13 +20,13 @@ describe('Wars', () => { describe('/GET wars', () => { it('it should GET all wars', (done) => { chai.request(server) - .get(urls.wars) - .end((err, res) => { - res.should.have.status(codes.success); - res.body.should.be.a('array'); - res.body.length.should.be.eql(0); - done(); - }); + .get(urls.wars) + .end((err, res) => { + res.should.have.status(codes.success); + res.body.should.be.a('array'); + res.body.length.should.be.eql(0); + done(); + }); }); }); @@ -37,15 +37,15 @@ describe('Wars', () => { it('it should not POST a war without auth-token provided', (done) => { chai.request(server) - .post(urls.wars) - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .post(urls.wars) + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); @@ -56,28 +56,28 @@ describe('Wars', () => { it('it should not accept DELETE method without id in url', (done) => { chai.request(server) - .delete(urls.wars) - .send({}) - .end((err, res) => { - res.should.have.status(codes.wrongmethod); - res.body.should.be.a('object'); - res.body.should.have.property('error').property('message') - .eql('this method is not allowed at ' + urls.wars); - done(); - }); + .delete(urls.wars) + .send({}) + .end((err, res) => { + res.should.have.status(codes.wrongmethod); + res.body.should.be.a('object'); + res.body.should.have.property('error').property('message') + .eql('this method is not allowed at ' + urls.wars); + done(); + }); }); it('it should not DELETE an awarding without auth-token provided', (done) => { chai.request(server) - .delete(urls.wars + '/someId') - .send({}) - .end((err, res) => { - res.should.have.status(codes.forbidden); - res.body.should.be.a('object'); - res.body.should.have.property('success').eql(false); - res.body.should.have.property('message').eql('No token provided.'); - done(); - }); + .delete(urls.wars + '/someId') + .send({}) + .end((err, res) => { + res.should.have.status(codes.forbidden); + res.body.should.be.a('object'); + res.body.should.have.property('success').eql(false); + res.body.should.have.property('message').eql('No token provided.'); + done(); + }); }); }); diff --git a/api/tools/log-parse-tool.js b/api/tools/log-parse-tool.js index 1f11138..6dce64f 100644 --- a/api/tools/log-parse-tool.js +++ b/api/tools/log-parse-tool.js @@ -5,6 +5,7 @@ const playerArrayContains = require('./util').playerArrayContains; const WHITESPACE = ' '; const parseWarLog = (lineArray, war) => { + const NAME_TOO_LONG_ERROR = 'Error: ENAMETOOLONG: name too long, open \''; const stats = { @@ -13,6 +14,7 @@ const parseWarLog = (lineArray, war) => { budget: [], points: [], kills: [], + vehicles: [], respawn: [], revive: [], flag: [], @@ -20,10 +22,21 @@ const parseWarLog = (lineArray, war) => { players: [] }; - const addPlayerIfNotExists = (inputPlayer) => { + const vehicleBlacklist = [ + 'Prowler (Unbewaffnet)', 'Prowler (Bewaffnet)', 'Hunter', + 'HEMTT Transporter', 'HEMTT Transporter (abgedeckt)', 'HEMTT SanitÀtsfahrzeug', + 'Remote Designator [NATO]', 'UGV Stomper', + 'Qilin (Unbewaffnet)', 'Qilin (Bewaffnet)', 'Ifrit', + 'Tempest-Transporter', 'Tempest-Transporter (abgedeckt)', 'Tempest SanitÀtsfahrzeug', + 'Remote Designator [CSAT]', 'UBF Saif', + 'Quad Bike', 'HuntIR' + ]; + + const addPlayerIfNotExists = (inputPlayer, steamUUID) => { const player = getPlayerAndFractionFromString(inputPlayer); if (player && player.name && player.fraction && !playerArrayContains(stats.players, player)) { player['warId'] = war._id; + player['steamUUID'] = steamUUID; stats.players.push(player); } }; @@ -37,23 +50,38 @@ const parseWarLog = (lineArray, war) => { } /** - * KILLS + * KILLS & VEHICLE KILLS */ - if (line.includes('(Abschuss)') && !line.includes('Fahrzeug')) { + if (line.includes('(Abschuss)')) { stats.clean.push(line); + const shooterString = line.substring(line.lastIndexOf(' von: ') + 6, line.lastIndexOf('."')); const shooter = getPlayerAndFractionFromString(shooterString); - const targetString = line.substring(line.lastIndexOf(' --- ') + 5, line.lastIndexOf(' von:')); - const target = getPlayerAndFractionFromString(targetString); - stats.kills.push({ - war: war._id, - time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]), - shooter: shooter ? shooter.name : null, - target: target ? target.name : null, - friendlyFire: shooter ? target.fraction === shooter.fraction : false, - fraction: shooter ? shooter.fraction : 'NONE' - }); + if (line.includes('Fahrzeug:')) { + const targetString = line.substring(line.lastIndexOf(' --- Fahrzeug: ') + 15, line.lastIndexOf(' von:')); + const target = getVehicleAndFractionFromString(targetString); + if (target && shooter && target.fraction !== shooter.fraction) { + stats.vehicles.push({ + war: war._id, + time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]), + shooter: shooter ? shooter.name : null, + target: target ? target.name : null, + fraction: shooter ? shooter.fraction : 'NONE' + }) + } + } else { + const targetString = line.substring(line.lastIndexOf(' --- ') + 5, line.lastIndexOf(' von:')); + const target = getPlayerAndFractionFromString(targetString); + stats.kills.push({ + war: war._id, + time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]), + shooter: shooter ? shooter.name : null, + target: target ? target.name : null, + friendlyFire: shooter ? target.fraction === shooter.fraction : false, + fraction: shooter ? shooter.fraction : 'NONE' + }); + } } /** @@ -171,7 +199,8 @@ const parseWarLog = (lineArray, war) => { */ else if (line.includes('(Fraktionsuebersicht)')) { const playerString = line.substring(line.lastIndexOf('--- ') + 4, line.lastIndexOf(', PUID')); - addPlayerIfNotExists(playerString) + const playerUUID = line.substring(line.lastIndexOf('PUID ') + 5, line.lastIndexOf('"')); + addPlayerIfNotExists(playerString, playerUUID) } }); @@ -179,6 +208,7 @@ const parseWarLog = (lineArray, war) => { const playerName = stats.players[i].name; stats.players[i]['respawn'] = stats.respawn.filter(res => res.player === playerName).length; stats.players[i]['kill'] = stats.kills.filter(kill => kill.shooter === playerName && !kill.friendlyFire).length; + stats.players[i]['vehicle'] = stats.vehicles.filter(vehicle => vehicle.shooter === playerName && vehicleBlacklist.indexOf(vehicle.target) < 0).length; stats.players[i]['friendlyFire'] = stats.kills.filter(kill => kill.shooter === playerName && kill.friendlyFire).length; stats.players[i]['death'] = stats.kills.filter(kill => kill.target === playerName).length; stats.players[i]['revive'] = stats.revive.filter(rev => rev.medic === playerName && !rev.stabilized).length; @@ -231,6 +261,21 @@ const getPlayerAndFractionFromString = (nameAndFractionString) => { } }; +const getVehicleAndFractionFromString = (nameAndFractionString) => { + const nameArray = nameAndFractionString.trim().split(WHITESPACE); + const fractionPart = nameArray[nameArray.length - 1]; + + // escape it is some parachute fraction identifier + if (fractionPart === 'OPF_F' || fractionPart === 'BLU_F') { + return; + } + + const fraction = fractionPart === '(OPT_NATO)' || fractionPart === '(OPT_NATO_T)' ? 'BLUFOR' : 'OPFOR'; + const name = nameAndFractionString.substring(0, nameAndFractionString.indexOf(fractionPart) - 1); + + return {name: name, fraction: fraction}; +}; + const transformMoneyString = (budgetString) => { if (!budgetString.includes('e+')) { return parseInt(budgetString); diff --git a/api/tools/signature-tool.js b/api/tools/signature-tool.js index d26fa13..f6d6ce4 100644 --- a/api/tools/signature-tool.js +++ b/api/tools/signature-tool.js @@ -31,79 +31,79 @@ let createSignature = (userId, res, next) => { return next(error); } UserModel.findById(userId, ['username', 'rankLvl', 'squadId']) - .populate('squadId', ['name', 'fraction']) - .exec() - .then((resUser) => { - user = resUser; - let platePath; - if (resUser && resUser.squadId && resUser.squadId.fraction === 'BLUFOR') { - platePath = __dirname + '/backplate/blufor' + fileExt; - } else if (resUser && resUser.squadId && resUser.squadId.fraction === 'OPFOR') { - platePath = __dirname + '/backplate/opfor' + fileExt; - } + .populate('squadId', ['name', 'fraction']) + .exec() + .then((resUser) => { + user = resUser; + let platePath; + if (resUser && resUser.squadId && resUser.squadId.fraction === 'BLUFOR') { + platePath = __dirname + '/backplate/blufor' + fileExt; + } else if (resUser && resUser.squadId && resUser.squadId.fraction === 'OPFOR') { + platePath = __dirname + '/backplate/opfor' + fileExt; + } - // kill process on undefined platePath - if (!platePath) { - throw new Error('Fraction not defined for user with id ' + userId); - } - return jimp.read(platePath) - }) - .then((image) => { - loadedImage = image; - }).then(() => { - return jimp.loadFont(__dirname + '/font/DEVAJU_SANS_19.fnt'); - }) - .then((font) => { - loadedImage.print(font, 128, 8, user.username) - }) - .then(() => { - return jimp.loadFont(__dirname + '/font/DEJAVU_SANS_13.fnt'); - }) - .then((font) => { - loadedImage.print(font, 128, 35, user.squadId.name); - return font; - }) - .then((font) => { - let rankH, rankW, rankX, rankY; + // kill process on undefined platePath + if (!platePath) { + throw new Error('Fraction not defined for user with id ' + userId); + } + return jimp.read(platePath) + }) + .then((image) => { + loadedImage = image; + }).then(() => { + return jimp.loadFont(__dirname + '/font/DEVAJU_SANS_19.fnt'); + }) + .then((font) => { + loadedImage.print(font, 128, 8, user.username) + }) + .then(() => { + return jimp.loadFont(__dirname + '/font/DEJAVU_SANS_13.fnt'); + }) + .then((font) => { + loadedImage.print(font, 128, 35, user.squadId.name); + return font; + }) + .then((font) => { + let rankH, rankW, rankX, rankY; - RankModel.findOne({'level': user.rankLvl, 'fraction': user.squadId.fraction}, (err, result) => { - if (err) { - return next(err) - } + RankModel.findOne({'level': user.rankLvl, 'fraction': user.squadId.fraction}, (err, result) => { + if (err) { + return next(err) + } - if (result) { - if (user.squadId.fraction === 'BLUFOR') { - rankW = 25; - rankH = 60; - rankX = 36; - rankY = 34; - } else { - rankW = 37; - rankH = 58; - rankX = 30; - rankY = 34; - } + if (result) { + if (user.squadId.fraction === 'BLUFOR') { + rankW = 25; + rankH = 60; + rankX = 36; + rankY = 34; + } else { + rankW = 37; + rankH = 58; + rankX = 30; + rankY = 34; + } - jimp.read(resourceDir + 'rank/' + result._id + fileExt) - .then((rankImage) => { - rankImage.resize(rankW, rankH); - loadedImage - .print(font, 128, 55, result.name) - .composite(rankImage, rankX, rankY) - }) - .then(() => { - addDecorationsAndSave(userId, loadedImage, res, next); - }) - } else { - // user has not any assignable rank in his fraction at this point, - // e.g. assigned rank has been deleted or switched fraction so rankLvl is not defined - addDecorationsAndSave(userId, loadedImage, res, next); - } - }) - }) - .catch((err) => { - next(err); - }) + jimp.read(resourceDir + 'rank/' + result._id + fileExt) + .then((rankImage) => { + rankImage.resize(rankW, rankH); + loadedImage + .print(font, 128, 55, result.name) + .composite(rankImage, rankX, rankY) + }) + .then(() => { + addDecorationsAndSave(userId, loadedImage, res, next); + }) + } else { + // user has not any assignable rank in his fraction at this point, + // e.g. assigned rank has been deleted or switched fraction so rankLvl is not defined + addDecorationsAndSave(userId, loadedImage, res, next); + } + }) + }) + .catch((err) => { + next(err); + }) }; @@ -130,110 +130,110 @@ let addDecorationsAndSave = (userId, loadedImage, res, next) => { 'userId': userId, 'confirmed': 1 }, ['decorationId', 'date']).populate('decorationId', ['isMedal', 'fraction']) - .exec((err, awardings) => { - if (err) { - return next(err); - } - if (awardings.length > 0) { + .exec((err, awardings) => { + if (err) { + return next(err); + } + if (awardings.length > 0) { - //TODO: simplify this sorting hell - awardings.sort((a1, a2) => { - if (!a1.decorationId.isMedal && !a2.decorationId.isMedal) { - if (a1.decorationId.fraction === a2.decorationId.fraction) { - if (a1.date !== a2.date) { - if (a1.date < a2.date) { - return -1; - } - if (a1.date > a2.date) { - return 1; - } - } - return 0; - } else { - if (a1.decorationId.fraction === 'GLOBAL' && a2.decorationId.fraction !== 'GLOBAL') { - return -1; - } - if (a2.decorationId.fraction === 'GLOBAL' && a1.decorationId.fraction !== 'GLOBAL') { - return 1; - } - } - } - if (a1.decorationId.isMedal !== a2.decorationId.isMedal) { - if (a1.decorationId.isMedal && !a2.decorationId.isMedal) { - return 1; - } - if (!a1.decorationId.isMedal && a2.decorationId.isMedal) { - return -1; - } - } - if (a1.decorationId.isMedal && a2.decorationId.isMedal) { - if (a1.date !== a2.date) { - if (a1.date < a2.date) { - return -1; - } - if (a1.date > a2.date) { - return 1; - } - } - return 0; - } - }); + //TODO: simplify this sorting hell + awardings.sort((a1, a2) => { + if (!a1.decorationId.isMedal && !a2.decorationId.isMedal) { + if (a1.decorationId.fraction === a2.decorationId.fraction) { + if (a1.date !== a2.date) { + if (a1.date < a2.date) { + return -1; + } + if (a1.date > a2.date) { + return 1; + } + } + return 0; + } else { + if (a1.decorationId.fraction === 'GLOBAL' && a2.decorationId.fraction !== 'GLOBAL') { + return -1; + } + if (a2.decorationId.fraction === 'GLOBAL' && a1.decorationId.fraction !== 'GLOBAL') { + return 1; + } + } + } + if (a1.decorationId.isMedal !== a2.decorationId.isMedal) { + if (a1.decorationId.isMedal && !a2.decorationId.isMedal) { + return 1; + } + if (!a1.decorationId.isMedal && a2.decorationId.isMedal) { + return -1; + } + } + if (a1.decorationId.isMedal && a2.decorationId.isMedal) { + if (a1.date !== a2.date) { + if (a1.date < a2.date) { + return -1; + } + if (a1.date > a2.date) { + return 1; + } + } + return 0; + } + }); - // use synchronized call to keep correct order of decorations - async.eachSeries(awardings, (award, callback) => { - jimp.read(resourceDir + 'decoration/' + award.decorationId._id + fileExt).then((decorationImage) => { - if (award.decorationId.isMedal) { - decorationImage.resize(medalW, medalH); - loadedImage.composite(decorationImage, medalPx, medalPy); - if (medalPy === 5) { - medalPx = medalPx - 1 - medalW; - } else { - medalPx = medalPx + 1 + medalW; - } - if (medalPx <= 300) { - medalPy = medalPy + 3 + medalH; - } - } else { - decorationImage.resize(ribbonW, ribbonH); - loadedImage.composite(decorationImage, ribbonPx, ribbonPy); - ribbonPx = ribbonPx - 2 - ribbonW; - if (ribbonPx <= 154) { - ribbonPy = ribbonPy - 3 - ribbonH; - ribbonPx = 598; - } - } - callback(); - }) - }, (err) => { - if (err) { - throw err; - } - compareImagesAndSave(loadedImage, userId, res, next); - }); - } else { - compareImagesAndSave(loadedImage, userId, res, next); - } - } - ) + // use synchronized call to keep correct order of decorations + async.eachSeries(awardings, (award, callback) => { + jimp.read(resourceDir + 'decoration/' + award.decorationId._id + fileExt).then((decorationImage) => { + if (award.decorationId.isMedal) { + decorationImage.resize(medalW, medalH); + loadedImage.composite(decorationImage, medalPx, medalPy); + if (medalPy === 5) { + medalPx = medalPx - 1 - medalW; + } else { + medalPx = medalPx + 1 + medalW; + } + if (medalPx <= 300) { + medalPy = medalPy + 3 + medalH; + } + } else { + decorationImage.resize(ribbonW, ribbonH); + loadedImage.composite(decorationImage, ribbonPx, ribbonPy); + ribbonPx = ribbonPx - 2 - ribbonW; + if (ribbonPx <= 154) { + ribbonPy = ribbonPy - 3 - ribbonH; + ribbonPx = 598; + } + } + callback(); + }) + }, (err) => { + if (err) { + throw err; + } + compareImagesAndSave(loadedImage, userId, res, next); + }); + } else { + compareImagesAndSave(loadedImage, userId, res, next); + } + } + ) }; let compareImagesAndSave = (generatedImage, userId, res, next) => { return jimp.read(resourceDir + 'signature/big/' + userId + fileExt) - .then((oldImage) => { - // compare hashes of image map to recognize difference - const sig1 = SHA1(generatedImage.bitmap.data); - const sig2 = SHA1(oldImage.bitmap.data); - if (sig1 !== sig2) { - saveJimpImageAndCompress(generatedImage, userId, res, next) - } else { - res.locals.items = {status: 'nothing to do'}; - next(); - } - }) - .catch((err) => { - saveJimpImageAndCompress(generatedImage, userId, res, next); - }) + .then((oldImage) => { + // compare hashes of image map to recognize difference + const sig1 = SHA1(generatedImage.bitmap.data); + const sig2 = SHA1(oldImage.bitmap.data); + if (sig1 !== sig2) { + saveJimpImageAndCompress(generatedImage, userId, res, next) + } else { + res.locals.items = {status: 'nothing to do'}; + next(); + } + }) + .catch((err) => { + saveJimpImageAndCompress(generatedImage, userId, res, next); + }) }; diff --git a/api/tools/util.js b/api/tools/util.js index e0ebbe7..d631c27 100644 --- a/api/tools/util.js +++ b/api/tools/util.js @@ -1,5 +1,10 @@ "use strict"; +const isSteamUUID = (input) => { + const steamUIDPattern = new RegExp("[0-9]{17}"); + return steamUIDPattern.test(input) +}; + const sortCollectionBy = (collection, key) => { collection.sort((a, b) => { a = a[key].toLowerCase(); @@ -44,6 +49,7 @@ const decimalToTimeString = (decimal) => { (seconds < 10 ? '0' + seconds : seconds); }; +exports.isSteamUUID = isSteamUUID; exports.sortCollection = sortCollectionBy; exports.playerArrayContains = playerArrayContains; exports.timeStringToDecimal = timeStringToDecimal; diff --git a/package.json b/package.json index 8640355..8b6fab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opt-cc", - "version": "1.6.12", + "version": "1.7.0", "author": "Florian Hartwich ", "private": true, "scripts": { diff --git a/static/src/app/admin/admin.component.css b/static/src/app/admin/admin.component.css index c35dcd3..4bc0441 100644 --- a/static/src/app/admin/admin.component.css +++ b/static/src/app/admin/admin.component.css @@ -1,5 +1,5 @@ .overview { - padding: 80px 0 0 10%!important; + padding: 80px 0 0 10% !important; } .trash { diff --git a/static/src/app/admin/admin.component.ts b/static/src/app/admin/admin.component.ts index 936f3b6..780d446 100644 --- a/static/src/app/admin/admin.component.ts +++ b/static/src/app/admin/admin.component.ts @@ -45,19 +45,19 @@ export class AdminComponent { } this.appUserService.updateUser(updateObject) - .subscribe(user => { - this.showSuccessLabel = true; - setTimeout(() => { - this.showSuccessLabel = false; - }, 2000) - }) + .subscribe(user => { + this.showSuccessLabel = true; + setTimeout(() => { + this.showSuccessLabel = false; + }, 2000) + }) } deleteUser(user) { if (confirm('Soll der Nutzer "' + user.username + '" wirklich gelöscht werden?')) { this.appUserService.deleteUser(user) - .subscribe((res) => { - }) + .subscribe((res) => { + }) } } diff --git a/static/src/app/app.component.css b/static/src/app/app.component.css index 2a8381f..7bb12bc 100644 --- a/static/src/app/app.component.css +++ b/static/src/app/app.component.css @@ -30,7 +30,7 @@ li { } .version-label { - display:block; + display: block; position: fixed; top: 32px; left: 106px; diff --git a/static/src/app/army/army-member.component.css b/static/src/app/army/army-member.component.css index 81b6206..2c149bf 100644 --- a/static/src/app/army/army-member.component.css +++ b/static/src/app/army/army-member.component.css @@ -8,7 +8,7 @@ .army-member-view-container { width: 90%; min-width: 870px; - margin:auto + margin: auto } .return-button { diff --git a/static/src/app/army/army-member.component.html b/static/src/app/army/army-member.component.html index 21f0e93..cf03a4d 100644 --- a/static/src/app/army/army-member.component.html +++ b/static/src/app/army/army-member.component.html @@ -2,7 +2,8 @@
< zurück zur Übersicht -

+

Auszeichnungen von {{user.username}}

diff --git a/static/src/app/army/army-member.component.ts b/static/src/app/army/army-member.component.ts index 19b0f7b..98f62de 100644 --- a/static/src/app/army/army-member.component.ts +++ b/static/src/app/army/army-member.component.ts @@ -41,16 +41,16 @@ export class ArmyMemberComponent { this.document.getElementById('right').setAttribute('style', CSSHelpers.getBackgroundCSS('../assets/bg.jpg')); this.subscription = this.route.params - .map(params => params['id']) - .filter(id => id != undefined) - .flatMap(id => this.userService.getUser(id)) - .subscribe(user => { - this.user = user; - this.signatureUrl = window.location.origin + '/resource/signature/' + user._id + '.png'; - this.awardingService.getUserAwardings(user._id).subscribe((awards => { - this.awards = awards; - })); - }); + .map(params => params['id']) + .filter(id => id != undefined) + .flatMap(id => this.userService.getUser(id)) + .subscribe(user => { + this.user = user; + this.signatureUrl = window.location.origin + '/resource/signature/' + user._id + '.png'; + this.awardingService.getUserAwardings(user._id).subscribe((awards => { + this.awards = awards; + })); + }); }; diff --git a/static/src/app/army/army.component.ts b/static/src/app/army/army.component.ts index b55a932..252f688 100644 --- a/static/src/app/army/army.component.ts +++ b/static/src/app/army/army.component.ts @@ -31,9 +31,9 @@ export class ArmyComponent { // init army data this.armyService.getArmy() - .subscribe(army => { - this.army = army; - }); + .subscribe(army => { + this.army = army; + }); }; ngOnDestroy() { diff --git a/static/src/app/decorations/decoration-list/decoration-list.component.html b/static/src/app/decorations/decoration-list/decoration-list.component.html index 05b0047..56f5a4b 100644 --- a/static/src/app/decorations/decoration-list/decoration-list.component.html +++ b/static/src/app/decorations/decoration-list/decoration-list.component.html @@ -23,10 +23,10 @@
+ [decoration]="decoration" + (decorationDelete)="deleteDecoration(decoration)" + (decorationSelected)="selectDecoration($event)" + [selected]="decoration._id == selectedDecorationId">
diff --git a/static/src/app/decorations/decoration-list/decoration-list.component.ts b/static/src/app/decorations/decoration-list/decoration-list.component.ts index 5397cab..e1d0e0f 100644 --- a/static/src/app/decorations/decoration-list/decoration-list.component.ts +++ b/static/src/app/decorations/decoration-list/decoration-list.component.ts @@ -35,17 +35,17 @@ export class DecorationListComponent implements OnInit { this.decorations$ = this.decorationService.decorations$; const paramsStream = this.route.queryParams - .map(params => decodeURI(params['query'] || '')) - .do(query => this.searchTerm.setValue(query)); + .map(params => decodeURI(params['query'] || '')) + .do(query => this.searchTerm.setValue(query)); const searchTermStream = this.searchTerm.valueChanges - .debounceTime(400) - .do(query => this.adjustBrowserUrl(query)); + .debounceTime(400) + .do(query => this.adjustBrowserUrl(query)); Observable.merge(paramsStream, searchTermStream) - .distinctUntilChanged() - .switchMap(query => this.decorationService.findDecorations(query, this.radioModel)) - .subscribe(); + .distinctUntilChanged() + .switchMap(query => this.decorationService.findDecorations(query, this.radioModel)) + .subscribe(); } openNewDecorationForm() { @@ -65,8 +65,8 @@ export class DecorationListComponent implements OnInit { if (confirm('Soll die Auszeichnung "' + decoration.name + '" (' + fraction + ') wirklich gelöscht werden?')) { this.decorationService.deleteDecoration(decoration) - .subscribe((res) => { - }) + .subscribe((res) => { + }) } } diff --git a/static/src/app/decorations/edit-decoration/edit-decoration.component.ts b/static/src/app/decorations/edit-decoration/edit-decoration.component.ts index f752c83..69ea836 100644 --- a/static/src/app/decorations/edit-decoration/edit-decoration.component.ts +++ b/static/src/app/decorations/edit-decoration/edit-decoration.component.ts @@ -35,13 +35,13 @@ export class EditDecorationComponent { ngOnInit() { this.subscription = this.route.params - .map(params => params['id']) - .filter(id => id != undefined) - .flatMap(id => this.decorationService.getDecoration(id)) - .subscribe(decoration => { - this.decoration = decoration; - this.imagePreviewSrc = 'resource/decoration/' + this.decoration._id + '.png?' + Date.now(); - }); + .map(params => params['id']) + .filter(id => id != undefined) + .flatMap(id => this.decorationService.getDecoration(id)) + .subscribe(decoration => { + this.decoration = decoration; + this.imagePreviewSrc = 'resource/decoration/' + this.decoration._id + '.png?' + Date.now(); + }); } ngOnDestroy() { @@ -64,9 +64,9 @@ export class EditDecorationComponent { if (this.fileList) { file = this.fileList[0]; this.decorationService.submitDecoration(this.decoration, file) - .subscribe(rank => { - this.router.navigate(['..'], {relativeTo: this.route}); - }) + .subscribe(rank => { + this.router.navigate(['..'], {relativeTo: this.route}); + }) } else { return window.alert(`Bild ist ein Pflichtfeld`); } @@ -76,16 +76,16 @@ export class EditDecorationComponent { } delete this.decoration['__v']; this.decorationService.submitDecoration(this.decoration, file) - .subscribe(rank => { - setTimeout(() => { - this.imagePreviewSrc = 'resource/decoration/' + this.decoration._id + '.png?' + Date.now(); - }, 300); - fileInput.value = ''; - this.showSuccessLabel = true; - setTimeout(() => { - this.showSuccessLabel = false; - }, 2000) - }) + .subscribe(rank => { + setTimeout(() => { + this.imagePreviewSrc = 'resource/decoration/' + this.decoration._id + '.png?' + Date.now(); + }, 300); + fileInput.value = ''; + this.showSuccessLabel = true; + setTimeout(() => { + this.showSuccessLabel = false; + }, 2000) + }) } } diff --git a/static/src/app/login/login.component.ts b/static/src/app/login/login.component.ts index f6fb1f8..8164280 100644 --- a/static/src/app/login/login.component.ts +++ b/static/src/app/login/login.component.ts @@ -35,18 +35,18 @@ export class LoginComponent implements OnInit { if (username.length > 0 && password.length > 0) { this.loading = true; this.loginService.login(username, password) - .subscribe( - data => { - this.router.navigate([this.returnUrl]); - }, - error => { - this.error = error._body; - this.showErrorLabel = true; - setTimeout(() => { - this.showErrorLabel = false; - }, 4000); - this.loading = false; - }); + .subscribe( + data => { + this.router.navigate([this.returnUrl]); + }, + error => { + this.error = error._body; + this.showErrorLabel = true; + setTimeout(() => { + this.showErrorLabel = false; + }, 4000); + this.loading = false; + }); } } } diff --git a/static/src/app/login/signup.component.ts b/static/src/app/login/signup.component.ts index 267c98c..52df89b 100644 --- a/static/src/app/login/signup.component.ts +++ b/static/src/app/login/signup.component.ts @@ -38,19 +38,19 @@ export class SignupComponent implements OnInit { if (username.length > 0 && password.length > 0 && secret.length > 0) { this.loading = true; this.loginService.signUp(username, password, secret) - .subscribe( - data => { - this.loading = false; - this.showSuccessLabel = true; - }, - error => { - this.error = error; - this.showErrorLabel = true; - setTimeout(() => { - this.showErrorLabel = false; - }, 4000); - this.loading = false; - }); + .subscribe( + data => { + this.loading = false; + this.showSuccessLabel = true; + }, + error => { + this.error = error; + this.showErrorLabel = true; + setTimeout(() => { + this.showErrorLabel = false; + }, 4000); + this.loading = false; + }); } } } diff --git a/static/src/app/models/model-interfaces.ts b/static/src/app/models/model-interfaces.ts index fce11f6..58b91e8 100644 --- a/static/src/app/models/model-interfaces.ts +++ b/static/src/app/models/model-interfaces.ts @@ -24,6 +24,7 @@ export interface Player { name?: string; warId?: War; kill?: number; + vehicle?: number; death?: number; friendlyFire?: number; revive?: number; diff --git a/static/src/app/ranks/edit-rank/edit-rank.component.ts b/static/src/app/ranks/edit-rank/edit-rank.component.ts index d1b50dd..96d0ac8 100644 --- a/static/src/app/ranks/edit-rank/edit-rank.component.ts +++ b/static/src/app/ranks/edit-rank/edit-rank.component.ts @@ -38,13 +38,13 @@ export class EditRankComponent { ngOnInit() { this.subscription = this.route.params - .map(params => params['id']) - .filter(id => id != undefined) - .flatMap(id => this.rankService.getRank(id)) - .subscribe(rank => { - this.rank = rank; - this.imagePreviewSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now(); - }); + .map(params => params['id']) + .filter(id => id != undefined) + .flatMap(id => this.rankService.getRank(id)) + .subscribe(rank => { + this.rank = rank; + this.imagePreviewSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now(); + }); } ngOnDestroy() { @@ -67,10 +67,10 @@ export class EditRankComponent { if (this.fileList) { file = this.fileList[0]; this.rankService.submitRank(this.rank, file) - .subscribe(rank => { - this.saved = true; - this.router.navigate(['..'], {relativeTo: this.route}); - }) + .subscribe(rank => { + this.saved = true; + this.router.navigate(['..'], {relativeTo: this.route}); + }) } else { return window.alert(`Bild ist ein Pflichtfeld`); } @@ -80,16 +80,16 @@ export class EditRankComponent { } delete this.rank['__v']; this.rankService.submitRank(this.rank, file) - .subscribe(rank => { - setTimeout(() => { - this.imagePreviewSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now(); - }, 300); - fileInput.value = ''; - this.showSuccessLabel = true; - setTimeout(() => { - this.showSuccessLabel = false; - }, 2000) - }) + .subscribe(rank => { + setTimeout(() => { + this.imagePreviewSrc = 'resource/rank/' + this.rank._id + '.png?' + Date.now(); + }, 300); + fileInput.value = ''; + this.showSuccessLabel = true; + setTimeout(() => { + this.showSuccessLabel = false; + }, 2000) + }) } } diff --git a/static/src/app/ranks/rank-list/rank-list.component.ts b/static/src/app/ranks/rank-list/rank-list.component.ts index ed8db72..1d2c9ca 100644 --- a/static/src/app/ranks/rank-list/rank-list.component.ts +++ b/static/src/app/ranks/rank-list/rank-list.component.ts @@ -36,17 +36,17 @@ export class RankListComponent implements OnInit { this.ranks$ = this.rankService.ranks$; const paramsStream = this.route.queryParams - .map(params => decodeURI(params['query'] || '')) - .do(query => this.searchTerm.setValue(query)); + .map(params => decodeURI(params['query'] || '')) + .do(query => this.searchTerm.setValue(query)); const searchTermStream = this.searchTerm.valueChanges - .debounceTime(400) - .do(query => this.adjustBrowserUrl(query)); + .debounceTime(400) + .do(query => this.adjustBrowserUrl(query)); Observable.merge(paramsStream, searchTermStream) - .distinctUntilChanged() - .switchMap(query => this.rankService.findRanks(query, this.radioModel)) - .subscribe(); + .distinctUntilChanged() + .switchMap(query => this.rankService.findRanks(query, this.radioModel)) + .subscribe(); } @@ -68,8 +68,8 @@ export class RankListComponent implements OnInit { const fraction = rank.fraction === 'OPFOR' ? Fraction.OPFOR : Fraction.BLUFOR; if (confirm('Soll der Rang ' + rank.name + ' (' + fraction + ') wirklich gelöscht werden?')) { this.rankService.deleteRank(rank) - .subscribe((res) => { - }) + .subscribe((res) => { + }) } } diff --git a/static/src/app/request/award/req-award.component.css b/static/src/app/request/award/req-award.component.css index 320c624..afba97b 100644 --- a/static/src/app/request/award/req-award.component.css +++ b/static/src/app/request/award/req-award.component.css @@ -1,6 +1,6 @@ .overview { - width: 100%!important; - margin-left: 25px!important; + width: 100% !important; + margin-left: 25px !important; } .decoration-preview { diff --git a/static/src/app/request/award/req-award.component.ts b/static/src/app/request/award/req-award.component.ts index 149e165..8db50a4 100644 --- a/static/src/app/request/award/req-award.component.ts +++ b/static/src/app/request/award/req-award.component.ts @@ -88,16 +88,16 @@ export class RequestAwardComponent { }; this.awardingService.requestAwarding(award).subscribe(() => { this.awardingService.getUserAwardings(this.user._id) - .subscribe(awards => { - this.awards = awards; - this.decoration = {_id: '0'}; - this.reason = previewImage.src = descriptionField.innerHTML = ''; - this.decoPreviewDisplay = 'none'; - this.showSuccessLabel = true; - setTimeout(() => { - this.showSuccessLabel = false; - }, 2000) - }) + .subscribe(awards => { + this.awards = awards; + this.decoration = {_id: '0'}; + this.reason = previewImage.src = descriptionField.innerHTML = ''; + this.decoPreviewDisplay = 'none'; + this.showSuccessLabel = true; + setTimeout(() => { + this.showSuccessLabel = false; + }, 2000) + }) }) } } diff --git a/static/src/app/request/confirm-award/confirm-award.component.css b/static/src/app/request/confirm-award/confirm-award.component.css index 68bab73..df217b2 100644 --- a/static/src/app/request/confirm-award/confirm-award.component.css +++ b/static/src/app/request/confirm-award/confirm-award.component.css @@ -1,5 +1,5 @@ .overview { - margin-left: 25px!important; + margin-left: 25px !important; } .decoration-preview { diff --git a/static/src/app/request/confirm-promotion/confirm-promotion.component.css b/static/src/app/request/confirm-promotion/confirm-promotion.component.css index 547e1a8..2ea383a 100644 --- a/static/src/app/request/confirm-promotion/confirm-promotion.component.css +++ b/static/src/app/request/confirm-promotion/confirm-promotion.component.css @@ -1,5 +1,5 @@ .overview { - margin-left: 25px!important; + margin-left: 25px !important; } .decoration-preview { diff --git a/static/src/app/request/promotion/req-promotion.component.css b/static/src/app/request/promotion/req-promotion.component.css index 936e755..58d23c6 100644 --- a/static/src/app/request/promotion/req-promotion.component.css +++ b/static/src/app/request/promotion/req-promotion.component.css @@ -1,5 +1,5 @@ .overview { - margin-left: 25px!important; + margin-left: 25px !important; } .decoration-preview { diff --git a/static/src/app/services/app-user-service/app-user.service.ts b/static/src/app/services/app-user-service/app-user.service.ts index a1ce4ea..7817ef7 100644 --- a/static/src/app/services/app-user-service/app-user.service.ts +++ b/static/src/app/services/app-user-service/app-user.service.ts @@ -19,10 +19,10 @@ export class AppUserService { getUsers() { this.http.get(this.config.apiAppUserPath) - .map(res => res.json()) - .do((users) => { - this.appUserStore.dispatch({type: LOAD, data: users}); - }).subscribe(_ => { + .map(res => res.json()) + .do((users) => { + this.appUserStore.dispatch({type: LOAD, data: users}); + }).subscribe(_ => { }); return this.users$; @@ -30,18 +30,18 @@ export class AppUserService { updateUser(user: AppUser) { return this.http.patch(this.config.apiAppUserPath + user._id, user) - .map(res => res.json()) - .do(savedUser => { - const action = {type: EDIT, data: savedUser}; - this.appUserStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedUser => { + const action = {type: EDIT, data: savedUser}; + this.appUserStore.dispatch(action); + }); } deleteUser(user) { return this.http.delete(this.config.apiAppUserPath + user._id) - .do(res => { - this.appUserStore.dispatch({type: REMOVE, data: user}); - }); + .do(res => { + this.appUserStore.dispatch({type: REMOVE, data: user}); + }); } } diff --git a/static/src/app/services/app-user-service/login-service.ts b/static/src/app/services/app-user-service/login-service.ts index 54ad27f..1cfc12c 100644 --- a/static/src/app/services/app-user-service/login-service.ts +++ b/static/src/app/services/app-user-service/login-service.ts @@ -18,25 +18,25 @@ export class LoginService { login(username: string, password: string) { return this.http.post(this.config.apiAuthenticationPath, {username: username, password: password}) - .map((response: Response) => { - // login successful if there's a jwt token in the response - let user = response.json(); - if (user && user.token) { - // store user details and jwt token in cookie - this.cookieService.set('currentUser', JSON.stringify(user)); - if (user.permission >= 2) { - const fraction = user.squad.fraction; - this.awardingService.checkUnprocessedAwards(fraction); - this.promotionService.checkUnconfirmedPromotions(fraction); - } - } - }); + .map((response: Response) => { + // login successful if there's a jwt token in the response + let user = response.json(); + if (user && user.token) { + // store user details and jwt token in cookie + this.cookieService.set('currentUser', JSON.stringify(user)); + if (user.permission >= 2) { + const fraction = user.squad.fraction; + this.awardingService.checkUnprocessedAwards(fraction); + this.promotionService.checkUnconfirmedPromotions(fraction); + } + } + }); } signUp(username: string, password: string, secret: string) { return this.http.post(this.config.apiSignupPath, {username: username, password: password, secret: secret}) - .map((response: Response) => { - }); + .map((response: Response) => { + }); } logout() { diff --git a/static/src/app/services/army-management/awarding.service.ts b/static/src/app/services/army-management/awarding.service.ts index f0e43f4..aa2d6a8 100644 --- a/static/src/app/services/army-management/awarding.service.ts +++ b/static/src/app/services/army-management/awarding.service.ts @@ -14,7 +14,7 @@ export class AwardingService { getUnconfirmedAwards(fraction?: string) { return this.http.get(this.config.apiAwardPath + '?inProgress=true&fractFilter=' + fraction) - .map(res => res.json()) + .map(res => res.json()) } checkUnprocessedAwards(fraction?: string) { @@ -30,7 +30,7 @@ export class AwardingService { */ getUserAwardings(userId: string) { return this.http.get(this.config.apiAwardPath + '?userId=' + userId) - .map(res => res.json()) + .map(res => res.json()) } addAwarding(award: Award) { @@ -39,7 +39,7 @@ export class AwardingService { updateAward(award) { return this.http.patch(this.config.apiAwardPath + '/' + award._id, award) - .map(res => res.json()) + .map(res => res.json()) } requestAwarding(award: Award) { diff --git a/static/src/app/services/army-management/decoration.service.ts b/static/src/app/services/army-management/decoration.service.ts index 3ae388c..f7dae4a 100644 --- a/static/src/app/services/army-management/decoration.service.ts +++ b/static/src/app/services/army-management/decoration.service.ts @@ -27,10 +27,10 @@ export class DecorationService { } this.http.get(this.config.apiDecorationPath, searchParams) - .map(res => res.json()) - .do((squads) => { - this.decorationStore.dispatch({type: LOAD, data: squads}); - }).subscribe(_ => { + .map(res => res.json()) + .do((squads) => { + this.decorationStore.dispatch({type: LOAD, data: squads}); + }).subscribe(_ => { }); return this.decorations$; @@ -38,7 +38,7 @@ export class DecorationService { getDecoration(id: number | string): Observable { return this.http.get(this.config.apiDecorationPath + id) - .map(res => res.json()); + .map(res => res.json()); } /** @@ -79,19 +79,19 @@ export class DecorationService { }); return this.http.request(requestUrl, options) - .map(res => res.json()) - .do(savedDecoration => { - const action = {type: accessType, data: savedDecoration}; - this.decorationStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedDecoration => { + const action = {type: accessType, data: savedDecoration}; + this.decorationStore.dispatch(action); + }); } deleteDecoration(decoration: Decoration) { return this.http.delete(this.config.apiDecorationPath + decoration._id) - .do(res => { - this.decorationStore.dispatch({type: REMOVE, data: decoration}); - }); + .do(res => { + this.decorationStore.dispatch({type: REMOVE, data: decoration}); + }); } } diff --git a/static/src/app/services/army-management/promotion.service.ts b/static/src/app/services/army-management/promotion.service.ts index 52fea32..0fd9a23 100644 --- a/static/src/app/services/army-management/promotion.service.ts +++ b/static/src/app/services/army-management/promotion.service.ts @@ -14,7 +14,7 @@ export class PromotionService { getUnconfirmedPromotions(fraction?: string) { return this.http.get(this.config.apiPromotionPath + '?inProgress=true&fractFilter=' + fraction) - .map(res => res.json()) + .map(res => res.json()) } checkUnconfirmedPromotions(fraction?: string) { @@ -27,7 +27,7 @@ export class PromotionService { getSquadPromotions(squadId: string) { return this.http.get(this.config.apiPromotionPath + '?squadId=' + squadId) - .map(res => res.json()) + .map(res => res.json()) } requestPromotion(promotion: Promotion) { @@ -36,7 +36,7 @@ export class PromotionService { updatePromotion(promotion) { return this.http.patch(this.config.apiPromotionPath + '/' + promotion._id, promotion) - .map(res => res.json()) + .map(res => res.json()) } deletePromotion(promotionId) { diff --git a/static/src/app/services/army-management/rank.service.ts b/static/src/app/services/army-management/rank.service.ts index 2c201e8..05994f2 100644 --- a/static/src/app/services/army-management/rank.service.ts +++ b/static/src/app/services/army-management/rank.service.ts @@ -28,10 +28,10 @@ export class RankService { } this.http.get(this.config.apiRankPath, searchParams) - .map(res => res.json()) - .do((ranks) => { - this.rankStore.dispatch({type: LOAD, data: ranks}); - }).subscribe(_ => { + .map(res => res.json()) + .do((ranks) => { + this.rankStore.dispatch({type: LOAD, data: ranks}); + }).subscribe(_ => { }); return this.ranks$; @@ -39,7 +39,7 @@ export class RankService { getRank(id: number | string): Observable { return this.http.get(this.config.apiRankPath + id) - .map(res => res.json()); + .map(res => res.json()); } /** @@ -80,14 +80,14 @@ export class RankService { }); return this.http.request(requestUrl, options) - .map(res => res.json()) - .do(savedRank => { - const action = {type: accessType, data: savedRank}; - // leave some time to save image file before accessing it through listview - setTimeout(() => { - this.rankStore.dispatch(action); - }, 300); - }); + .map(res => res.json()) + .do(savedRank => { + const action = {type: accessType, data: savedRank}; + // leave some time to save image file before accessing it through listview + setTimeout(() => { + this.rankStore.dispatch(action); + }, 300); + }); } /** @@ -103,11 +103,11 @@ export class RankService { }); return this.http.request(this.config.apiRankPath + rank._id, options) - .map(res => res.json()) - .do(savedRank => { - const action = {type: EDIT, data: savedRank}; - this.rankStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedRank => { + const action = {type: EDIT, data: savedRank}; + this.rankStore.dispatch(action); + }); } /** @@ -125,18 +125,18 @@ export class RankService { // provide token as query value, because there is no actual body // and x-access-token is ignored in multipart request return this.http.patch(this.config.apiRankPath + rankId, formData) - .map(res => res.json()) - .do(savedDecoration => { - const action = {type: EDIT, data: savedDecoration}; - this.rankStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedDecoration => { + const action = {type: EDIT, data: savedDecoration}; + this.rankStore.dispatch(action); + }); } deleteRank(rank: Rank) { return this.http.delete(this.config.apiRankPath + rank._id) - .do(res => { - this.rankStore.dispatch({type: REMOVE, data: rank}); - }); + .do(res => { + this.rankStore.dispatch({type: REMOVE, data: rank}); + }); } diff --git a/static/src/app/services/army-management/squad.service.ts b/static/src/app/services/army-management/squad.service.ts index d8b09b0..f1d20aa 100644 --- a/static/src/app/services/army-management/squad.service.ts +++ b/static/src/app/services/army-management/squad.service.ts @@ -24,10 +24,10 @@ export class SquadService { searchParams.append('fractFilter', fractionFilter); this.http.get(this.config.apiSquadPath, searchParams) - .map(res => res.json()) - .do((squads) => { - this.squadStore.dispatch({type: LOAD, data: squads}); - }).subscribe(_ => { + .map(res => res.json()) + .do((squads) => { + this.squadStore.dispatch({type: LOAD, data: squads}); + }).subscribe(_ => { }); return this.squads$; @@ -36,7 +36,7 @@ export class SquadService { getSquad(id: number | string): Observable { return this.http.get(this.config.apiSquadPath + id) - .map(res => res.json()); + .map(res => res.json()); } @@ -77,18 +77,18 @@ export class SquadService { }); return this.http.request(requestUrl, options) - .map(res => res.json()) - .do(savedSquad => { - const action = {type: accessType, data: savedSquad}; - this.squadStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedSquad => { + const action = {type: accessType, data: savedSquad}; + this.squadStore.dispatch(action); + }); } deleteSquad(squad: Squad) { return this.http.delete(this.config.apiSquadPath + squad._id) - .do(res => { - this.squadStore.dispatch({type: REMOVE, data: squad}); - }); + .do(res => { + this.squadStore.dispatch({type: REMOVE, data: squad}); + }); } } diff --git a/static/src/app/services/army-management/user.service.ts b/static/src/app/services/army-management/user.service.ts index a1996d2..af907b3 100644 --- a/static/src/app/services/army-management/user.service.ts +++ b/static/src/app/services/army-management/user.service.ts @@ -31,46 +31,47 @@ export class UserService { searchParams.append('limit', limit); searchParams.append('offset', offset); this.http.get(this.config.apiUserPath, searchParams) - .do((res) => { - let headerCount = parseInt(res.headers.get('x-total-count')); - if (headerCount) { - this.totalCount = headerCount; - } - }).map(res => res.json()).do((users) => { + .do((res) => { + let headerCount = parseInt(res.headers.get('x-total-count')); + if (headerCount) { + this.totalCount = headerCount; + } + }).map(res => res.json()).do((users) => { this.userStore.dispatch({type: action, data: users}); - }).subscribe(_ => {}); + }).subscribe(_ => { + }); return this.users$; } getUser(_id: number | string): Observable { return this.http.get(this.config.apiUserPath + _id) - .map(res => res.json()); + .map(res => res.json()); } submitUser(user) { return this.http.post(this.config.apiUserPath, user) - .map(res => res.json()) - .do(savedUser => { - const action = {type: ADD, data: savedUser}; - this.userStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedUser => { + const action = {type: ADD, data: savedUser}; + this.userStore.dispatch(action); + }); } updateUser(user) { return this.http.put(this.config.apiUserPath + user._id, user) - .map(res => res.json()) - .do(savedUser => { - const action = {type: EDIT, data: savedUser}; - this.userStore.dispatch(action); - }); + .map(res => res.json()) + .do(savedUser => { + const action = {type: EDIT, data: savedUser}; + this.userStore.dispatch(action); + }); } deleteUser(user) { return this.http.delete(this.config.apiUserPath + user._id) - .do(res => { - this.userStore.dispatch({type: REMOVE, data: user}); - }); + .do(res => { + this.userStore.dispatch({type: REMOVE, data: user}); + }); } } diff --git a/static/src/app/services/army-service/army.service.ts b/static/src/app/services/army-service/army.service.ts index e31c023..5cb0c3a 100644 --- a/static/src/app/services/army-service/army.service.ts +++ b/static/src/app/services/army-service/army.service.ts @@ -12,7 +12,7 @@ export class ArmyService { getArmy() { return this.http.get(this.config.apiOverviewPath) - .map(res => res.json()); + .map(res => res.json()); } } diff --git a/static/src/app/services/logs/campaign.service.ts b/static/src/app/services/logs/campaign.service.ts index 392bb85..151dbaf 100644 --- a/static/src/app/services/logs/campaign.service.ts +++ b/static/src/app/services/logs/campaign.service.ts @@ -14,17 +14,17 @@ export class CampaignService { getAllCampaigns() { return this.http.get(this.config.apiWarPath) - .map(res => res.json()) + .map(res => res.json()) } submitCampaign(campaign: Campaign) { return this.http.post(this.config.apiCampaignPath, campaign) - .map(res => res.json()) + .map(res => res.json()) } deleteCampaign(id: string) { return this.http.delete(this.config.apiCampaignPath + '/' + id) - .map(res => res.json()) + .map(res => res.json()) } } diff --git a/static/src/app/services/logs/logs.service.ts b/static/src/app/services/logs/logs.service.ts index f956b81..92ea958 100644 --- a/static/src/app/services/logs/logs.service.ts +++ b/static/src/app/services/logs/logs.service.ts @@ -12,28 +12,28 @@ export class LogsService { getFullLog(warId: string) { return this.http.get(this.config.apiLogsPath + '/' + warId) - .map(res => res.json()) + .map(res => res.json()) } getBudgetLogs(warId: string, fraction = '') { const params = new URLSearchParams(); params.append('fraction', fraction); return this.http.get(this.config.apiLogsPath + '/' + warId + '/budget', params) - .map(res => res.json()) + .map(res => res.json()) } getRespawnLogs(warId: string, playerName = '') { const params = new URLSearchParams(); params.append('player', playerName); return this.http.get(this.config.apiLogsPath + '/' + warId + '/respawn', params) - .map(res => res.json()) + .map(res => res.json()) } getPointsLogs(warId: string, fraction = '') { const params = new URLSearchParams(); params.append('fraction', fraction); return this.http.get(this.config.apiLogsPath + '/' + warId + '/points', params) - .map(res => res.json()) + .map(res => res.json()) } getReviveLogs(warId: string, medicName = '', patientName = '', fraction = '', stabilizedOnly = false, reviveOnly = false) { @@ -44,7 +44,7 @@ export class LogsService { params.append('stabilized', stabilizedOnly ? 'true' : ''); params.append('revive', reviveOnly ? 'true' : ''); return this.http.get(this.config.apiLogsPath + '/' + warId + '/revive', params) - .map(res => res.json()) + .map(res => res.json()) } getKillLogs(warId: string, shooterName = '', targetName = '', fraction = '', friendlyFireOnly = false, notFriendlyFireOnly = false) { @@ -55,7 +55,7 @@ export class LogsService { params.append('friendlyFire', friendlyFireOnly ? 'true' : ''); params.append('noFriendlyFire', notFriendlyFireOnly ? 'true' : ''); return this.http.get(this.config.apiLogsPath + '/' + warId + '/kills', params) - .map(res => res.json()) + .map(res => res.json()) } getTransportLogs(warId: string, driverName = '', passengerName = '', fraction = '') { @@ -64,7 +64,7 @@ export class LogsService { params.append('passenger', passengerName); params.append('fraction', fraction); return this.http.get(this.config.apiLogsPath + '/' + warId + '/transport', params) - .map(res => res.json()) + .map(res => res.json()) } getFlagLogs(warId: string, playerName = '', fraction = '', captureOnly = false, defendOnly = false) { @@ -74,7 +74,7 @@ export class LogsService { params.append('capture', captureOnly ? 'true' : ''); params.append('defend', defendOnly ? 'true' : ''); return this.http.get(this.config.apiLogsPath + '/' + warId + '/flag', params) - .map(res => res.json()) + .map(res => res.json()) } } diff --git a/static/src/app/services/logs/player.service.ts b/static/src/app/services/logs/player.service.ts index 8fa3b53..a7dbf91 100644 --- a/static/src/app/services/logs/player.service.ts +++ b/static/src/app/services/logs/player.service.ts @@ -11,12 +11,12 @@ export class PlayerService { getCampaignPlayer(campaignId: string, playerName: string) { return this.http.get(this.config.apiPlayersPath + '/single/' + campaignId + '/' + playerName) - .map(res => res.json()) + .map(res => res.json()) } getCampaignHighscore(campaignId: string) { return this.http.get(this.config.apiPlayersPath + '/ranking/' + campaignId) - .map(res => res.json()) + .map(res => res.json()) } } diff --git a/static/src/app/services/logs/war.service.ts b/static/src/app/services/logs/war.service.ts index ee17911..e3e9696 100644 --- a/static/src/app/services/logs/war.service.ts +++ b/static/src/app/services/logs/war.service.ts @@ -12,7 +12,7 @@ export class WarService { getWar(warId: string) { return this.http.get(this.config.apiWarPath + '/' + warId) - .map(res => res.json()) + .map(res => res.json()) } @@ -30,12 +30,12 @@ export class WarService { } return this.http.post(this.config.apiWarPath, body) - .map(res => res.json()) + .map(res => res.json()) } deleteWar(id: string) { return this.http.delete(this.config.apiWarPath + '/' + id) - .map(res => res.json()) + .map(res => res.json()) } } diff --git a/static/src/app/squads/edit-squad/edit-squad.component.ts b/static/src/app/squads/edit-squad/edit-squad.component.ts index 3a7d4fa..89e792a 100644 --- a/static/src/app/squads/edit-squad/edit-squad.component.ts +++ b/static/src/app/squads/edit-squad/edit-squad.component.ts @@ -38,13 +38,13 @@ export class EditSquadComponent { ngOnInit() { this.subscription = this.route.params - .map(params => params['id']) - .filter(id => id != undefined) - .flatMap(id => this.squadService.getSquad(id)) - .subscribe(squad => { - this.squad = squad; - this.imagePreviewSrc = 'resource/squad/' + this.squad._id + '.png?' + Date.now(); - }); + .map(params => params['id']) + .filter(id => id != undefined) + .flatMap(id => this.squadService.getSquad(id)) + .subscribe(squad => { + this.squad = squad; + this.imagePreviewSrc = 'resource/squad/' + this.squad._id + '.png?' + Date.now(); + }); } ngOnDestroy() { @@ -67,10 +67,10 @@ export class EditSquadComponent { if (this.fileList) { file = this.fileList[0]; this.squadService.submitSquad(this.squad, file) - .subscribe(rank => { - this.saved = true; - this.router.navigate(['..'], {relativeTo: this.route}); - }) + .subscribe(rank => { + this.saved = true; + this.router.navigate(['..'], {relativeTo: this.route}); + }) } else { return window.alert(`Bild ist ein Pflichtfeld`); } @@ -80,16 +80,16 @@ export class EditSquadComponent { } delete this.squad['__v']; this.squadService.submitSquad(this.squad, file) - .subscribe(rank => { - setTimeout(() => { - this.imagePreviewSrc = 'resource/squad/' + this.squad._id + '.png?' + Date.now(); - }, 300); - fileInput.value = ''; - this.showSuccessLabel = true; - setTimeout(() => { - this.showSuccessLabel = false; - }, 2000) - }) + .subscribe(rank => { + setTimeout(() => { + this.imagePreviewSrc = 'resource/squad/' + this.squad._id + '.png?' + Date.now(); + }, 300); + fileInput.value = ''; + this.showSuccessLabel = true; + setTimeout(() => { + this.showSuccessLabel = false; + }, 2000) + }) } } diff --git a/static/src/app/squads/squad-list/squad-list.component.ts b/static/src/app/squads/squad-list/squad-list.component.ts index ca54e1e..31ae8cb 100644 --- a/static/src/app/squads/squad-list/squad-list.component.ts +++ b/static/src/app/squads/squad-list/squad-list.component.ts @@ -36,17 +36,17 @@ export class SquadListComponent implements OnInit { this.squads$ = this.squadService.squads$; const paramsStream = this.route.queryParams - .map(params => decodeURI(params['query'] || '')) - .do(query => this.searchTerm.setValue(query)); + .map(params => decodeURI(params['query'] || '')) + .do(query => this.searchTerm.setValue(query)); const searchTermStream = this.searchTerm.valueChanges - .debounceTime(400) - .do(query => this.adjustBrowserUrl(query)); + .debounceTime(400) + .do(query => this.adjustBrowserUrl(query)); Observable.merge(paramsStream, searchTermStream) - .distinctUntilChanged() - .switchMap(query => this.squadService.findSquads(query, this.radioModel)) - .subscribe(); + .distinctUntilChanged() + .switchMap(query => this.squadService.findSquads(query, this.radioModel)) + .subscribe(); } @@ -64,8 +64,8 @@ export class SquadListComponent implements OnInit { const fraction = squad.fraction === 'OPFOR' ? Fraction.OPFOR : Fraction.BLUFOR; if (confirm('Soll das Squad "' + squad.name + '" (' + fraction + ') wirklich gelöscht werden?')) { this.squadService.deleteSquad(squad) - .subscribe((res) => { - }) + .subscribe((res) => { + }) } } diff --git a/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html b/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html index 3f0e0e8..0d07b97 100644 --- a/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html +++ b/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html @@ -81,6 +81,26 @@
+
+ + +
+
{ - this.campaignPlayer = campaignPlayer; - this.killData = this.assignData(this.yAxisKill, "kill"); - this.friendlyFireData = this.assignData(this.yAxisFriendlyFire, "friendlyFire"); - this.deathData = this.assignData(this.yAxisDeath, "death"); - this.respawnData = this.assignData(this.yAxisRespawn, "respawn"); - this.reviveData = this.assignData(this.yAxisRevive, "revive"); - this.captureData = this.assignData(this.yAxisCapture, "flagTouch"); + .subscribe(campaignPlayer => { + this.campaignPlayer = campaignPlayer; + this.killData = this.assignData(this.yAxisKill, "kill"); + this.vehicleKillData = this.assignData(this.yAxisVehicleKill, "vehicle"); + this.friendlyFireData = this.assignData(this.yAxisFriendlyFire, "friendlyFire"); + this.deathData = this.assignData(this.yAxisDeath, "death"); + this.respawnData = this.assignData(this.yAxisRespawn, "respawn"); + this.reviveData = this.assignData(this.yAxisRevive, "revive"); + this.captureData = this.assignData(this.yAxisCapture, "flagTouch"); - this.kdRatio = parseFloat((this.totalKills / (this.totalDeath === 0 ? 1 : this.totalDeath)).toFixed(2)); - if (this.kdRatio > 1) this.maxKd = this.kdRatio * 1.7; + this.kdRatio = parseFloat((this.totalKills / (this.totalDeath === 0 ? 1 : this.totalDeath)).toFixed(2)); + if (this.kdRatio > 1) this.maxKd = this.kdRatio * 1.7; - this.respawnDeathRatio = parseFloat((this.totalRespawn / (this.totalDeath === 0 ? 1 : this.totalDeath)).toFixed(2)); + this.respawnDeathRatio = parseFloat((this.totalRespawn / (this.totalDeath === 0 ? 1 : this.totalDeath)).toFixed(2)); - this.sumData = [ - { - name: this.yAxisKill, - value: this.totalKills - }, - { - name: this.yAxisFriendlyFire, - value: this.totalFriendlyFire - }, - { - name: this.yAxisDeath, - value: this.totalDeath - }, - { - name: this.yAxisRespawn, - value: this.totalRespawn - }, - { - name: this.yAxisRevive, - value: this.totalRevive - }, - { - name: this.yAxisCapture, - value: this.totalCapture - } - ]; + this.sumData = [ + { + name: this.yAxisKill, + value: this.totalKills + }, + { + name: this.yAxisFriendlyFire, + value: this.totalFriendlyFire + }, + { + name: this.yAxisVehicleKill, + value: this.totalVehicleKills + }, + { + name: this.yAxisDeath, + value: this.totalDeath + }, + { + name: this.yAxisRespawn, + value: this.totalRespawn + }, + { + name: this.yAxisRevive, + value: this.totalRevive + }, + { + name: this.yAxisCapture, + value: this.totalCapture + } + ]; - Object.assign(this, [this.sumData, this.killData, this.friendlyFireData, this.deathData, this.respawnData, this.reviveData, this.captureData]); - }); + Object.assign(this, [this.sumData, this.killData, this.friendlyFireData, this.vehicleKillData, this.deathData, this.respawnData, this.reviveData, this.captureData]); + }); } private assignData(label, field) { @@ -138,12 +147,12 @@ export class CampaignPlayerDetailComponent { let total = 0; for (let i = 0; i < playerLength; i++) { const warDateString = ChartUtils.getShortDateString(this.campaignPlayer.players[i].warId.date); - const warKills = this.campaignPlayer.players[i][field]; + const value = this.campaignPlayer.players[i][field]; killObj.series.push({ name: warDateString, - value: warKills + value: value }); - total += warKills; + total += value; } switch (field) { case 'kill': @@ -154,6 +163,10 @@ export class CampaignPlayerDetailComponent { this.friendlyFireRefLines.push({value: total / playerLength, name: this.avgLabel}); this.totalFriendlyFire = total; break; + case 'vehicle': + this.vehicleKillRefLines.push({value: total / playerLength, name: this.avgLabel}); + this.totalVehicleKills = total; + break; case 'death': this.deathRefLines.push({value: total / playerLength, name: this.avgLabel}); this.totalDeath = total; diff --git a/static/src/app/statistic/campaign-submit/campaign-submit.component.ts b/static/src/app/statistic/campaign-submit/campaign-submit.component.ts index 59c67cf..45c5453 100644 --- a/static/src/app/statistic/campaign-submit/campaign-submit.component.ts +++ b/static/src/app/statistic/campaign-submit/campaign-submit.component.ts @@ -27,13 +27,13 @@ export class CampaignSubmitComponent { saveCampaign() { this.campaignService.submitCampaign(this.campaign) - .subscribe(campaign => { - this.router.navigate(['../overview/' + campaign._id], {relativeTo: this.route}); - }, - error => { - this.error = error._body.error.message; - this.showErrorLabel = true; - }); + .subscribe(campaign => { + this.router.navigate(['../overview/' + campaign._id], {relativeTo: this.route}); + }, + error => { + this.error = error._body.error.message; + this.showErrorLabel = true; + }); } cancel() { diff --git a/static/src/app/statistic/highscore/highscore.component.css b/static/src/app/statistic/highscore/highscore.component.css index 959c515..fb0eac8 100644 --- a/static/src/app/statistic/highscore/highscore.component.css +++ b/static/src/app/statistic/highscore/highscore.component.css @@ -18,12 +18,10 @@ ngx-datatable { float: left; border: solid #dfdfdf 1px; border-radius: 10px 10px 2px 2px; - border-right-style: none; - border-top-style: none; } :host /deep/ .datatable-header { - width: 350px!important; + width: 350px !important; background: #222222; font-weight: 700; border-radius: 10px 10px 0 0; @@ -74,5 +72,4 @@ ngx-datatable { background: #4b4b4b; -webkit-box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.5); } - /* Table Scrollbar END */ diff --git a/static/src/app/statistic/highscore/highscore.component.html b/static/src/app/statistic/highscore/highscore.component.html index 1e4297d..7bb51f6 100644 --- a/static/src/app/statistic/highscore/highscore.component.html +++ b/static/src/app/statistic/highscore/highscore.component.html @@ -36,6 +36,48 @@ + + + + + + {{value}} + + + + + + + + + + + + {{value}} + + + + + + - - - - - - {{value}} - - - - - - params['id']) - .subscribe((id) => { - this.id = id; - if (this.campaignService.campaigns) { - this.initData(); - } else { - this.campaignService.getAllCampaigns().subscribe(campaigns => { - this.initData() - }) - } - }); + .map(params => params['id']) + .subscribe((id) => { + this.id = id; + if (this.campaignService.campaigns) { + this.initData(); + } else { + this.campaignService.getAllCampaigns().subscribe(campaigns => { + this.initData() + }) + } + }); const searchTermStream = this.searchTerm.valueChanges.debounceTime(400); Observable.merge(searchTermStream) - .distinctUntilChanged().map(query => this.filterPlayers()) - .subscribe(); + .distinctUntilChanged().map(query => this.filterPlayers()) + .subscribe(); } initData() { this.title = this.campaignService.campaigns - .filter(camp => camp._id === this.id).pop().title; + .filter(camp => camp._id === this.id).pop().title; this.playerService.getCampaignHighscore(this.id).subscribe(players => { this.players = players; @@ -86,6 +86,7 @@ export class StatisticHighScoreComponent { this.players = { kill: this.filterPlayerAttribute('kill'), friendlyFire: this.filterPlayerAttribute('friendlyFire'), + vehicle: this.filterPlayerAttribute('vehicle'), death: this.filterPlayerAttribute('death'), respawn: this.filterPlayerAttribute('respawn'), revive: this.filterPlayerAttribute('revive'), diff --git a/static/src/app/statistic/overview/stats-overview.component.ts b/static/src/app/statistic/overview/stats-overview.component.ts index a7eb8a4..c7430d0 100644 --- a/static/src/app/statistic/overview/stats-overview.component.ts +++ b/static/src/app/statistic/overview/stats-overview.component.ts @@ -43,17 +43,17 @@ export class StatisticOverviewComponent { ngOnInit() { this.route.params - .map(params => params['id']) - .subscribe((id) => { - this.id = id; - if (this.campaignService.campaigns) { - this.initWars(this.campaignService.campaigns); - } else { - this.campaignService.getAllCampaigns().subscribe(campaigns => { - this.initWars(campaigns); - }) - } - }); + .map(params => params['id']) + .subscribe((id) => { + this.id = id; + if (this.campaignService.campaigns) { + this.initWars(this.campaignService.campaigns); + } else { + this.campaignService.getAllCampaigns().subscribe(campaigns => { + this.initWars(campaigns); + }) + } + }); } initWars(campaigns) { diff --git a/static/src/app/statistic/war-detail/fraction-stats/fraction-stats.component.html b/static/src/app/statistic/war-detail/fraction-stats/fraction-stats.component.html index dec5e6c..7a56849 100644 --- a/static/src/app/statistic/war-detail/fraction-stats/fraction-stats.component.html +++ b/static/src/app/statistic/war-detail/fraction-stats/fraction-stats.component.html @@ -28,6 +28,8 @@ btnRadio="{{labelKill}}">{{labelKill}} + diff --git a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts index ae07c5b..2f2676c 100644 --- a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts +++ b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts @@ -1,6 +1,7 @@ -import {Component, EventEmitter, SimpleChanges} from "@angular/core"; +import {Component, ElementRef, EventEmitter, SimpleChanges} from "@angular/core"; import {War} from "../../../models/model-interfaces"; import {Fraction} from "../../../utils/fraction.enum"; +import {PlayerUtils} from "../../../utils/player-utils"; @Component({ selector: 'scoreboard', @@ -17,6 +18,8 @@ export class ScoreboardComponent { war: War; + isSteamUUID = PlayerUtils.isSteamUUID; + fractionFilterSelected: string; cellHeight = 40; @@ -30,19 +33,22 @@ export class ScoreboardComponent { sortDescending: 'glyphicon glyphicon-triangle-bottom', }; - constructor() { + constructor(private elRef: ElementRef) { } - ngOnInit() { - } - - selectPlayerDetail(view: number, playerName: string) { - this.playerTabSwitch.emit({view: view, player: playerName}) + selectPlayerDetail(view: number, player) { + this.playerTabSwitch.emit({ + view: view, + player: player + }) } ngOnChanges(changes: SimpleChanges) { if (changes.war) { this.rows = changes.war.currentValue.players; + this.elRef.nativeElement + .querySelector('.datatable-body') + .scrollTo(0, 0); } if (changes.fractionFilterSelected) { this.filterPlayersByFraction(this.fractionFilterSelected) diff --git a/static/src/app/statistic/war-detail/war-detail.component.css b/static/src/app/statistic/war-detail/war-detail.component.css index d104953..9e60f76 100644 --- a/static/src/app/statistic/war-detail/war-detail.component.css +++ b/static/src/app/statistic/war-detail/war-detail.component.css @@ -19,6 +19,9 @@ } .nav-tabs { + width: 980px; + margin: auto; + clear: both; border-bottom: 0; } diff --git a/static/src/app/statistic/war-detail/war-detail.component.html b/static/src/app/statistic/war-detail/war-detail.component.html index ddcf567..f6b6452 100644 --- a/static/src/app/statistic/war-detail/war-detail.component.html +++ b/static/src/app/statistic/war-detail/war-detail.component.html @@ -50,7 +50,7 @@
-