Merge branch 'release/v1.7.2' of hardi/opt-cc into master

pull/35/head v1.7.2
hardi 2018-04-02 13:30:30 +02:00 committed by Gogs
commit 878d61de2e
148 changed files with 6848 additions and 2022 deletions

4
.gitignore vendored
View File

@ -4,6 +4,7 @@
dist/
tmp/
etc/
api/apib/documentation.apib
# dependencies
node_modules
@ -46,7 +47,8 @@ Thumbs.db
# Internal Data
public/
mongodb-data/
resource/
api/resource/
api/apib/dredd/data/tmp-resource
backup/
# System

View File

@ -25,7 +25,7 @@ In addition, it sets up the mongo deamon to start up automatically with the syst
For production setup run the script, described in _Setup for development_, adding the parameter `prod`
./docs/opt-cc-environment/3rd-party-install.sh prod
./docs/opt-cc-environment/3rd-party-install.sh prod
This adds the [`pm2` process manager](http://pm2.keymetrics.io/) to be installed and start the _opt-cc_ server as `pm2` process.
Run the `sudo` command printed as last output to configure the `pm2` process for automatic start on the system.

11
api/.eslintrc.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
'extends': 'google',
'parserOptions': {
'ecmaVersion': 6
},
'rules': {
'max-len': [2, 120, 4, {
'ignoreUrls': true
}],
}
};

View File

@ -0,0 +1,39 @@
### List App Users [GET /account]
List all app users, ordered by username
**Permission: 4**
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[AppUser], fixed-type)
### Update App User [PATCH /account/{id}]
Update an app user, identified by its id
**Permission: 4**
+ Parameters
+ id: `5abf5064861d950f157c4a09` (string, required) - unique id of app user
+ Request (application/json)
+ Attributes (UpdateAppUser)
+ Response 200 (application/json; charset=utf-8)
+ Attributes (AppUser, fixed-type)
### Delete App User [DELETE /account/{id}]
**Permission: 4**
Delete an app user
+ Parameters
+ id: `5ac0de67b5edc7771c027b94` (string, required) - unique id of app user
+ Response 204

View File

@ -0,0 +1,16 @@
### Update signature image [POST /cmd/createSignature/{userId}]
Update an army members signature image
**Permission: 4**
+ Parameters
+ userId: `5ab68d42f547ed304064e5f7` (string, required) - army members unique user id
+ Request (application/json)
{}
+ Response 200 (application/json; charset=utf-8)
+ Attributes
+ status: `success` (string, required) - action result message

View File

@ -0,0 +1,7 @@
### Get Army Overview [GET /overview]
Get bundled army, squad and member data for the participating fractions
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[Fraction])

View File

@ -0,0 +1,60 @@
### Get Awardings [GET /awardings{?userId,inProgress,fractFilter}]
List all awardings
+ Parameters
+ userId: `5ab68d42f547ed304064e5f7` (string, optional)
specific army member Id to show the awardings for
+ inProgress: false (boolean, optional)
true to filter by awarding state 'in progress'
+ Default: false
+ fractFilter: `BLUFOR` (enum[string], optional)
Field to filter by fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[AwardingPopulated], fixed-type)
### Create Awarding [POST /awardings]
Create a new awarding which is immediatly assigned to the user
**Permission: 2**
+ Request (application/json)
+ Attributes
+ userId: `5ab68d42f547ed304064e5f7` (string, required) - unique id of the army member to give award
+ decorationId: `5abd3dff6e6a0334d95b8ba0` (string, required) - unique id of the decoration
+ reason: `Good boy` (string, required) - reason for giving the awarding
+ proposer: `5ab68ceef547ed304064e5f6` (string, required) - app user id, who requested this awarding
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Awarding, fixed-type)
### Create Awarding Proposal [POST /request/award]
Create a new awarding proposal, that needs to be approved by higher permission level user to take effect
**Permission: 1**
+ Request (application/json)
+ Attributes
+ userId: `5ab68d42f547ed304064e5f7` (string, required) - unique id of the army member to give award
+ decorationId: `5abd3dff6e6a0334d95b8ba0` (string, required) - unique id of the decoration
+ reason: `Good boy` (string, required) - reason for giving the awarding
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Awarding, fixed-type)

View File

@ -0,0 +1,127 @@
### List Decorations [GET /decorations{?q,fractFilter}]
List all decorations
+ Parameters
+ q: `tapferkeit` (string, optional)
Filter string for the partial decoration name
+ fractFilter: `BLUFOR` (enum[string], optional)
Field to filter by fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[Decoration], fixed-type)
### Get Decoration [GET /decorations/{id}]
Retrieve single decoration data
+ Parameters
+ id: `5abd3dff6e6a0334d95b8ba0` (string, required) - unique id of the decoration to fetch
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Decoration, fixed-type)
### Create Decoration [POST /decorations]
Create a new decoration
**Permission: 2**
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="name"
Content-Type: text/plain
Super medal 2000
-----BOUNDARY
Content-Disposition: form-data; name="fraction"
Content-Type: text/plain
OPFOR
-----BOUNDARY
Content-Disposition: form-data; name="description"
Content-Type: text/plain
For good performance in a battle
-----BOUNDARY
Content-Disposition: form-data; name="isMedal"
Content-Type: text/plain
true
-----BOUNDARY
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlMA
QObYZgAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=
-----BOUNDARY--
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Decoration, fixed-type)
### Update Decoration [PATCH /decorations/{id}]
Update decoration, identified by its id
**Permission: 2**
+ Parameters
+ id: `5abeb420b987672bb1ede643` (string, required) - unique id of the decoration
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="name"
Content-Type: text/plain
Super medal 2000
-----BOUNDARY
Content-Disposition: form-data; name="fraction"
Content-Type: text/plain
OPFOR
-----BOUNDARY
Content-Disposition: form-data; name="description"
Content-Type: text/plain
For good performance in a battle
-----BOUNDARY
Content-Disposition: form-data; name="sortingNumber"
Content-Type: text/plain
2
-----BOUNDARY
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlMA
QObYZgAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=
-----BOUNDARY--
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Decoration, fixed-type)
### Delete Decoration [DELETE /decorations/{id}]
Delete a decoration
**Permission: 2**
+ Parameters
+ id: `5abeb43cb987672bb1ede644` (string, required) - unique id of the decoration
+ Response 204

View File

@ -0,0 +1,65 @@
### List Promotions [GET /request/promotion{?squadId,inProgress,fractFilter}]
List all promotion requests
+ Parameters
+ squadId: `591470249e9fae286e008e31` (string, optional)
specific squad Id to show the promotion requests for
+ inProgress: false (boolean, optional)
true to filter by promotion state 'in progress'
+ Default: false
+ fractFilter: `BLUFOR` (enum[string], optional)
Field to filter by fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[PromotionPopulated])
### Create Promotion Proposal [POST /request/promotion]
Create a new proposal for a promotion, that needs to be approved by higher permission level user to take effect
**Permission: 1**
+ Request (application/json)
+ Attributes
+ userId: `5ab68d42f547ed304064e5f7` (string, required) - id of the army member to promote
+ oldRankLvl: 0 (number, required) - previous rank level
+ newRankLvl: 5 (number, required) - new rank level to apply
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Promotion, fixed-type)
### Update Promotion Proposal [PATCH /request/promotion/{id}]
Update the promotion proposal
**Permission: 2**
+ Parameters
+ id: `5abf50d9861d950f157c4a0a` (string, required) - unique id of the promotion
+ Request (application/json)
+ Attributes
+ _id: `5abf50d9861d950f157c4a0a` (string, optional) - id of the promotion proposal
+ userId: `5ab68d42f547ed304064e5f7` (string, optional) - id of the army member to promote
+ proposer: `5abf5064861d950f157c4a09` (string, optional) - id of the proposing app user
+ oldRankLvl: 0 (number, optional) - previous rank level
+ newRankLvl: 5 (number, optional) - new rank level to apply
+ confirmed: 1 (number, optional) confirmation status of the promotion (0 - in progress, 1 - approved & applied, 2 - rejected) \nThis automatically applies the *newRankLvl*value as new rank level to the target user when confirmed = 1
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Promotion, fixed-type)

View File

@ -0,0 +1,116 @@
### List Ranks [GET /ranks{?q,fractFilter}]
List all ranks
+ Parameters
+ q: `Gefr` (string, optional) - filter string which filters for partial rank name
+ fractFilter: `BLUFOR` (enum[string], optional)
Field to filter by fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[Rank], fixed-type)
### Get Rank [GET /ranks/{id}]
Retrieve single rank data
+ Parameters
+ id: `5aba5504eadcce6332c6a775` (string, required) - unique id of the rank to fetch
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Rank, fixed-type)
### Create Rank [POST /ranks]
Create a new rank
**Permission: 2**
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="name"
Content-Type: text/plain
Obergefreiter
-----BOUNDARY
Content-Disposition: form-data; name="fraction"
Content-Type: text/plain
BLUFOR
-----BOUNDARY
Content-Disposition: form-data; name="level"
Content-Type: text/plain
2
-----BOUNDARY
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlMA
QObYZgAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=
-----BOUNDARY--
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Rank, fixed-type)
### Update Rank [PATCH /ranks/{id}]
Update rank, identified by its id
**Permission: 2**
+ Parameters
+ id: `5abeb23995cf43205225710b` (string, required) - unique id of the rank
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="name"
Content-Type: text/plain
Stabsunteroffizier
-----BOUNDARY
Content-Disposition: form-data; name="fraction"
Content-Type: text/plain
BLUFOR
-----BOUNDARY
Content-Disposition: form-data; name="level"
Content-Type: text/plain
10
-----BOUNDARY
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlMA
QObYZgAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=
-----BOUNDARY--
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Rank, fixed-type)
### Delete Rank [DELETE /ranks/{id}]
Delete a rank
**Permission: 2**
+ Parameters
+ id: `5abeb1b995cf43205225710a` (string, required) - unique id of the rank
+ Response 204

View File

@ -0,0 +1,110 @@
### List Squads [GET /squads{?q,fractFilter}]
Get single army member information
+ Parameters
+ q: `alpha` (string, optional) - filter string which filters for partial squadname
+ fractFilter: `BLUFOR` (enum[string], optional)
Field to filter by fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[Squad], fixed-type)
### Get Squad [GET /squads/{id}]
Get single squad information
+ Parameters
+ id: `5aba54eaeadcce6332c6a774` (string, required) - unique id of the squad
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Squad, fixed-type)
### Create Squad [POST /squads]
Create a new squad
**Permission: 2**
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="name"
Content-Type: text/plain
Delta
-----BOUNDARY
Content-Disposition: form-data; name="fraction"
Content-Type: text/plain
BLUFOR
-----BOUNDARY
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlMA
QObYZgAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=
-----BOUNDARY--
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Squad, fixed-type)
### Update Squad [PATCH /squads/{id}]
Update squad, identified by its id
**Permission: 2**
+ Parameters
+ id: `5abe166f8b7488392a623f12` (string, required) - unique id of the squad
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="name"
Content-Type: text/plain
test-CSAT
-----BOUNDARY
Content-Disposition: form-data; name="fraction"
Content-Type: text/plain
OPFOR
-----BOUNDARY
Content-Disposition: form-data; name="sortingNumber"
Content-Type: text/plain
3
-----BOUNDARY
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png
Content-Transfer-Encoding: base64
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlMA
QObYZgAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=
-----BOUNDARY--
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Squad, fixed-type)
### Delete Squad [DELETE /squads/{id}]
Delete a squad
**Permission: 2**
+ Parameters
+ id: `5abe16f98b7488392a623f17` (string, required) - unique id of the squad
+ Response 204

View File

@ -0,0 +1,93 @@
### List Users [GET /users{?q,fractFilter,limit,offset}]
Get single army member information
+ Parameters
+ q: `hardi` (string, optional) - filter string which filters for partial username
+ fractFilter: `BLUFOR` (enum[string], optional)
Field to filter by fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ limit: 20 (number, optional)
Maximum number of users to return
+ Default: Infinity
+ offset: 0 (number, optional)
Offset into result-set (useful for pagination)
+ Default: 0
+ Response 200 (application/json; charset=utf-8)
+ Headers
X-Total-Count: 1
+ Attributes (array[UserPopulated], fixed-type)
### Get User [GET /users/{id}]
Get single army member information
+ Parameters
+ id: `5ab68d42f547ed304064e5f7` (string, required) - unique id of army-member
+ Response 200 (application/json; charset=utf-8)
+ Attributes (UserPopulated, fixed-type)
### Create User [POST /users]
Create a new army member
**Permission: 2**
+ Request (application/json)
+ Attributes
+ username: `[GNC]Paolo` (string, required) - display name of the user
+ Response 201 (application/json; charset=utf-8)
+ Attributes (UserInclTimestamp, fixed-type)
### Update User [PUT /users/{id}]
Update an army member, identified by its id
**Permission: 2**
+ Parameters
+ id: `5abd4780396bc0487068be0e` (string, required) - unique id of army-member
+ Request (application/json)
+ Attributes
+ _id: `5abd4780396bc0487068be0e` (string, required) - unique id of army-member
+ username: `Paolo` (string, optional) - display name of the user
+ rankLvl: 22 (number, optional) - rank level representing the rank
+ squadId: `591470249e9fae286e008e31` (string, optional) - squadId of squad which army member is part of
+ Response 200 (application/json; charset=utf-8)
+ Attributes (User, fixed-type)
### Delete User [DELETE /users/{id}]
Delete an army member
**Permission: 2**
+ Parameters
+ id: `5abd4780396bc0487068be0e` (string, required) - unique id of army-member
+ Response 204

11
api/apib/auth/login.apib Normal file
View File

@ -0,0 +1,11 @@
### Login - Generate Token [POST /authenticate]
Generate a token which can be used to make API requests.
+ Request Generate Token (application/json)
+ Attributes (Login)
+ Response 200 (application/json; charset=utf-8)
+ Attributes (LoginResponse)

13
api/apib/auth/signup.apib Normal file
View File

@ -0,0 +1,13 @@
### Sign up - Create account [POST /authenticate/signup]
Create a new application user account.
+ Request Create Account (application/json)
+ Attributes (Registration)
+ Response 201 (application/json; charset=utf-8)
+ Attributes
+ message: `User successfully created` (string, required)

54
api/apib/base/access.apib Normal file
View File

@ -0,0 +1,54 @@
## Location
The API can only be accessed via HTTPS in the cloud instance.
You can reach the API for the cloud instance at
```
https://cc.noarch.de/api/
```
Local installations can access the API using the respective domain with the path `/api/` appended.
So if your server is reachable at `https://cc.myserver.lan` the API endpoint is located at
```
# Note: `/api/` is appended to the locale installation server url
https://cc.myserver.lan/api/
```
## Authentication
Requests to most of the API endpoints have to be authenticated using an API token which is received after successfully providing your application credentials to the API.
Once acquired the token has to be provided in the ``X-Access-Token`` header of every request that requires authentication.
The following example illustrates how to provide the token when using `curl`
```
# via Authorization header
> curl -H "X-Access-Token: $CC_API_TOKEN" <API_RESOURCE_PATH_GOES_HERE>
```
## Permission Levels
The permission level is resolved by the API token you send, which resolves the specific app user information.
Endpoints that require a certain permission level to be accessed, mention the **minimum** required permission level in the description.
::: note
Permission Levels:
0 - User
1 - Squadlead
2 - High Command
3 - Maintainer
4 - Administrator
:::

View File

@ -0,0 +1,10 @@
## Timezones & Date Format
**Date Format**
In general the API returns and expects dates & times in the ISO 8601 format.
For example: `2017-02-27T17:05:33.155Z` for a time or `2017-02-27` for a date.
**Timezone**
The default timezone returned will be the UTC timezone

View File

@ -0,0 +1,36 @@
The running express server instance saves entity related files like squad logos and statistic logs on the file system.
In the original MEAN application they are served in the `/resource` path of the application server.
The following examples show where those files are located, to be accessed on the cloud instance.
## Images
**Decoration Image**
`https://cc.noarch.de/resource/decoration/{decorationId}.png`
**Rank Image**
`https://cc.noarch.de/resource/rank/{rankId}.png`
**Signature Image**
`https://cc.noarch.de/resource/signature/{userId}.png`
**Squad Logo**
`https://cc.noarch.de/resource/squad/{squadId}.png`
## Log-Files
**Sanitized Log File**
`https://cc.noarch.de/resource/logs/{warId}/clean.log`
**Original Uploaded Log File**
`https://cc.noarch.de/resource/logs/{warId}/war.log`

View File

@ -0,0 +1,31 @@
# Proposer (object)
Representation of an app user who proposed an awarding or a promotion
## Properties
+ _id: `5abf5064861d950f157c4a09` (string, required) - unique id of the app user
+ username: `hardiready` (string, required) - username of the app user
+ squad: `591470249e9fae286e308e41` (string, required) - squad id associated with the app user
# AppUser (Proposer)
An app user instance with populated squad
## Properties
+ activated: true (boolean, required) - account activation status
+ password: `$1s23$1$H7dl7RTFZUBIBNUZ213IIOUasdNEI571sMuzXmzi4` (string, required) - password hash value
+ permission: 1 (number, required) - permission level
+ secret: `I like tacos` (string, required) - secret used for account activation comparison
+ squad (Squad, required, nullable) - squad the app user is responsible for
+ timestamp: `2017-08-02T07:48:56.378Z` (string, required) - creation timestamp
+ updatedAt: `2017-08-02T08:07:20.929Z` (string, required) - version timestamp
+ __v: 3 (number, required) - version number
# UpdateAppUser (Proposer)
An app user instance for PATCH updating
## Properties
+ activated: true (boolean, optional) - account activation status
+ password: `$1s23$1$H7dl7RTFZUBIBNUZ213IIOUasdNEI571sMuzXmzi4` (string, optional) - password hash value
+ permission: 1 (number, optional) - permission level
+ secret: `I like tacos` (string, optional) - secret used for account activation comparison
+ squad: `5abe166f8b7488392a623f12` (string, optional) - id of squad the app user is responsible for

View File

@ -0,0 +1,26 @@
# Fraction (object)
Single fraction object
## Properties
+ squads (array[ArmySquad], required) - Array containing all squads of the fraction, sorted by 'sortingNumber'
+ memberCount: 1 (number, required) - sum of fraction members in all squads
# ArmySquad (object)
A single squad as appearing in the army overview
## Properties
+ _id: `59146e6aef2ad810623ed519` (string, required) - the unique id of the squad
+ name: `Führungsstab` (string, required) - the squad display name
+ members (array[ArmyMember], required) - List of participants being member in this squad
+ memberCount: 1 (number, required) - Number of members the squad has
# ArmyMember (object)
Unique member in a army squad
## Properties
+ _id: `5918d2ca574b0b16820a0b28` (string, required) - unique id of the army member
+ username: `Jagernaut` (string, required) - display name of the army member
+ rank: `General` (string, required) - display name of the rank which is assigned to this army member

View File

@ -0,0 +1,29 @@
# Login (object)
User related entity for creating a token using application credentials
## Properties
+ username: `testuser` (string, required) - username of the app-user
+ password: `password` (string, required) - password of the app-user
# LoginResponse (object)
Response object on successful token creation
## Properties
+ _id: `593d632772d35225232bcabb` (string, required) - the unique id of the app-user
+ username: `kallek` (string, required) - the username of the app-user
+ permission: 3 (number, required)- the permission level of the app-user (0 - user, 1 - sql, 2 - hl, 3 - maintainer, 4 - admin)
+ squad (Squad, required) - squad the app-user is member of
+ token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1OTNkNWUzZjcyZDM1MjI1MjIyYmNhYmEiLCJpYXQiOjE1MjEzOTQ5NDYsImV4cCI6MTUyMzgxNDE0Nn0.vOAwA5--qrx8BglhyGZWVYx7LeuRKOHH0NQXmHFNhIQ` (string,required) - generated access token
+ tokenExpireDate: `2018-04-15T17:42:25.050Z` (string,required) - expiration date of the token
# Registration (object)
User related entity for reflecting membership to another resource (groups, material databases)
## Properties
+ username: `kallek` (string, required) - the username of the app-user
+ password: `password` (string, required) - password of the app-user
+ secret: `random secret text` (string, required) - secret used for sign-up identification in connection with board usage

View File

@ -0,0 +1,22 @@
# Awarding (object)
Awarding associating a decoration to a user
## Properties
+ _id: `591ca81c1ee62711cfc19002` (string, required) - unique id of the awarding
+ confirmed: 1 (number, required) - status of the awarding request (0 - in progress, 1 - approved, 2 - rejected)
+ date: `2017-05-17T19:44:24.926Z` (string, required) - date when the awarding was requested
+ decorationId: `591c81981ee62711cfc18f4a` (string, required) - id of decoration that is given with the awarding
+ reason: `war dabei` (string, required) - reason for giving the awarding
+ proposer: 593d5e3f72d35225222bcaba (string, required) - id of app user who requested this awarding
+ timestamp: `2017-05-17T19:44:28.751Z` (string, required) - creation date
+ updatedAt: `"2017-05-17T19:44:28.751Z` (string, required) - version date
+ userId: `5918d2ca574b0b1d820a0b24` (string, required) - the unique id of the user who got this awarding
+ __v: 0 (number, required) - version number
# AwardingPopulated (Awarding)
Awarding with populated decoration and proposer
## Properties
+ proposer (Proposer, required) - app user who requested this awarding
+ decorationId (Decoration, required) - populated decoration object that is given with the awarding

View File

@ -0,0 +1,16 @@
# Campaign (object)
Campaign entity
## Properties
+ _id:`5abd55ea9e30a76bfef747d6` (string, required) - unique id of the campaign
+ title: `Ein Kessel Buntes` (string, required) - display title of the campaign
+ timestamp: `2017-05-17T19:44:28.751Z` (string, required) - creation date
+ updatedAt: `"2017-05-17T19:44:28.751Z` (string, required) - version date
+ __v: 0 (number, required) - version number
#WarCampaign (object)
Cmpaign entity with attached War collection
## Properties
+ _id: `5abd55ea9e30a76bfef747d6` (string, required) - unique id of the campaign
+ title: `Ein Kessel Buntes` (string, required) - display title of the campaign
+ wars (array[WarWithPlayers], requied)

View File

@ -0,0 +1,19 @@
# Decoration (object)
A decoration representing a ribbon or medal
## Properties
+ _id: `591c81981ee62711cfc18f4a` (string, required) - unique id of the decoration
+ description: `Verliehen an alle Teilnehmer der ArmA3-Kampagne "Sturm über der Ägäis" im Jahr 2016` (string, required) - description expressing what the decoration should be given for
+ fraction: `GLOBAL` (string, required) - enum expressing the fraction in which the decoration is given
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ isMedal: false (boolean, required) - show if decoration is medal or ribbon
+ name: `OPT4 pt.1 - "Sturm über der Ägäis"` (string, required) - display name of the decoration
+ sortingNumber: 10 (number, required) - sorting number for lists
+ timestamp: `2017-05-17T17:00:08.684Z` (string, required) - creation timestamp
+ updatedAt: `2018-01-13T09:57:50.827Z` (string, required) - version timestamp
+ __v: 2 (number, required) - version number

View File

@ -0,0 +1,79 @@
# Log (object)
## Properties
+ _id: `` (string, required) - log entry id
+ war: `` (string, required) - warId
+ time: `` (string, required) - logging timestamp
+ __v: 0 (number, required) - object version number
# LogPoints (Log)
## Properties
+ ptBlufor: 2 (number, required) - standings for BLUFOR
+ ptOpfor: 4 (number, required) - standings for OPFOR
+ fraction: `OPFOR` (enum, required) - dominating fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `NONE`
#LogBudget (Log)
## Properties
+ oldBudget: 400000 (number, required) - budget before buy action
+ newBudget: 380000 (number, required) - budget after buy action
+ fraction: `BLUFOR` (enum, required) - buying fraction
+ Members
+ `BLUFOR`
+ `OPFOR`
#LogFlag (Log)
## Properties
+ player: `HardiReady` (string, required) - name of player who captured/secured flag
+ capture: true (boolean, required) - true if flag was captured, false if it was secured
+ flagFraction: `BLUFOR` (enum, required) - fraction who owns the flag
+ Members
+ `BLUFOR`
+ `OPFOR`
#LogKill (Log)
## Properties
+ shooter: `HardiReady` (string, required) - name of player who made the kill
+ target: `KalleK` (string, required) - name of player which got killed
+ friendlyFire: true (boolean, required) - true if it was a friendly fire kill, false if it was a normal kill
+ fraction: `BLUFOR` (enum, required) - fraction of shooter
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `NONE`
#LogRespawn (Log)
## Properties
+ player: `radical1976` (string, required) - name of the player who respawns
#LogRevive (Log)
## Properties
+ medic: `radical1976` (string, required) - name of the player who revives/stabilizes
+ patient: `HardiReady` (string, required) - name of the player who is revived/stabilized
+ stabilized: false (boolean, required) - false if it is a revive, true if it is stabilizing
+ fraction: `BLUFOR` (enum, required) - fraction of the medic
+ Members
+ `BLUFOR`
+ `OPFOR`
#LogTransport (Log)
## Properties
+ driver: `radical1976` (string, required) - name of the vehicle driver/pilot
+ passenger: `radical1976` (string, required) - name of the passenger being transported
+ distance: 2435 (number, required) - distance of transport in meters
+ fraction: `BLUFOR` (enum, required) - fraction of the driver
+ Members
+ `BLUFOR`
+ `OPFOR`
#LogVehicle (Log)
## Properties
+ shooter: `HardiReady` (string, required) - name of player who shot the vehicle
+ target: `T-100` (string, required) - name of the vehicle
+ fraction: `BLUFOR` (enum, required) - fraction of the shooter
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `NONE`

View File

@ -0,0 +1,34 @@
# BasicPlayer (object)
Basic player statistic information object
##Properties
+ name: `Jagernaut` (string, required) - the displayed username of the army member
+ fraction: `OPFOR` (string, required) - fraction of the player
+ kill: 5 (number, required) - sum of kills
+ friendlyFire: 0 (number, required) - sum of friendly fire kills
+ vehicle: 1 (number, required) - sum of vehicle kills
+ death: 3 (number, required) - sum of deaths
+ respawn: 2 (number, required) - sum of respawns
+ flagTouch: 1 (number, required) - sum of flag captures
+ revive: 0 (number, required) - sum of revives
# WarPlayer (BasicPlayer)
A player campaign statistics object
## Properties
+ _id: `5ab68d42f547ed304064e5f7` (string, required) - unique id of the army member
+ warId: `5abf65ae3fc5fa349ffd5ca3` (string, required) - war in which this player took part
+ steamUUID: `76561192214911200` (string, required) - unique ID for STEAM platform account
+ sort: 1 (number, required) - sorting number calculated by (kill + revive + flagTouch - friendlyFire - death - respawn)
+ timestamp: `2018-02-24T01:01:25.825Z` - the entity creation timestamp
+ updatedAt: `2018-02-24T01:01:25.825Z` - the version timestamp
+ __v: 0 (number, required) - the version number of the object
# HighscorePlayer (BasicPlayer)
A player object as resturned for the highscore arrays
## Properties
+ num: 1

View File

@ -0,0 +1,20 @@
# Promotion (object)
Representation of a promotion request for a army member
## Properties
+ _id: `5as7d05dcb90ce4da68c4f5f` (string, required) - unique id of the promotion request
+ confirmed: 0 (number, required) - number representing status of the promotion (0 - in progress, 1 - approved, 2 - rejected)
+ newRankLvl: 14 (number, required)- new rank level that is requested in this promotion
+ oldRankLvl: 10 (number, required)- old rank level of the user
+ proposer: `5abf5064861d950f157c4a09` (string, required) - id of app user who requested the promotion
+ timestamp: `2018-03-25T18:54:21.609Z` (string, required) - creation timestamp
+ updatedAt: `2018-03-25T18:54:21.609Z` (string, required) - version timestamp
+ userId: `5ab68d42f547ed304064e5f7` (string, required) - id of army member the promotion is requested for
+ __v: 0 (number, required) - version number of promotion instance
# PromotionPopulated (object)
Promotion with populated proposer and army member
## Properties
+ proposer (Proposer, required) - app user who requested the promotion
+ userId (User, required) - populated user instance of user the promotion is requested for

View File

@ -0,0 +1,16 @@
# Rank (object)
A rank that can be applied to army members by level/fraction
## Properties
+ _id: `591469c9ef4a6810623ed4ea` (string, required) - the unique id of the rank
+ name: `Gefreiter` (string, required) - display name of the rank
+ fraction: `BLUFOR` (enum, required) - enum expressing the fraction in which the decoration is given
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `GLOBAL`
+ level: 1 (number, required) - rank level
+ updatedAt: `2017-09-20T20:25:29.995Z` (string, required) - the version timestamp
+ timestamp: `2017-05-11T13:40:25.051Z` (string, required) - the creation timestamp
+ __v: 1 (number, required) - version number

View File

@ -0,0 +1,13 @@
# Squad (object)
A Squad entity
## Properties
+ _id: `591470249e9fae286e008e31` (string, required) - the unique id of the squad
+ sortingNumber: 30 (number, required) - the number for orders in lists of squads
+ updatedAt: `2017-05-31T20:43:07.165Z` (string, required) - version timestamp
+ timestamp: `2017-05-11T14:07:32.471Z` (string, required) - creation timestamp
+ name: `Alpha` (string, required) - display name of the squad
+ fraction: `BLUFOR` (string, required) - fraction the squad is part of
+ __v: 2 (number, required) - version number

View File

@ -0,0 +1,22 @@
# User (object)
A participant managed in the system
## Properties
+ _id: `5ab68d42f547ed304064e5f7` (string, required) - unique id of the army member
+ username: `Jagernaut` (string, required) - the displayed username of the army member
+ rankLvl: 22 (number, required) - rank level representing the rank
+ squadId: `591470249e9fae286e008e31` (string, required, nullable) - id of squad which the army member is part of
+ updatedAt: `2018-02-24T01:01:25.825Z` - the version timestamp
+ __v: 35 (number, required) - the version number of the object
# UserPopulated (User)
User object with populated squad
## Properties
+ squadId (Squad, required) - populated squad which the army member is part of
# UserInclTimestamp (User)
A participant managed in the system, incl. creation timestamp
## Properties
+ timestamp: 2018-01-20T12:11:23.125Z (string, required) - instance creation timestamp

View File

@ -0,0 +1,26 @@
# War (object)
A war as used in statistics
## Properties
+ _id: `5ab68d41f537ed304064e5f7` (string, required) - unique id of the war
+ title: `Battle No.1` (string, required) - the display neme of the war
+ date: `2018-02-24T20:01:25.825Z` (string, required) - war start timestamp
+ endDate: `2018-02-24T22:31:26.855Z` (string, required) - war end timestamp
+ ptBlufor: 11 (number, required) - final points fraction Blufor
+ ptOpfor: 12 (number, required) - final points fraction Opfor
+ playersBlufor: 36 (number, required) - player count of fraction Blufor
+ playersOpfor: 34 (number, required) - player count of fraction opfor
+ campaign: `5abd55ea9e30a76bfef747d6` (string, required) - uniquer id of the campaign in which the war was played
+ budgetBlufor: 3900000 (number, required) - start budget of fraction Blufor
+ budgetOpfor: 4100000 (number, required) - start budget of fraction Opfor
+ endBudgetBlufor: 924000 (number, required) - end budget of fraction Blufor
+ endBudgetOpfor: 12400 (number, required) - end budget of fraction Opfor
+ timestamp: `2018-02-24T01:01:25.825Z` (string, required) - creation date
+ updatedAt: `2018-02-24T01:01:25.825Z` (string, required) - the version timestamp
+ __v: 0 (number, required) - the version number of the object
# WarWithPlayers (War)
A war response object on creation
## Properties
+ players (array[WarPlayer], required) - collection of all participating players with their statistics

View File

@ -0,0 +1,27 @@
# Data Structures
:[Gists](_app-user.apib)
:[Gists](_army.apib)
:[Gists](_auth.apib)
:[Gists](_awarding.apib)
:[Gists](_campaign.apib)
:[Gists](_decoration.apib)
:[Gists](_log.apib)
:[Gists](_player.apib)
:[Gists](_promotion.apib)
:[Gists](_rank.apib)
:[Gists](_squad.apib)
:[Gists](_user.apib)
:[Gists](_war.apib)

71
api/apib/dev-doc.apib Normal file
View File

@ -0,0 +1,71 @@
FORMAT: 1A
# Operation Pandora Trigger Command Center API Documentation
:[Gists](data_structures/index.apib)
# Group General Introduction
:[Gists](base/access.apib)
:[Gists](base/datetime.apib)
# Group File Resources
:[Gists](base/file-resources.apib)
# Group Access
:[Gists](auth/signup.apib)
:[Gists](auth/login.apib)
# Group Admin
:[Gists](admin/account.apib)
:[Gists](admin/signature.apib)
# Group Army Overview
:[Gists](army-management/army.apib)
# Group Awardings
:[Gists](army-management/awardings.apib)
# Group Campaigns
:[Gists](statistics/campaigns.apib)
# Group Decorations
:[Gists](army-management/decorations.apib)
# Group Logs
:[Gists](statistics/logs.apib)
# Group Players
:[Gists](statistics/players.apib)
# Group Promotion
:[Gists](army-management/promotions.apib)
# Group Ranks
:[Gists](army-management/ranks.apib)
# Group Squads
:[Gists](army-management/squads.apib)
# Group Users
:[Gists](army-management/users.apib)
# Group Wars
:[Gists](statistics/wars.apib)

View File

@ -0,0 +1,3 @@
{"_id":{"$oid":"5abf5064861d950f157c4a09"},"squad":{"$oid":"5aba54eaeadcce6332c6a774"},"permission":1,"activated":true,"username":"some-squadlead","secret":"asd","password":"$2a$10$PGUw2F6HY.2Q3h0AL4Mea.3EKAGFO8S897z56CyU8q/5BC.5VEPAy","timestamp":{"$date":"2018-03-31T09:09:56.161Z"},"updatedAt":{"$date":"2018-03-31T09:09:56.161Z"},"__v":1}
{"_id":{"$oid":"5ab68ceef547ed304064e5f6"},"squad":{"$oid":"5aba54eaeadcce6332c6a774"},"permission":4,"activated":true,"username":"testuser","secret":"my secret","password":"$2a$10$wvgBbcckHrFu8Ctw8hSPNuFLoBy4sRubioyiK1NabOC0UgYD.KITi","timestamp":{"$date":"2018-03-24T17:37:50.668Z"},"updatedAt":{"$date":"2018-03-24T17:37:50.668Z"},"__v":0}
{"_id":{"$oid":"5ac0de67b5edc7771c027b94"},"squad":null,"permission":0,"activated":false,"username":"lord","secret":"tank","password":"$2a$10$k9LG.OiedyyGrySbDX7KZekesHEU.BadBhUUyS2ujM07NpIrLPFxe","timestamp":{"$date":"2018-04-01T13:28:07.147Z"},"updatedAt":{"$date":"2018-04-01T13:28:07.147Z"},"__v":0}

View File

@ -0,0 +1 @@
{"_id":{"$oid":"5abf50e7861d950f157c4a0b"},"confirmed":0,"date":{"$date":"2018-03-31T09:12:07.861Z"},"userId":{"$oid":"5ab68d42f547ed304064e5f7"},"decorationId":{"$oid":"5abd3dff6e6a0334d95b8ba0"},"reason":"bester mann!","proposer":{"$oid":"5abf5064861d950f157c4a09"},"timestamp":{"$date":"2018-03-31T09:12:07.873Z"},"updatedAt":{"$date":"2018-03-31T09:12:07.873Z"},"__v":0}

View File

@ -0,0 +1,2 @@
{"_id":{"$oid":"5abd55ea9e30a76bfef747d6"},"title":"Ein Kessel Buntes","timestamp":{"$date":"2018-03-29T21:08:58.123Z"},"updatedAt":{"$date":"2018-03-29T21:08:58.123Z"},"__v":0}
{"_id":{"$oid":"5abd58989e30a76bfef747e6"},"title":"This Is The End","timestamp":{"$date":"2018-03-29T21:20:24.558Z"},"updatedAt":{"$date":"2018-03-29T21:20:24.558Z"},"__v":0}

View File

@ -0,0 +1,3 @@
{"_id":{"$oid":"5abeb420b987672bb1ede643"},"sortingNumber":0,"name":"Teilnehmer der Kampagne X","fraction":"GLOBAL","isMedal":false,"description":"Dabei sein ist alles","timestamp":{"$date":"2018-03-30T22:03:12.228Z"},"updatedAt":{"$date":"2018-03-30T22:03:12.228Z"},"__v":0}
{"_id":{"$oid":"5abd3dff6e6a0334d95b8ba0"},"sortingNumber":0,"name":"Orden der Tapferkeit","fraction":"BLUFOR","isMedal":true,"description":"Das ist ein Orden den tapfere Leute bekommen","timestamp":{"$date":"2018-03-29T19:26:55.387Z"},"updatedAt":{"$date":"2018-03-29T19:26:55.387Z"},"__v":0}
{"_id":{"$oid":"5abeb43cb987672bb1ede644"},"sortingNumber":0,"name":"Ehrenmedaille","fraction":"OPFOR","isMedal":true,"description":"Fuer die Ehre","timestamp":{"$date":"2018-03-30T22:03:40.560Z"},"updatedAt":{"$date":"2018-03-30T22:03:40.560Z"},"__v":0}

View File

@ -0,0 +1,4 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb6"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:01:00.000Z"},"fraction":"BLUFOR","oldBudget":4535500,"newBudget":4195500,"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5ccf"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"fraction":"OPFOR","oldBudget":2384250,"newBudget":2324250,"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cce"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:01:00.000Z"},"fraction":"BLUFOR","oldBudget":4535500,"newBudget":4195500,"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb7"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"fraction":"OPFOR","oldBudget":2384250,"newBudget":2324250,"__v":0}

View File

@ -0,0 +1,4 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb5"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:13:00.000Z"},"player":"ALASTOR","flagFraction":"OPFOR","capture":false,"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5ccc"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:12:00.000Z"},"player":"Wiesl","flagFraction":"OPFOR","capture":true,"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5ccd"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:13:00.000Z"},"player":"ALASTOR","flagFraction":"OPFOR","capture":false,"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb4"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:12:00.000Z"},"player":"Wiesl","flagFraction":"OPFOR","capture":true,"__v":0}

View File

@ -0,0 +1,8 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cac"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"shooter":null,"target":"Saxe","friendlyFire":false,"fraction":"NONE","__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cae"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:12:00.000Z"},"shooter":"Wiesl","target":"patze","friendlyFire":false,"fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cad"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"shooter":"Saxe","target":"Pumarang","friendlyFire":true,"fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5caf"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:12:00.000Z"},"shooter":"Wiesl","target":"Nicolas","friendlyFire":true,"fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc4"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"shooter":null,"target":"Saxe","friendlyFire":false,"fraction":"NONE","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc5"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"shooter":"Saxe","target":"Pumarang","friendlyFire":true,"fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc7"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:12:00.000Z"},"shooter":"Wiesl","target":"Nicolas","friendlyFire":true,"fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc6"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:12:00.000Z"},"shooter":"Wiesl","target":"patze","friendlyFire":false,"fraction":"BLUFOR","__v":0}

View File

@ -0,0 +1,4 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cba"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:13:00.000Z"},"ptBlufor":1,"ptOpfor":0,"fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb9"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:10:00.000Z"},"ptBlufor":0,"ptOpfor":0,"fraction":"NONE","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cd1"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:10:00.000Z"},"ptBlufor":0,"ptOpfor":0,"fraction":"NONE","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cd2"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:13:00.000Z"},"ptBlufor":1,"ptOpfor":0,"fraction":"BLUFOR","__v":0}

View File

@ -0,0 +1,2 @@
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc9"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"player":"Pumarang","__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb1"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:04:00.000Z"},"player":"Pumarang","__v":0}

View File

@ -0,0 +1,4 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb3"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:35:00.000Z"},"stabilized":false,"medic":"Wiesl","patient":"Andi-de","fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5ccb"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:35:00.000Z"},"stabilized":false,"medic":"Wiesl","patient":"Andi-de","fraction":"BLUFOR","__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb2"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:35:00.000Z"},"stabilized":true,"medic":"ALASTOR","patient":"Bodochecker","fraction":"OPFOR","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cca"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:35:00.000Z"},"stabilized":true,"medic":"ALASTOR","patient":"Bodochecker","fraction":"OPFOR","__v":0}

View File

@ -0,0 +1,2 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb8"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:31:00.000Z"},"driver":"Ponykloete","passenger":" Dominik","fraction":"BLUFOR","distance":8666,"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cd0"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:31:00.000Z"},"driver":"Ponykloete","passenger":" Dominik","fraction":"BLUFOR","distance":8666,"__v":0}

View File

@ -0,0 +1,2 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cb0"},"war":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"time":{"$date":"2018-03-19T23:16:00.000Z"},"shooter":"Murda]X[","target":"Hunter-HMG","fraction":"OPFOR","__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc8"},"war":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"time":{"$date":"2018-03-19T23:16:00.000Z"},"shooter":"Murda]X[","target":"Hunter-HMG","fraction":"OPFOR","__v":0}

View File

@ -0,0 +1,16 @@
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca4"},"name":"Pumarang","fraction":"BLUFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.656119805032149e+16,"respawn":1,"kill":0,"vehicle":0,"friendlyFire":0,"death":1,"revive":0,"flagTouch":0,"sort":-2,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca5"},"name":"Mercurat","fraction":"BLUFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.65611982788425e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca6"},"name":"KalleK","fraction":"OPFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.656119797767603e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca7"},"name":"MAPster","fraction":"OPFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.656119800988213e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca8"},"name":"LyrikEmu","fraction":"BLUFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.65611982189104e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca9"},"name":"Philipp","fraction":"OPFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.656119804179206e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5caa"},"name":"Wiesl","fraction":"BLUFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.65611980596481e+16,"respawn":0,"kill":1,"vehicle":0,"friendlyFire":1,"death":0,"revive":1,"flagTouch":1,"sort":2,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5cab"},"name":"Murda]X[","fraction":"OPFOR","warId":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"steamUUID":7.656119797112163e+16,"respawn":0,"kill":0,"vehicle":1,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:40:46.712Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.712Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cbc"},"name":"Pumarang","fraction":"BLUFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.656119805032149e+16,"respawn":1,"kill":0,"vehicle":0,"friendlyFire":0,"death":1,"revive":0,"flagTouch":0,"sort":-2,"timestamp":{"$date":"2018-03-31T10:41:28.459Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.459Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cbd"},"name":"Mercurat","fraction":"BLUFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.65611982788425e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:41:28.459Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.459Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cbe"},"name":"KalleK","fraction":"OPFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.656119797767603e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:41:28.459Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.459Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc1"},"name":"Philipp","fraction":"OPFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.656119804179206e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:41:28.459Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.459Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc0"},"name":"LyrikEmu","fraction":"BLUFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.65611982189104e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:41:28.459Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.459Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc2"},"name":"Wiesl","fraction":"BLUFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.65611980596481e+16,"respawn":0,"kill":1,"vehicle":0,"friendlyFire":1,"death":0,"revive":1,"flagTouch":1,"sort":2,"timestamp":{"$date":"2018-03-31T10:41:28.460Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.460Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cc3"},"name":"Murda]X[","fraction":"OPFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.656119797112163e+16,"respawn":0,"kill":0,"vehicle":1,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:41:28.460Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.460Z"},"__v":0}
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cbf"},"name":"MAPster","fraction":"OPFOR","warId":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"steamUUID":7.656119800988213e+16,"respawn":0,"kill":0,"vehicle":0,"friendlyFire":0,"death":0,"revive":0,"flagTouch":0,"sort":0,"timestamp":{"$date":"2018-03-31T10:41:28.459Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.459Z"},"__v":0}

View File

@ -0,0 +1 @@
{"_id":{"$oid":"5abf50d9861d950f157c4a0a"},"userId":{"$oid":"5ab68d42f547ed304064e5f7"},"oldRankLvl":0,"newRankLvl":10,"confirmed":0,"proposer":{"$oid":"5abf5064861d950f157c4a09"},"timestamp":{"$date":"2018-03-31T09:11:53.364Z"},"updatedAt":{"$date":"2018-03-31T09:11:53.364Z"},"__v":0}

View File

@ -0,0 +1,3 @@
{"_id":{"$oid":"5abeb23995cf43205225710b"},"name":"Unteroffizier","fraction":"BLUFOR","level":10,"timestamp":{"$date":"2018-03-30T21:55:05.625Z"},"updatedAt":{"$date":"2018-03-30T21:55:05.625Z"},"__v":0}
{"_id":{"$oid":"5aba5504eadcce6332c6a775"},"name":"Gefreiter","fraction":"BLUFOR","level":0,"timestamp":{"$date":"2018-03-27T14:28:20.948Z"},"updatedAt":{"$date":"2018-03-27T14:28:20.948Z"},"__v":0}
{"_id":{"$oid":"5abeb1b995cf43205225710a"},"name":"General","fraction":"OPFOR","level":22,"timestamp":{"$date":"2018-03-30T21:52:57.204Z"},"updatedAt":{"$date":"2018-03-30T21:52:57.204Z"},"__v":0}

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

View File

@ -0,0 +1,34 @@
2018/03/20, 20:05:43 "[OPT] (Budget) LOG: 0:00:00 --- Startbudget: NATO 4.5355e+006 - CSAT 4.22125e+006"
2018/03/20, 20:06:45 "[OPT] (Budget) LOG: 0:01:02 --- NATO alt: 4.5355e+006 - neu: 4.1955e+006 - Differenz: -340000"
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Saxe (WEST) von: Selbstverschulden."
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Pumarang (WEST) von: Saxe (WEST)."
2018/03/20, 20:10:13 "[OPT] (Budget) LOG: 0:04:30 --- CSAT alt: 2.38425e+006 - neu: 2.32425e+006 - Differenz: -60000"
2018/03/20, 20:15:54 "[OPT] (Punkte) LOG: 0:10:11 --- Kein Dominator (NATO 0 | CSAT 0)"
2018/03/20, 20:17:51 "[OPT] (Abschuss) LOG: 0:12:08 --- patze (EAST) von: Wiesl (WEST)."
2018/03/20, 20:18:20 "[OPT] (Fahne) LOG: 0:12:37 --- CSAT Flagge erobert von Wiesl"
2018/03/20, 20:18:38 "[OPT] (Abschuss) LOG: 0:12:55 --- Nicolas (WEST) von: Wiesl (WEST)."
2018/03/20, 20:18:59 "[OPT] (Punkte) LOG: 0:13:16 --- NATO +1 (NATO 1 | CSAT 0)"
2018/03/20, 20:19:38 "[OPT] (Fahne) LOG: 0:13:56 --- CSAT Flagge gesichert von ALASTOR"
2018/03/20, 20:22:18 "[OPT] (Abschuss) LOG: 0:16:35 --- Fahrzeug: Hunter-HMG (OPT_NATO) von: Murda]X[ (EAST)."
2018/03/20, 20:37:19 "[OPT] (Transport) LOG: 0:31:36 --- Dominik (WEST) wurde von Ponykloete (WEST) eingeflogen (8666.94 m)"
2018/03/20, 20:41:27 "[OPT] (Revive) LOG: 0:35:44 --- Bodochecker (EAST) wurde von ALASTOR (EAST) stabilisiert."
2018/03/20, 20:41:35 "[OPT] (Revive) LOG: 0:35:52 --- Andi-de (WEST) wurde von Wiesl (WEST) wiederbelebt."
2018/03/20, 22:35:43 "[OPT] (Budget) LOG: 2:30:00 --- Endbudget: (NATO 1997000 | CSAT 512000)"
2018/03/20, 22:35:43 "[OPT] (Punkte) LOG: 2:30:00 --- Endpunktestand: (NATO 34 | CSAT 25)"

View File

@ -0,0 +1,29 @@
Error: ENOENT: no such file or directory, open '
2018/03/20, 20:05:43 "[OPT] (Budget) LOG: 0:00:00 --- Startbudget: NATO 4.5355e+006 - CSAT 4.22125e+006"
2018/03/20, 20:06:45 "[OPT] (Budget) LOG: 0:01:02 --- NATO alt: 4.5355e+006 - neu: 4.1955e+006 - Differenz: -340000"
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Saxe (WEST) von: Selbstverschulden."
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Pumarang (WEST) von: Saxe (WEST)."
2018/03/20, 20:10:13 "[OPT] (Budget) LOG: 0:04:30 --- CSAT alt: 2.38425e+006 - neu: 2.32425e+006 - Differenz: -60000"
2018/03/20, 20:10:38 "[OPT] (Respawn) LOG: 0:04:55 --- Spieler: Pumarang - Kosten: 3000"
2018/03/20, 20:15:54 "[OPT] (Punkte) LOG: 0:10:11 --- Kein Dominator (NATO 0 | CSAT 0)"
2018/03/20, 20:17:51 "[OPT] (Abschuss) LOG: 0:12:08 --- patze (EAST) von: Wiesl (WEST)."
2018/03/20, 20:18:20 "[OPT] (Fahne) LOG: 0:12:37 --- CSAT Flagge erobert von Wiesl"
2018/03/20, 20:18:38 "[OPT] (Abschuss) LOG: 0:12:55 --- Nicolas (WEST) von: Wiesl (WEST)."
2018/03/20, 20:18:59 "[OPT] (Punkte) LOG: 0:13:16 --- NATO +1 (NATO 1 | CSAT 0)"
2018/03/20, 20:19:38 "[OPT] (Fahne) LOG: 0:13:56 --- CSAT Flagge gesichert von ALASTOR"
2018/03/20, 20:22:18 "[OPT] (Abschuss) LOG: 0:16:35 --- Fahrzeug: Hunter-HMG (OPT_NATO) von: Murda]X[ (EAST)."
2018/03/20, 20:37:19 "[OPT] (Transport) LOG: 0:31:36 --- Dominik (WEST) wurde von Ponykloete (WEST) eingeflogen (8666.94 m)"
2018/03/20, 20:41:27 "[OPT] (Revive) LOG: 0:35:44 --- Bodochecker (EAST) wurde von ALASTOR (EAST) stabilisiert."
2018/03/20, 20:41:35 "[OPT] (Revive) LOG: 0:35:52 --- Andi-de (WEST) wurde von Wiesl (WEST) wiederbelebt."
2018/03/20, 22:35:43 "[OPT] (Mission) LOG: 2:30:00 --- Missionzeit abgelaufen"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Pumarang (WEST), PUID 76561198050321485"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Mercurat (WEST), PUID 76561198278842491"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- KalleK (EAST), PUID 76561197977676036"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- MAPster (EAST), PUID 76561198009882133"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- LyrikEmu (WEST), PUID 76561198218910400"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Philipp (EAST), PUID 76561198041792069"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Wiesl (WEST), PUID 76561198059648090"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Murda]X[ (EAST), PUID 76561197971121630"
2018/03/20, 22:35:43 "[OPT] (Budget) LOG: 2:30:00 --- Endbudget: (NATO 1997000 | CSAT 512000)"
2018/03/20, 22:35:43 "[OPT] (Punkte) LOG: 2:30:00 --- Endpunktestand: (NATO 34 | CSAT 25)"
'

View File

@ -0,0 +1,34 @@
2018/03/20, 20:05:43 "[OPT] (Budget) LOG: 0:00:00 --- Startbudget: NATO 4.5355e+006 - CSAT 4.22125e+006"
2018/03/20, 20:06:45 "[OPT] (Budget) LOG: 0:01:02 --- NATO alt: 4.5355e+006 - neu: 4.1955e+006 - Differenz: -340000"
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Saxe (WEST) von: Selbstverschulden."
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Pumarang (WEST) von: Saxe (WEST)."
2018/03/20, 20:10:13 "[OPT] (Budget) LOG: 0:04:30 --- CSAT alt: 2.38425e+006 - neu: 2.32425e+006 - Differenz: -60000"
2018/03/20, 20:15:54 "[OPT] (Punkte) LOG: 0:10:11 --- Kein Dominator (NATO 0 | CSAT 0)"
2018/03/20, 20:17:51 "[OPT] (Abschuss) LOG: 0:12:08 --- patze (EAST) von: Wiesl (WEST)."
2018/03/20, 20:18:20 "[OPT] (Fahne) LOG: 0:12:37 --- CSAT Flagge erobert von Wiesl"
2018/03/20, 20:18:38 "[OPT] (Abschuss) LOG: 0:12:55 --- Nicolas (WEST) von: Wiesl (WEST)."
2018/03/20, 20:18:59 "[OPT] (Punkte) LOG: 0:13:16 --- NATO +1 (NATO 1 | CSAT 0)"
2018/03/20, 20:19:38 "[OPT] (Fahne) LOG: 0:13:56 --- CSAT Flagge gesichert von ALASTOR"
2018/03/20, 20:22:18 "[OPT] (Abschuss) LOG: 0:16:35 --- Fahrzeug: Hunter-HMG (OPT_NATO) von: Murda]X[ (EAST)."
2018/03/20, 20:37:19 "[OPT] (Transport) LOG: 0:31:36 --- Dominik (WEST) wurde von Ponykloete (WEST) eingeflogen (8666.94 m)"
2018/03/20, 20:41:27 "[OPT] (Revive) LOG: 0:35:44 --- Bodochecker (EAST) wurde von ALASTOR (EAST) stabilisiert."
2018/03/20, 20:41:35 "[OPT] (Revive) LOG: 0:35:52 --- Andi-de (WEST) wurde von Wiesl (WEST) wiederbelebt."
2018/03/20, 22:35:43 "[OPT] (Budget) LOG: 2:30:00 --- Endbudget: (NATO 1997000 | CSAT 512000)"
2018/03/20, 22:35:43 "[OPT] (Punkte) LOG: 2:30:00 --- Endpunktestand: (NATO 34 | CSAT 25)"

View File

@ -0,0 +1,29 @@
Error: ENOENT: no such file or directory, open '
2018/03/20, 20:05:43 "[OPT] (Budget) LOG: 0:00:00 --- Startbudget: NATO 4.5355e+006 - CSAT 4.22125e+006"
2018/03/20, 20:06:45 "[OPT] (Budget) LOG: 0:01:02 --- NATO alt: 4.5355e+006 - neu: 4.1955e+006 - Differenz: -340000"
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Saxe (WEST) von: Selbstverschulden."
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Pumarang (WEST) von: Saxe (WEST)."
2018/03/20, 20:10:13 "[OPT] (Budget) LOG: 0:04:30 --- CSAT alt: 2.38425e+006 - neu: 2.32425e+006 - Differenz: -60000"
2018/03/20, 20:10:38 "[OPT] (Respawn) LOG: 0:04:55 --- Spieler: Pumarang - Kosten: 3000"
2018/03/20, 20:15:54 "[OPT] (Punkte) LOG: 0:10:11 --- Kein Dominator (NATO 0 | CSAT 0)"
2018/03/20, 20:17:51 "[OPT] (Abschuss) LOG: 0:12:08 --- patze (EAST) von: Wiesl (WEST)."
2018/03/20, 20:18:20 "[OPT] (Fahne) LOG: 0:12:37 --- CSAT Flagge erobert von Wiesl"
2018/03/20, 20:18:38 "[OPT] (Abschuss) LOG: 0:12:55 --- Nicolas (WEST) von: Wiesl (WEST)."
2018/03/20, 20:18:59 "[OPT] (Punkte) LOG: 0:13:16 --- NATO +1 (NATO 1 | CSAT 0)"
2018/03/20, 20:19:38 "[OPT] (Fahne) LOG: 0:13:56 --- CSAT Flagge gesichert von ALASTOR"
2018/03/20, 20:22:18 "[OPT] (Abschuss) LOG: 0:16:35 --- Fahrzeug: Hunter-HMG (OPT_NATO) von: Murda]X[ (EAST)."
2018/03/20, 20:37:19 "[OPT] (Transport) LOG: 0:31:36 --- Dominik (WEST) wurde von Ponykloete (WEST) eingeflogen (8666.94 m)"
2018/03/20, 20:41:27 "[OPT] (Revive) LOG: 0:35:44 --- Bodochecker (EAST) wurde von ALASTOR (EAST) stabilisiert."
2018/03/20, 20:41:35 "[OPT] (Revive) LOG: 0:35:52 --- Andi-de (WEST) wurde von Wiesl (WEST) wiederbelebt."
2018/03/20, 22:35:43 "[OPT] (Mission) LOG: 2:30:00 --- Missionzeit abgelaufen"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Pumarang (WEST), PUID 76561198050321485"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Mercurat (WEST), PUID 76561198278842491"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- KalleK (EAST), PUID 76561197977676036"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- MAPster (EAST), PUID 76561198009882133"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- LyrikEmu (WEST), PUID 76561198218910400"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Philipp (EAST), PUID 76561198041792069"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Wiesl (WEST), PUID 76561198059648090"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Murda]X[ (EAST), PUID 76561197971121630"
2018/03/20, 22:35:43 "[OPT] (Budget) LOG: 2:30:00 --- Endbudget: (NATO 1997000 | CSAT 512000)"
2018/03/20, 22:35:43 "[OPT] (Punkte) LOG: 2:30:00 --- Endpunktestand: (NATO 34 | CSAT 25)"
'

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

View File

@ -0,0 +1,3 @@
{"_id":{"$oid":"5aba54eaeadcce6332c6a774"},"sortingNumber":0,"name":"Alpha","fraction":"BLUFOR","timestamp":{"$date":"2018-03-27T14:27:54.399Z"},"updatedAt":{"$date":"2018-03-27T14:27:54.399Z"},"__v":0}
{"_id":{"$oid":"5abe166f8b7488392a623f12"},"sortingNumber":0,"name":"Führungsstab","fraction":"OPFOR","timestamp":{"$date":"2018-03-30T10:50:23.121Z"},"updatedAt":{"$date":"2018-03-30T10:50:23.121Z"},"__v":0}
{"_id":{"$oid":"5abe16f98b7488392a623f17"},"sortingNumber":4,"name":"Bravo","fraction":"BLUFOR","timestamp":{"$date":"2018-03-30T10:52:41.396Z"},"updatedAt":{"$date":"2018-03-30T10:52:41.396Z"},"__v":0}

View File

@ -0,0 +1,2 @@
{"_id":{"$oid":"5ab68d42f547ed304064e5f7"},"rankLvl":0,"squadId":{"$oid":"5aba54eaeadcce6332c6a774"},"username":"hardiready","updatedAt":{"$date":"2018-03-27T14:28:04.533Z"},"__v":0}
{"_id":{"$oid":"5abd4780396bc0487068be0e"},"rankLvl":0,"squadId":null,"username":"KalleK","timestamp":{"$date":"2018-03-29T20:07:28.960Z"},"updatedAt":{"$date":"2018-03-29T20:07:28.960Z"},"__v":0}

View File

@ -0,0 +1,2 @@
{"_id":{"$oid":"5abf65d83fc5fa349ffd5cbb"},"playersBlufor":4,"playersOpfor":4,"budgetBlufor":4535500,"budgetOpfor":4221250,"endBudgetBlufor":997000,"endBudgetOpfor":512000,"title":"Battle #1","campaign":{"$oid":"5abd55ea9e30a76bfef747d6"},"date":{"$date":"2018-03-19T23:00:00.000Z"},"endDate":{"$date":"2018-03-20T01:30:00.000Z"},"ptBlufor":34,"ptOpfor":25,"timestamp":{"$date":"2018-03-31T10:41:28.451Z"},"updatedAt":{"$date":"2018-03-31T10:41:28.451Z"},"__v":0}
{"_id":{"$oid":"5abf65ae3fc5fa349ffd5ca3"},"playersBlufor":4,"playersOpfor":4,"budgetBlufor":4535500,"budgetOpfor":4221250,"endBudgetBlufor":997000,"endBudgetOpfor":512000,"title":"Test Battle #0","campaign":{"$oid":"5abd55ea9e30a76bfef747d6"},"date":{"$date":"2018-03-19T23:00:00.000Z"},"endDate":{"$date":"2018-03-20T01:30:00.000Z"},"ptBlufor":34,"ptOpfor":25,"timestamp":{"$date":"2018-03-31T10:40:46.695Z"},"updatedAt":{"$date":"2018-03-31T10:40:46.695Z"},"__v":0}

39
api/apib/dredd/populate-data.sh Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
########### IMPORT EXPORT/SCRIPT FOR DREDD TEST DATA ##############
###-------------------------------------------------------------###
###------------------- HOW TO USE THIS FILE --------------------###
###-------------------------------------------------------------###
### 1. start express server with `npm run start-test` ###
### 2. import data by executing script: `./populate-data.sh` ###
### 3. change data in app as you need for tests ###
### 4. export data state with: `./populate-data.sh -m save` ###
###################################################################
# execute script in its location folder
cd $(dirname $0)
while getopts m:p:d: option
do
case "${option}"
in
m) MODE=${OPTARG};;
p) PORT=${OPTARG};;
d) DATABASE=${OPTARG};;
esac
done
# array of available collection names
col=(app_user awarding campaign decoration logBudget logFlag logKill logPoints logRespawn logRevive logTransport logVehicle player promotion rank squad user war )
for i in "${col[@]}"
do
# provide date for restore process, if data import is needed
if [ "$MODE" == "save" ]
then
echo -e "$(date "+%Y-%m-%dT%T.%3N%z")\tTable: ${i}"
mongoexport --db ${DATABASE} --port ${PORT} --collection $i --out data/${i}.json;
else
mongoimport --db ${DATABASE} --port ${PORT} --collection $i --drop --file data/${i}.json
fi
done

View File

@ -0,0 +1,37 @@
### Get Campaign [GET /campaigns/{id}]
Get single campaign information
+ Parameters
+ id: `5abd55ea9e30a76bfef747d6` (string, required) - unique id of campaign
+ Response 200 (application/json; charset=utf-8)
+ Attributes (Campaign, fixed-type)
### Create Campaign [POST /campaigns]
Create a new campaign
**Permission: 3**
+ Request Create new army member (application/json)
+ Attributes
+ title: `Return To Kessel In A Schmelz` (string, required) - display name of the campaign
+ Response 201 (application/json; charset=utf-8)
+ Attributes (Campaign, fixed-type)
### Delete Campaign [DELETE /campaigns/{id}]
Delete a campaign
**Permission: 3**
+ Parameters
+ id: `5abd58989e30a76bfef747e6` (string, required) - unique id of campaign
+ Response 204

View File

@ -0,0 +1,18 @@
### Get War Logs [GET /logs/{warId}]
Ge the combined log object, containing all events in collections, for a single war
+ Parameters
+ warId: `5abf65ae3fc5fa349ffd5ca3` (string, required) - unique id of the war
+ Response 200 (application/json; charset=utf-8)
+ Attributes
+ points (array[LogPoints], required)
+ budget (array[LogBudget], required)
+ respawn (array[LogRespawn], required)
+ revive (array[LogRevive], required)
+ kill (array[LogKill], required)
+ vehicle (array[LogVehicle], required)
+ transport (array[LogTransport], required)
+ flag (array[LogFlag], required)

View File

@ -0,0 +1,34 @@
### Get Player Highscore [GET /players/ranking/{campaignId}]
List highscores for player statistics over all wars of a certain campaign.
Every highscore player object contains a field *sum*, representing its order number in the collection
+ Parameters
+ campaignId: `5abd55ea9e30a76bfef747d6` (string, required) - unique id of the campaign
+ Response 200 (application/json; charset=utf-8)
+ Attributes
+ kill (array[HighscorePlayer],required) - player highscore for kill
+ death (array[HighscorePlayer],required) - player highscore for death
+ friendlyFire (array[HighscorePlayer],required) - player highscore for friendly fire
+ vehicle (array[HighscorePlayer],required) - player highscore for vehicle
+ revive (array[HighscorePlayer],required) - player highscore for revive
+ respawn (array[HighscorePlayer],required) - player highscore for respawn
+ flagTouch (array[HighscorePlayer],required) - player highscore for flag captures
### Get Player Campaign Statistics [GET /players/single/{campaignId}/{playerSteamId}]
Get statistics for a single player for all wars of a campaign he took part at
+ Parameters
+ campaignId: `5abd55ea9e30a76bfef747d6` (string, required) - unique id of the campaign
+ playerSteamId: `76561198050321490` (string, required) - STEAM application unique user id of player
+ Response 200 (application/json; charset=utf-8)
+ Attributes
+ name: `Pumarang` (string, required) - latest used playername
+ campaign (Campaign,required) - campaign reflected from request is
+ players (array[WarPlayer],required) - collection of player instances in the campaign

View File

@ -0,0 +1,89 @@
### List Wars [GET /wars]
List all wars, subordinate to their campaign
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[WarCampaign], fixed-type)
### Get War [GET /wars/{id}]
Retrieve single war data
+ Parameters
+ id: `5abf65ae3fc5fa349ffd5ca3` (string, required) - unique id of the war to fetch
+ Response 200 (application/json; charset=utf-8)
+ Attributes (WarWithPlayers, fixed-type)
### Create War [POST /wars]
Create a new war
**Permission: 3**
*NOTE: First line of the log file sent should be NOT beginning of actual game logs! This might result in unparsable line.*
+ Request (multipart/form-data; boundary=---BOUNDARY)
-----BOUNDARY
Content-Disposition: form-data; name="title"
Content-Type: text/plain
Battle XY
-----BOUNDARY
Content-Disposition: form-data; name="campaign"
Content-Type: text/plain
5abd55ea9e30a76bfef747d6
-----BOUNDARY
Content-Disposition: form-data; name="log"; filename="war_2018_log.rpt"
Content-Type: text/plain
\
2018/03/20, 20:05:43 "[OPT] (Budget) LOG: 0:00:00 --- Startbudget: NATO 4.5355e+006 - CSAT 4.22125e+006"
2018/03/20, 20:06:45 "[OPT] (Budget) LOG: 0:01:02 --- NATO alt: 4.5355e+006 - neu: 4.1955e+006 - Differenz: -340000"
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Saxe (WEST) von: Selbstverschulden."
2018/03/20, 20:10:11 "[OPT] (Abschuss) LOG: 0:04:28 --- Pumarang (WEST) von: Saxe (WEST)."
2018/03/20, 20:10:13 "[OPT] (Budget) LOG: 0:04:30 --- CSAT alt: 2.38425e+006 - neu: 2.32425e+006 - Differenz: -60000"
2018/03/20, 20:10:38 "[OPT] (Respawn) LOG: 0:04:55 --- Spieler: Pumarang - Kosten: 3000"
2018/03/20, 20:15:54 "[OPT] (Punkte) LOG: 0:10:11 --- Kein Dominator (NATO 0 | CSAT 0)"
2018/03/20, 20:17:51 "[OPT] (Abschuss) LOG: 0:12:08 --- patze (EAST) von: Wiesl (WEST)."
2018/03/20, 20:18:20 "[OPT] (Fahne) LOG: 0:12:37 --- CSAT Flagge erobert von Wiesl"
2018/03/20, 20:18:38 "[OPT] (Abschuss) LOG: 0:12:55 --- Nicolas (WEST) von: Wiesl (WEST)."
2018/03/20, 20:18:59 "[OPT] (Punkte) LOG: 0:13:16 --- NATO +1 (NATO 1 | CSAT 0)"
2018/03/20, 20:19:38 "[OPT] (Fahne) LOG: 0:13:56 --- CSAT Flagge gesichert von ALASTOR"
2018/03/20, 20:22:18 "[OPT] (Abschuss) LOG: 0:16:35 --- Fahrzeug: Hunter-HMG (OPT_NATO) von: Murda]X[ (EAST)."
2018/03/20, 20:37:19 "[OPT] (Transport) LOG: 0:31:36 --- Dominik (WEST) wurde von Ponykloete (WEST) eingeflogen (8666.94 m)"
2018/03/20, 20:41:27 "[OPT] (Revive) LOG: 0:35:44 --- Bodochecker (EAST) wurde von ALASTOR (EAST) stabilisiert."
2018/03/20, 20:41:35 "[OPT] (Revive) LOG: 0:35:52 --- Andi-de (WEST) wurde von Wiesl (WEST) wiederbelebt."
2018/03/20, 22:35:43 "[OPT] (Mission) LOG: 2:30:00 --- Missionzeit abgelaufen"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Pumarang (WEST), PUID 76561198050321485"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Mercurat (WEST), PUID 76561198278842491"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- KalleK (EAST), PUID 76561197977676036"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- MAPster (EAST), PUID 76561198009882133"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- LyrikEmu (WEST), PUID 76561198218910400"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Philipp (EAST), PUID 76561198041792069"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Wiesl (WEST), PUID 76561198059648090"
2018/03/20, 22:35:43 "[OPT] (Fraktionsuebersicht) LOG: 2:30:00 --- Murda]X[ (EAST), PUID 76561197971121630"
2018/03/20, 22:35:43 "[OPT] (Budget) LOG: 2:30:00 --- Endbudget: (NATO 1997000 | CSAT 512000)"
2018/03/20, 22:35:43 "[OPT] (Punkte) LOG: 2:30:00 --- Endpunktestand: (NATO 34 | CSAT 25)"
-----BOUNDARY--
+ Response 201 (application/json; charset=utf-8)
+ Attributes (War, fixed-type)
### Delete War [DELETE /wars/{id}]
Delete a war
**Permission: 3**
+ Parameters
+ id: `5abf65d83fc5fa349ffd5cbb` (string, required) - unique id of the war
+ Response 204

View File

@ -17,5 +17,5 @@ module.exports = {
signUp: rootRoute + '/authenticate/signup',
squads: rootRoute + '/squads',
users: rootRoute + '/users',
wars: rootRoute + '/wars'
wars: rootRoute + '/wars',
};

View File

@ -1,5 +1,6 @@
module.exports = {
port: 8091,
resourceLocation: __dirname + '/../resource',
database: {
uri: 'mongodb://localhost:27017/',
@ -7,17 +8,22 @@ module.exports = {
},
prod: {
env: 'production'
env: 'production',
},
dev: {
env: 'dev'
env: 'dev',
},
test: {
port: 3001,
db: 'cc-test',
env: 'test'
}
port: 3001,
dredd: {
env: 'dreddTest',
resourceLocation: __dirname + '/../apib/dredd/data/tmp-resource',
},
unit: {
env: 'unitTest',
},
},
};

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const cron = require('cron');
const async = require('async');
@ -19,8 +19,8 @@ const createAllSignatures = () => {
// mock response
const res = {
locals: {
items: {}
}
items: {},
},
};
// re-create signature image for each active user
@ -29,21 +29,21 @@ const createAllSignatures = () => {
// mock next to execute callback
const next = (err) => {
if (!err || (err && err.message.startsWith('Fraction not defined'))) {
callback()
callback();
} else {
error('\x1b[41m%s\x1b[0m', new Date().toLocaleString()
+ ': Error in execution - UPDATE SIGNATURES: ' + err.message)
+ ': Error in execution - UPDATE SIGNATURES: ' + err.message);
}
};
signatureTool(user._id, res, next)
signatureTool(user._id, res, next);
}, () => {
if (err) {
error('\x1b[41m%s\x1b[0m', new Date().toLocaleString()
+ ': Error in execution - UPDATE SIGNATURES: ' + err.message)
+ ': Error in execution - UPDATE SIGNATURES: ' + err.message);
}
logger('\x1b[35m%s\x1b[0m', new Date().toLocaleString()
+ ': finished successful - UPDATE SIGNATURES');
})
});
});
};
@ -57,7 +57,7 @@ const createBackup = () => {
logger('\x1b[32m%s\x1b[0m', stderr);
logger('\x1b[35m%s\x1b[0m', new Date().toLocaleString()
+ ': cron job finished - CREATE BACKUP');
})
});
};
// Execute daily @ 02:30 AM
@ -69,5 +69,5 @@ const cronJobBackup = cron.job('00 00 04 * * mon,thu,sat', createBackup);
module.exports = {
cronJobSignature: cronJobSignature,
cronJobBackup: cronJobBackup,
createAllSignatures: createAllSignatures
createAllSignatures: createAllSignatures,
};

34
api/dredd.yml Normal file
View File

@ -0,0 +1,34 @@
dry-run: null
hookfiles: null
language: nodejs
sandbox: false
server: npm run start-api-test
server-wait: 3
init: false
custom: {}
names: false
only: []
reporter: base
output: []
header: ['X-Access-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1YWI2OGNlZWY1NDdlZDMwNDA2NGU1ZjYiLCJpYXQiOjE1MjIzNTMwNzAsImV4cCI6MTAyMzIyMzUzMDcwfQ.aC4TYkPc8gYcpdsYNnYcs8D8e6OPilrgSGZF6pI3g1w']
sorted: false
user: null
inline-errors: false
details: false
method: []
color: true
level: info
timestamp: false
silent: false
path: []
hooks-worker-timeout: 5000
hooks-worker-connect-timeout: 1500
hooks-worker-connect-retry: 500
hooks-worker-after-connect-wait: 100
hooks-worker-term-timeout: 5000
hooks-worker-term-retry: 500
hooks-worker-handler-host: 127.0.0.1
hooks-worker-handler-port: 61321
config: ./dredd.yml
blueprint: ./apib/documentation.apib
endpoint: 'http://localhost:3001/api'

View File

@ -1,17 +1,15 @@
"use strict";
'use strict';
const jwt = require('jsonwebtoken');
const config = require('../config/config');
const AppUser = require('../models/app-user');
const apiAuthentication = (req, res, next) => {
// check header or url parameters or post parameters for token
const token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
const secret = process.env.NODE_ENV === config.prod.env ? process.env.JWS_SECRET : 'dev-secret';
// verifies secret and checks exp
@ -25,7 +23,7 @@ const apiAuthentication = (req, res, next) => {
if (err) {
return res.status(403).send({
success: false,
message: 'token is not associated to any actual user'
message: 'token is not associated to any actual user',
});
}
req.user = item;
@ -33,16 +31,13 @@ const apiAuthentication = (req, res, next) => {
});
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
message: 'No token provided.',
});
}
};

View File

@ -1,43 +1,43 @@
/** This module provides middleware to respond with proper JSON error objects
* using NODE_ENV setting to production or development. In dev mode it send the stacktrace.
*
* You call the returned function with an app instance
*
* @author Johannes Konert
* @licence CC BY-SA 4.0
*
*
* @module restapi/error-response
* @type {Function}
*/
"use strict";
const logger = require('debug')('me2:error-response');
module.exports = (app) => {
// development error handler
// will print stacktrace as JSON response
if (app.get('env') === 'development') {
app.use(function (err, req, res, next) {
logger('Internal Error: ', err.stack);
res.status(err.status || 500);
res.json({
error: {
message: err.message,
error: err.stack
}
});
});
} else {
// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.json({
error: {
message: err.message,
error: {}
}
});
});
}
};
/** This module provides middleware to respond with proper JSON error objects
* using NODE_ENV setting to production or development. In dev mode it send the stacktrace.
*
* You call the returned function with an app instance
*
* @author Johannes Konert
* @licence CC BY-SA 4.0
*
*
* @module restapi/error-response
* @type {Function}
*/
'use strict';
const logger = require('debug')('me2:error-response');
module.exports = (app) => {
// development error handler
// will print stacktrace as JSON response
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
logger('Internal Error: ', err.stack);
res.status(err.status || 500);
res.json({
error: {
message: err.message,
error: err.stack,
},
});
});
} else {
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.json({
error: {
message: err.message,
error: {},
},
});
});
}
};

View File

@ -1,104 +1,105 @@
/** This module defines a express.Router() instance
* - supporting filter=key1,key2
* - it sets res.locals.filter to a string "key1 key2"
* - calls next with error if a filter=key is given, but key does not exist (not raised on empty item arrays!)
*
* Note: it expects to be called before any data is fetched from DB
* Note: it sets an Error-Object to next with error.status set to HTTP status code 400
*
* @author Johannes Konert
* @licence CC BY-SA 4.0
*
* @module restapi/filter-middleware-mongo
* @type {Factory function returning an Router}
* @param Schema {Object} a MongooseSchema.path value or similar object with attributes representing the valid keys
* @param suppressId {Boolean} if true, the _id is not returned implicitly
*/
// remember: in modules you have 3 variables given by CommonJS
// 1.) require() function
// 2.) module.exports
// 3.) exports (which is module.exports)
"use strict";
const express = require('express');
const logger = require('debug')('middleware:filterware');
/**
* private helper function to filter Objects by given keys
* @param keys {Array} the keys from GET parameter filter
* @param schema [Object} containing the keys as attributes that are allowed
* @returns {Object or Error} either the filtered items or an Error object
*/
const limitFilterToSchema = (keys, schema) => {
if (!keys || !schema) { // empty arrays evaluate to false
return undefined; // means no filter at all
}
let error = null;
const result = [];
// now for each given filter=key1,key2in the array check that the schema allows this key and store it in result
keys.forEach((key) => {
if (schema.hasOwnProperty(key)) {
result.push(key);
} else {
error = new Error('given key for filter does not exist in ressource: ' + key);
}
});
return error ? error : result
};
/**
* closure function as factory returning the router
*
* @param Schema {Object} a MongooseSchema.path value or similar object with attributes representing the valid keys
* @param suppressId {Boolean} if true, the _id is not returned implicitly
* @returns {Router}
*/
const createFilterRouter = (schema, supressID) => {
const router = express.Router();
// the exported router with handler
router.use((req, res, next) => {
const filterString = req.query.filter;
let filterKeys = [];
let err = null;
if (filterString !== undefined) {
filterKeys = filterString.split(',');
filterKeys.forEach((item, index, array) => {
array[index] = item.trim();
});
filterKeys = filterKeys.filter((item) => {
return item.length > 0;
});
if (filterKeys.length === 0) {
err = new Error('given filter does not contain any keys');
err.status = 400;
} else {
const result = limitFilterToSchema(filterKeys, schema);
if (result instanceof Error) {
err = result;
err.status = 400;
} else {
res.locals.filter = result.join(' '); // create a string with space as seperator
if (supressID) {
res.locals.filter = '-_id ' + res.locals.filter;
}
}
}
}
if (err) {
logger(err);
next(err)
} else {
if (res.locals.filter) {
logger('Successfully set filter to ' + res.locals.filter);
}
next()
}
});
return router;
};
module.exports = createFilterRouter;
/** This module defines a express.Router() instance
* - supporting filter=key1,key2
* - it sets res.locals.filter to a string "key1 key2"
* - calls next with error if a filter=key is given, but key does not exist (not raised on empty item arrays!)
*
* Note: it expects to be called before any data is fetched from DB
* Note: it sets an Error-Object to next with error.status set to HTTP status code 400
*
* @author Johannes Konert
* @licence CC BY-SA 4.0
*
* @module restapi/filter-middleware-mongo
* @type {Factory function returning an Router}
* @param Schema {Object} a MongooseSchema.path value or similar object with attributes representing the valid keys
* @param suppressId {Boolean} if true, the _id is not returned implicitly
*/
// remember: in modules you have 3 variables given by CommonJS
// 1.) require() function
// 2.) module.exports
// 3.) exports (which is module.exports)
'use strict';
const express = require('express');
const logger = require('debug')('middleware:filterware');
/**
* private helper function to filter Objects by given keys
*
* @param {Array} keys - the keys from GET parameter filter
* @param {Object} schema - containing the keys as attributes that are allowed
* @return {Object} either the filtered items or an Error object
*/
const limitFilterToSchema = (keys, schema) => {
if (!keys || !schema) { // empty arrays evaluate to false
return undefined; // means no filter at all
}
let error = null;
const result = [];
// now for each given filter=key1,key2in the array check that the schema allows this key and store it in result
keys.forEach((key) => {
if (schema.hasOwnProperty(key)) {
result.push(key);
} else {
error = new Error('given key for filter does not exist in ressource: ' + key);
}
});
return error ? error : result;
};
/**
* closure function as factory returning the router
*
* @param Schema {Object} a MongooseSchema.path value or similar object with attributes representing the valid keys
* @param suppressId {Boolean} if true, the _id is not returned implicitly
* @returns {Router}
*/
const createFilterRouter = (schema, supressID) => {
const router = new express.Router();
// the exported router with handler
router.use((req, res, next) => {
const filterString = req.query.filter;
let filterKeys = [];
let err = null;
if (filterString !== undefined) {
filterKeys = filterString.split(',');
filterKeys.forEach((item, index, array) => {
array[index] = item.trim();
});
filterKeys = filterKeys.filter((item) => {
return item.length > 0;
});
if (filterKeys.length === 0) {
err = new Error('given filter does not contain any keys');
err.status = 400;
} else {
const result = limitFilterToSchema(filterKeys, schema);
if (result instanceof Error) {
err = result;
err.status = 400;
} else {
res.locals.filter = result.join(' '); // create a string with space as seperator
if (supressID) {
res.locals.filter = '-_id ' + res.locals.filter;
}
}
}
}
if (err) {
logger(err);
next(err);
} else {
if (res.locals.filter) {
logger('Successfully set filter to ' + res.locals.filter);
}
next();
}
});
return router;
};
module.exports = createFilterRouter;

View File

@ -17,9 +17,10 @@
// 1.) require() function
// 2.) module.exports
// 3.) exports (which is module.exports)
"use strict";
'use strict';
const router = require('express').Router();
const express = require('express');
const router = new express.Router();
const logger = require('debug')('middleware:offsetlimit');
@ -36,10 +37,9 @@ router.use((req, res, next) => {
if (!isNaN(offsetString)) {
offset = parseInt(offsetString);
if (offset < 0) {
err = new Error('offset is negative')
err = new Error('offset is negative');
}
}
else {
} else {
err = new Error('given offset is not a valid number ' + offsetString);
}
}
@ -47,22 +47,21 @@ router.use((req, res, next) => {
if (!isNaN(limitString)) {
limit = parseInt(limitString);
if (limit < 1) {
err = new Error('limit is zero or negative')
err = new Error('limit is zero or negative');
}
}
else {
} else {
err = new Error('given limit is not a valid number ' + limitString);
}
}
if (err) {
logger('problem occurred with limit/offset values');
err.status = 400;
next(err)
next(err);
} else {
res.locals.limitskip = {}; // mongoDB uses parameter object for skip/limit
if (limit) res.locals.limitskip.limit = limit;
if (offset) res.locals.limitskip.skip = offset;
next()
next();
}
});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
let check = (requiredPermission, actualPermission, res, next) => {
@ -7,21 +7,21 @@ let check = (requiredPermission, actualPermission, res, next) => {
}
return res.status(403).send({
success: false,
message: 'permission denied'
message: 'permission denied',
});
};
module.exports = {
checkSql: (req, res, next) => {
check(1, req.user.permission, res, next)
check(1, req.user.permission, res, next);
},
checkHl: (req, res, next) => {
check(2, req.user.permission, res, next)
check(2, req.user.permission, res, next);
},
checkMT: (req, res, next) => {
check(3, req.user.permission, res, next)
check(3, req.user.permission, res, next);
},
checkAdmin: (req, res, next) => {
check(4, req.user.permission, res, next)
}
check(4, req.user.permission, res, next);
},
};

View File

@ -1,67 +1,67 @@
/** This module defines a express.Router() instance
* - checking Accept-Version header to be 1.0
* - body-data to be JSON on POST/PUT/PATCH
* - body to be not empty on POST/PUT/PATCH
* - Request accepts JSOn as reply content-type
*
* @author Johannes Konert
* @licence CC BY-SA 4.0
*
* @module restapi/request-checks
* @type {Router}
*/
// remember: in modules you have 3 variables given by CommonJS
// 1.) require() function
// 2.) module.exports
// 3.) exports (which is module.exports)
"use strict";
const router = require('express').Router();
// API-Version control. We use HTTP Header field Accept-Version instead of URL-part /v1/
router.use((req, res, next) => {
// expect the Accept-Version header to be NOT set or being 1.0
const versionWanted = req.get('Accept-Version');
if (versionWanted !== undefined && versionWanted !== '1.0') {
// 406 Accept-* header cannot be fulfilled.
res.status(406).send('Accept-Version cannot be fulfilled').end();
} else {
next(); // all OK, call next handler
}
});
// 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'))))) {
// 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')) {
// send 406 that response will be application/json and request does not support it by now as answer
// user has REQUESTED the wrong type
res.status(406).send('response of application/json only supported, please accept this');
}
else {
next(); // let this request pass through as it is OK
}
});
// request POST, PUT check that any content was send
router.use((req, res, next) => {
let err = undefined;
if (['POST', 'PUT', 'PATCH'].indexOf(req.method) > -1 && parseInt(req.get('Content-Length')) === 0) {
err = new Error("content in body is missing");
err.status = 400;
next(err);
} else if ('PUT' === req.method && !(req.body.id || req.body._id)) {
err = new Error("content in body is missing field id");
err.status = 400;
next(err);
}
next();
});
module.exports = router;
/** This module defines a express.Router() instance
* - checking Accept-Version header to be 1.0
* - body-data to be JSON on POST/PUT/PATCH
* - body to be not empty on POST/PUT/PATCH
* - Request accepts JSOn as reply content-type
*
* @author Johannes Konert
* @licence CC BY-SA 4.0
*
* @module restapi/request-checks
* @type {Router}
*/
// remember: in modules you have 3 variables given by CommonJS
// 1.) require() function
// 2.) module.exports
// 3.) exports (which is module.exports)
'use strict';
const express = require('express');
const router = new express.Router();
// API-Version control. We use HTTP Header field Accept-Version instead of URL-part /v1/
router.use((req, res, next) => {
// expect the Accept-Version header to be NOT set or being 1.0
const versionWanted = req.get('Accept-Version');
if (versionWanted !== undefined && versionWanted !== '1.0') {
// 406 Accept-* header cannot be fulfilled.
res.status(406).send('Accept-Version cannot be fulfilled').end();
} else {
next(); // all OK, call next handler
}
});
// 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'))))) {
// 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')) {
// send 406 that response will be application/json and request does not support it by now as answer
// user has REQUESTED the wrong type
res.status(406).send('response of application/json only supported, please accept this');
} else {
next(); // let this request pass through as it is OK
}
});
// request POST, PUT check that any content was send
router.use((req, res, next) => {
let err = undefined;
if (['POST', 'PUT', 'PATCH'].indexOf(req.method) > -1 && parseInt(req.get('Content-Length')) === 0) {
err = new Error('content in body is missing');
err.status = 400;
next(err);
} else if ('PUT' === req.method && !(req.body.id || req.body._id)) {
err = new Error('content in body is missing field id');
err.status = 400;
next(err);
}
next();
});
module.exports = router;

View File

@ -0,0 +1,12 @@
'use strict';
const config = require('../config/config');
const resourceLocation = () => {
if (process.env.NODE_ENV === config.test.dredd.env) {
return config.test.dredd.resourceLocation;
}
return config.resourceLocation;
};
exports.resourceLocation = resourceLocation;

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
// HTTP status codes by name
const codes = require('../routes/http-codes');

View File

@ -1,16 +1,21 @@
"use strict";
'use strict';
// HTTP status codes by name
const codes = require('../routes/http-codes');
/**
* check if id has valid UUID format
*
* @param {object} req
* @param {function} res
* @param {function} next
* @return {boolean}
*/
const idValidator = (req, res, next) => {
const reqId = req.params.id;
if (!reqId.match(/^[0-9a-fA-F]{24}$/)) {
const err = new Error("Invalid request id format");
const err = new Error('Invalid request id format');
err.status = codes.notfound;
return next(err);
}

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,36 +7,36 @@ const AppUserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
unique: true,
},
password: {
type: String,
required: true
required: true,
},
squad: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Squad',
default: null
default: null,
},
permission: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
get: (v) => Math.round(v),
set: (v) => Math.round(v),
min: 0,
max: 4,
default: 0
default: 0,
},
secret: {
type: String,
required: true
required: true,
},
activated: {
type: Boolean,
default: false
}
default: false,
},
}, {
collection: 'app_user',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
AppUserSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,39 +6,39 @@ const Schema = mongoose.Schema;
const AwardingSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
ref: 'User',
},
decorationId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Decoration'
ref: 'Decoration',
},
reason: {
type: String,
required: true
required: true,
},
proposer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'AppUser',
required: true
required: true,
},
confirmed: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
get: (v) => Math.round(v),
set: (v) => Math.round(v),
min: 0,
max: 2,
default: 0
default: 0,
},
rejectReason: {
type: String
type: String,
},
date: {
type: Date,
default: Date.now()
}
default: Date.now(),
},
}, {
collection: 'awarding',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
AwardingSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,11 +6,11 @@ const Schema = mongoose.Schema;
const CampaignSchema = new Schema({
title: {
type: String,
required: true
}
required: true,
},
}, {
collection: 'campaign',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
CampaignSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,27 +6,27 @@ const Schema = mongoose.Schema;
const DecorationSchema = new Schema({
name: {
type: String,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR', 'GLOBAL'],
required: true
required: true,
},
description: {
type: String,
required: true
required: true,
},
sortingNumber: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
isMedal: {type: Boolean, required: true}
isMedal: {type: Boolean, required: true},
}, {
collection: 'decoration',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
DecorationSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,31 +7,31 @@ const LogBudgetSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
required: true,
},
oldBudget: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
newBudget: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
}
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
}, {
collection: 'logBudget'
collection: 'logBudget',
});
// optional more indices
LogBudgetSchema.index({war: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,27 +7,27 @@ const LogFlagSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
player: {
type: String,
required: true
required: true,
},
flagFraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
required: true,
},
capture: {
type: Boolean,
required: true
}
required: true,
},
}, {
collection: 'logFlag'
collection: 'logFlag',
});
// optional more indices
LogFlagSchema.index({war: 1, player: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,30 +7,30 @@ const LogKillSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
shooter: {
type: String
type: String,
},
target: {
type: String,
required: true
required: true,
},
friendlyFire: {
type: Boolean,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR', 'NONE'],
required: true
}
required: true,
},
}, {
collection: 'logKill'
collection: 'logKill',
});
// optional more indices
LogKillSchema.index({war: 1, shooter: 1, target: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,31 +7,31 @@ const LogKillSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
ptBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
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
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR', 'NONE'],
required: true
}
required: true,
},
}, {
collection: 'logPoints'
collection: 'logPoints',
});
// optional more indices
LogKillSchema.index({war: 1, shooter: 1, target: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,18 +7,18 @@ const LogRespawnSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
player: {
type: String,
required: true
}
required: true,
},
}, {
collection: 'logRespawn'
collection: 'logRespawn',
});
// optional more indices
LogRespawnSchema.index({war: 1, player: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,31 +7,31 @@ const LogReviveSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
medic: {
type: String,
required: true
required: true,
},
patient: {
type: String,
required: true
required: true,
},
stabilized: {
type: Boolean,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
}
required: true,
},
}, {
collection: 'logRevive'
collection: 'logRevive',
});
// optional more indices
LogReviveSchema.index({war: 1, medic: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,33 +7,33 @@ const LogTransportSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
driver: {
type: String,
required: true
required: true,
},
passenger: {
type: String,
required: true
required: true,
},
distance: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
}
required: true,
},
}, {
collection: 'logTransport'
collection: 'logTransport',
});
// optional more indices
LogTransportSchema.index({war: 1, driver: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,26 +7,26 @@ const LogVehicleKillSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
time: {
type: Date,
required: true
required: true,
},
shooter: {
type: String
type: String,
},
target: {
type: String,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR', 'NONE'],
required: true
}
required: true,
},
}, {
collection: 'logVehicle'
collection: 'logVehicle',
});
// optional more indices
LogVehicleKillSchema.index({war: 1, shooter: 1, target: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,73 +6,73 @@ const Schema = mongoose.Schema;
const PlayerSchema = new Schema({
name: {
type: String,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
required: true,
},
warId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
required: true,
},
kill: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
vehicle: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
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
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
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
revive: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
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
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),
required: true
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
sort: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v)
get: (v) => Math.round(v),
set: (v) => Math.round(v),
},
steamUUID: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v)
}
get: (v) => Math.round(v),
set: (v) => Math.round(v),
},
}, {
collection: 'player',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
PlayerSchema.index({warId: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,39 +6,39 @@ const Schema = mongoose.Schema;
const PromotionSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
ref: 'User',
},
proposer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'AppUser',
required: true
required: true,
},
oldRankLvl: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
newRankLvl: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
confirmed: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
get: (v) => Math.round(v),
set: (v) => Math.round(v),
min: 0,
max: 2,
required: true
required: true,
},
rejectReason: {
type: String
}
type: String,
},
}, {
collection: 'promotion',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
PromotionSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,22 +6,22 @@ const Schema = mongoose.Schema;
const RankSchema = new Schema({
name: {
type: String,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
required: true,
},
level: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
}
get: (v) => Math.round(v),
set: (v) => Math.round(v),
required: true,
},
}, {
collection: 'rank',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
RankSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,22 +6,22 @@ const Schema = mongoose.Schema;
const SquadSchema = new Schema({
name: {
type: String,
required: true
required: true,
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
required: true,
},
sortingNumber: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
}
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
}, {
collection: 'squad',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
SquadSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -7,22 +7,22 @@ const UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
unique: true,
},
rankLvl: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
squadId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Squad',
default: null
}
default: null,
},
}, {
collection: 'user',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
UserSchema.index({timestamp: 1});

View File

@ -1,4 +1,4 @@
"use strict";
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
@ -6,7 +6,7 @@ const Schema = mongoose.Schema;
const WarSchema = new Schema({
title: {
type: String,
required: true
required: true,
},
date: {
type: Date,
@ -16,58 +16,58 @@ const WarSchema = new Schema({
},
ptBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
get: (v) => Math.round(v),
set: (v) => Math.round(v),
},
ptOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
get: (v) => Math.round(v),
set: (v) => Math.round(v),
},
playersBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
playersOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
campaign: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Campaign',
required: true
required: true,
},
budgetBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
budgetOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
endBudgetBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
endBudgetOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
}
get: (v) => Math.round(v),
set: (v) => Math.round(v),
default: 0,
},
}, {
collection: 'war',
timestamps: {createdAt: 'timestamp'}
timestamps: {createdAt: 'timestamp'},
});
// optional more indices
WarSchema.index({timestamp: 1});

3963
api/package-lock.json generated

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More