Compare commits

...

8 Commits

25 changed files with 11016 additions and 9 deletions

5
.gitignore vendored
View File

@ -1,8 +1,9 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
dist/
tmp/
etc/
# dependencies
/node_modules

View File

@ -11,5 +11,6 @@ module.exports = {
squads: '/squads',
users: '/users',
account: '/account',
request: '/request'
request: '/request',
wars: '/wars'
};

58
api/models/player.js Normal file
View File

@ -0,0 +1,58 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PlayerSchema = new Schema({
name: {
type: String,
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
},
warId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
kill: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
death: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
friendlyFire: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
respawn: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
flagTouch: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
}
}, {
collection: 'player',
timestamps: {createdAt: 'timestamp'}
});
// optional more indices
PlayerSchema.index({timestamp: 1});
module.exports = mongoose.model('Player', PlayerSchema);

39
api/models/war.js Normal file
View File

@ -0,0 +1,39 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const WarSchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
required: true
},
ptBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
ptOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
bestPlayerId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Player',
default: null
}
}, {
collection: 'war',
timestamps: {createdAt: 'timestamp'}
});
// optional more indices
WarSchema.index({timestamp: 1});
module.exports = mongoose.model('War', WarSchema);

4085
api/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,8 @@
"jimp": "^0.2.27",
"jsonwebtoken": "^7.4.0",
"lodash": "^4.17.4",
"mongoose": "^4.3.0",
"mkdirp": "^0.5.1",
"mongoose": "^4.11.1",
"morgan": "~1.6.1",
"multer": "^1.3.0",
"node-sha1": "^1.0.1",

138
api/routes/wars.js Normal file
View File

@ -0,0 +1,138 @@
"use strict";
// modules
const fs = require('fs');
const mkdirp = require("mkdirp");
const {exec} = require('child_process');
const express = require('express');
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
const logger = require('debug')('cc:squads');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkMT = require('../middleware/permission-check').checkMT;
const routerHandling = require('../middleware/router-handling');
// Mongoose Model using mongoDB
const WarModel = require('../models/war');
const PlayerModel = require('../models/player');
const wars = express.Router();
// routes **********************
wars.route('/')
.get((req, res, next) => {
const filter = {};
WarModel.find(filter, {}, {sort: {date: 'desc'}}, (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, checkMT, upload.single('log'), (req, res, next) => {
let body = req.body;
let parts = body.date.split("-");
body.date = new Date(parseInt(parts[0], 10),
parseInt(parts[1], 10) - 1,
parseInt(parts[2], 10));
const war = new WarModel(body);
if (req.file) {
war.save((err, item) => {
if (err) {
return next(err);
}
const folderName = __dirname + '/../resource/logs/' + item._id;
mkdirp(folderName, function (err) {
if (err) {
return next(err);
}
fs.appendFile(folderName + '/war.log', new Buffer(req.file.buffer), (err) => {
if (err) {
return next(err);
}
//TODO: combine run and clean in one script
exec(__dirname + '/../war-parser/run.sh ' + folderName + ' ' + item._id, (error, stdout) => {
if (error) {
return next(error);
}
let obj = JSON.parse(`${stdout}`);
PlayerModel.create(obj, function (err) {
if (err) {
return next(err);
}
exec(__dirname + '/../war-parser/clean.sh /../' + folderName + ' | tee ' + folderName + '/clean.log', (error) => {
if (error) {
return next(error);
}
res.status(codes.created);
res.locals.items = item;
return next();
});
});
});
});
});
})
} else {
const err = new Error('no Logfile provided');
err.status = codes.wrongmediasend;
next(err);
}
})
.all(
routerHandling.httpMethodNotAllowed
);
wars.route('/:id')
.get((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();
});
});
})
.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
wars.use(routerHandling.emptyResponse);
module.exports = wars;

View File

@ -31,6 +31,7 @@ const awardingRouter = require('./routes/awardings');
const requestRouter = require('./routes/request');
const signatureRouter = require('./routes/signatures');
const commandRouter = require('./routes/command');
const warRouter = require('./routes/wars');
// Configuration ***********************************
// mongoose promise setup
@ -75,6 +76,7 @@ app.use(urls.ranks, rankRouter);
app.use(urls.decorations, decorationRouter);
app.use(urls.request, requestRouter);
app.use(urls.awards, awardingRouter);
app.use(urls.wars, warRouter);
app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter);
app.use(urls.account, apiAuthenticationMiddleware, checkAdmin, accountRouter);

View File

@ -1,4 +1,4 @@
"use strict"
"use strict";
// modules used for graphic manipulation
const jimp = require('jimp');

20
api/war-parser/clean.sh Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
FILE="$1/war.log"
while IFS='' read -r line || [[ -n "$line" ]]; do
case "$line" in
*"Budget"* | *"Mission"* | *"Abschuss"* | *"Respawn"* | *"Punkte"*)
echo $line;
echo ""
;;
esac
done < ${FILE}
# Add OPT Scoreboard
while IFS= read -r line; do
if [[ $line =~ [^[:space:]] ]]; then
echo "$line"
echo ""
fi
done < <(grep -A 100 Scoreboard ${FILE} )

90
api/war-parser/run.sh Executable file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env bash
createScoreboard() {
NAME="$1"
FILE="$2"
WAR_ID="$3"
KILL=0
FF=0
DEATH=0
RESPAWN=0
FLAG=0
FRACTION=
#escape '[' -> somehow escapes all special chars, hah?
ESC_NAME=$(echo "$NAME" | sed -r 's/[\[]+/\\[/g')
while IFS= read -r line; do
case "$line" in
*\(WEST\)[" "]von:[" "]${ESC_NAME}[" "]\(EAST\)* | *\(EAST\)[" "]von:[" "]${ESC_NAME}[" "]\(WEST\)*)
((KILL++))
;;
*\(EAST\)[" "]von:[" "]${ESC_NAME}[" "]\(EAST\)* | *\(WEST\)[" "]von:[" "]${ESC_NAME}[" "]\(WEST\)*)
((FF++))
;;
*${ESC_NAME}[" "]*von:*)
((DEATH++))
;;
*Spieler:*${ESC_NAME}*)
((RESPAWN++))
;;
esac
if [[ -z ${FRACTION} && ( "$line" == *${ESC_NAME}[" "]\(WEST\)* || "$line" == *${ESC_NAME}[" "]\(NATO\)* ) ]]; then
FRACTION="BLUFOR"
fi
if [[ -z ${FRACTION} && ( "$line" == *${ESC_NAME}[" "]\(EAST\)* || "$line" == *${ESC_NAME}[" "]\(CSAT\)* ) ]]; then
FRACTION="OPFOR"
fi
done < <(grep -- "${ESC_NAME}" ${FILE})
printf "\t{\"name\":\"$NAME\", \"fraction\":\"$FRACTION\", \"kill\":${KILL}, \"friendlyFire\":${FF}, \"death\":${DEATH}, \"respawn\":${RESPAWN}, \"flagTouch\":${FLAG}, \"warId\":\"${WAR_ID}\"} "
if [[ -z ${4} ]]; then
printf ",\n"
else
printf "\n"
fi
}
FILE="$1/war.log"
WAR_ID="$2"
PLAYERS=()
while IFS='' read -r line || [[ -n "$line" ]]; do
if [[ -n $line ]]; then
case "$line" in
*"TFAR_RadioRequestEvent"*)
RES=$(echo "$(grep -oP ':[0-9]+\s\(\K.*?(?=\)\sREMOTE)' <<< "$line")")
;;
*"Respawn"*)
RES=$(echo "$(grep -oP '\|\|\sSpieler:\s\K.*?(?=\s-\sKosten:)' <<< "$line")")
;;
*"Abschuss"*)
RES=$(echo "$(grep -oP '\|\|\s\K.*?(?=\s\((EAST|WEST|CIV))' <<< "$line")")
RES1=$(echo "$(grep -oP 'von:\s\K.*?(?=\s\((EAST|WEST|CIV))' <<< "$line")")
;;
esac
if [[ $RES != *"Error: No unit"* && $RES1 != *"Error: No unit"* ]]; then
if [[ -n $RES && " ${PLAYERS[*]} " != *" $RES "* ]]; then
PLAYERS+=("$RES")
fi
if [[ -n $RES1 && " ${PLAYERS[*]} " != *" $RES1 "* ]]; then
PLAYERS+=("$RES1")
fi
fi
fi
done < ${FILE}
echo "["
for ((i=0; i<${#PLAYERS[*]}; i++));
do
if [[ "$((i+1))" -eq "${#PLAYERS[*]}" ]]; then
last="true"
fi
createScoreboard "${PLAYERS[i]}" ${FILE} ${WAR_ID} ${last}
done
echo "]"

View File

@ -1,7 +1,7 @@
#!/bin/bash
# array of available collection names
col=(app_user awarding decoration rank squad user promotion)
col=(app_user awarding decoration rank squad user promotion player war)
for i in "${col[@]}"
do

515
package-lock.json generated Normal file
View File

@ -0,0 +1,515 @@
{
"name": "opt-cc",
"version": "1.0.0",
"lockfileVersion": 1,
"dependencies": {
"ajv": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
"integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
"dev": true
},
"ansi-regex": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
"integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
"dev": true
},
"ansi-styles": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
"integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
"dev": true
},
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
"dev": true
},
"assert-plus": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
"dev": true
},
"aws4": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
"dev": true,
"optional": true
},
"boom": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
"dev": true,
"dependencies": {
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
}
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"chalk": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
"integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
"dev": true,
"dependencies": {
"supports-color": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
"integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
"dev": true
}
}
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true
},
"combined-stream": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
"integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
"dev": true
},
"commander": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
"integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=",
"dev": true
},
"concurrently": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.5.0.tgz",
"integrity": "sha1-jPG3cHppFqeKT/W3e7BN7FSzebI=",
"dev": true
},
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=",
"dev": true
},
"cryptiles": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
"dev": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"date-fns": {
"version": "1.28.5",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.28.5.tgz",
"integrity": "sha1-JXz8RdMi30XvVlhmWWfuhBzXP68=",
"dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"dev": true,
"optional": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
"dev": true
},
"extsprintf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz",
"integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true
},
"form-data": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
"integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
"dev": true
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"har-schema": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
"integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
"dev": true
},
"har-validator": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
"integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
"dev": true
},
"has-ansi": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
"integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
"dev": true
},
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
"dev": true
},
"hawk": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
"integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
"dev": true,
"dependencies": {
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
}
}
},
"hoek": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.1.1.tgz",
"integrity": "sha1-nMVz/7ore0CPtenCoTeWvpTN3Ok=",
"dev": true
},
"http-signature": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
"dev": true
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
"isemail": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz",
"integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=",
"dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"items": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/items/-/items-2.1.1.tgz",
"integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=",
"dev": true
},
"joi": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-9.2.0.tgz",
"integrity": "sha1-M4WseQGSEwy+Iw6ALsAskhW7/to=",
"dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true,
"optional": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true
},
"json-stable-stringify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
"integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
"dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"jsonify": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
"dev": true
},
"jsprim": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz",
"integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=",
"dev": true,
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
"dev": true
},
"mime-db": {
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
"integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=",
"dev": true
},
"mime-types": {
"version": "2.1.15",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz",
"integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=",
"dev": true
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"moment": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
"integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=",
"dev": true
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
"dev": true
},
"performance-now": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
"dev": true
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
"dev": true
},
"qs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
"dev": true
},
"request": {
"version": "2.81.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
"integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
"dev": true
},
"rx": {
"version": "2.3.24",
"resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz",
"integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=",
"dev": true
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
},
"sntp": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
"integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
"dev": true,
"dependencies": {
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
}
}
},
"spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
"dev": true
},
"sshpk": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
"dev": true,
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
}
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
"dev": true
},
"strip-ansi": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
"integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
"dev": true
},
"supports-color": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
"integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
"dev": true
},
"topo": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz",
"integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=",
"dev": true
},
"tough-cookie": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz",
"integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=",
"dev": true
},
"tree-kill": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.1.0.tgz",
"integrity": "sha1-yWPc8DciiS7FnLpWnpQLcZVNFyk=",
"dev": true
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true,
"optional": true
},
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
"dev": true
},
"verror": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz",
"integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=",
"dev": true
},
"wait-on": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-2.0.2.tgz",
"integrity": "sha1-CoT9BwJMb8Joyw6r5YW+IXqvK6o=",
"dev": true,
"dependencies": {
"rx": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
"integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=",
"dev": true
}
}
}
}
}

View File

@ -9,7 +9,7 @@
"deploy-static": "cd ./static && ng build && ln -s ../api/resource/ ../public/resource",
"deploy-static-prod": "cd ./static && ng build --env=prod && ln -s ../api/resource/ ../public/resource",
"postinstall": "npm install --prefix ./static && npm install --prefix ./api",
"mongodb": "mongod --dbpath ./mongodb-data",
"mongodb": "mkdir -p mongodb-data && mongod --dbpath ./mongodb-data",
"test": "npm test --prefix ./api",
"e2e": "npm run deploy-static && concurrently \"npm run e2e --prefix ./api\" \"wait-on -t 60000 http://localhost:3001/ && npm run e2e --prefix ./static\" --success first --kill-others",
"start-e2e": "npm run deploy-static && npm run e2e --prefix ./api",

5794
static/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,18 @@
<li routerLinkActive="active">
<a routerLink='/cc-overview' class="link">Armeeübersicht</a>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
Schlacht Statistik
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li *ngFor="let war of wars">
<a [routerLink]="['/cc-wars/' + war._id]">{{war.title}} <small>{{war.date | date: 'dd.MM.yy'}}</small></a>
</li>
</ul>
</li>
<li *ngIf="loginService.hasPermission(2)" routerLinkActive="active">
<a routerLink='/cc-users' class="link">Teilnehmer</a>
</li>

View File

@ -7,6 +7,8 @@ import {
import {LoginService} from './services/login-service/login-service';
import {Title} from '@angular/platform-browser';
import {AUTH_ENABLED} from './app.tokens';
import {WarService} from "./services/war-service/war.service";
import {War} from "./models/model-interfaces";
@Component({
selector: 'app-root',
@ -17,8 +19,11 @@ export class AppComponent {
defaultTitle: string;
wars: War[] = [];
constructor(@Optional() @Inject(AUTH_ENABLED) public authEnabled,
private loginService: LoginService,
private warService: WarService,
private activatedRoute: ActivatedRoute,
private router: Router,
private titleService: Title) {
@ -31,7 +36,11 @@ export class AppComponent {
.filter(event => event instanceof NavigationEnd)
.subscribe(event => {
this.setBrowserTitle();
});
this.warService.getAllWars().subscribe((wars) => {
this.wars = wars;
})
}
setBrowserTitle() {

View File

@ -14,5 +14,6 @@ export class AppConfig {
public readonly apiOverviewPath = '/overview';
public readonly apiRequestAwardPath = '/request/award';
public readonly apiPromotionPath = '/request/promotion';
public readonly apiWarPath = '/wars';
}

View File

@ -31,6 +31,7 @@ import {AppUserService} from "./services/app-user-service/app-user.service";
import {AppUserStore} from "./services/stores/app-user.store";
import {PromotionService} from "./services/promotion-service/promotion.service";
import {FilterRankPipe} from "./filter/filter.pipe";
import {WarService} from "./services/war-service/war.service";
@NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule, ClipboardModule],
@ -53,6 +54,7 @@ import {FilterRankPipe} from "./filter/filter.pipe";
RankStore,
AwardingService,
PromotionService,
WarService,
AppConfig,
Title,
routingProviders,

View File

@ -13,6 +13,7 @@ import {RequestAwardComponent} from "./request/award/req-award.component";
import {RequestPromotionComponent} from "./request/promotion/req-promotion.component";
import {ConfirmPromotionComponent} from "./request/confirm-promotion/confirm-promotion.component";
import {ConfirmAwardComponent} from "./request/confirm-award/confirm-award.component";
import {WarDetailComponent} from "./wars/war-detail.component";
export const appRoutes: Routes = [
@ -20,6 +21,8 @@ export const appRoutes: Routes = [
{path: 'cc-overview', children: armyRoutes},
{path: '', redirectTo: '/cc-overview', pathMatch: 'full'},
{path: 'cc-wars/:id', component: WarDetailComponent},
{path: 'login', component: LoginComponent},
{path: 'signup', component: SignupComponent},
@ -44,6 +47,6 @@ export const appRouting = RouterModule.forRoot(appRoutes);
export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, RequestPromotionComponent, ConfirmAwardComponent,
ConfirmPromotionComponent, AdminComponent, ...armyRoutingComponents, NotFoundComponent, ...usersRoutingComponents,
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents];
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents, WarDetailComponent];
export const routingProviders = [LoginGuardHL];

View File

@ -16,6 +16,27 @@ export interface User {
awards?: Award[];
}
export interface Player {
_id?: string;
fraction?: string;
name?: string;
warId?: War;
kill?: number;
death?: number;
friendlyFire?: number;
respawn?: number;
flagTouch?: number;
}
export interface War {
_id?: string;
title?: string;
date?: Date;
ptBlufor?: number;
ptOpfor?: number;
bestPlayerId?: Player;
players: Player[];
}
export interface Squad {
_id?: string;
name?: string;

View File

@ -0,0 +1,45 @@
import {Injectable} from "@angular/core";
import {War} from "../../models/model-interfaces";
import {AppConfig} from "../../app.config";
import {HttpClient} from "../http-client";
@Injectable()
export class WarService {
constructor(private http: HttpClient,
private config: AppConfig) {
}
getAllWars() {
return this.http.get(this.config.apiUrl + this.config.apiWarPath)
.map(res => res.json())
}
getWar(warId: string) {
return this.http.get(this.config.apiUrl + this.config.apiWarPath + '/' + warId)
.map(res => res.json())
}
submitDecoration(war: War, logFile?) {
let body;
if (logFile) {
body = new FormData();
Object.keys(war).map((objectKey) => {
if (war[objectKey] !== undefined) {
body.append(objectKey, war[objectKey]);
}
});
body.append('log', logFile, logFile.name);
}
return this.http.post(this.config.apiUrl + this.config.apiDecorationPath, body)
.map(res => res.json())
.do(savedDecoration => {
console.log('saved successfully')
});
}
}

View File

@ -0,0 +1,43 @@
.overview {
position: fixed;
overflow-y: scroll;
overflow-x: hidden;
bottom: 20px;
width: 100%;
padding-left: 50px;
padding-top: 70px;
margin-left: 10px;
height: 100vh;
}
.trash {
cursor: pointer;
}
.table {
overflow-wrap: break-word;
table-layout: fixed;
}
.table-container {
margin-top: 10px;
overflow-x: auto;
}
.table-head {
background: #222222;
color: white;
}
.cell-outline {
outline: 1px solid #D4D4D4;
}
.text-opfor {
color: firebrick;
}
.text-blufor {
color: blue;
}

View File

@ -0,0 +1,81 @@
<div class="overview" xmlns="http://www.w3.org/1999/html">
<div style="margin-left: 5%">
<h2>{{war.title}} - Schlacht vom {{war.date | date: 'dd.MM.yyyy'}}</h2>
<h3 class="pull-left">
<h4>Endpunktestand:</h4>
<span class="text-blufor" style="font-weight: bold; margin-right: 10px">NATO {{war.ptBlufor}}</span>
<span style="font-size: x-large">|</span>
<span class="text-opfor" style="font-weight: bold; margin-left: 10px;">{{war.ptOpfor}} CSAT</span>
</h3>
<div style="margin-left: 50%; margin-top:1%">
<a class="btn btn-default btn-" style="margin: 20px" target="_blank" href="resource/logs/{{war._id}}/clean.log">Logfile anzeigen</a>
<form class="form-group">
<label class="radio-inline">
<input type="radio" name="fractSelect"
[checked]="(fractionRadioSelect == undefined) ? 'true' : 'false'"
[(ngModel)]="fractionRadioSelect"
(change)="filterPlayersByFraction()">Alle
</label>
<label class="radio-inline">
<input type="radio" name="fractSelect" value="BLUFOR"
[(ngModel)]="fractionRadioSelect"
#fractRadioBufor
(change)="filterPlayersByFraction(fractRadioBufor.value)">NATO
</label>
<label class="radio-inline">
<input type="radio" name="fractSelect" value="OPFOR"
[(ngModel)]="fractionRadioSelect"
#fractRadioOpfor
(change)="filterPlayersByFraction(fractRadioOpfor.value)">CSAT
</label>
<br>
</form>
</div>
</div>
<div class="pull-left" style="margin-top:20px;">
<div class="table-container" style="width: 75%; min-width: 500px">
<table class="table table-hover">
<thead>
<tr class="table-head">
<th class="col-sm-2" style="border-radius: 10px 0 0 0;">Spieler</th>
<th class="col-sm-1">Fraktion</th>
<th class="col-sm-1">Kills</th>
<th class="col-sm-1">FriendlyFire</th>
<th class="col-sm-1">Eroberungen</th>
<th class="col-sm-1">Tode</th>
<th class="col-sm-1" style="border-radius: 0 10px 0 0;">Respawn</th>
</tr>
</thead>
<tbody *ngFor="let player of players">
<tr class="cell-outline">
<td style="font-weight: bold" [ngClass]="player.fraction === 'BLUFOR' ? 'text-blufor' : 'text-opfor'">
{{player.name}}
</td>
<td>
{{player.fraction === 'BLUFOR' ? 'NATO' : 'CSAT'}}
</td>
<td>
{{player.kill}}
</td>
<td>
{{player.friendlyFire}}
</td>
<td>
{{player.flagTouch}}
</td>
<td>
{{player.death}}
</td>
<td>
{{player.respawn}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,46 @@
import {Component} from "@angular/core";
import {Player, War} from "../models/model-interfaces";
import {WarService} from "../services/war-service/war.service";
import {ActivatedRoute} from "@angular/router";
@Component({
selector: 'war-detail',
templateUrl: './war-detail.component.html',
styleUrls: ['./war-detail.component.css']
})
export class WarDetailComponent {
war: War = {players: []};
players: Player[] = [];
fractionRadioSelect: string;
constructor(private route: ActivatedRoute,
private warService: WarService) {
}
ngOnInit() {
this.route.params
.map(params => params['id'])
.filter(id => id != undefined)
.flatMap(id => this.warService.getWar(id))
.subscribe(war => {
this.war = war;
this.players = war.players;
});
}
filterPlayersByFraction(fraction: string) {
if (fraction) {
this.players = this.war.players.filter((player) => {
console.log(player.fraction + " .... " + fraction);
return player.fraction === fraction;
})
} else {
this.players = this.war.players;
}
}
}