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/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..a999fd9 100644 --- a/api/routes/logs.js +++ b/api/routes/logs.js @@ -34,122 +34,122 @@ 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 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) + }; - 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..731bda1 100644 --- a/api/routes/players.js +++ b/api/routes/players.js @@ -18,95 +18,103 @@ 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 = []; + 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); + }); - 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; - } + 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; + } - res.locals.items = { - kill: getSortedField('kill'), - death: getSortedField('death'), - friendlyFire: getSortedField('friendlyFire'), - revive: getSortedField('revive'), - respawn: getSortedField('respawn'), - flagTouch: getSortedField('flagTouch') - }; - next(); - }) - }) - }) + res.locals.items = { + kill: getSortedField('kill'), + death: getSortedField('death'), + friendlyFire: getSortedField('friendlyFire'), + revive: getSortedField('revive'), + respawn: getSortedField('respawn'), + flagTouch: getSortedField('flagTouch') + }; + next(); + }) + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .all( + routerHandling.httpMethodNotAllowed + ); 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(); - }) - }) - }) - }) + .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(); + }) + }) + }) + }) - .all( - routerHandling.httpMethodNotAllowed - ); + .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..a253acf 100644 --- a/api/routes/wars.js +++ b/api/routes/wars.js @@ -38,87 +38,88 @@ 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 () { + 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 clean log file - const cleanFile = fs.createWriteStream(folderName + '/clean.log'); - statsResult.clean.forEach(cleanLine => { - cleanFile.write(cleanLine + '\n\n') - }); - cleanFile.end(); + // save clean log file + const cleanFile = fs.createWriteStream(folderName + '/clean.log'); + statsResult.clean.forEach(cleanLine => { + cleanFile.write(cleanLine + '\n\n') + }); + cleanFile.end(); - // save raw log file - const rawFile = fs.createWriteStream(folderName + '/war.log'); - lineArray.forEach(rawLine => { - rawFile.write(rawLine + '\n') - }); - rawFile.end(); + // 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(); + res.status(codes.created); + res.locals.items = war; + next(); + }) }) }) }) @@ -128,95 +129,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/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/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 @@