diff --git a/api/config/api-url.js b/api/config/api-url.js index 58b1170..d7dbc34 100644 --- a/api/config/api-url.js +++ b/api/config/api-url.js @@ -10,5 +10,6 @@ module.exports = { signatures: '/signatures', squads: '/squads', users: '/users', - account: '/account' + account: '/account', + request: '/request' }; diff --git a/api/models/awarding.js b/api/models/awarding.js index fa2b2f4..d653369 100644 --- a/api/models/awarding.js +++ b/api/models/awarding.js @@ -16,6 +16,11 @@ const AwardingSchema = new Schema({ type: String, required: true }, + proposer: { + type: mongoose.Schema.Types.ObjectId, + ref: 'AppUser', + required: true + }, confirmed: { type: Boolean, default: true diff --git a/api/models/promotion.js b/api/models/promotion.js new file mode 100644 index 0000000..acd3706 --- /dev/null +++ b/api/models/promotion.js @@ -0,0 +1,33 @@ +"use strict"; + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const PromotionSchema = new Schema({ + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + }, + proposer: { + type: mongoose.Schema.Types.ObjectId, + ref: 'AppUser', + required: true + }, + rankLvl: { + type: Number, + get: v => Math.round(v), + set: v => Math.round(v), + required: true + }, + confirmed: { + type: Boolean, + default: true + } +}, { + collection: 'promotion', + timestamps: {createdAt: 'timestamp'} +}); +// optional more indices +PromotionSchema.index({timestamp: 1}); + +module.exports = mongoose.model('Promotion', PromotionSchema); diff --git a/api/routes/authenticate.js b/api/routes/authenticate.js index 8ab87fa..955a0c4 100644 --- a/api/routes/authenticate.js +++ b/api/routes/authenticate.js @@ -61,6 +61,7 @@ let authCheck = (username, password, res) => { _id: user._id, username: user.username, permission: user.permission, + squad: user.squad, token: jwt.sign({sub: user._id}, config.secret, {expiresIn: diff * 60}), tokenExpireDate: new Date(Date.now().valueOf() + diff * 60000 - 1000) }); diff --git a/api/routes/awardings.js b/api/routes/awardings.js index d05b957..96beea3 100644 --- a/api/routes/awardings.js +++ b/api/routes/awardings.js @@ -9,9 +9,23 @@ const codes = require('./http-codes'); const routerHandling = require('../middleware/router-handling'); +const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); +const checkHl = require('../middleware/permission-check').checkHl; + // Mongoose Model using mongoDB const AwardingModel = require('../models/awarding'); +// result set for proposer(appUser) population +const resultSet = { + '__v': 0, + 'updatedAt': 0, + 'timestamp': 0, + 'password': 0, + 'permission': 0, + 'secret': 0, + 'activated': 0 +}; + const awarding = express.Router(); @@ -39,7 +53,7 @@ awarding.route('/') }); } else { AwardingModel.find(filter, {}, {sort: {date: 'desc'}}) - .populate('decorationId').exec((err, items) => { + .populate('decorationId').populate('proposer', resultSet).exec((err, items) => { if (err) { err.status = codes.servererror; return next(err); @@ -56,17 +70,18 @@ awarding.route('/') } }) - .post((req, res, next) => { - const rank = new AwardingModel(req.body); + .post(apiAuthenticationMiddleware, checkHl, (req, res, next) => { + const award = new AwardingModel(req.body); + award.proposer = req.user._id; // timestamp and default are set automatically by Mongoose Schema Validation - rank.save((err) => { + 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 = rank; + res.locals.items = award; next(); }); }) @@ -76,7 +91,7 @@ awarding.route('/') ); awarding.route('/:id') - .delete((req, res, next) => { + .delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => { AwardingModel.findByIdAndRemove(req.params.id, (err, item) => { if (err) { err.status = codes.wrongrequest; diff --git a/api/routes/request.js b/api/routes/request.js new file mode 100644 index 0000000..f2e09c3 --- /dev/null +++ b/api/routes/request.js @@ -0,0 +1,54 @@ +"use strict"; + +// modules +const express = require('express'); +const logger = require('debug')('cc:awardings'); + +// HTTP status codes by name +const codes = require('./http-codes'); + +const routerHandling = require('../middleware/router-handling'); + +// Mongoose Model using mongoDB +const AwardingModel = require('../models/awarding'); + +const request = express.Router(); + + +// routes ********************** +request.route('/award') + + .post((req, res, next) => { + const award = new AwardingModel(req.body); + award.confirmed = false; + 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 + ); + +request.route('/promotion') + + + .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 +request.use(routerHandling.emptyResponse); + + +module.exports = request; diff --git a/api/routes/users.js b/api/routes/users.js index 797cf72..f8942ef 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -42,8 +42,9 @@ users.route('/') else { const nameQuery = req.query.q; const fractionFilter = req.query.fractFilter; + const squadFilter = req.query.squadId; - UserModel.find({}, res.locals.filter, res.locals.limitskip, (err, users) => { + UserModel.find({}, (err, users) => { if (err) return next(err); if (users.length === 0) { res.locals.items = users; @@ -56,8 +57,18 @@ users.route('/') // filter by name if (!nameQuery || (nameQuery && user.username.toLowerCase().includes(nameQuery.toLowerCase()))) { getExtendedUser(user, next, (extUser) => { + + // filter by squad + if (squadFilter) { + if (extUser.squad && extUser.squad._id.toString() === squadFilter) { + resUsers.push(extUser); + } + else { + rowsLength -= 1; + } + } // filter by fraction - if (!fractionFilter || + else if (!fractionFilter || (fractionFilter && extUser.squad && extUser.squad.fraction.toLowerCase() === fractionFilter) || (fractionFilter && fractionFilter === 'unassigned' && !extUser.squad)) { resUsers.push(extUser); diff --git a/api/server.js b/api/server.js index a5fa160..51a3b16 100644 --- a/api/server.js +++ b/api/server.js @@ -15,7 +15,7 @@ const urls = require('./config/api-url'); const restAPIchecks = require('./middleware/request-checks.js'); const errorResponseWare = require('./middleware/error-response'); const apiAuthenticationMiddleware = require('./middleware/auth-middleware'); -const checkHl = require('./middleware/permission-check').checkHl; +const checkSql = require('./middleware/permission-check').checkSql; const checkAdmin = require('./middleware/permission-check').checkAdmin; const signatureCronJob = require('./cron-job/update-signatures'); @@ -28,6 +28,7 @@ const squadRouter = require('./routes/squads'); const rankRouter = require('./routes/ranks'); const decorationRouter = require('./routes/decorations'); const awardingRouter = require('./routes/awardings'); +const requestRouter = require('./routes/request'); const signatureRouter = require('./routes/signatures'); const commandRouter = require('./routes/command'); @@ -72,7 +73,8 @@ app.use(urls.users, userRouter); app.use(urls.squads, squadRouter); app.use(urls.ranks, rankRouter); app.use(urls.decorations, decorationRouter); -app.use(urls.awards, apiAuthenticationMiddleware, checkHl, awardingRouter); +app.use(urls.request, apiAuthenticationMiddleware, checkSql, requestRouter); +app.use(urls.awards, awardingRouter); app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter); app.use(urls.account, apiAuthenticationMiddleware, checkAdmin, accountRouter); diff --git a/static/src/app/app.config.ts b/static/src/app/app.config.ts index b9c0608..0869196 100644 --- a/static/src/app/app.config.ts +++ b/static/src/app/app.config.ts @@ -12,5 +12,6 @@ export class AppConfig { public readonly apiSquadPath = '/squads/'; public readonly apiUserPath = '/users/'; public readonly apiOverviewPath = '/overview'; + public readonly apiRequestAwardPath = '/request/award' } diff --git a/static/src/app/app.routing.ts b/static/src/app/app.routing.ts index 709810d..abafadb 100644 --- a/static/src/app/app.routing.ts +++ b/static/src/app/app.routing.ts @@ -1,7 +1,7 @@ import {Routes, RouterModule} from '@angular/router'; import {LoginComponent} from './login/index'; import {NotFoundComponent} from './not-found/not-found.component'; -import {LoginGuardAdmin, LoginGuardHL} from './login/login.guard'; +import {LoginGuardAdmin, LoginGuardHL, LoginGuardSQL} from './login/login.guard'; import {usersRoutes, usersRoutingComponents} from "./users/users.routing"; import {squadsRoutes, squadsRoutingComponents} from "./squads/squads.routing"; import {decorationsRoutes, decorationsRoutingComponents} from "./decorations/decoration.routing"; @@ -9,6 +9,7 @@ import {ranksRoutes, ranksRoutingComponents} from "./ranks/ranks.routing"; import {armyRoutes, armyRoutingComponents} from "./army/army.routing"; import {SignupComponent} from "./login/signup.component"; import {AdminComponent} from "./admin/admin.component"; +import {RequestAwardComponent} from "./request/award/req-award.component"; export const appRoutes: Routes = [ @@ -18,6 +19,9 @@ export const appRoutes: Routes = [ {path: 'login', component: LoginComponent}, {path: 'signup', component: SignupComponent}, + + {path: 'request-award', component: RequestAwardComponent, canActivate: [LoginGuardSQL]}, + {path: 'cc-users', children: usersRoutes, canActivate: [LoginGuardHL]}, {path: 'cc-squads', children: squadsRoutes, canActivate: [LoginGuardHL]}, {path: 'cc-decorations', children: decorationsRoutes, canActivate: [LoginGuardHL]}, @@ -32,7 +36,7 @@ export const appRoutes: Routes = [ export const appRouting = RouterModule.forRoot(appRoutes); -export const routingComponents = [LoginComponent, SignupComponent, AdminComponent, ...armyRoutingComponents , NotFoundComponent, ...usersRoutingComponents, +export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, AdminComponent, ...armyRoutingComponents , NotFoundComponent, ...usersRoutingComponents, ...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents]; export const routingProviders = [LoginGuardHL]; diff --git a/static/src/app/army/army-member.component.html b/static/src/app/army/army-member.component.html index 01ebaed..1392d10 100644 --- a/static/src/app/army/army-member.component.html +++ b/static/src/app/army/army-member.component.html @@ -32,7 +32,7 @@
-