Compare commits

...

418 Commits

Author SHA1 Message Date
hardi c0a714d2b0 Release v1.9.8 (#63) 2020-04-23 10:41:03 +02:00
hardi 91d5095890 Release v1.9.7 (#62) 2020-03-08 13:46:55 +01:00
hardi 7851e64fd5 Release v1.9.6 (#61) 2019-10-11 20:25:07 +02:00
hardi 12ca58d43e Release v1.9.5 (#60) 2019-10-01 13:51:03 +02:00
hardi 845593b2ec Release v1.9.4 (#59) 2019-03-03 18:09:20 +01:00
hardi 35e8b14a34 Release v1.9.3 RC2 (#58) 2019-02-27 23:13:23 +01:00
hardi df6abd39aa Release v1.9.3 - navigation rework (#56) 2019-02-25 14:32:29 +01:00
hardi 3d2223f063 Release 1.9.2-RC2 (#55)
* Improve chart labels for flag chart (CC-72)
2019-02-17 20:36:38 +01:00
hardi b6deb56fc0 Release v1.9.2 (#54)
* Fix transport stats value drop on navigation (CC-88)
* Add player transport stats to campaign detail view (CC-83)
* Add additional budget information to db enry (CC-15)
* use sass for war header
* Improve fraction stats and scss usage
* Use stacked bar chart for flag capture visualization (CC-72)
* Update mongoose method usage o drop deprecated use
* Security restrictive npm dependency updates and mongoose settings adjustment
* Migrate load indicator to SASS (CC-87)
* Improve load indicator positioning (CC-87)
* Add OPT branded load indicator (CC-87)
2019-02-17 19:46:24 +01:00
hardi ec2beb56a0 Merge branch 'release/v1.9.1' of hardi/opt-cc into master 2019-02-14 21:00:15 +01:00
HardiReady 0ee2806cca Merge branch 'master' of git.noarch.de:hardi/opt-cc into release/v1.9.1 2019-02-14 20:46:27 +01:00
HardiReady 9467fc5699 Fix log parsing player name resolve (CC-86) 2019-02-14 20:46:22 +01:00
hardi 334a92a242 Merge branch 'release/v1.9.0' of hardi/opt-cc into master 2019-02-11 23:15:22 +01:00
HardiReady 4a96b99619 fix linting 2019-02-11 23:06:49 +01:00
HardiReady 4843da3c26 Merge branch 'release/v1.9.1' into release/v1.9.0 2019-02-11 23:04:17 +01:00
HardiReady 939613d55c Add war participation count for highscore page (CC-85); Add player transport stats for highscore page (CC-84) 2019-02-11 22:57:31 +01:00
HardiReady 22340d32e8 Add war list mouseover and background color select state 2019-02-11 21:56:00 +01:00
HardiReady 2c99565b13 Add war list mouseover and background color select state 2019-02-11 21:55:16 +01:00
hardi 75a20e54cb Merge branch 'release/v1.9.0' of hardi/opt-cc into master 2019-02-11 19:05:55 +01:00
HardiReady e2764c938d Update travel distance header svg 2019-02-11 18:52:25 +01:00
HardiReady 294f1db552 Rename mongoose model serverFps 2019-02-11 18:45:21 +01:00
hardi 3234c45047 Merge branch 'release/v1.9.0-rebase' of hardi/opt-cc into release/v1.9.0 2019-02-10 20:19:17 +01:00
HardiReady 25abfdf66b Update db schema 2019-02-10 20:10:22 +01:00
HardiReady cf8f04c01a Update backup.sh 2019-02-10 20:08:15 +01:00
HardiReady 1003154311 Fix gitignore 2019-02-10 20:04:26 +01:00
HardiReady b136278031 Fix stats war header for FF 2019-02-10 18:50:45 +01:00
HardiReady 1d91f8efba Improve performance tab hiding (CC-80); Fix scoreboard table mis-alignment (CC-33) 2019-02-10 18:37:15 +01:00
HardiReady b3c68f72ba Optimize scoreboard SVGs; Add new transport stats to scoreboard (CC-33) 2019-02-10 18:22:47 +01:00
HardiReady 836da0c6d4 Add log parsing for transport data into player object (CC-33) 2019-02-10 15:22:57 +01:00
HardiReady b351301bea Update API docs 2019-02-10 11:01:11 +01:00
HardiReady 9532cfeda8 Update code style 2019-02-10 10:35:30 +01:00
HardiReady 24fcb05f70 Add translations for server stats charts (CC-80) 2019-02-10 10:33:25 +01:00
HardiReady 1f176e0256 Fix and improve APIB docs and API tests (CC-82) 2019-02-10 09:36:45 +01:00
HardiReady 9de759805a Hide snackbar on navigate (CC-81) 2019-02-10 08:53:39 +01:00
HardiReady 2fffad3c60 fix lint & improve kill log parse -> add friendlyFire default 2019-02-10 01:04:19 +01:00
HardiReady 79aec147e3 Add line chart visualizations for server performance (CC-80) 2019-02-10 00:54:25 +01:00
HardiReady 9c6f74c14f Add bar chart per player fps visualization (CC-80) 2019-02-09 21:55:13 +01:00
HardiReady 03073f5436 Add parsing and persisting for FPS logs (CC-80) 2019-02-09 20:42:24 +01:00
HardiReady 7c23c302c6 Update schema docs 2019-02-09 18:51:01 +01:00
HardiReady de05260298 remove version key from db log entries 2019-02-09 14:24:14 +01:00
HardiReady 3c02c353e7 Update version label 2019-02-09 13:18:22 +01:00
HardiReady 9e6c52648b Fix lint 2019-02-09 13:17:32 +01:00
HardiReady 82f3dc923f Delete campaign war resources and logs on campaign delete (CC-78) 2019-02-09 13:16:41 +01:00
HardiReady a3665b5476 Add delet db entries player count on delete war 2019-02-09 12:41:02 +01:00
HardiReady f34a748d7f Add player count graph print in FE 2019-02-09 12:07:09 +01:00
HardiReady 2538f1838a Add player count for log parsing 2019-02-09 11:54:16 +01:00
HardiReady 712071865f Prepare fraction stats page select buttons for alternating data availability 2019-02-08 22:34:50 +01:00
HardiReady 2d64c32598 Add chart select label names 2019-02-07 21:44:25 +01:00
HardiReady b99825b01a Add new server performance stats page 2019-02-07 21:05:58 +01:00
HardiReady 818e99e973 change scoreboard header row from position fixed to absolute (low res fix) 2019-02-04 21:08:37 +01:00
HardiReady eea8eb2104 Merge branch 'master' of git.noarch.de:hardi/opt-cc 2019-02-04 17:30:02 +01:00
HardiReady e6ca8f10fc HOTFIX: api package renaming issues 2019-02-04 17:29:16 +01:00
hardi 34c2f4326f Merge branch 'release/v1.8.6' of hardi/opt-cc into master 2019-02-04 16:58:38 +01:00
HardiReady 4f6a4b3efa Fix dredd tests 2019-02-04 16:49:24 +01:00
HardiReady 96ea3ca182 Replace load indicator logo png by svg 2019-02-04 13:48:34 +01:00
HardiReady 1e7d5cc122 Replace opt logo png with svg 2019-02-04 13:18:28 +01:00
HardiReady 6e0ca3131e Improve scoreboard tabs svg positioning and coloring logic 2019-02-04 12:48:47 +01:00
HardiReady c61e290844 unify svg file naming 2019-02-04 12:41:59 +01:00
HardiReady e4f637093c Replace scoreboard tab images with svg 2019-02-04 12:36:18 +01:00
HardiReady ab3a9e5d7c Remove unused image 2019-02-03 23:45:45 +01:00
HardiReady 141a340aad Add upload image dimension check/limitation for rank, squad and decoration images (CC-69) 2019-02-03 12:16:22 +01:00
HardiReady 68ad467e4d * Add image dimension check for rank submit/update
* Update server dependencies
* Update gitignore after api package rename
2019-02-03 11:49:48 +01:00
HardiReady 557d0de60a Fix log timestamp precision (CC-75) 2019-02-02 23:52:02 +01:00
HardiReady 8eb7d150ab simplify language selector 2019-02-02 23:15:27 +01:00
HardiReady fea52653f1 rename module 'api' to 'server' 2019-02-02 22:54:48 +01:00
hardi 98cfc802b6 Merge branch 'release/v1.8.5' of hardi/opt-cc into master 2019-02-02 12:51:12 +01:00
HardiReady f0108d52f4 Update Readme 2019-02-02 12:40:02 +01:00
HardiReady b216c89fe7 Scoreboard: Fixed table header and header icon tooltip in FF 2019-02-02 12:28:22 +01:00
HardiReady 4fb2326ef6 Fix misaligned score table header by flex box use 2019-01-30 20:35:14 +01:00
HardiReady 694edc7afe Update dependencies: angular and ngx-charts 2019-01-30 19:49:54 +01:00
hardi 1f308ec3a5 Merge branch 'release/v1.8.4' of hardi/opt-cc into master 2018-11-24 14:40:20 +01:00
HardiReady f33c1e556a Fix error message display for login page (CC-71) 2018-11-24 14:12:05 +01:00
HardiReady 118214a906 Rework admin user management to use default two culumn layout (CC-68) 2018-11-24 14:05:52 +01:00
HardiReady 72d3cac89f Prove flag to force exit mocha on test run 2018-10-21 15:48:19 +02:00
HardiReady 1a9122cf52 Update mocha 2018-10-21 15:34:48 +02:00
HardiReady d056363ebe revert npm package updated 2018-10-20 23:08:20 +02:00
HardiReady 40ac545d5a downgrade mongo in memory server 2018-10-20 23:00:43 +02:00
HardiReady 2c996a14e2 Restrict delete user API endpoint to MT+ level (CC-67) 2018-10-20 22:52:45 +02:00
HardiReady ac83629824 Restrict delete visibiity for user to MT+ level (CC-67) 2018-10-20 22:50:46 +02:00
HardiReady 859df0359d Replace use of deprecated RequestOptions and RequestMethod (CC-66) 2018-10-20 22:40:18 +02:00
HardiReady 67aaf50f26 Add error output on http service get 2018-10-20 20:33:36 +02:00
HardiReady c0b3e27d18 Fix lint 2018-10-20 20:06:13 +02:00
HardiReady d3b5571022 Merge branch 'feature/opt-slotting' into release/v1.8.4 2018-10-20 20:00:54 +02:00
HardiReady 631eaff9f7 Resolve username from opt board id 2018-10-20 20:00:36 +02:00
HardiReady ce1004a94f set feature variables from ts environment 2018-10-20 16:16:11 +02:00
hardi ee1bdba661 Update 'README.md' 2018-10-20 11:18:20 +02:00
HardiReady 9dafb33896 Add public module i18n, previously skipped by gitignore 2018-10-20 09:46:31 +02:00
hardi a5dc9179d6 Merge branch 'release/v1.8.3' of hardi/opt-cc into master 2018-10-20 09:23:55 +02:00
HardiReady 6146534990 Update GET /wars doc description 2018-10-20 08:54:04 +02:00
HardiReady 0c04cd7f8f Fix undefined param submit for GET users 2018-10-20 08:48:50 +02:00
HardiReady 229b133777 Update API docs for wars & campaigns following CC-62 changes 2018-10-18 20:56:06 +02:00
HardiReady 9fb39b0af2 Access campaigns in campaign navigation from Observable 2018-10-18 20:36:57 +02:00
HardiReady 14c4cf68ff Do not process/count duplicated flagTouch 2018-10-18 20:23:29 +02:00
HardiReady 21e2d81b06 Deactivate localization selector 2018-10-18 20:07:50 +02:00
HardiReady f6317d7fbc Migrate main gateway from Http to HttpClient - 1st iteration (CC-63) 2018-10-14 15:56:52 +02:00
HardiReady 2291ec20bf Http -> HttpClient for army/admin/login (CC-63) 2018-10-13 09:07:41 +02:00
HardiReady e099ff572f Update codestyle: remove JS space in brackets; Fix formatting & lint 2018-10-10 23:20:20 +02:00
HardiReady 4a7b1d60d5 Attach cron job to single cluster worker & ensure it is reassigned on crash (CC-64) 2018-10-10 23:16:29 +02:00
HardiReady 16fa17a2d4 Rename custom HttpClient to HttpGateway (CC-63) 2018-10-10 00:05:32 +02:00
HardiReady 66022e4141 Rename custom HttpClient to HttpGateway (CC-63) 2018-10-09 23:23:41 +02:00
HardiReady b425617a7d Use store for war and use depending loading on url resolving (CC-62) 2018-10-09 10:42:28 +02:00
HardiReady bb69907447 Optimize campaign store use 2018-10-08 10:53:22 +02:00
HardiReady c9fce51ef6 Fix Highscore link and overview after redirect from edit campaign 2018-10-08 00:03:01 +02:00
HardiReady 1be757bbc1 Add use of Store for CampaignService (CC-62) 2018-10-07 23:41:43 +02:00
HardiReady 8e46b6c6f1 Refactor stores to use generic store (CC-61) 2018-10-07 17:46:29 +02:00
HardiReady 421d15d19a Fix lint 2018-10-05 15:56:35 +02:00
HardiReady 18118d021e Add snackbar i18n usage + translations 2018-10-05 15:51:46 +02:00
HardiReady a24c8d3ac3 Add flag dropdown for language selection 2018-10-05 13:01:51 +02:00
HardiReady bf4b219234 Fix translations in campaign overview 2018-10-05 10:29:57 +02:00
HardiReady f9c9e20339 Add english translations (CC-60) 2018-10-05 10:21:50 +02:00
HardiReady 8ea5e188a0 Add basic language changing functionality 2018-10-05 09:55:39 +02:00
HardiReady 1d46b301f5 Add english translation manage-module (CC-60) 2018-10-05 08:55:12 +02:00
HardiReady 35a6cdd806 Unify all manage components to a single module (CC-58) 2018-10-04 14:08:29 +02:00
HardiReady 36e213bbbf Replace strings with i18n keys in request module 2018-10-04 13:06:14 +02:00
HardiReady b2fa02d4eb Finish translations for manage part 2018-10-04 11:48:22 +02:00
HardiReady 78abec80fd Add manage ranks i18n replacements 2018-10-04 10:59:15 +02:00
HardiReady ac604f4e08 Add manage user i18n replacements 2018-10-04 10:33:15 +02:00
HardiReady e902ed5ec4 Add i18n for squad management 2018-10-03 15:22:14 +02:00
HardiReady 4eef29bd05 Add error messages i18n 2018-10-03 14:22:19 +02:00
HardiReady 0adb9b7cf9 Add i18n for submit campaign 2018-10-03 11:54:37 +02:00
HardiReady ccbcece0ab Merge edit and add war into one component and add i18n 2018-10-03 11:49:17 +02:00
HardiReady 8ad9000766 Add more i18n strings 2018-10-03 11:22:21 +02:00
HardiReady 37eab6f33d Add missing depndencies to npm package 2018-10-02 14:40:25 +02:00
HardiReady 55fd5a0dcb Add i18n for stats fraction/scoreboard/overview 2018-10-02 14:31:26 +02:00
HardiReady 7a7a2f6ceb Add i18n for login/signup 2018-10-02 12:56:51 +02:00
HardiReady 6259fdd766 Use i18n for public components 2018-10-02 12:28:14 +02:00
HardiReady 6b43057328 Add translation service for public sub module (CC-57) 2018-10-02 12:16:51 +02:00
HardiReady 0f37c06fc6 Add translation module and add i18n strings for initial pages (CC-57) 2018-10-02 10:57:45 +02:00
HardiReady 4c781dd101 Update ngx-charts to v9.0 (CC-49) 2018-10-02 09:58:07 +02:00
HardiReady f4f43dc227 Allow worker count > cpuCount 2018-10-01 09:00:47 +02:00
HardiReady 93856f07db Fix stats sidebar height fill element behavior 2018-10-01 08:53:58 +02:00
hardi d2aa29253e Merge branch 'release/v1.8.2' of hardi/opt-cc into master 2018-10-01 08:36:57 +02:00
HardiReady 6ab20db473 Dismiss bottom sheet on navigate (CC-56) 2018-10-01 07:53:38 +02:00
HardiReady d4667a0736 Update vehicle kill blacklist (CC-53) 2018-10-01 07:42:07 +02:00
HardiReady c2822893ca Fix lint 2018-09-30 14:44:59 +02:00
HardiReady e1804a9918 Fix player fraction resolving for FF and others (CC-55) 2018-09-30 14:43:01 +02:00
HardiReady 22cf01727b Fix horizontal scroll for campaign 2018-09-16 12:29:40 +02:00
HardiReady 8b0e2d91d3 Run express server using cluster setup (CC-54) 2018-08-25 13:20:59 +02:00
Florian Hartwich d279556457 HOTFIX: re-establish award icon in user overview 2018-08-21 09:11:02 +02:00
hardi 3be0c2d1ca Merge branch 'release/v1.8.1' of hardi/opt-cc into master 2018-08-20 08:56:04 +02:00
HardiReady a997b3ba21 * Use SVG icons for highscore and scoreboard table header (CC-52)
* Fix fraction stats vehicle chart for legacy wars (CC-51)
2018-08-19 22:35:40 +02:00
HardiReady 06123a2301 Fix switch view selection on stats page (CC-50) 2018-08-18 17:38:21 +02:00
HardiReady e23ed09b25 Fix flag capture player name resolve 2018-08-13 19:24:50 +02:00
HardiReady ee5dcd7fa8 Fix point string resolve for OPFOR in default PUNKTE entry 2018-08-13 19:08:14 +02:00
HardiReady 854a9720f5 Fix point string resolve for OPFOR in default PUNKTE entry 2018-08-13 19:07:33 +02:00
hardi 3adc751ac8 Merge branch 'release/v1.8.0' of hardi/opt-cc into master 2018-08-11 18:44:34 +02:00
HardiReady 15ea13e225 Add url state representation for decoration overview 2018-08-11 18:43:41 +02:00
HardiReady ba987e1b67 Fix pos and sizing of decoration show box 2018-08-11 17:43:50 +02:00
HardiReady 0600c2ecc6 Add GET campaigns APIB docs 2018-08-11 17:29:58 +02:00
HardiReady 88861e595c Implement new award showcase 2018-08-09 22:07:47 +02:00
HardiReady 8e70ca831f Redirect to campaign/war/highscore by url 2018-08-08 21:42:57 +02:00
HardiReady d8544edb5e Add new war and campaign edit/delete menus 2018-08-06 20:50:04 +02:00
HardiReady 447925b44f Use scroll navigation for public decoration overview 2018-08-04 16:53:58 +02:00
HardiReady cdc2fb35ae Use material toggle in campaign overview 2018-08-04 12:54:37 +02:00
HardiReady ae7dd6afaf Fix stats campaig navigation scroll 2018-08-04 11:56:54 +02:00
HardiReady 085c7d3294 optimize bg on navigate 2018-08-04 10:41:02 +02:00
HardiReady 6395aac17a stats campaign lineredesign and new bg 2018-08-04 10:35:26 +02:00
HardiReady 7bcbf182bc Set background image for public pages and fix user bottom sheet redirect 2018-08-01 21:39:39 +02:00
HardiReady 58db2eda9a improve styles 2018-08-01 20:53:25 +02:00
hardi a4efa7a69c Merge branch 'task/release-v1.8.0-rework-outlet-system' of hardi/opt-cc into release/v1.8.0 2018-08-01 19:06:44 +02:00
HardiReady e7904315c6 Rework management two column layouts 2018-07-30 21:30:59 +02:00
HardiReady 24f6b2a156 Add stats list sidebar collapse 2018-07-30 20:43:47 +02:00
Florian Hartwich b1cd7a9c19 Add new router outlet setup and add root setup for new statistics page 2018-07-29 20:59:09 +02:00
HardiReady e8319d351f Remove global outlets 2018-07-28 19:13:30 +02:00
HardiReady b2d18e5cd0 Finish campaign navigation behavior and load 2018-07-28 16:04:38 +02:00
Florian Hartwich b93830993b Fix horizontal hover scrolling 2018-07-27 15:00:58 +02:00
HardiReady da6037d001 Add basic horizontal campaign navigation 2018-07-27 07:20:57 +02:00
HardiReady 0ef9a6aac2 Replace trace page with bottom sheet on rank and decoration overviews 2018-07-25 21:01:58 +02:00
HardiReady 51b11cb2a1 Add unified material return navigation button 2018-07-24 21:43:09 +02:00
HardiReady 8dd1abff55 Update deprecated 'res' sending 2018-07-22 18:21:19 +02:00
HardiReady 88346db70e Add new filter parameter to get users after award owning and use for trace pages 2018-07-22 18:18:46 +02:00
HardiReady 78cc61232b Add tracing result pages for ranks/awards and connect to user detail pages 2018-07-22 13:09:51 +02:00
HardiReady b3b166d283 determine input value type in finder 2018-07-21 12:20:43 +02:00
HardiReady 6ac4fc5a66 Add and connect new page for rank/decoration trace 2018-07-21 12:03:50 +02:00
HardiReady ed87dd5df1 Unify public item mouseover effect 2018-07-20 22:33:53 +02:00
HardiReady e2c8714d02 Refurbish card layout and functionalities for pub decoration overview 2018-07-20 22:18:50 +02:00
HardiReady 1a59724dbc Switch to basic card layout for decorations 2018-07-19 22:10:32 +02:00
HardiReady 783d30e91e Add public decoration table (CC-38) 2018-07-18 21:15:42 +02:00
HardiReady 14901883e6 Distinct all time overview vs campaign overview 2018-07-18 20:05:06 +02:00
HardiReady 2b824bac7c Update alltime stats to use timeline (CC-47) 2018-07-18 19:55:22 +02:00
HardiReady e218076ae3 * Use timeline with dates in all time stats overview (CC-47)
* Update menu to group managing points
2018-07-18 19:53:06 +02:00
HardiReady 0a5c70d465 Improve scoreboard table header 2018-07-17 13:39:56 +02:00
HardiReady 48ce904d16 Fix csv export after table column order change (CC-46) 2018-07-17 12:12:57 +02:00
HardiReady 8029885b41 Show loading indicator only in loading state 2018-07-17 11:40:18 +02:00
HardiReady d72db1ed4f * Revert to legacy load indicator and improve it
* Fix enter submit of forgotten forms
2018-07-17 11:34:32 +02:00
HardiReady f2e2133496 * Suppress enter key action in critical forms (CC-39)
* Add spinner service
2018-07-17 10:07:12 +02:00
HardiReady 374e7df34d clear background for left element on specifiv pages 2018-07-17 09:09:56 +02:00
HardiReady 1f24e19eec Fix initial sidebar view on route change 2018-07-17 08:37:57 +02:00
HardiReady e548d6821c Fix initial sidebar view on route change 2018-07-17 08:32:01 +02:00
HardiReady 478dfef4de Add window resize event on sidenav change 2018-07-15 18:01:27 +02:00
HardiReady 3672bfe112 Add basic side panel toggle for stats 2018-07-15 15:38:33 +02:00
hardi 612c894e6a Merge branch 'task/1.8.0-material-scoreboard' of hardi/opt-cc into release/v1.8.0 2018-07-15 13:21:13 +02:00
HardiReady 99304649ea Remove ngx-datatable dependency 2018-07-15 13:03:32 +02:00
HardiReady 8cabea74fd Add fixed header for scoreboard table 2018-07-15 12:59:37 +02:00
HardiReady dfaddfa992 * Use mat-table in highscores
* Fix fraction filter for scoreboard
2018-07-15 12:27:41 +02:00
HardiReady 42cfadb9e9 Add more fancy tabbing feeling and fix initial ordering of stats 2018-07-14 12:54:53 +02:00
HardiReady 6f4274a365 remove datatable html for scoreboard 2018-07-12 21:49:16 +02:00
HardiReady 99482625a2 Add scoreboard table sorting 2018-07-12 21:48:08 +02:00
HardiReady 9a0cd3544e migrate to basic material table 2018-07-11 21:10:55 +02:00
HardiReady ed238e311e Add tables for rank overview 2018-07-10 20:48:52 +02:00
HardiReady fab0b438cd Use material buttons in war list 2018-07-10 19:57:18 +02:00
HardiReady 5ba7c48744 Fix lint 2018-07-10 06:56:17 +02:00
HardiReady f837117d90 restyle left component box 2018-07-09 21:01:39 +02:00
HardiReady 1812f20ac4 Fix filter list icon btn for firefox 2018-07-09 20:07:10 +02:00
HardiReady 2d0dd0f000 Use material tooltip allover 2018-07-09 19:54:57 +02:00
HardiReady 44a689a359 Fix method call 2018-07-09 19:41:55 +02:00
HardiReady dd6196555f fix lint 2018-07-08 18:29:40 +02:00
HardiReady 84ddfe5a0b Use generified components in list views 2018-07-08 18:27:24 +02:00
HardiReady f4a9ef7a9d refactor filter input field into standalone component 2018-07-08 17:59:11 +02:00
HardiReady 0255e63c3a Refactor filter buttons to standalone component 2018-07-08 16:46:13 +02:00
HardiReady ff0e615862 re-add static package lock 2018-07-08 13:28:57 +02:00
HardiReady 6e1eaafd95 Refactor army squad into own component 2018-07-08 13:28:21 +02:00
HardiReady 59483b29d8 fix lint 2018-07-06 23:45:03 +02:00
HardiReady 8f9b955e42 update stats upload buttons 2018-07-06 23:42:50 +02:00
HardiReady 798727bbae replace all add buttons 2018-07-06 23:23:29 +02:00
Florian Hartwich 96163b4266 use svg mat icon and drop icon dependency 2018-07-06 17:01:59 +02:00
HardiReady 753b3ea682 Fix linting 2018-07-05 21:57:47 +02:00
HardiReady b8a8b1302d Use material icon button for add functionality 2018-07-05 21:56:32 +02:00
HardiReady 65015f701f redesign war list menu 2018-07-05 21:04:43 +02:00
HardiReady 8451952f4a redesign war list menu 2018-07-05 20:44:12 +02:00
HardiReady 0887f21c9f Update nav header design 2018-07-03 21:51:55 +02:00
HardiReady 3a6e9b093a * Remove ngx-bootstrap dependency
* Add material button toggle for decorations
2018-07-03 20:58:21 +02:00
HardiReady 0aefb9c6d9 Add material button toggle to user management 2018-07-03 20:42:10 +02:00
HardiReady eebcbc32a5 apply ngFor on fraction stats toggle btns 2018-07-03 20:26:55 +02:00
Florian Hartwich 4daaf6d365 Fix production build 2018-07-03 13:10:24 +02:00
Florian Hartwich 55a87756a0 fix linting 2018-07-03 11:38:20 +02:00
HardiReady e92cb6d59f Fix deploy exec script 2018-07-02 22:18:37 +02:00
HardiReady 24a081de95 Replace button toggle squad with material 2018-07-02 22:17:16 +02:00
HardiReady 3bb7bd0e1d Fix linting 2018-07-02 21:57:56 +02:00
HardiReady 68ca503a76 * Add toggle button filter replacement blueprint/util
* Improve startup scripts to match ng6 requirements
2018-07-02 20:23:47 +02:00
HardiReady cf43fab89f Update to angular 6 with rxjs compatibility fix 2018-07-02 20:04:59 +02:00
HardiReady 7b58adad60 Replace bootstrap button toggle with material in fraction stats 2018-07-01 13:03:51 +02:00
HardiReady d1ba6170ba Finish basic war-list redesign and list imrovement 2018-07-01 11:33:03 +02:00
HardiReady ce86593c0f Apply custom design for acordion component 2018-07-01 00:02:59 +02:00
HardiReady 5f0dcae272 Replace arcodion 2018-06-30 22:22:46 +02:00
HardiReady 99a4e0ceb6 remove html errorLabel leftover 2018-06-30 17:52:18 +02:00
HardiReady b7a3c69540 Use snackbar for success/error labels; Add material loading indicator 2018-06-30 17:29:58 +02:00
HardiReady 69f0ec41d6 Switch to snack bar service; customize snack bar 2018-06-30 10:29:22 +02:00
HardiReady 10784f55f4 Add angular material and use snackbar for user edit success msg 2018-06-29 22:20:30 +02:00
HardiReady dd89fc7e5a Add main pages for ranks and decorations listing and apply routing 2018-06-24 18:17:52 +02:00
HardiReady 85850316a3 Include unprocessed squad awardings into getAwarding and generify service call 2018-06-24 14:06:27 +02:00
hardi de22e744fa Merge branch 'release/v1.7.7' of hardi/opt-cc into master 2018-06-22 09:32:24 +02:00
HardiReady 535c2c5b4f add class wise data for fraction stats vehicle kills (CC-35) 2018-06-22 09:18:45 +02:00
HardiReady ea20522524 Add permission level info to apib# 2018-06-18 21:09:09 +02:00
HardiReady 9270ce24f1 Add permission level info to apib 2018-06-18 21:08:57 +02:00
HardiReady 8c27f1b454 Add get squad unprocessed awardings apib tests; FIx lint 2018-06-18 21:02:28 +02:00
HardiReady a6b6677eb7 Move login btn to right and center login form 2018-06-18 20:54:43 +02:00
HardiReady 0af2ebc1e7 Use new endpoint for sql dashboard 2018-06-18 20:44:18 +02:00
HardiReady 43a3f219b3 Add endpoint for unprocessed awarding by squad 2018-06-18 20:33:11 +02:00
HardiReady 1bf7d17615 Fix lint 2018-06-17 16:45:44 +02:00
HardiReady 2a698092e1 Add SQL Dashboard (lazy implementation) (CC-24) 2018-06-17 16:44:31 +02:00
HardiReady d1f53f78c9 clean up code 2018-06-17 16:03:25 +02:00
HardiReady 162453c894 Fix lint 2018-06-17 12:48:08 +02:00
HardiReady a29a39d8e0 add possibility to delete multiple player awardings at once (CC-23) 2018-06-17 12:46:58 +02:00
HardiReady 07a6822920 Save vehicles and magazine on kill log (CC-36) 2018-06-17 11:52:43 +02:00
HardiReady 026a3611a6 Fix CSV export headline (CC-32) 2018-06-17 10:47:02 +02:00
HardiReady bf8ff5d2f6 HotFix release v1.7.6 additionalShooter array check 2018-06-10 19:58:37 +02:00
hardi 1ac58ea71a Merge branch 'release/v1.7.6' of hardi/opt-cc into master 2018-06-10 19:38:25 +02:00
HardiReady 7c67567002 Update docs for multi user vehicle kill 2018-06-10 19:34:29 +02:00
HardiReady 646a7b3030 Update version # 2018-06-09 22:13:23 +02:00
HardiReady 13a6e0f157 Apply vehicle kill to all multi shooters 2018-06-09 22:06:18 +02:00
HardiReady 236e445ca0 Remove vehicle kill items from db on war delete 2018-06-09 19:33:22 +02:00
HardiReady fc5eaf868a Save additional shopoters for vehicle kill 2018-06-09 19:31:22 +02:00
HardiReady d10fc8e49a resolve multi player names 2018-06-09 19:17:38 +02:00
hardi 7797397764 Merge branch 'release/v1.7.5' of hardi/opt-cc into master 2018-06-06 19:16:58 +02:00
HardiReady b05275616c Add direct link to opt board 2018-06-06 19:02:33 +02:00
HardiReady 58d0c781a2 Fix vehicle kill parsing 2018-06-06 18:45:25 +02:00
HardiReady f3f5638667 Fix basic player information parsing 2018-06-04 21:46:07 +02:00
HardiReady c14b33f9c5 Fix parsing for vehicle info 2018-06-04 20:34:17 +02:00
hardi b1cc8a8f9a Update 'README.md' 2018-05-20 08:45:38 +02:00
HardiReady 06a5074d75 Fix log parsing budget for respawn 2018-05-05 12:59:26 +02:00
hardi 639d7d0aa8 Merge branch 'release/v1.7.4' of hardi/opt-cc into master 2018-05-05 10:01:04 +02:00
HardiReady be9bdc23d4 clean up import 2018-05-05 09:43:14 +02:00
HardiReady 96afbefbf2 Adjust player stat attribute order; improve code for player campaign stats init 2018-05-05 09:32:16 +02:00
HardiReady d6703f715d Simplify highscore code and adjust font in table head 2018-05-05 09:14:49 +02:00
HardiReady e54168ec22 Fix linting 2018-05-05 08:17:17 +02:00
HardiReady 819f8ee469 simplify code for highscore and use icons in highscore 2018-05-05 08:08:33 +02:00
HardiReady e8ea7dc6a9 Finish icons for scoreboard 2018-05-04 21:42:12 +02:00
HardiReady 8a78b695b7 Add generic method for get by id 2018-05-04 20:49:45 +02:00
HardiReady bdb07ecc1e Add generic patch method for reuse 2018-05-04 20:37:35 +02:00
HardiReady f0e5b11054 Fix respawn log parsing (CC-22) 2018-04-29 12:00:21 +02:00
HardiReady 63a89ebf7b Add apib doc for update war 2018-04-29 11:30:40 +02:00
HardiReady 6bc6d14b6d Add edit war in API 2018-04-29 11:12:09 +02:00
HardiReady 4b617ec67a Extend editable values for edit war page 2018-04-29 10:39:01 +02:00
HardiReady 266ce9a48a Add login guard for statistic pages; Add basic edit war page 2018-04-29 10:32:14 +02:00
HardiReady 1ac78b7550 refactor package structure for statistics 2018-04-29 10:12:27 +02:00
HardiReady 5f1686c64e refactor package structure for statistics 2018-04-29 10:07:20 +02:00
HardiReady 20f92ce1e9 Change signup PN hyperlink (CC-21) 2018-04-29 09:53:51 +02:00
HardiReady 0bf730e0d4 Fix code style 2018-04-29 09:51:28 +02:00
Florian Hartwich da29c39b88 add working capaign update apib test 2018-04-28 16:54:35 +02:00
Florian Hartwich 8600eb5f3c clean new war file extension check 2018-04-28 16:31:14 +02:00
Florian Hartwich b40c664bce add functional endpoint for update campaign and unhappy frontend processing 2018-04-28 11:55:58 +02:00
Florian Hartwich 3754974bfb Add edit campaign frontend 2018-04-28 10:44:52 +02:00
HardiReady 6381c1ca86 Add button for edit campaign 2018-04-27 15:19:23 +02:00
HardiReady 2cd7541ca6 Fix linting, update codestyle file 2018-04-27 09:29:30 +02:00
HardiReady 216c19dccd Simplify logic for player campaign detail page init 2018-04-27 09:23:43 +02:00
HardiReady dd7bce59e3 Simplify logic for player campaign detail page init 2018-04-27 09:03:57 +02:00
Florian Hartwich 629f36e1e0 loop over scoreboard values from array 2018-04-26 12:26:07 +02:00
HardiReady 835af7b2d3 Add vehicle icons 2018-04-24 19:14:10 +02:00
HardiReady bd0489d6c2 Use custom table header for scoreboard 2018-04-22 13:31:38 +02:00
hardi f4917941a6 Merge branch 'release/v1.7.3' of hardi/opt-cc into master 2018-04-15 16:43:31 +02:00
HardiReady 11c2bf6374 Fix end budget parsing 2018-04-15 16:36:47 +02:00
HardiReady db1222d0d5 fix api tests 2018-04-15 13:47:48 +02:00
HardiReady 8b3151db53 Finish vehicle class parsing 2018-04-15 13:37:51 +02:00
HardiReady 5e8a2b4db2 add vehicle class kills to highscore and player details 2018-04-14 18:05:37 +02:00
HardiReady 9767e1bf25 Fix player parsing for vehicle classes 2018-04-14 17:46:01 +02:00
HardiReady 9d6c6e01b3 Add model fields and correct enum usage for vehicle class 2018-04-14 13:44:50 +02:00
HardiReady b927d48b86 parse vehicle class from log 2018-04-14 13:20:10 +02:00
HardiReady 910df3d267 Fix populated user data for get awardings endpoint (CC-14) 2018-04-11 20:08:09 +02:00
HardiReady 3256cfd57d Fix semi transparent background css for ms edge (CC-13) 2018-04-11 20:00:14 +02:00
hardi 878d61de2e Merge branch 'release/v1.7.2' of hardi/opt-cc into master 2018-04-02 13:30:30 +02:00
HardiReady 4c2678fcad fix code style 2018-04-02 13:22:04 +02:00
HardiReady 94c5ac2261 Add Scoreboard SCV download (CC-11) 2018-04-02 13:20:44 +02:00
HardiReady 3745241d75 Add CSV download button FE 2018-04-02 11:20:04 +02:00
HardiReady a24f3bf4b1 remove ghost file 2018-04-02 10:39:24 +02:00
HardiReady 4a33b87216 clean up api docs 2018-04-02 10:38:16 +02:00
HardiReady c921afa3fe APIB docs full width and add permission level info 2018-04-02 10:28:20 +02:00
HardiReady d2c5850a3d Add descriptions for log instance variables 2018-04-02 10:02:02 +02:00
HardiReady 4d50a9a5c0 Merge branch 'release/v1.7.2' of git.noarch.de:hardi/opt-cc into release/v1.7.2 2018-04-02 09:52:35 +02:00
Florian Hartwich a994f419e7 use in memory db for dredd tests 2018-04-02 00:32:45 +02:00
Florian Hartwich 739cd92073 fix code style 2018-04-01 23:35:57 +02:00
Florian Hartwich 7422d06f22 fix dredd for simplified armyoverview 2018-04-01 23:34:16 +02:00
Florian Hartwich 0e571136bb simplify armyoverview data structure 2018-04-01 23:29:10 +02:00
HardiReady 9d70664186 Add needed permission level info for docs 2018-04-01 16:32:44 +02:00
HardiReady 193b136b0a Clean up code and add admin api docs 2018-04-01 16:01:43 +02:00
HardiReady 14b3d7c4ee Fix apib tests 2018-04-01 15:12:24 +02:00
HardiReady 01b2378d6d Fix tests by adding json analysis 2018-04-01 10:59:09 +02:00
HardiReady 74b1b587a6 add api docs for player endpoints 2018-03-31 19:23:49 +02:00
HardiReady 054bdbdb46 Finish API docs for war 2018-03-31 13:20:18 +02:00
HardiReady e52881faf8 Add statistic test context and war basic doc 2018-03-31 13:05:21 +02:00
HardiReady 08c9e2754e Add request endpoint docs 2018-03-31 12:05:37 +02:00
Florian Hartwich 053c9730ef add api docs create awarding 2018-03-31 00:25:23 +02:00
Florian Hartwich 03eae2fc4b add api docs for ranks and decorations 2018-03-31 00:14:30 +02:00
HardiReady 6d5f068860 Fix code style 2018-03-30 20:28:31 +02:00
HardiReady 29c53c03a5 add file resources for apib tests and env dependent tmp file usage for tests 2018-03-30 20:21:28 +02:00
HardiReady eda2389eda add app start on unit test exec 2018-03-30 18:16:52 +02:00
HardiReady a375124074 Fix code style 2018-03-30 17:57:38 +02:00
HardiReady b09222bd73 Use mongodb in memory for unit tests 2018-03-30 17:26:09 +02:00
HardiReady 37860fd115 Finish squad apib docs 2018-03-30 12:53:49 +02:00
HardiReady e547155e4c fix code style 2018-03-30 10:14:39 +02:00
HardiReady bd18f7b4cf change api test PATCH user to PUT user 2018-03-30 09:16:11 +02:00
HardiReady 3c5dbcd71d Add squad docs until creation to apib 2018-03-30 00:05:13 +02:00
HardiReady 7d3bf9a426 Add campaign apib docs 2018-03-29 23:22:37 +02:00
HardiReady 85b97c771f Add all users endpoints to apib 2018-03-29 22:14:04 +02:00
HardiReady 0f6a954ae4 update intelliJ code style file 2018-03-29 21:19:33 +02:00
Florian Hartwich 85be1ff3c9 clean up unused groups from api docs 2018-03-29 17:55:31 +02:00
Florian Hartwich d3779de03f Fix code style 2018-03-29 17:14:45 +02:00
Florian Hartwich f8db29408f Add GET ranks to apib 2018-03-29 17:13:55 +02:00
Florian Hartwich eaff8b4ea9 remove unused import 2018-03-29 17:01:49 +02:00
Florian Hartwich 1b90cde45e Add scroll to top button (CC-10) 2018-03-29 17:01:24 +02:00
Florian Hartwich 0a12f5470a use var for repeatitive calculation 2018-03-28 15:07:08 +02:00
Florian Hartwich 81f6f0129c fix typo in player campaign page (CC-08) 2018-03-28 15:03:12 +02:00
Florian Hartwich d41c8706fd add vehicle kill log table export for backup/dredd 2018-03-28 14:58:48 +02:00
Florian Hartwich 7f1704387f Add count response header check for apib GET users 2018-03-27 16:30:58 +02:00
Florian Hartwich 0cffa96b41 Merge branch 'release/v1.7.2' of git.noarch.de:hardi/opt-cc into release/v1.7.2 2018-03-27 16:04:12 +02:00
HardiReady c64700ed38 Add file resource doc to apib 2018-03-25 21:30:27 +02:00
HardiReady ea96bca991 Add get users apib docs 2018-03-25 21:18:13 +02:00
HardiReady b85f987088 add get promotions to apib 2018-03-25 21:06:57 +02:00
HardiReady e2722afee7 Add proposer for awardings docs 2018-03-25 20:44:19 +02:00
HardiReady dc0de2db89 remove awardings simple parameter 2018-03-25 20:33:13 +02:00
HardiReady e284fffdee correct apib config and add dredd test data import/export + working run 2018-03-24 19:04:22 +01:00
Florian Hartwich 96bbdb53ef Add test-data export/import script 2018-03-20 13:22:55 +01:00
Florian Hartwich 7b58dac768 Fix apib signup test 2018-03-20 13:15:23 +01:00
Florian Hartwich c3f5c3f971 Add access infos for apib docs 2018-03-20 10:48:00 +01:00
HardiReady e6715c537b Add API docs 2018-03-19 20:32:59 +01:00
hardi fe8797570c fix npm script naming for api-docs 2018-03-19 16:20:15 +01:00
Florian Hartwich 50d532b17d Add hercule for apib processing and dredd for apib tests 2018-03-19 16:02:56 +01:00
HardiReady 9dfbc74377 Add basic infos and login endpoint to docs 2018-03-18 20:21:24 +01:00
HardiReady 67abb1948b Add no-progress flag to static prod build 2018-03-12 16:54:18 +01:00
HardiReady f46798ebd9 Include basic APIB build & clean up npm script executions 2018-03-12 16:28:24 +01:00
HardiReady e595eb11ab revert adding swagger 2018-03-12 15:50:06 +01:00
HardiReady df12f5133c re-add chai method imports in tests 2018-03-12 12:35:19 +01:00
HardiReady 73fa23567c set up node lint run script 2018-03-12 10:44:42 +01:00
HardiReady 140ba2f254 Apply code convention for passing linting 2018-03-12 10:39:56 +01:00
HardiReady e7df8a0c94 Apply max-line length 2018-03-12 09:59:43 +01:00
HardiReady a2215eff59 reconfigure linting rules 2018-03-12 09:50:42 +01:00
HardiReady 7b9d23d1b1 Apply eslint automatic fix 2018-03-12 09:26:44 +01:00
HardiReady c78651a703 Add and configure eslint for express package linting 2018-03-12 09:23:13 +01:00
hardi 8af6605a36 Merge branch 'release/v1.7.1' of hardi/opt-cc into master 2018-03-12 08:17:07 +01:00
HardiReady 7d8c19e7ed Fix fraction stats graph views 2018-03-12 08:14:54 +01:00
hardi 0097616454 Merge branch 'release/v1.7.1' of hardi/opt-cc into master 2018-03-11 15:15:09 +01:00
HardiReady a8094ec802 Add reject reason field for promotions; optimize design for confirm pages; 2018-03-11 14:29:03 +01:00
HardiReady 7808101c58 show reject reason for squad leader 2018-03-11 13:59:04 +01:00
HardiReady 7287f6b8a1 Add possibility to edit request award reason and give reject reason 2018-03-11 13:52:26 +01:00
HardiReady 84ab4410f5 extend token expiration time to 28 days 2018-03-11 09:50:15 +01:00
HardiReady e19dc983cd Add intelliJ IDEA style xml for typescript & javascript 2018-03-11 09:44:37 +01:00
HardiReady 53a99020b8 reformat code 2018-03-11 09:39:05 +01:00
Florian Hartwich c4a876bbda update version number 2018-03-08 10:19:55 +01:00
Florian Hartwich 8859287c51 Resolve TS Lint errors 2018-03-08 10:17:10 +01:00
Florian Hartwich 084edaa53c Update angular lint config 2018-03-08 10:11:05 +01:00
Florian Hartwich f764bd52d5 clean up code according to lint 2018-03-08 09:44:35 +01:00
Florian Hartwich 996ea41224 remove app-prefix rule for component linting 2018-03-07 14:07:38 +01:00
Florian Hartwich 82cc3e1e50 Optimize code style 2018-03-07 11:56:50 +01:00
hardi 08aeda0de3 Merge branch 'release/v1.7.0' of hardi/opt-cc into master 2018-03-04 20:05:44 +01:00
HardiReady dabf919f7e Update vehicle kill count blacklist 2018-03-04 19:59:54 +01:00
HardiReady a09064ea38 Use steamUUID in highscore if available 2018-03-04 10:55:59 +01:00
HardiReady 5d3c9cbe91 adjust version number 2018-03-04 10:33:26 +01:00
HardiReady 2b10661a3d resolve player campaign stats by uuid if esisting 2018-03-03 19:15:33 +01:00
HardiReady 7867c1d480 Add vehicle kill count to highscore page 2018-03-03 13:49:25 +01:00
HardiReady 40931645d1 add vehicle kill stats for cmpaign player view 2018-03-03 13:40:15 +01:00
HardiReady 840695bf0e Add vehicle kills to fraction stats 2018-03-03 13:17:36 +01:00
HardiReady 57f093f4dc Blacklist non-meaningful vehicle kills and add frontend for scoreboard 2018-03-03 12:57:31 +01:00
HardiReady d18986cb1f Modify parsing and models to catch vehicle kills 2018-03-03 12:26:24 +01:00
HardiReady 237926fdf6 Fix scoreboard view on scroll 2018-02-26 09:54:08 +01:00
HardiReady fc0bf18b03 save steamUUID for player 2018-02-26 09:33:28 +01:00
HardiReady 46642aea4d apply new formattinh 2018-02-26 09:04:27 +01:00
HardiReady b52c5fc484 auto scroll top on stats table change 2018-02-26 08:53:51 +01:00
HardiReady 2ba35167bc fix war detail tab alignment firefox 2018-02-26 08:36:46 +01:00
hardi 8bbd99376a Merge branch 'release/v1.6.12' of hardi/opt-cc into master 2018-02-25 18:08:27 +01:00
HardiReady 5c5430314a fix date parsing +1 month 2018-02-25 17:46:18 +01:00
HardiReady 6d202e0192 update Readme 3rd party 2018-02-25 17:30:13 +01:00
HardiReady 9eba31182f Merge branch 'release/v1.6.12' of git.noarch.de:hardi/opt-cc into release/v1.6.12 2018-02-25 17:27:17 +01:00
HardiReady 5821dd31f9 update 3rd party install script 2018-02-25 16:43:46 +01:00
HardiReady 554985a356 Merge branch 'master' of git.noarch.de:hardi/opt-cc into release/v1.6.12 2018-02-25 09:18:12 +01:00
HardiReady 1617296db3 fix pm2 setup and execution from auto init script 2018-02-24 14:06:07 +01:00
HardiReady 0d4a8fff1f remove war detail tab switch scroll top 2018-02-21 21:03:05 +01:00
HardiReady 2123d5fdc4 remove general auto top scroll 2018-02-21 19:37:30 +01:00
HardiReady b0c149f05d remove war detail tab change scroll top; Fix scrollbar integration in ngx tables 2018-02-18 12:19:42 +01:00
559 changed files with 38537 additions and 23378 deletions

9
.gitignore vendored
View File

@ -4,6 +4,7 @@
dist/
tmp/
etc/
server/apib/documentation.apib
# dependencies
node_modules
@ -44,10 +45,11 @@ Thumbs.db
.directory
# Internal Data
public/
mongodb-data/
resource/
/public/
server/resource/
server/apib/dredd/data/tmp-resource
backup/
!backup.sh
# System
.npm/
@ -57,4 +59,3 @@ backup/
.cache/motd.legal-displayed
.profile
.ssh/

View File

@ -1,7 +1,7 @@
# Operation Pandora Trigger Commandcenter
A [MEAN Application](http://mean.io/) created for [operation-pandora.com](https://www.operation-pandora.com) Arma3 Community
A [MEAN Stack](http://mean.io/) application created for [https://www.opt4.net](https://www.opt4.net) Arma3 Community
## Installation
@ -14,27 +14,25 @@ All steps described here are working with a Debian based Linux system
Run the installation script located in the docs folder:
./docs/opt-cc-environment/3rd-party-install
```text
./docs/opt-cc-environment/3rd-party-install.sh
```
It installs NPM, Node and MongoDB on latest versions.
In addition, it sets up the mongo deamon to start up automatically with the system.
#### Setup for Production
**NOTE:** It his highly recommended not to run the following steps as _root_ user!
Before triggering the environment execution run
npm install
inside the main folder, to process all needed npm package installations for the program execution.
**NOTE:** It his highly recommended **not** to run the following steps as _root_ user!
For production setup run the script, described in _Setup for development_, adding the parameter `prod`
./docs/opt-cc-environment/3rd-party-install prod
```text
./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, as the current user.
Run the `sudo` command printed as last output to configure the `pm2` process for automatic start on the system.
## Development
@ -43,19 +41,25 @@ Run the `sudo` command printed as last output to configure the `pm2` process for
Before triggering the environment execution run
```text
npm install
```
inside the main folder, to process all needed npm package installations for the program execution.
To compile the Angular code and afterwards start the Express server with `nodemon` for development purpose run
```text
npm run dev
```
Any changes on `api` code will trigger an automatic restart of the Express server.
Any changes on `server` code will trigger an automatic restart of the Express server.
Changes on `static` code can be submitted with
```text
npm run deploy-static
```
The page must be reloaded after this build step is finished, in order to make changes visible.
@ -65,10 +69,10 @@ _TODO_
## License Information
### Express API (`/api`)
### NodeJS Express Server (`/server`)
published under [CC BY-SA 4.0 License](https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt) \
Main concept for API Server, pagination and MongoDB usage by [Prof. Dr.-Ing. Johannes Konert](https://prof.beuth-hochschule.de/konert/) \
All endpoints, signature image builder and Arma3 RPT-Log parsing by [Florian Hartwich](https://de.linkedin.com/in/florian-hartwich-b67b02125)
### Angular 5 Frontend (`/static`)
### Angular 6 Frontend (`/static`)
published under [MIT License](https://opensource.org/licenses/MIT)

View File

@ -1,23 +0,0 @@
module.exports = {
port: 8091,
database: {
uri: 'mongodb://localhost:27017/',
db: 'cc',
},
prod: {
env: 'production'
},
dev: {
env: 'dev'
},
test: {
port: 3001,
db: 'cc-test',
env: 'test'
}
};

View File

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

View File

@ -1,20 +0,0 @@
"use strict";
// HTTP status codes by name
const codes = require('../routes/http-codes');
/**
* check if id has valid UUID format
*/
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");
err.status = codes.notfound;
return next(err);
}
next();
};
exports.idValidator = idValidator;

View File

@ -1,39 +0,0 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogBudgetSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
},
oldBudget: {
type: Number,
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
}
}, {
collection: 'logBudget'
});
// optional more indices
LogBudgetSchema.index({war: 1});
module.exports = mongoose.model('LogBudget', LogBudgetSchema);

View File

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

View File

@ -1,75 +0,0 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const WarSchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
},
endDate : {
type: Date,
},
ptBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
},
ptOpfor: {
type: Number,
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
},
playersOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
},
campaign: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Campaign',
required: true
},
budgetBlufor: {
type: Number,
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
},
endBudgetBlufor: {
type: Number,
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
}
}, {
collection: 'war',
timestamps: {createdAt: 'timestamp'}
});
// optional more indices
WarSchema.index({timestamp: 1});
module.exports = mongoose.model('War', WarSchema);

6230
api/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
{
"name": "opt-cc-api",
"licence": "CC BY-SA 4.0",
"description": "RESTful API for Operation Pandora Trigger Command Center, includes signature generator",
"main": "server.js",
"author": "Florian Hartwich <hardi@noarch.de>",
"private": true,
"scripts": {
"start": "DEBUG='cc:*' NODE_ENV=production node server.js",
"dev": "DEBUG='cc:*' NODE_ENV=dev nodemon server.js",
"test": "mocha --require ./test/config/spec_helper.js",
"e2e": "NODE_ENV=test node server.js"
},
"dependencies": {
"async": "^2.5.0",
"bcryptjs": "^2.4.3",
"body-parser": "~1.13.2",
"cors": "^2.8.4",
"cron": "^1.3.0",
"debug": "^3.1.0",
"express": "^4.16.2",
"imagemin": "^5.2.2",
"imagemin-pngquant": "^5.0.0",
"jimp": "^0.2.27",
"jsonwebtoken": "^7.4.3",
"lodash": "^4.17.4",
"mkdirp": "^0.5.1",
"mongoose": "^5.0.3",
"morgan": "~1.6.1",
"multer": "^1.3.0",
"node-sha1": "^1.0.1",
"q": "^1.5.0",
"serve-favicon": "~2.3.0",
"supports-color": "^5.1.0"
},
"devDependencies": {
"chai": "^3.5.0",
"chai-http": "^3.0.0",
"mocha": "^3.5.3",
"nodemon": "^1.14.12"
}
}

View File

@ -1,87 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:awardings');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
// Mongoose Model using mongoDB
const AppUserModel = require('../models/app-user');
const account = express.Router();
account.route('/')
.get((req, res, next) => {
AppUserModel.find({}, {}, {sort: {username: 1}}).populate('squad').exec((err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
res.locals.items = items;
res.locals.processed = true;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
// routes **********************
account.route('/:id')
.patch((req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// increment version manually as we do not use .save(.)
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
AppUserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}).populate('squad').exec((err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("appUser not found");
err.status = codes.notfound;
}
else {
res.locals.items = item;
}
next(err);
})
})
.delete((req, res, next) => {
AppUserModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next(err); // this works because err is in normal case undefined and that is the same as no parameter
});
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
account.use(routerHandling.emptyResponse);
module.exports = account;

View File

@ -1,161 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:awardings');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkHl = require('../middleware/permission-check').checkHl;
// Mongoose Model using mongoDB
const AwardingModel = require('../models/awarding');
// result set for proposer(appUser) population
const resultSet = {
'__v': 0,
'updatedAt': 0,
'timestamp': 0,
'password': 0,
'permission': 0,
'secret': 0,
'activated': 0
};
const awarding = express.Router();
// routes **********************
awarding.route('/')
.get((req, res, next) => {
const filter = {};
if (req.query.userId) {
filter.userId = req.query.userId;
}
if (req.query.inProgress) {
filter.confirmed = 0;
}
if (req.query.simple) {
AwardingModel.find(filter, {}, {sort: {date: 'desc'}}, (err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
// with return before (or after) the next(err) we prevent that the code continues here after next(err) has finished.
// this saves an extra else {..}
}
// if the collection is empty we do not send empty arrays back.
if (items && items.length > 0) {
res.locals.items = items;
}
res.locals.processed = true;
next();
});
} else {
AwardingModel.find(filter, {}, {sort: {date: 'desc'}})
.populate('decorationId').populate('proposer', resultSet).populate('userId').exec((err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
// with return before (or after) the next(err) we prevent that the code continues here after next(err) has finished.
// this saves an extra else {..}
}
let results = [];
if (req.query.fractFilter) {
for (let item of items) {
if (item.decorationId.fraction === req.query.fractFilter) {
results.push(item)
}
}
res.locals.items = results;
} else {
res.locals.items = items;
}
res.locals.processed = true;
next();
});
}
})
.post(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
const award = new AwardingModel(req.body);
award.confirmed = 1;
award.proposer = req.user._id;
// timestamp and default are set automatically by Mongoose Schema Validation
award.save((err) => {
if (err) {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
return next(err);
}
res.status(codes.created);
res.locals.items = award;
next();
});
})
.all(
routerHandling.httpMethodNotAllowed
);
awarding.route('/:id')
.patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// optional task 3: increment version manually as we do not use .save(.)
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
AwardingModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
res.locals.items = item;
}
next(err);
})
})
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
AwardingModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next(err); // this works because err is in normal case undefined and that is the same as no parameter
});
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
awarding.use(routerHandling.emptyResponse);
module.exports = awarding;

View File

@ -1,88 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:campaigns');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkMT = require('../middleware/permission-check').checkMT;
const routerHandling = require('../middleware/router-handling');
const idValidator = require('../middleware/validators').idValidator;
// Mongoose Model using mongoDB
const CampaignModel = require('../models/campaign');
const WarModel = require('../models/war');
const campaigns = express.Router();
// routes **********************
campaigns.route('/')
.post(apiAuthenticationMiddleware, checkMT, (req, res, next) => {
const campaign = new CampaignModel(req.body);
// timestamp and default are set automatically by Mongoose Schema Validation
campaign.save((err) => {
if (err) {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
return next(err);
}
res.status(codes.created);
res.locals.items = campaign;
next();
});
})
.all(
routerHandling.httpMethodNotAllowed
);
campaigns.route('/:id')
.get(idValidator, (req, res, next) => {
CampaignModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
res.locals.items = item;
return next();
});
})
.delete((req, res, next) => {
CampaignModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
WarModel.find({campaign: req.params.id}).remove().exec();
res.locals.processed = true;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
campaigns.use(routerHandling.emptyResponse);
module.exports = campaigns;

View File

@ -1,41 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:command');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
const createAllSignatures = require('../cron-job/cron').createAllSignatures;
const createSignature = require('../tools/signature-tool');
const command = express.Router();
command.route('/createSignature')
.post((req, res, next) => {
createAllSignatures();
})
.all(
routerHandling.httpMethodNotAllowed
);
command.route('/createSignature/:id')
.post((req, res, next) => {
const userId = req.params.id;
createSignature(userId, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
command.use(routerHandling.emptyResponse);
module.exports = command;

View File

@ -1,165 +0,0 @@
"use strict";
// modules
const fs = require('fs');
const express = require('express');
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
const logger = require('debug')('cc:decorations');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkHl = require('../middleware/permission-check').checkHl;
const routerHandling = require('../middleware/router-handling');
const idValidator = require('../middleware/validators').idValidator;
// Mongoose Model using mongoDB
const DecorationModel = require('../models/decoration');
const AwardingsModel = require('../models/awarding');
const decoration = express.Router();
// routes **********************
decoration.route('/')
.get((req, res, next) => {
const filter = {};
if (req.query.fractFilter) {
filter.fraction = req.query.fractFilter.toUpperCase()
}
if (req.query.q) {
filter.name = {$regex: req.query.q, $options: 'i'}
}
DecorationModel.find(filter, {}, {sort: {fraction: 'asc', isMedal: 'asc', sortingNumber: 'asc', name: 'asc'}}, (err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (items && items.length > 0) {
res.locals.items = items;
} else {
res.locals.items = [];
}
res.locals.processed = true;
next();
});
})
.post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
const decoration = new DecorationModel(req.body);
// timestamp and default are set automatically by Mongoose Schema Validation
decoration.save((err) => {
if (err) {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
return next(err);
}
res.status(codes.created);
res.locals.items = decoration;
fs.appendFile(__dirname + '/../resource/decoration/' + decoration.id + '.png', new Buffer(req.file.buffer),
(err) => {
if (err) next(err);
});
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
decoration.route('/:id')
.get(idValidator, (req, res, next) => {
DecorationModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
res.locals.items = item;
next();
});
})
.patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// optional task 3: increment version manually as we do not use .save(.)
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
if (req.file) {
const file = __dirname + '/../resource/decoration/' + req.body._id + '.png';
fs.unlink(file, (err) => {
if (err) next(err);
fs.appendFile(file, new Buffer(req.file.buffer), (err) => {
if (err) next(err);
});
});
}
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
DecorationModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
res.locals.items = item;
}
next(err);
})
})
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
DecorationModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
// deleted all awardings linked to this decoration
AwardingsModel.find({decorationId: req.params.id}).remove().exec();
// delete graphic
fs.unlink(__dirname + '/../resource/decoration/' + req.params.id + '.png',
(err) => {
if (err) next(err);
});
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next(err); // this works because err is in normal case undefined and that is the same as no parameter
});
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
decoration.use(routerHandling.emptyResponse);
module.exports = decoration;

View File

@ -1,156 +0,0 @@
"use strict";
// modules
const express = require('express');
const async = require('async');
const logger = require('debug')('cc:logs');
const routerHandling = require('../middleware/router-handling');
const decimalToTimeString = require('../tools/util').decimalToTimeString;
// Mongoose Model using mongoDB
const LogBudgetModel = require('../models/logs/budget');
const LogRespawnModel = require('../models/logs/respawn');
const LogReviveModel = require('../models/logs/revive');
const LogKillModel = require('../models/logs/kill');
const LogTransportModel = require('../models/logs/transport');
const LogFlagModel = require('../models/logs/flag');
const LogPointsModel = require('../models/logs/points');
const logsRouter = express.Router();
function processLogRequest(model, filter, res, next) {
model.find(filter, {}, {sort: {time: 1}}, (err, log) => {
if (err) return next(err);
if (!log || log.length === 0) {
const err = new Error('No logs found');
err.status = require('./http-codes').notfound;
return next(err)
}
res.locals.items = log;
next();
})
}
// routes **********************
logsRouter.route('/:warId')
.get((req, res, next) => {
const filter = {war: req.params.warId};
const sort = {sort: {time: 1}};
const pointsObjects = LogPointsModel.find(filter, {}, sort);
const budgetObjects = LogBudgetModel.find(filter, {}, sort);
const respawnObjects = LogRespawnModel.find(filter, {}, sort);
const reviveObjects = LogReviveModel.find(filter, {}, sort);
const killObjects = LogKillModel.find(filter, {}, sort);
const transportObjects = LogTransportModel.find(filter, {}, sort);
const flagObjects = LogFlagModel.find(filter, {}, sort);
const resources = {
points: pointsObjects.exec.bind(pointsObjects),
budget: budgetObjects.exec.bind(budgetObjects),
respawn: respawnObjects.exec.bind(respawnObjects),
revive: reviveObjects.exec.bind(reviveObjects),
kill: killObjects.exec.bind(killObjects),
transport: transportObjects.exec.bind(transportObjects),
flag: flagObjects.exec.bind(flagObjects)
};
async.parallel(resources, function (error, results){
if (error) {
res.status(500).send(error);
return;
}
res.locals.items = results;
next();
});
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/budget')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.fraction) filter['fraction'] = req.query.fraction;
processLogRequest(LogBudgetModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/respawn')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.player) filter['player'] = req.query.player;
processLogRequest(LogRespawnModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/revive')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.medic) filter['medic'] = req.query.medic;
if (req.query.patient) filter['patient'] = req.query.patient;
if (req.query.stabilized) filter['stabilized'] = true;
if (req.query.revive) filter['stabilized'] = false;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
processLogRequest(LogReviveModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/kills')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.shooter) filter['shooter'] = req.query.shooter;
if (req.query.target) filter['target'] = req.query.target;
if (req.query.friendlyFire) filter['friendlyFire'] = true;
if (req.query.noFriendlyFire) filter['friendlyFire'] = false;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
processLogRequest(LogKillModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/transport')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.driver) filter['driver'] = req.query.driver;
if (req.query.passenger) filter['passenger'] = req.query.passenger;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
processLogRequest(LogTransportModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/flag')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.player) filter['player'] = req.query.player;
if (req.query.capture) filter['capture'] = true;
if (req.query.defend) filter['capture'] = false;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
processLogRequest(LogFlagModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/points')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.fraction) filter['fraction'] = req.query.fraction;
processLogRequest(LogPointsModel, filter, res, next);
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.use(routerHandling.emptyResponse);
module.exports = logsRouter;

View File

@ -1,113 +0,0 @@
"use strict";
// modules
const async = require('async');
const express = require('express');
const logger = require('debug')('cc:overview');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
// Mongoose Model using mongoDB
const UserModel = require('../models/user');
const SquadModel = require('../models/squad');
const RankModel = require('../models/rank');
const overview = express.Router();
// routes **********************
overview.route('/')
.get((req, res, next) => {
let countOpfor = 0;
let countBlufor = 0;
const armyOverview = {
BLUFOR: {
squads: []
},
OPFOR: {
squads: []
}
};
SquadModel.find({}, {'sortingNumber': 0, 'updatedAt': 0, 'timestamp': 0, '__v': 0}, {
sort: {
sortingNumber: 'asc',
name: 'asc'
}
}, (err, squads) => {
if (err) {
return next(err);
}
async.eachSeries(squads, (squad, callback) => {
UserModel.find({squadId: squad._id}, {
'squadId': 0,
'updatedAt': 0,
'timestamp': 0,
'__v': 0
}, {sort: {rankLvl: 'desc', name: 'asc'}}, (err, users) => {
const squadMembers = [];
async.eachSeries(users, (user, callback) => {
const usr = user.toObject();
RankModel.findOne({level: user.rankLvl, fraction: squad.fraction}, (err, rank) => {
if (err) {
return next(err);
}
// not defined if rank was deleted / rankLvl not available for fraction
if (rank) {
usr.rank = rank.name;
}
delete usr.rankLvl;
squadMembers.push(usr)
callback();
});
}, (err) => {
if (err) {
return next(err);
}
// do not return empty squads
if (squadMembers.length > 0) {
const s = squad.toObject();
s.members = squadMembers;
s.memberCount = squadMembers.length;
if (s.fraction === 'BLUFOR') {
delete s.fraction;
armyOverview.BLUFOR.squads.push(s);
countBlufor += s.members.length;
}
if (s.fraction === 'OPFOR') {
delete s.fraction;
armyOverview.OPFOR.squads.push(s);
countOpfor += s.members.length;
}
}
callback();
});
});
}, (err) => {
if (err) {
return next(err);
}
armyOverview.BLUFOR.memberCount = countBlufor;
armyOverview.OPFOR.memberCount = countOpfor;
res.locals.items = armyOverview;
res.locals.processed = true;
next();
});
});
})
.all(
routerHandling.httpMethodNotAllowed
);
overview.use(routerHandling.emptyResponse);
module.exports = overview;

View File

@ -1,114 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:players');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
// Mongoose Model using mongoDB
const CampaignModel = require('../models/campaign');
const PlayerModel = require('../models/player');
const WarModel = require('../models/war');
const campaignPlayer = express.Router();
// routes **********************
campaignPlayer.route('/ranking/:campaignId')
.get((req, res, next) => {
WarModel.find({campaign: req.params.campaignId}, '_id', (err, wars) => {
if (err) return next(err);
const warIds = wars.map((obj) => {
return obj._id;
});
PlayerModel.find({warId: {"$in": warIds}}, (err, items) => {
if (err) return next(err);
if (!items || items.length === 0) {
const err = new Error('No players for given campaignId');
err.status = codes.notfound;
return next(err)
}
const rankingItems = [];
new Set(items.map(x => x.name)).forEach(playerName => {
const playerInstances = items.filter(p => p.name === playerName);
const resItem = {name: playerName, kill: 0, death: 0, friendlyFire: 0, revive: 0, respawn: 0, flagTouch: 0};
for (let i = 0; i < playerInstances.length; i++) {
resItem.kill += playerInstances[i].kill;
resItem.death += playerInstances[i].death;
resItem.friendlyFire += playerInstances[i].friendlyFire;
resItem.revive += playerInstances[i].revive;
resItem.respawn += playerInstances[i].respawn;
resItem.flagTouch += playerInstances[i].flagTouch;
}
resItem.fraction = playerInstances[playerInstances.length - 1].fraction;
rankingItems.push(resItem);
});
function getSortedField(fieldName) {
let num = 1;
rankingItems.sort((a, b) => b[fieldName] - a[fieldName])
const res = JSON.parse(JSON.stringify(rankingItems));
for (const entity of res) {
entity.num = num++;
}
return res;
}
res.locals.items = {
kill: getSortedField('kill'),
death: getSortedField('death'),
friendlyFire: getSortedField('friendlyFire'),
revive: getSortedField('revive'),
respawn: getSortedField('respawn'),
flagTouch: getSortedField('flagTouch')
};
next();
})
})
})
.all(
routerHandling.httpMethodNotAllowed
);
campaignPlayer.route('/single/:campaignId/:playerName')
.get((req, res, next) => {
CampaignModel.findById(req.params.campaignId, (err, campaign) => {
if (err) return next(err);
WarModel.find({campaign: req.params.campaignId}, '_id', (err, wars) => {
if (err) return next(err);
const warIds = wars.map((obj) => {
return obj._id;
});
PlayerModel.find({name: req.params.playerName, warId: {"$in": warIds}})
.populate('warId')
.exec((err, items) => {
if (err) return next(err);
if (!items || items.length === 0) {
const err = new Error('Unknown player name');
err.status = codes.notfound;
return next(err)
}
res.locals.items = {
name: req.params.playerName,
campaign: campaign,
players: items
};
next();
})
})
})
})
.all(
routerHandling.httpMethodNotAllowed
);
campaignPlayer.use(routerHandling.emptyResponse);
module.exports = campaignPlayer;

View File

@ -1,158 +0,0 @@
"use strict";
// modules
const fs = require('fs');
const express = require('express');
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
const logger = require('debug')('cc:ranks');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkHl = require('../middleware/permission-check').checkHl;
const routerHandling = require('../middleware/router-handling');
const idValidator = require('../middleware/validators').idValidator;
// Mongoose Model using mongoDB
const RankModel = require('../models/rank');
const ranks = express.Router();
// routes **********************
ranks.route('/')
.get((req, res, next) => {
const filter = {};
if (req.query.fractFilter) {
filter.fraction = req.query.fractFilter.toUpperCase()
}
if (req.query.q) {
filter.name = {$regex: req.query.q, $options: 'i'}
}
RankModel.find(filter, {}, {sort: {fraction: 'asc', level: 'asc'}}, (err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (items && items.length > 0) {
res.locals.items = items;
} else {
res.locals.items = [];
}
res.locals.processed = true;
next();
});
})
.post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
const rank = new RankModel(req.body);
// timestamp and default are set automatically by Mongoose Schema Validation
rank.save((err) => {
if (err) {
err.status = codes.wrongrequest;
next(err);
}
res.status(codes.created);
res.locals.items = rank;
fs.appendFile(__dirname + '/../resource/rank/' + rank.id + '.png', new Buffer(req.file.buffer),
(err) => {
if (err) next(err);
});
next();
});
})
.all(
routerHandling.httpMethodNotAllowed
);
ranks.route('/:id')
.get(idValidator, (req, res, next) => {
RankModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
res.locals.items = item;
next();
});
})
.patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// optional task 3: increment version manually as we do not use .save(.)
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
if (req.file) {
const file = __dirname + '/../resource/rank/' + req.body._id + '.png';
fs.unlink(file, (err) => {
if (err) next(err);
fs.appendFile(file, new Buffer(req.file.buffer),
(err) => {
if (err) next(err);
});
});
}
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
RankModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
res.locals.items = item;
}
next(err);
})
})
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
RankModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next(err); // this works because err is in normal case undefined and that is the same as no parameter
})
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
ranks.use(routerHandling.emptyResponse);
module.exports = ranks;

View File

@ -1,183 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:awardings');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkSql = require('../middleware/permission-check').checkSql;
const checkHl = require('../middleware/permission-check').checkHl;
// Mongoose Model using mongoDB
const UserModel = require('../models/user');
const AwardingModel = require('../models/awarding');
const PromotionModel = require('../models/promotion');
// result set for proposer(appUser) population
const resultSet = {
'__v': 0,
'updatedAt': 0,
'timestamp': 0,
'password': 0,
'permission': 0,
'secret': 0,
'activated': 0
};
const request = express.Router();
// routes **********************
request.route('/award')
.post(apiAuthenticationMiddleware, checkSql, (req, res, next) => {
const award = new AwardingModel(req.body);
award.confirmed = 0;
award.proposer = req.user._id;
// timestamp and default are set automatically by Mongoose Schema Validation
award.save((err) => {
if (err) {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
return next(err);
}
res.status(codes.created);
res.locals.items = award;
next();
});
})
.all(
routerHandling.httpMethodNotAllowed
);
request.route('/promotion')
.get((req, res, next) => {
// TODO: add SQL authentication
const squadFilter = req.query.squadId;
const fractFilter = req.query.fractFilter;
const progressFilter = req.query.inProgress;
let filter;
if (squadFilter) {
filter = {squadId: squadFilter};
}
let userIds = [];
UserModel.find(filter).populate('squadId').exec((err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
for (let item of items) {
if (!fractFilter || (fractFilter && item.squadId && item.squadId.fraction === fractFilter)) {
userIds.push(item._id);
}
}
let promotionFilter = {
userId: {"$in": userIds}
};
if (progressFilter) {
promotionFilter.confirmed = 0;
}
PromotionModel.find(promotionFilter, {}, {sort: {timestamp: 'desc'}})
.populate('userId').populate('proposer', resultSet).exec((err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (items && items.length > 0) {
res.locals.items = items;
} else {
res.locals.items = [];
}
res.locals.processed = true;
next();
})
});
})
.post(apiAuthenticationMiddleware, checkSql, (req, res, next) => {
const promotion = new PromotionModel(req.body);
promotion.confirmed = 0;
promotion.proposer = req.user._id;
// timestamp and default are set automatically by Mongoose Schema Validation
promotion.save((err) => {
if (err) {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
return next(err);
}
res.status(codes.created);
res.locals.items = promotion;
next();
});
})
.all(
routerHandling.httpMethodNotAllowed
);
request.route('/promotion/:id')
.patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
PromotionModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
if (item.confirmed === 1) {
let updateUser = {
_id: item.userId,
rankLvl: item.newRankLvl
};
UserModel.findByIdAndUpdate(updateUser._id, updateUser, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("user not found");
err.status = codes.notfound;
}
});
}
res.locals.items = item;
}
next(err);
})
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
request.use(routerHandling.emptyResponse);
module.exports = request;

View File

@ -1,58 +0,0 @@
"use strict";
// modules
const fs = require('fs');
const express = require('express');
const logger = require('debug')('cc:signatures');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
const UserModel = require('../models/user');
const signatures = express.Router();
// routes **********************
signatures.route('/:id')
// does not use idValidator since it works by username
.get((req, res, next) => {
// decode UTF8-escape sequences (special characters)
const uri = decodeURIComponent(req.params.id);
UserModel.findOne({username: uri}, (err, user) => {
const emptyFile = 'resource/signature/0.png';
if (user === null) {
res.sendFile(emptyFile, {root: __dirname + '/../'});
} else {
const file = 'resource/signature/' + user._id + '.png';
fs.stat(__dirname + '/../' + file, (err, stat) => {
if (err === null) {
res.sendFile(file, {root: __dirname + '/../'});
} else if (err.code === 'ENOENT') {
res.sendFile(emptyFile, {root: __dirname + '/../'});
} else {
err = new Error("Internal server error");
err.status = codes.servererror;
return next(err);
}
});
}
})
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
signatures.use(routerHandling.emptyResponse);
module.exports = signatures;

View File

@ -1,164 +0,0 @@
"use strict";
// modules
const fs = require('fs');
const express = require('express');
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
const logger = require('debug')('cc:squads');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkHl = require('../middleware/permission-check').checkHl;
const routerHandling = require('../middleware/router-handling');
const idValidator = require('../middleware/validators').idValidator;
// Mongoose Model using mongoDB
const SquadModel = require('../models/squad');
const squads = express.Router();
// routes **********************
squads.route('/')
.get((req, res, next) => {
const filter = {};
if (req.query.fractFilter) {
filter.fraction = req.query.fractFilter.toUpperCase()
}
if (req.query.q) {
filter.name = {$regex: req.query.q, $options: 'i'}
}
SquadModel.find(filter, {}, {sort: {fraction: 'asc', sortingNumber: 'asc'}}, (err, items) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (items) {
res.locals.items = items;
} else {
res.locals.items = [];
}
res.locals.processed = true;
next();
});
})
.post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
const squad = new SquadModel(req.body);
// timestamp and default are set automatically by Mongoose Schema Validation
if (req.file) {
squad.save((err) => {
if (err) {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
return next(err);
}
res.status(codes.created);
res.locals.items = squad;
fs.appendFile(__dirname + '/../resource/squad/' + squad.id + '.png', new Buffer(req.file.buffer), (err) => {
if (err) next(err);
});
next();
})
} else {
const err = new Error('no image file provided');
err.status = codes.wrongmediasend;
next(err);
}
})
.all(
routerHandling.httpMethodNotAllowed
);
squads.route('/:id')
.get(idValidator, (req, res, next) => {
SquadModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
res.locals.items = item;
next();
});
})
.patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// increment version manually as we do not use .save(.)
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
if (req.file) {
const file = __dirname + '/../resource/squad/' + req.body._id + '.png';
fs.unlink(file, (err) => {
if (err) next(err);
fs.appendFile(file, new Buffer(req.file.buffer), (err) => {
if (err) next(err);
});
});
}
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
SquadModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
res.locals.items = item;
}
next(err);
})
})
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
SquadModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
// delete graphic
fs.unlink(__dirname + '/../resource/squad/' + req.params.id + '.png', (err) => {
if (err) next(err);
});
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next(err); // this works because err is in normal case undefined and that is the same as no parameter
});
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
squads.use(routerHandling.emptyResponse);
module.exports = squads;

View File

@ -1,243 +0,0 @@
"use strict";
// modules
const express = require('express');
const fs = require('fs');
const logger = require('debug')('me2u5:users');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkHl = require('../middleware/permission-check').checkHl;
const offsetlimitMiddleware = require('../middleware/limitoffset-middleware-mongo');
const filterHandlerCreator = require('../middleware/filter-handler-mongo');
const routerHandling = require('../middleware/router-handling');
const idValidator = require('../middleware/validators').idValidator;
// Mongoose Model using mongoDB
const UserModel = require('../models/user');
const SquadModel = require('../models/squad');
const AwardingModel = require('../models/awarding');
const users = express.Router();
users.get('/', filterHandlerCreator(UserModel.schema.paths));
users.get('/', offsetlimitMiddleware);
// routes **********************
users.route('/')
.get((req, res, next) => {
const userQuery = () => {
UserModel.find(dbFilter, res.locals.filter, res.locals.limitskip)
.populate('squadId')
.collation({locale: "en", strength: 2}) // case insensitive order
.sort('username').exec((err, users) => {
if (err) return next(err);
if (users.length === 0) {
res.locals.items = users;
res.locals.processed = true;
return next();
}
UserModel.count(dbFilter, (err, totalCount) => {
res.set('x-total-count', totalCount);
res.locals.items = users;
res.locals.processed = true;
return next();
})
})
};
if (!req.query.q) req.query.q = '';
const dbFilter = {username: {"$regex": req.query.q, "$options": "i"}};
if (req.query.squadId) dbFilter["squadId"] = {"$eq": req.query.squadId};
// squad / fraction filter setup
if (req.query.fractFilter && req.query.fractFilter !== 'UNASSIGNED' && !req.query.squadId) {
SquadModel.find({'fraction': req.query.fractFilter}, {_id: 1}, (err, squads) => {
dbFilter['squadId'] = {$in: squads.map(squad => squad.id)};
userQuery();
})
} else {
if (req.query.fractFilter === 'UNASSIGNED') {
dbFilter['squadId'] = {$eq: null};
}
userQuery();
}
})
.post(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
const user = new UserModel(req.body);
// timestamp and default are set automatically by Mongoose Schema Validation
user.save((err) => {
if (err) {
err.status = codes.wrongrequest;
return next(err);
}
res.status(codes.created);
UserModel.populate(user, {path: 'squadId'}, (err, extUser) => {
res.locals.items = extUser;
res.locals.processed = true;
return next();
})
});
})
.all(routerHandling.httpMethodNotAllowed);
users.route('/:id')
.get(idValidator, (req, res, next) => {
UserModel.findById(req.params.id).populate('squadId').exec((err, user) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!user) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
res.locals.items = user;
res.locals.processed = true;
return next();
});
})
.patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// optional task 3: increment version manually as we do not use .save(.)
req.body.updatedAt = new Date();
req.body.$inc = {__v: 1};
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
UserModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
UserModel.populate(item, {path: 'squadId'}, (err, extUser) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (!user) {
res.locals.items = {};
res.locals.processed = true;
return next();
}
res.locals.items = extUser;
res.locals.processed = true;
return next();
})
})
})
.put(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
// first check that the given element id is the same as the URL id
if (!req.body || req.body._id !== req.params.id) {
// the URL does not fit the given element
var err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
err.status = codes.notfound;
next(err);
return; // prevent node to process this function further after next() has finished.
}
// main difference of PUT and PATCH is that PUT expects all data in request: checked by using the schema
const user = new UserModel(req.body);
UserModel.findById(req.params.id, req.body, {new: true}, function (err, item) {
// with parameter {new: true} the TweetNModel will return the new and changed object from the DB and not the old one.
if (err) {
err.status = codes.wrongrequest;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
// check that version is still accurate
else if (user.__v !== item.__v) {
err = new Error("version outdated. Meanwhile update on item happened. Please GET resource again")
err.status = codes.conflict;
return next(err);
}
// now update all fields in DB item with body data in variable video
for (var field in UserModel.schema.paths) {
if ((field !== '_id') && (field !== '__v')) {
// this includes undefined. is important to reset attributes that are missing in req.body
item.set(field, user[field]);
}
}
// update updatedAt and increase version
item.updatedAt = new Date();
item.increment(); // this sets __v++
item.save(function (err) {
if (!err) {
res.locals.items = item;
} else {
err.status = codes.wrongrequest;
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
}
UserModel.populate(item, {path: 'squadId'}, (err, extUser) => {
res.locals.items = extUser;
res.locals.processed = true;
return next();
})
});
})
})
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
UserModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
// deleted all awardings linked to this user
AwardingModel.find({userId: req.params.id}).remove().exec();
// check if signature exists and delete compressed and uncompressed file
const fileMinified = __dirname + '/../resource/signature/' + req.params.id + '.png';
if (fs.existsSync(fileMinified)) {
fs.unlink(fileMinified, (err) => {
});
}
const file = __dirname + '/../resource/signature/big/' + req.params.id + '.png';
if (fs.existsSync(file)) {
fs.unlink(file, (err) => {
});
}
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next(err); // this works because err is in normal case undefined and that is the same as no parameter
});
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
users.use(routerHandling.emptyResponse);
module.exports = users;

View File

@ -1,225 +0,0 @@
"use strict";
// modules
const fs = require('fs');
const mkdirp = require("mkdirp");
const express = require('express');
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
const logger = require('debug')('cc:wars');
// HTTP status codes by name
const codes = require('./http-codes');
// access check
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkMT = require('../middleware/permission-check').checkMT;
const routerHandling = require('../middleware/router-handling');
const idValidator = require('../middleware/validators').idValidator;
// log paser tool
const parseWarLog = require('../tools/log-parse-tool');
// Mongoose Model using mongoDB
const CampaignModel = require('../models/campaign');
const WarModel = require('../models/war');
const PlayerModel = require('../models/player');
const LogKillModel = require('../models/logs/kill');
const LogRespawnModel = require('../models/logs/respawn');
const LogReviveModel = require('../models/logs/revive');
const LogTransportModel = require('../models/logs/transport');
const LogFlagModel = require('../models/logs/flag');
const LogBudgetModel = require('../models/logs/budget');
const LogPointsModel = require('../models/logs/points');
const wars = express.Router();
// routes **********************
wars.route('/')
.get((req, res, next) => {
let result = [];
CampaignModel.find({}, {}, {sort: {timestamp: 'desc'}}, (err, campaigns) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (campaigns) {
WarModel.find({}, {}, {sort: {date: 'desc'}}, (err, wars) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
if (wars) {
campaigns.forEach(campaign => {
let entry = {_id: campaign._id, title: campaign.title, wars: []};
wars.forEach((war) => {
if (String(campaign._id) === String(war.campaign)) {
entry.wars.push(war);
}
});
result.push(entry);
});
res.locals.items = result;
}
res.locals.processed = true;
next();
}
)
;
}
})
})
.post(apiAuthenticationMiddleware, checkMT, upload.single('log'), (req, res, next) => {
const body = req.body;
const warBody = new WarModel(body);
if (req.file) {
fs.readFile(req.file.buffer, (file, err) => {
if (err) {
return next(err);
}
const lineArray = file.toString().split("\n");
const statsResult = parseWarLog(lineArray, warBody);
statsResult.war.save((err, war) => {
if (err) {
return next(err);
}
PlayerModel.create(statsResult.players, function (err) {
if (err) {
return next(err);
}
LogKillModel.create(statsResult.kills, function () {
LogRespawnModel.create(statsResult.respawn, function () {
LogReviveModel.create(statsResult.revive, function () {
LogFlagModel.create(statsResult.flag, function () {
LogBudgetModel.create(statsResult.budget, function () {
LogTransportModel.create(statsResult.transport, function () {
LogPointsModel.create(statsResult.points, function () {
const folderName = __dirname + '/../resource/logs/' + war._id;
mkdirp(folderName, function (err) {
if (err) return next(err);
// save clean log file
const cleanFile = fs.createWriteStream(folderName + '/clean.log');
statsResult.clean.forEach(cleanLine => {
cleanFile.write(cleanLine + '\n\n')
});
cleanFile.end();
// save raw log file
const rawFile = fs.createWriteStream(folderName + '/war.log');
lineArray.forEach(rawLine => {
rawFile.write(rawLine + '\n')
});
rawFile.end();
res.status(codes.created);
res.locals.items = war;
next();
})
})
})
})
})
})
})
})
})
})
});
} else {
const err = new Error('no Logfile provided');
err.status = codes.wrongmediasend;
next(err);
}
})
.all(
routerHandling.httpMethodNotAllowed
);
wars.route('/:id')
.get(idValidator, (req, res, next) => {
WarModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
PlayerModel.find({warId: item._id}, {}, {sort: {kill: 'desc'}}, (err, items) => {
if (err) {
return next(err);
}
const responseObj = item.toObject();
responseObj.players = items;
res.locals.items = responseObj;
return next();
});
});
})
.delete(apiAuthenticationMiddleware, checkMT, (req, res, next) => {
WarModel.findByIdAndRemove(req.params.id, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
// delete linked appearances
PlayerModel.find({warId: item._id}).remove().exec();
LogKillModel.find({war: item._id}).remove().exec();
LogRespawnModel.find({war: item._id}).remove().exec();
LogReviveModel.find({war: item._id}).remove().exec();
LogFlagModel.find({war: item._id}).remove().exec();
LogBudgetModel.find({war: item._id}).remove().exec();
LogTransportModel.find({war: item._id}).remove().exec();
LogPointsModel.find({war: item._id}).remove().exec();
// check if logfiles exist and delete from fs
const warDir = __dirname + '/../resource/logs/' + req.params.id;
if (fs.existsSync(warDir)) {
const cleanLog = warDir + '/clean.log';
if (fs.existsSync(cleanLog)) {
fs.unlink(cleanLog, (err) => {
});
}
const sourceLog = warDir + '/war.log';
if (fs.existsSync(sourceLog)) {
fs.unlink(sourceLog, (err) => {
});
}
fs.rmdir(warDir, (err) => {
});
}
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler user.use(..)
res.locals.processed = true;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
// this middleware function can be used, if you like or remove it
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
wars.use(routerHandling.emptyResponse);
module.exports = wars;

View File

@ -1,127 +0,0 @@
"use strict";
const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const bodyParser = require('body-parser');
const requestLogger = require('morgan');
const debug = require('debug');
const error = debug('cc:server:err');
const logger = debug('cc:server');
logger.log = console.log.bind(console);
const cors = require('cors')
const mongoose = require('mongoose');
// own modules
const config = require('./config/config');
const urls = require('./config/api-url');
const restAPIchecks = require('./middleware/request-checks.js');
const errorResponseWare = require('./middleware/error-response');
const apiAuthenticationMiddleware = require('./middleware/auth-middleware');
const checkAdmin = require('./middleware/permission-check').checkAdmin;
const signatureCronJob = require('./cron-job/cron').cronJobSignature;
const backupCronJob = require('./cron-job/cron').cronJobBackup;
// router modules
const authenticateRouter = require('./routes/authenticate');
const accountRouter = require('./routes/account');
const overviewRouter = require('./routes/overview');
const userRouter = require('./routes/users');
const squadRouter = require('./routes/squads');
const rankRouter = require('./routes/ranks');
const decorationRouter = require('./routes/decorations');
const awardingRouter = require('./routes/awardings');
const requestRouter = require('./routes/request');
const playerRouter = require('./routes/players');
const signatureRouter = require('./routes/signatures');
const commandRouter = require('./routes/command');
const campaignRouter = require('./routes/campaigns');
const warRouter = require('./routes/wars');
const logRouter = require('./routes/logs');
// Configuration ***********************************
// mongoose promise setup
mongoose.Promise = global.Promise;
// app creation
const app = express();
// Middlewares *************************************************
// setup CORS-middleware
const corsOptions = {
methods: ['GET'],
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
app.use(favicon(path.join(__dirname + '/..', 'public', 'favicon.ico')));
app.use(express.static(path.join(__dirname + '/..', 'public')));
app.use(bodyParser.json({limit: '10mb'}));
app.use(bodyParser.urlencoded({limit: '10mb', extended: true}));
// API request checks for API-version and JSON etc.
app.use(restAPIchecks);
// Routes ******************************************************
app.use(urls.signatures, signatureRouter);
// initialize logging at this point to exclude /signature requests
if (process.env.NODE_ENV === config.dev.env) {
// development logging
app.use(requestLogger('dev'));
} else if (process.env.NODE_ENV !== config.test.env) {
// production logging, apache style
app.use(requestLogger(':date[clf] :method :url :response-time ms :status'));
}
app.use(urls.auth, authenticateRouter);
app.use(urls.overview, overviewRouter);
app.use(urls.users, userRouter);
app.use(urls.squads, squadRouter);
app.use(urls.ranks, rankRouter);
app.use(urls.decorations, decorationRouter);
app.use(urls.request, requestRouter);
app.use(urls.awards, awardingRouter);
app.use(urls.wars, warRouter);
app.use(urls.players, playerRouter);
app.use(urls.campaigns, campaignRouter);
app.use(urls.logs, logRouter);
app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter);
app.use(urls.account, apiAuthenticationMiddleware, checkAdmin, accountRouter);
// send index.html on all different paths
app.use(function (req, res) {
res.sendFile("public/index.html", {root: __dirname + '/..'});
});
// register error handlers
errorResponseWare(app);
// Start the server
if (process.env.NODE_ENV !== config.test.env) {
const mongoosePromise = mongoose.connect(config.database.uri + config.database.db);
mongoosePromise.then((db) => {
app.listen(config.port, (err) => {
if (err !== undefined) {
error('Error on startup, ', err);
}
else {
logger('Listening on port ' + config.port);
signatureCronJob.start();
backupCronJob.start();
}
});
})
} else {
const mongoosePromise = mongoose.connect(config.database.uri + config.test.db);
mongoosePromise.then((db) => {
app.listen(config.test.port);
logger('Listening on port ' + config.test.port);
})
}
module.exports = app;

View File

@ -1,110 +0,0 @@
let mongoose = require("mongoose");
let AwardingModel = require('../models/awarding');
let urls = require('../config/api-url');
let codes = require('../routes/http-codes');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server');
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Awardings', () => {
beforeEach((done) => { //Before each test we empty the database
AwardingModel.remove({}, (err) => {
done();
});
});
/*
* Test the /GET awardings
*/
describe('/GET awardings', () => {
it('it should GET all awardings', (done) => {
chai.request(server)
.get(urls.awards)
.end((err, res) => {
res.should.have.status(codes.success);
res.body.should.be.a('array');
res.body.length.should.be.eql(0);
done();
});
});
});
/*
* Test the /POST awardings
*/
describe('/POST awardings', () => {
it('it should not POST an awarding without auth-token provided', (done) => {
chai.request(server)
.post(urls.awards)
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /PATCH awardings
*/
describe('/PATCH awardings', () => {
it('it should not PATCH an awarding without auth-token provided', (done) => {
chai.request(server)
.patch(urls.awards + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /DELETE awardings
*/
describe('/DELETE awardings', () => {
it('it should not accept DELETE method without id in url', (done) => {
chai.request(server)
.delete(urls.awards)
.send({})
.end((err, res) => {
res.should.have.status(codes.wrongmethod);
res.body.should.be.a('object');
res.body.should.have.property('error').property('message')
.eql('this method is not allowed at ' + urls.awards);
done();
});
});
it('it should not DELETE an awarding without auth-token provided', (done) => {
chai.request(server)
.delete(urls.awards + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
});

View File

@ -1,39 +0,0 @@
let mongoose = require("mongoose");
let AwardingModel = require('../models/awarding');
let urls = require('../config/api-url');
let codes = require('../routes/http-codes');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server');
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Command', () => {
beforeEach((done) => { //Before each test we empty the database
AwardingModel.remove({}, (err) => {
done();
});
});
/*
* Test the /GET awardings
*/
describe('/POST command to create signature', () => {
it('it should not succeed without auth-token provided', (done) => {
chai.request(server)
.post(urls.cmdCreateSig + "/someId")
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
});

View File

@ -1,109 +0,0 @@
let mongoose = require("mongoose");
let DecorationModel = require('../models/decoration');
let urls = require('../config/api-url');
let codes = require('../routes/http-codes');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server');
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Decorations', () => {
beforeEach((done) => { //Before each test we empty the database
DecorationModel.remove({}, (err) => {
done();
});
});
/*
* Test the /GET decorations
*/
describe('/GET decorations', () => {
it('it should GET all the decorations', (done) => {
chai.request(server)
.get(urls.decorations)
.end((err, res) => {
res.should.have.status(codes.success);
res.body.should.be.a('array');
res.body.length.should.be.eql(0);
done();
});
});
});
/*
* Test the /POST decorations
*/
describe('/POST decorations', () => {
it('it should not POST a decoration without auth-token provided', (done) => {
chai.request(server)
.post(urls.decorations)
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /PATCH decoration
*/
describe('/PATCH decorations', () => {
it('it should not PATCH a decoration without auth-token provided', (done) => {
chai.request(server)
.patch(urls.decorations + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /DELETE decorations
*/
describe('/DELETE decorations', () => {
it('it should not accept DELETE method without id in url', (done) => {
chai.request(server)
.delete(urls.decorations)
.send({})
.end((err, res) => {
res.should.have.status(codes.wrongmethod);
res.body.should.be.a('object');
res.body.should.have.property('error').property('message')
.eql('this method is not allowed at ' + urls.decorations);
done();
});
});
it('it should not DELETE a decoration without auth-token provided', (done) => {
chai.request(server)
.delete(urls.decorations + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
});

View File

@ -1,110 +0,0 @@
let mongoose = require("mongoose");
let RankModel = require('../models/rank');
let urls = require('../config/api-url');
let codes = require('../routes/http-codes');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server');
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Ranks', () => {
beforeEach((done) => { //Before each test we empty the database
RankModel.remove({}, (err) => {
done();
});
});
/*
* Test the /GET ranks
*/
describe('/GET ranks', () => {
it('it should GET all the ranks', (done) => {
chai.request(server)
.get(urls.ranks)
.end((err, res) => {
res.should.have.status(codes.success);
res.body.should.be.a('array');
res.body.length.should.be.eql(0);
done();
});
});
});
/*
* Test the /POST ranks
*/
describe('/POST ranks', () => {
it('it should not POST a rank without auth-token provided', (done) => {
chai.request(server)
.post(urls.ranks)
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
})
/*
* Test the /PATCH rank
*/
describe('/PATCH ranks', () => {
it('it should not PATCH a rank without auth-token provided', (done) => {
chai.request(server)
.patch(urls.ranks + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /DELETE rank
*/
describe('/DELETE ranks', () => {
it('it should not accept DELETE method without id in url', (done) => {
chai.request(server)
.delete(urls.ranks)
.send({})
.end((err, res) => {
res.should.have.status(codes.wrongmethod);
res.body.should.be.a('object');
res.body.should.have.property('error').property('message')
.eql('this method is not allowed at ' + urls.ranks);
done();
});
});
it('it should not DELETE a rank without auth-token provided', (done) => {
chai.request(server)
.delete(urls.ranks + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
});

View File

@ -1,109 +0,0 @@
let mongoose = require("mongoose");
let SquadModel = require('../models/squad');
let urls = require('../config/api-url');
let codes = require('../routes/http-codes');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server');
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Squads', () => {
beforeEach((done) => { //Before each test we empty the database
SquadModel.remove({}, (err) => {
done();
});
});
/*
* Test the /GET route
*/
describe('/GET squads', () => {
it('it should GET all the squads', (done) => {
chai.request(server)
.get(urls.squads)
.end((err, res) => {
res.should.have.status(codes.success);
res.body.should.be.a('array');
res.body.length.should.be.eql(0);
done();
});
});
});
/*
* Test the /POST squad
*/
describe('/POST squads', () => {
it('it should not POST a squad without auth-token provided', (done) => {
chai.request(server)
.post(urls.squads)
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /PATCH squad
*/
describe('/PATCH squads', () => {
it('it should not PATCH a squad without auth-token provided', (done) => {
chai.request(server)
.patch(urls.squads + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /DELETE squad
*/
describe('/DELETE squads', () => {
it('it should not accept DELETE method without id in url', (done) => {
chai.request(server)
.delete(urls.squads)
.send({})
.end((err, res) => {
res.should.have.status(codes.wrongmethod);
res.body.should.be.a('object');
res.body.should.have.property('error').property('message')
.eql('this method is not allowed at ' + urls.squads);
done();
});
});
it('it should not DELETE a squad without auth-token provided', (done) => {
chai.request(server)
.delete(urls.squads + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
});

View File

@ -1,85 +0,0 @@
let mongoose = require("mongoose");
let AwardingModel = require('../models/awarding');
let urls = require('../config/api-url');
let codes = require('../routes/http-codes');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
let server = require('../server');
let should = chai.should();
chai.use(chaiHttp);
//Our parent block
describe('Wars', () => {
/*
* Test the /GET awardings
*/
describe('/GET wars', () => {
it('it should GET all wars', (done) => {
chai.request(server)
.get(urls.wars)
.end((err, res) => {
res.should.have.status(codes.success);
res.body.should.be.a('array');
res.body.length.should.be.eql(0);
done();
});
});
});
/*
* Test the /POST awardings
*/
describe('/POST wars', () => {
it('it should not POST a war without auth-token provided', (done) => {
chai.request(server)
.post(urls.wars)
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
/*
* Test the /DELETE awardings
*/
describe('/DELETE wars', () => {
it('it should not accept DELETE method without id in url', (done) => {
chai.request(server)
.delete(urls.wars)
.send({})
.end((err, res) => {
res.should.have.status(codes.wrongmethod);
res.body.should.be.a('object');
res.body.should.have.property('error').property('message')
.eql('this method is not allowed at ' + urls.wars);
done();
});
});
it('it should not DELETE an awarding without auth-token provided', (done) => {
chai.request(server)
.delete(urls.wars + '/someId')
.send({})
.end((err, res) => {
res.should.have.status(codes.forbidden);
res.body.should.be.a('object');
res.body.should.have.property('success').eql(false);
res.body.should.have.property('message').eql('No token provided.');
done();
});
});
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,813 +0,0 @@
info face="DejaVu Sans" size=19 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2
common lineHeight=23 base=18 scaleW=512 scaleH=512 pages=1 packed=0
page id=0 file="DEVAJU_SANS_19.png"
chars count=193
char id=0 x=298 y=0 width=13 height=20 xoffset=-1 yoffset=3 xadvance=11 page=0 chnl=0
char id=10 x=0 y=0 width=22 height=22 xoffset=-1 yoffset=-1 xadvance=19 page=0 chnl=0
char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=17 xadvance=6 page=0 chnl=0
char id=33 x=500 y=42 width=4 height=16 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=0
char id=34 x=67 y=75 width=7 height=7 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=0
char id=35 x=40 y=59 width=15 height=16 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=0
char id=36 x=287 y=0 width=11 height=20 xoffset=1 yoffset=2 xadvance=13 page=0 chnl=0
char id=37 x=22 y=59 width=18 height=16 xoffset=0 yoffset=3 xadvance=18 page=0 chnl=0
char id=38 x=55 y=59 width=15 height=16 xoffset=0 yoffset=3 xadvance=14 page=0 chnl=0
char id=39 x=507 y=59 width=4 height=7 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=0
char id=40 x=240 y=0 width=7 height=20 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=0
char id=41 x=247 y=0 width=7 height=20 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=0
char id=42 x=489 y=59 width=11 height=11 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=0
char id=43 x=199 y=59 width=14 height=14 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=0
char id=44 x=74 y=75 width=5 height=7 xoffset=0 yoffset=14 xadvance=6 page=0 chnl=0
char id=45 x=119 y=75 width=7 height=4 xoffset=0 yoffset=10 xadvance=7 page=0 chnl=0
char id=46 x=115 y=75 width=4 height=5 xoffset=1 yoffset=14 xadvance=6 page=0 chnl=0
char id=47 x=51 y=22 width=9 height=18 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=0
char id=48 x=0 y=59 width=12 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=49 x=396 y=42 width=11 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0
char id=50 x=407 y=42 width=11 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=51 x=418 y=42 width=11 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=52 x=429 y=42 width=13 height=16 xoffset=-1 yoffset=3 xadvance=12 page=0 chnl=0
char id=53 x=442 y=42 width=11 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=54 x=453 y=42 width=12 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=55 x=465 y=42 width=11 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0
char id=56 x=476 y=42 width=12 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=57 x=488 y=42 width=12 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=58 x=430 y=59 width=4 height=12 xoffset=1 yoffset=7 xadvance=6 page=0 chnl=0
char id=59 x=152 y=59 width=5 height=15 xoffset=0 yoffset=6 xadvance=6 page=0 chnl=0
char id=60 x=171 y=59 width=14 height=14 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=0
char id=61 x=30 y=75 width=14 height=8 xoffset=1 yoffset=8 xadvance=16 page=0 chnl=0
char id=62 x=185 y=59 width=14 height=14 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=0
char id=63 x=12 y=59 width=10 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
char id=64 x=268 y=0 width=19 height=20 xoffset=0 yoffset=2 xadvance=19 page=0 chnl=0
char id=65 x=78 y=42 width=15 height=16 xoffset=-1 yoffset=3 xadvance=13 page=0 chnl=0
char id=66 x=93 y=42 width=12 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0
char id=67 x=105 y=42 width=13 height=16 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
char id=68 x=118 y=42 width=14 height=16 xoffset=1 yoffset=3 xadvance=15 page=0 chnl=0
char id=69 x=132 y=42 width=11 height=16 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=0
char id=70 x=143 y=42 width=10 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0
char id=71 x=153 y=42 width=14 height=16 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=0
char id=72 x=167 y=42 width=13 height=16 xoffset=1 yoffset=3 xadvance=15 page=0 chnl=0
char id=73 x=504 y=22 width=4 height=16 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=0
char id=74 x=233 y=0 width=7 height=20 xoffset=-2 yoffset=3 xadvance=6 page=0 chnl=0
char id=75 x=180 y=42 width=13 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0
char id=76 x=193 y=42 width=11 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0
char id=77 x=204 y=42 width=15 height=16 xoffset=1 yoffset=3 xadvance=17 page=0 chnl=0
char id=78 x=219 y=42 width=13 height=16 xoffset=1 yoffset=3 xadvance=15 page=0 chnl=0
char id=79 x=232 y=42 width=15 height=16 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=0
char id=80 x=247 y=42 width=11 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0
char id=81 x=11 y=22 width=15 height=19 xoffset=0 yoffset=3 xadvance=15 page=0 chnl=0
char id=82 x=258 y=42 width=13 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0
char id=83 x=271 y=42 width=12 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=84 x=283 y=42 width=14 height=16 xoffset=-1 yoffset=3 xadvance=12 page=0 chnl=0
char id=85 x=297 y=42 width=13 height=16 xoffset=1 yoffset=3 xadvance=14 page=0 chnl=0
char id=86 x=310 y=42 width=15 height=16 xoffset=-1 yoffset=3 xadvance=13 page=0 chnl=0
char id=87 x=325 y=42 width=21 height=16 xoffset=-1 yoffset=3 xadvance=19 page=0 chnl=0
char id=88 x=346 y=42 width=15 height=16 xoffset=-1 yoffset=3 xadvance=13 page=0 chnl=0
char id=89 x=361 y=42 width=14 height=16 xoffset=-1 yoffset=3 xadvance=12 page=0 chnl=0
char id=90 x=375 y=42 width=13 height=16 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
char id=91 x=254 y=0 width=7 height=20 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=0
char id=92 x=60 y=22 width=9 height=18 xoffset=-1 yoffset=3 xadvance=6 page=0 chnl=0
char id=93 x=261 y=0 width=7 height=20 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=0
char id=94 x=16 y=75 width=14 height=8 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=0
char id=95 x=126 y=75 width=12 height=4 xoffset=-1 yoffset=20 xadvance=10 page=0 chnl=0
char id=96 x=100 y=75 width=8 height=6 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=0
char id=97 x=255 y=59 width=11 height=13 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
char id=98 x=308 y=22 width=11 height=17 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=0
char id=99 x=266 y=59 width=10 height=13 xoffset=0 yoffset=6 xadvance=10 page=0 chnl=0
char id=100 x=319 y=22 width=11 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=101 x=276 y=59 width=12 height=13 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
char id=102 x=330 y=22 width=9 height=17 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=0
char id=103 x=339 y=22 width=11 height=17 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
char id=104 x=350 y=22 width=11 height=17 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=0
char id=105 x=361 y=22 width=4 height=17 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=0
char id=106 x=39 y=0 width=6 height=21 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=0
char id=107 x=365 y=22 width=12 height=17 xoffset=1 yoffset=2 xadvance=11 page=0 chnl=0
char id=108 x=377 y=22 width=4 height=17 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=0
char id=109 x=288 y=59 width=18 height=13 xoffset=1 yoffset=6 xadvance=19 page=0 chnl=0
char id=110 x=306 y=59 width=11 height=13 xoffset=1 yoffset=6 xadvance=12 page=0 chnl=0
char id=111 x=317 y=59 width=12 height=13 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
char id=112 x=381 y=22 width=11 height=17 xoffset=1 yoffset=6 xadvance=12 page=0 chnl=0
char id=113 x=392 y=22 width=11 height=17 xoffset=0 yoffset=6 xadvance=12 page=0 chnl=0
char id=114 x=329 y=59 width=8 height=13 xoffset=1 yoffset=6 xadvance=9 page=0 chnl=0
char id=115 x=337 y=59 width=10 height=13 xoffset=0 yoffset=6 xadvance=10 page=0 chnl=0
char id=116 x=388 y=42 width=8 height=16 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
char id=117 x=347 y=59 width=11 height=13 xoffset=1 yoffset=6 xadvance=13 page=0 chnl=0
char id=118 x=358 y=59 width=13 height=13 xoffset=-1 yoffset=6 xadvance=11 page=0 chnl=0
char id=119 x=371 y=59 width=17 height=13 xoffset=-1 yoffset=6 xadvance=16 page=0 chnl=0
char id=120 x=388 y=59 width=13 height=13 xoffset=-1 yoffset=6 xadvance=11 page=0 chnl=0
char id=121 x=403 y=22 width=13 height=17 xoffset=0 yoffset=6 xadvance=11 page=0 chnl=0
char id=122 x=401 y=59 width=10 height=13 xoffset=0 yoffset=6 xadvance=10 page=0 chnl=0
char id=123 x=45 y=0 width=10 height=21 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=0
char id=124 x=22 y=0 width=4 height=22 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=0
char id=125 x=55 y=0 width=10 height=21 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=0
char id=126 x=79 y=75 width=14 height=7 xoffset=1 yoffset=8 xadvance=16 page=0 chnl=0
char id=160 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=17 xadvance=6 page=0 chnl=0
char id=161 x=504 y=42 width=4 height=16 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=0
char id=162 x=501 y=0 width=10 height=19 xoffset=1 yoffset=3 xadvance=12 page=0 chnl=0
char id=163 x=70 y=59 width=11 height=16 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=164 x=213 y=59 width=14 height=14 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=0
char id=165 x=81 y=59 width=14 height=16 xoffset=-1 yoffset=3 xadvance=12 page=0 chnl=0
char id=166 x=26 y=22 width=4 height=19 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=0
char id=167 x=30 y=22 width=10 height=19 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
char id=168 x=138 y=75 width=8 height=4 xoffset=1 yoffset=2 xadvance=10 page=0 chnl=0
char id=169 x=416 y=22 width=16 height=17 xoffset=2 yoffset=2 xadvance=20 page=0 chnl=0
char id=170 x=434 y=59 width=9 height=12 xoffset=-1 yoffset=3 xadvance=8 page=0 chnl=0
char id=171 x=443 y=59 width=11 height=12 xoffset=0 yoffset=6 xadvance=11 page=0 chnl=0
char id=172 x=44 y=75 width=15 height=8 xoffset=1 yoffset=8 xadvance=17 page=0 chnl=0
char id=173 x=119 y=75 width=7 height=4 xoffset=0 yoffset=10 xadvance=7 page=0 chnl=0
char id=174 x=432 y=22 width=16 height=17 xoffset=2 yoffset=2 xadvance=20 page=0 chnl=0
char id=175 x=146 y=75 width=8 height=4 xoffset=1 yoffset=2 xadvance=10 page=0 chnl=0
char id=176 x=59 y=75 width=8 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=0
char id=177 x=227 y=59 width=14 height=14 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=0
char id=178 x=500 y=59 width=7 height=10 xoffset=0 yoffset=3 xadvance=7 page=0 chnl=0
char id=179 x=0 y=75 width=8 height=10 xoffset=-1 yoffset=3 xadvance=7 page=0 chnl=0
char id=180 x=108 y=75 width=7 height=6 xoffset=2 yoffset=1 xadvance=10 page=0 chnl=0
char id=181 x=448 y=22 width=12 height=17 xoffset=1 yoffset=6 xadvance=13 page=0 chnl=0
char id=182 x=40 y=22 width=11 height=19 xoffset=0 yoffset=3 xadvance=12 page=0 chnl=0
char id=183 x=115 y=75 width=4 height=5 xoffset=1 yoffset=8 xadvance=6 page=0 chnl=0
char id=184 x=93 y=75 width=7 height=7 xoffset=1 yoffset=17 xadvance=10 page=0 chnl=0
char id=185 x=8 y=75 width=8 height=10 xoffset=-1 yoffset=3 xadvance=7 page=0 chnl=0
char id=186 x=454 y=59 width=10 height=12 xoffset=-1 yoffset=3 xadvance=9 page=0 chnl=0
char id=187 x=464 y=59 width=11 height=12 xoffset=1 yoffset=6 xadvance=12 page=0 chnl=0
char id=188 x=69 y=22 width=20 height=18 xoffset=-1 yoffset=2 xadvance=18 page=0 chnl=0
char id=189 x=89 y=22 width=19 height=18 xoffset=-1 yoffset=2 xadvance=18 page=0 chnl=0
char id=190 x=108 y=22 width=21 height=18 xoffset=-1 yoffset=2 xadvance=19 page=0 chnl=0
char id=191 x=95 y=59 width=10 height=16 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=0
char id=192 x=311 y=0 width=15 height=20 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=0
char id=193 x=326 y=0 width=15 height=20 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=0
char id=194 x=341 y=0 width=15 height=20 xoffset=-1 yoffset=-1 xadvance=13 page=0 chnl=0
char id=195 x=65 y=0 width=16 height=21 xoffset=-2 yoffset=-2 xadvance=12 page=0 chnl=0
char id=196 x=356 y=0 width=16 height=20 xoffset=-1 yoffset=-1 xadvance=14 page=0 chnl=0
char id=197 x=81 y=0 width=15 height=21 xoffset=-1 yoffset=-2 xadvance=13 page=0 chnl=0
char id=198 x=105 y=59 width=20 height=16 xoffset=-2 yoffset=3 xadvance=18 page=0 chnl=0
char id=199 x=372 y=0 width=13 height=20 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0
char id=200 x=385 y=0 width=11 height=20 xoffset=1 yoffset=-1 xadvance=12 page=0 chnl=0
char id=201 x=396 y=0 width=11 height=20 xoffset=1 yoffset=-1 xadvance=12 page=0 chnl=0
char id=202 x=407 y=0 width=11 height=20 xoffset=1 yoffset=-1 xadvance=12 page=0 chnl=0
char id=203 x=418 y=0 width=11 height=20 xoffset=1 yoffset=-1 xadvance=12 page=0 chnl=0
char id=204 x=429 y=0 width=7 height=20 xoffset=-1 yoffset=-1 xadvance=6 page=0 chnl=0
char id=205 x=436 y=0 width=7 height=20 xoffset=0 yoffset=-1 xadvance=6 page=0 chnl=0
char id=206 x=443 y=0 width=8 height=20 xoffset=-1 yoffset=-1 xadvance=6 page=0 chnl=0
char id=207 x=451 y=0 width=8 height=20 xoffset=0 yoffset=-1 xadvance=7 page=0 chnl=0
char id=208 x=125 y=59 width=16 height=16 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=0
char id=209 x=96 y=0 width=13 height=21 xoffset=1 yoffset=-2 xadvance=15 page=0 chnl=0
char id=210 x=109 y=0 width=15 height=21 xoffset=0 yoffset=-2 xadvance=15 page=0 chnl=0
char id=211 x=124 y=0 width=15 height=21 xoffset=0 yoffset=-2 xadvance=15 page=0 chnl=0
char id=212 x=139 y=0 width=15 height=21 xoffset=0 yoffset=-2 xadvance=15 page=0 chnl=0
char id=213 x=154 y=0 width=15 height=21 xoffset=0 yoffset=-2 xadvance=15 page=0 chnl=0
char id=214 x=459 y=0 width=15 height=20 xoffset=0 yoffset=-1 xadvance=15 page=0 chnl=0
char id=215 x=241 y=59 width=14 height=14 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=0
char id=216 x=129 y=22 width=16 height=18 xoffset=-1 yoffset=2 xadvance=15 page=0 chnl=0
char id=217 x=169 y=0 width=13 height=21 xoffset=1 yoffset=-2 xadvance=14 page=0 chnl=0
char id=218 x=182 y=0 width=13 height=21 xoffset=1 yoffset=-2 xadvance=14 page=0 chnl=0
char id=219 x=195 y=0 width=13 height=21 xoffset=1 yoffset=-2 xadvance=14 page=0 chnl=0
char id=220 x=474 y=0 width=13 height=20 xoffset=1 yoffset=-1 xadvance=14 page=0 chnl=0
char id=221 x=487 y=0 width=14 height=20 xoffset=-1 yoffset=-1 xadvance=12 page=0 chnl=0
char id=222 x=141 y=59 width=11 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0
char id=223 x=460 y=22 width=12 height=17 xoffset=1 yoffset=2 xadvance=13 page=0 chnl=0
char id=224 x=145 y=22 width=11 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=225 x=156 y=22 width=11 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=226 x=167 y=22 width=11 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=227 x=472 y=22 width=11 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=228 x=483 y=22 width=11 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=229 x=0 y=22 width=11 height=20 xoffset=0 yoffset=-1 xadvance=12 page=0 chnl=0
char id=230 x=411 y=59 width=19 height=13 xoffset=0 yoffset=6 xadvance=19 page=0 chnl=0
char id=231 x=494 y=22 width=10 height=17 xoffset=0 yoffset=6 xadvance=10 page=0 chnl=0
char id=232 x=178 y=22 width=12 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=233 x=190 y=22 width=12 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=234 x=202 y=22 width=12 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=235 x=0 y=42 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=236 x=214 y=22 width=8 height=18 xoffset=-2 yoffset=1 xadvance=6 page=0 chnl=0
char id=237 x=222 y=22 width=8 height=18 xoffset=0 yoffset=1 xadvance=6 page=0 chnl=0
char id=238 x=230 y=22 width=9 height=18 xoffset=-2 yoffset=1 xadvance=6 page=0 chnl=0
char id=239 x=12 y=42 width=8 height=17 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=0
char id=240 x=20 y=42 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=241 x=32 y=42 width=11 height=17 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=0
char id=242 x=239 y=22 width=12 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=243 x=251 y=22 width=12 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=244 x=263 y=22 width=12 height=18 xoffset=0 yoffset=1 xadvance=12 page=0 chnl=0
char id=245 x=43 y=42 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=246 x=55 y=42 width=12 height=17 xoffset=0 yoffset=2 xadvance=12 page=0 chnl=0
char id=247 x=475 y=59 width=14 height=12 xoffset=1 yoffset=6 xadvance=16 page=0 chnl=0
char id=248 x=157 y=59 width=14 height=15 xoffset=-1 yoffset=5 xadvance=12 page=0 chnl=0
char id=249 x=275 y=22 width=11 height=18 xoffset=1 yoffset=1 xadvance=13 page=0 chnl=0
char id=250 x=286 y=22 width=11 height=18 xoffset=1 yoffset=1 xadvance=13 page=0 chnl=0
char id=251 x=297 y=22 width=11 height=18 xoffset=1 yoffset=1 xadvance=13 page=0 chnl=0
char id=252 x=67 y=42 width=11 height=17 xoffset=1 yoffset=2 xadvance=13 page=0 chnl=0
char id=253 x=26 y=0 width=13 height=22 xoffset=0 yoffset=1 xadvance=11 page=0 chnl=0
char id=254 x=208 y=0 width=11 height=21 xoffset=1 yoffset=2 xadvance=12 page=0 chnl=0
char id=255 x=219 y=0 width=14 height=21 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=0
kernings count=615
kerning first=65 second=171 amount=-1
kerning first=194 second=87 amount=-1
kerning first=86 second=58 amount=-2
kerning first=89 second=229 amount=-3
kerning first=196 second=65 amount=1
kerning first=45 second=66 amount=-1
kerning first=221 second=67 amount=-1
kerning first=45 second=71 amount=1
kerning first=187 second=88 amount=-1
kerning first=45 second=74 amount=1
kerning first=221 second=79 amount=-1
kerning first=45 second=81 amount=1
kerning first=65 second=84 amount=-1
kerning first=76 second=85 amount=-1
kerning first=45 second=86 amount=-1
kerning first=66 second=87 amount=-1
kerning first=45 second=88 amount=-1
kerning first=82 second=89 amount=-1
kerning first=65 second=192 amount=1
kerning first=84 second=97 amount=-3
kerning first=221 second=101 amount=-3
kerning first=65 second=102 amount=-1
kerning first=70 second=105 amount=-1
kerning first=89 second=250 amount=-2
kerning first=89 second=117 amount=-2
kerning first=192 second=119 amount=-1
kerning first=243 second=120 amount=-1
kerning first=82 second=253 amount=-1
kerning first=84 second=121 amount=-3
kerning first=76 second=217 amount=-1
kerning first=84 second=187 amount=-1
kerning first=192 second=193 amount=1
kerning first=65 second=194 amount=1
kerning first=221 second=196 amount=-1
kerning first=171 second=198 amount=1
kerning first=89 second=252 amount=-2
kerning first=75 second=210 amount=-1
kerning first=76 second=211 amount=-1
kerning first=75 second=212 amount=-1
kerning first=89 second=225 amount=-3
kerning first=84 second=228 amount=-2
kerning first=75 second=45 amount=-2
kerning first=84 second=231 amount=-3
kerning first=75 second=233 amount=-1
kerning first=87 second=234 amount=-1
kerning first=120 second=235 amount=-1
kerning first=80 second=244 amount=-1
kerning first=87 second=245 amount=-1
kerning first=120 second=246 amount=-1
kerning first=86 second=249 amount=-1
kerning first=192 second=253 amount=-1
kerning first=82 second=67 amount=-1
kerning first=221 second=213 amount=-1
kerning first=120 second=245 amount=-1
kerning first=87 second=111 amount=-1
kerning first=84 second=196 amount=-1
kerning first=195 second=65 amount=1
kerning first=89 second=210 amount=-1
kerning first=65 second=119 amount=-1
kerning first=192 second=121 amount=-1
kerning first=75 second=84 amount=-1
kerning first=79 second=46 amount=-1
kerning first=82 second=249 amount=-1
kerning first=80 second=101 amount=-1
kerning first=76 second=213 amount=-1
kerning first=221 second=243 amount=-3
kerning first=80 second=243 amount=-1
kerning first=70 second=225 amount=-2
kerning first=107 second=111 amount=-1
kerning first=75 second=235 amount=-1
kerning first=86 second=45 amount=-1
kerning first=87 second=242 amount=-1
kerning first=120 second=111 amount=-1
kerning first=107 second=249 amount=-1
kerning first=86 second=225 amount=-1
kerning first=193 second=84 amount=-1
kerning first=196 second=118 amount=-1
kerning first=75 second=250 amount=-1
kerning first=221 second=214 amount=-1
kerning first=195 second=119 amount=-1
kerning first=70 second=246 amount=-1
kerning first=87 second=194 amount=-1
kerning first=192 second=194 amount=1
kerning first=89 second=233 amount=-3
kerning first=120 second=244 amount=-1
kerning first=84 second=244 amount=-2
kerning first=76 second=121 amount=-2
kerning first=84 second=225 amount=-3
kerning first=76 second=219 amount=-1
kerning first=211 second=221 amount=-1
kerning first=195 second=192 amount=1
kerning first=107 second=253 amount=-1
kerning first=86 second=235 amount=-1
kerning first=86 second=250 amount=-1
kerning first=80 second=97 amount=-1
kerning first=221 second=97 amount=-3
kerning first=196 second=253 amount=-1
kerning first=82 second=234 amount=-1
kerning first=221 second=199 amount=-1
kerning first=196 second=171 amount=-1
kerning first=70 second=250 amount=-1
kerning first=86 second=227 amount=-1
kerning first=82 second=192 amount=-1
kerning first=187 second=192 amount=-1
kerning first=107 second=245 amount=-1
kerning first=193 second=65 amount=1
kerning first=84 second=117 amount=-3
kerning first=221 second=224 amount=-3
kerning first=80 second=224 amount=-1
kerning first=65 second=121 amount=-1
kerning first=89 second=235 amount=-3
kerning first=71 second=84 amount=-1
kerning first=45 second=87 amount=-1
kerning first=87 second=58 amount=-1
kerning first=89 second=212 amount=-1
kerning first=84 second=171 amount=-2
kerning first=187 second=195 amount=-1
kerning first=195 second=196 amount=1
kerning first=121 second=46 amount=-3
kerning first=89 second=199 amount=-1
kerning first=213 second=45 amount=1
kerning first=107 second=255 amount=-1
kerning first=45 second=89 amount=-2
kerning first=79 second=88 amount=-1
kerning first=187 second=84 amount=-2
kerning first=82 second=84 amount=-1
kerning first=187 second=193 amount=-1
kerning first=195 second=86 amount=-1
kerning first=196 second=193 amount=1
kerning first=65 second=221 amount=-1
kerning first=75 second=214 amount=-1
kerning first=84 second=246 amount=-2
kerning first=222 second=46 amount=-1
kerning first=195 second=121 amount=-1
kerning first=193 second=195 amount=1
kerning first=45 second=79 amount=1
kerning first=80 second=193 amount=-1
kerning first=253 second=58 amount=-1
kerning first=221 second=193 amount=-1
kerning first=171 second=89 amount=-1
kerning first=82 second=255 amount=-1
kerning first=86 second=229 amount=-1
kerning first=87 second=244 amount=-1
kerning first=194 second=192 amount=1
kerning first=193 second=193 amount=1
kerning first=89 second=79 amount=-1
kerning first=193 second=253 amount=-1
kerning first=210 second=45 amount=1
kerning first=192 second=171 amount=-1
kerning first=87 second=171 amount=-1
kerning first=45 second=210 amount=1
kerning first=195 second=194 amount=1
kerning first=88 second=67 amount=-1
kerning first=194 second=89 amount=-1
kerning first=87 second=246 amount=-1
kerning first=70 second=117 amount=-1
kerning first=82 second=232 amount=-1
kerning first=214 second=221 amount=-1
kerning first=82 second=86 amount=-1
kerning first=187 second=86 amount=-2
kerning first=221 second=105 amount=-1
kerning first=82 second=46 amount=-1
kerning first=75 second=252 amount=-1
kerning first=89 second=187 amount=-1
kerning first=211 second=88 amount=-1
kerning first=196 second=194 amount=1
kerning first=86 second=101 amount=-1
kerning first=70 second=233 amount=-1
kerning first=221 second=226 amount=-3
kerning first=80 second=65 amount=-1
kerning first=221 second=65 amount=-1
kerning first=80 second=226 amount=-1
kerning first=87 second=196 amount=-1
kerning first=192 second=196 amount=1
kerning first=102 second=46 amount=-1
kerning first=89 second=101 amount=-3
kerning first=70 second=234 amount=-1
kerning first=80 second=229 amount=-1
kerning first=214 second=88 amount=-1
kerning first=86 second=244 amount=-1
kerning first=221 second=251 amount=-2
kerning first=86 second=252 amount=-1
kerning first=214 second=45 amount=1
kerning first=76 second=84 amount=-3
kerning first=84 second=67 amount=-1
kerning first=75 second=101 amount=-1
kerning first=75 second=79 amount=-1
kerning first=84 second=111 amount=-3
kerning first=88 second=213 amount=-1
kerning first=84 second=58 amount=-2
kerning first=221 second=228 amount=-3
kerning first=80 second=228 amount=-1
kerning first=66 second=89 amount=-1
kerning first=75 second=121 amount=-1
kerning first=88 second=235 amount=-1
kerning first=82 second=242 amount=-1
kerning first=221 second=252 amount=-2
kerning first=194 second=121 amount=-1
kerning first=82 second=196 amount=-1
kerning first=187 second=196 amount=-1
kerning first=84 second=45 amount=-2
kerning first=89 second=192 amount=-1
kerning first=84 second=233 amount=-3
kerning first=89 second=195 amount=-1
kerning first=194 second=195 amount=1
kerning first=87 second=227 amount=-1
kerning first=107 second=242 amount=-1
kerning first=195 second=84 amount=-1
kerning first=84 second=253 amount=-3
kerning first=75 second=221 amount=-1
kerning first=88 second=232 amount=-1
kerning first=86 second=224 amount=-1
kerning first=196 second=89 amount=-1
kerning first=84 second=114 amount=-3
kerning first=87 second=117 amount=-1
kerning first=75 second=245 amount=-1
kerning first=120 second=234 amount=-1
kerning first=107 second=117 amount=-1
kerning first=193 second=89 amount=-1
kerning first=213 second=221 amount=-1
kerning first=76 second=87 amount=-2
kerning first=107 second=243 amount=-1
kerning first=82 second=243 amount=-1
kerning first=45 second=221 amount=-2
kerning first=221 second=249 amount=-2
kerning first=193 second=192 amount=1
kerning first=84 second=193 amount=-1
kerning first=84 second=250 amount=-3
kerning first=84 second=234 amount=-3
kerning first=45 second=211 amount=1
kerning first=192 second=221 amount=-1
kerning first=82 second=199 amount=-1
kerning first=89 second=214 amount=-1
kerning first=76 second=221 amount=-3
kerning first=75 second=244 amount=-1
kerning first=89 second=58 amount=-3
kerning first=75 second=220 amount=-1
kerning first=82 second=117 amount=-1
kerning first=192 second=87 amount=-1
kerning first=70 second=196 amount=-2
kerning first=211 second=45 amount=1
kerning first=87 second=229 amount=-1
kerning first=70 second=114 amount=-1
kerning first=107 second=251 amount=-1
kerning first=87 second=233 amount=-1
kerning first=89 second=194 amount=-1
kerning first=194 second=194 amount=1
kerning first=86 second=245 amount=-1
kerning first=193 second=119 amount=-1
kerning first=194 second=102 amount=-1
kerning first=82 second=111 amount=-1
kerning first=196 second=192 amount=1
kerning first=221 second=245 amount=-3
kerning first=193 second=196 amount=1
kerning first=89 second=224 amount=-3
kerning first=193 second=86 amount=-1
kerning first=208 second=221 amount=-1
kerning first=82 second=246 amount=-1
kerning first=221 second=235 amount=-3
kerning first=80 second=235 amount=-1
kerning first=195 second=87 amount=-1
kerning first=221 second=212 amount=-1
kerning first=255 second=58 amount=-1
kerning first=75 second=171 amount=-1
kerning first=86 second=195 amount=-1
kerning first=89 second=234 amount=-3
kerning first=81 second=45 amount=1
kerning first=192 second=65 amount=1
kerning first=221 second=45 amount=-2
kerning first=213 second=89 amount=-1
kerning first=70 second=227 amount=-2
kerning first=89 second=244 amount=-3
kerning first=86 second=255 amount=-1
kerning first=86 second=65 amount=-1
kerning first=221 second=225 amount=-3
kerning first=80 second=225 amount=-1
kerning first=75 second=85 amount=-1
kerning first=82 second=193 amount=-1
kerning first=76 second=253 amount=-2
kerning first=194 second=171 amount=-1
kerning first=89 second=171 amount=-2
kerning first=194 second=86 amount=-1
kerning first=86 second=251 amount=-1
kerning first=84 second=194 amount=-1
kerning first=210 second=46 amount=-1
kerning first=196 second=255 amount=-1
kerning first=70 second=111 amount=-1
kerning first=120 second=233 amount=-1
kerning first=84 second=115 amount=-3
kerning first=114 second=45 amount=-1
kerning first=194 second=221 amount=-1
kerning first=45 second=118 amount=-1
kerning first=89 second=196 amount=-1
kerning first=221 second=232 amount=-3
kerning first=114 second=171 amount=-1
kerning first=65 second=87 amount=-1
kerning first=88 second=199 amount=-1
kerning first=245 second=120 amount=-1
kerning first=75 second=251 amount=-1
kerning first=192 second=102 amount=-1
kerning first=70 second=101 amount=-1
kerning first=70 second=121 amount=-2
kerning first=196 second=119 amount=-1
kerning first=196 second=86 amount=-1
kerning first=89 second=105 amount=-1
kerning first=75 second=218 amount=-1
kerning first=80 second=46 amount=-3
kerning first=221 second=46 amount=-4
kerning first=70 second=58 amount=-1
kerning first=89 second=45 amount=-2
kerning first=212 second=46 amount=-1
kerning first=84 second=227 amount=-2
kerning first=70 second=194 amount=-2
kerning first=80 second=192 amount=-1
kerning first=221 second=192 amount=-1
kerning first=89 second=211 amount=-1
kerning first=45 second=214 amount=1
kerning first=107 second=246 amount=-1
kerning first=66 second=86 amount=-1
kerning first=87 second=250 amount=-1
kerning first=210 second=89 amount=-1
kerning first=76 second=220 amount=-1
kerning first=89 second=65 amount=-1
kerning first=194 second=65 amount=1
kerning first=222 second=58 amount=-1
kerning first=82 second=252 amount=-1
kerning first=193 second=121 amount=-1
kerning first=192 second=195 amount=1
kerning first=87 second=195 amount=-1
kerning first=84 second=199 amount=-1
kerning first=87 second=97 amount=-1
kerning first=187 second=66 amount=-1
kerning first=171 second=86 amount=-1
kerning first=82 second=45 amount=-1
kerning first=118 second=45 amount=-1
kerning first=89 second=251 amount=-2
kerning first=75 second=67 amount=-1
kerning first=86 second=232 amount=-1
kerning first=84 second=101 amount=-3
kerning first=120 second=101 amount=-1
kerning first=192 second=118 amount=-1
kerning first=107 second=250 amount=-1
kerning first=196 second=84 amount=-1
kerning first=65 second=193 amount=1
kerning first=75 second=111 amount=-1
kerning first=70 second=245 amount=-1
kerning first=70 second=224 amount=-2
kerning first=80 second=196 amount=-1
kerning first=89 second=228 amount=-3
kerning first=86 second=234 amount=-1
kerning first=75 second=211 amount=-1
kerning first=86 second=253 amount=-1
kerning first=84 second=99 amount=-3
kerning first=107 second=252 amount=-1
kerning first=195 second=193 amount=1
kerning first=196 second=196 amount=1
kerning first=86 second=46 amount=-2
kerning first=102 second=58 amount=-1
kerning first=82 second=87 amount=-1
kerning first=187 second=87 amount=-1
kerning first=75 second=253 amount=-1
kerning first=120 second=243 amount=-1
kerning first=187 second=89 amount=-2
kerning first=221 second=242 amount=-3
kerning first=80 second=242 amount=-1
kerning first=45 second=84 amount=-2
kerning first=76 second=214 amount=-1
kerning first=221 second=233 amount=-3
kerning first=221 second=195 amount=-1
kerning first=171 second=221 amount=-1
kerning first=66 second=221 amount=-1
kerning first=196 second=121 amount=-1
kerning first=82 second=250 amount=-1
kerning first=82 second=233 amount=-1
kerning first=196 second=102 amount=-1
kerning first=76 second=212 amount=-1
kerning first=221 second=117 amount=-2
kerning first=79 second=89 amount=-1
kerning first=70 second=226 amount=-2
kerning first=76 second=218 amount=-1
kerning first=195 second=253 amount=-1
kerning first=84 second=243 amount=-3
kerning first=88 second=79 amount=-1
kerning first=89 second=111 amount=-3
kerning first=84 second=224 amount=-2
kerning first=87 second=193 amount=-1
kerning first=75 second=255 amount=-1
kerning first=89 second=249 amount=-2
kerning first=89 second=67 amount=-1
kerning first=89 second=227 amount=-3
kerning first=193 second=255 amount=-1
kerning first=246 second=120 amount=-1
kerning first=75 second=234 amount=-1
kerning first=192 second=84 amount=-1
kerning first=87 second=101 amount=-1
kerning first=107 second=233 amount=-1
kerning first=84 second=195 amount=-1
kerning first=87 second=243 amount=-1
kerning first=213 second=46 amount=-1
kerning first=75 second=249 amount=-1
kerning first=221 second=58 amount=-3
kerning first=119 second=46 amount=-2
kerning first=79 second=45 amount=1
kerning first=194 second=253 amount=-1
kerning first=86 second=171 amount=-2
kerning first=68 second=89 amount=-1
kerning first=80 second=194 amount=-1
kerning first=221 second=194 amount=-1
kerning first=242 second=120 amount=-1
kerning first=195 second=255 amount=-1
kerning first=84 second=226 amount=-2
kerning first=210 second=88 amount=-1
kerning first=89 second=245 amount=-3
kerning first=70 second=65 amount=-2
kerning first=70 second=97 amount=-2
kerning first=70 second=228 amount=-2
kerning first=107 second=235 amount=-1
kerning first=221 second=229 amount=-3
kerning first=80 second=233 amount=-1
kerning first=66 second=171 amount=-1
kerning first=86 second=194 amount=-1
kerning first=87 second=114 amount=-1
kerning first=213 second=88 amount=-1
kerning first=221 second=234 amount=-3
kerning first=80 second=234 amount=-1
kerning first=84 second=105 amount=-1
kerning first=70 second=251 amount=-1
kerning first=194 second=255 amount=-1
kerning first=82 second=235 amount=-1
kerning first=194 second=118 amount=-1
kerning first=120 second=242 amount=-1
kerning first=87 second=224 amount=-1
kerning first=221 second=171 amount=-2
kerning first=70 second=195 amount=-2
kerning first=195 second=171 amount=-1
kerning first=114 second=46 amount=-2
kerning first=255 second=46 amount=-3
kerning first=76 second=210 amount=-1
kerning first=86 second=192 amount=-1
kerning first=84 second=65 amount=-1
kerning first=70 second=193 amount=-2
kerning first=70 second=253 amount=-2
kerning first=87 second=45 amount=-1
kerning first=194 second=84 amount=-1
kerning first=195 second=118 amount=-1
kerning first=75 second=89 amount=-1
kerning first=89 second=232 amount=-3
kerning first=71 second=89 amount=-1
kerning first=212 second=89 amount=-1
kerning first=65 second=118 amount=-1
kerning first=84 second=251 amount=-3
kerning first=87 second=226 amount=-1
kerning first=196 second=221 amount=-1
kerning first=119 second=58 amount=-1
kerning first=212 second=88 amount=-1
kerning first=89 second=46 amount=-4
kerning first=210 second=221 amount=-1
kerning first=86 second=242 amount=-1
kerning first=195 second=89 amount=-1
kerning first=193 second=102 amount=-1
kerning first=75 second=217 amount=-1
kerning first=221 second=211 amount=-1
kerning first=221 second=246 amount=-3
kerning first=65 second=86 amount=-1
kerning first=75 second=232 amount=-1
kerning first=194 second=119 amount=-1
kerning first=80 second=246 amount=-1
kerning first=102 second=171 amount=-1
kerning first=86 second=121 amount=-1
kerning first=75 second=242 amount=-1
kerning first=70 second=243 amount=-1
kerning first=193 second=87 amount=-1
kerning first=86 second=111 amount=-1
kerning first=82 second=121 amount=-1
kerning first=84 second=46 amount=-2
kerning first=88 second=233 amount=-1
kerning first=192 second=86 amount=-1
kerning first=88 second=234 amount=-1
kerning first=75 second=199 amount=-1
kerning first=45 second=213 amount=1
kerning first=68 second=221 amount=-1
kerning first=70 second=255 amount=-2
kerning first=221 second=250 amount=-2
kerning first=86 second=243 amount=-1
kerning first=65 second=196 amount=1
kerning first=87 second=65 amount=-1
kerning first=88 second=212 amount=-1
kerning first=194 second=196 amount=1
kerning first=87 second=252 amount=-1
kerning first=70 second=235 amount=-1
kerning first=107 second=121 amount=-1
kerning first=114 second=120 amount=-1
kerning first=75 second=219 amount=-1
kerning first=196 second=87 amount=-1
kerning first=102 second=45 amount=-1
kerning first=82 second=101 amount=-1
kerning first=221 second=227 amount=-3
kerning first=80 second=227 amount=-1
kerning first=75 second=243 amount=-1
kerning first=88 second=210 amount=-1
kerning first=87 second=249 amount=-1
kerning first=89 second=242 amount=-3
kerning first=70 second=232 amount=-1
kerning first=214 second=89 amount=-1
kerning first=221 second=210 amount=-1
kerning first=82 second=244 amount=-1
kerning first=211 second=89 amount=-1
kerning first=86 second=246 amount=-1
kerning first=82 second=58 amount=-1
kerning first=70 second=46 amount=-3
kerning first=211 second=46 amount=-1
kerning first=195 second=221 amount=-1
kerning first=80 second=245 amount=-1
kerning first=107 second=101 amount=-1
kerning first=193 second=221 amount=-1
kerning first=82 second=195 amount=-1
kerning first=65 second=65 amount=1
kerning first=89 second=243 amount=-3
kerning first=80 second=111 amount=-1
kerning first=86 second=117 amount=-1
kerning first=221 second=111 amount=-3
kerning first=84 second=235 amount=-3
kerning first=84 second=255 amount=-3
kerning first=84 second=229 amount=-2
kerning first=70 second=192 amount=-2
kerning first=88 second=171 amount=-1
kerning first=82 second=171 amount=-1
kerning first=193 second=171 amount=-1
kerning first=82 second=194 amount=-1
kerning first=187 second=194 amount=-1
kerning first=45 second=212 amount=1
kerning first=65 second=253 amount=-1
kerning first=82 second=245 amount=-1
kerning first=76 second=86 amount=-2
kerning first=86 second=228 amount=-1
kerning first=86 second=196 amount=-1
kerning first=86 second=233 amount=-1
kerning first=84 second=249 amount=-3
kerning first=88 second=45 amount=-1
kerning first=79 second=221 amount=-1
kerning first=88 second=211 amount=-1
kerning first=89 second=97 amount=-3
kerning first=87 second=228 amount=-1
kerning first=192 second=255 amount=-1
kerning first=171 second=84 amount=-1
kerning first=84 second=245 amount=-2
kerning first=89 second=213 amount=-1
kerning first=86 second=187 amount=-1
kerning first=75 second=117 amount=-1
kerning first=76 second=79 amount=-1
kerning first=65 second=89 amount=-1
kerning first=70 second=229 amount=-2
kerning first=253 second=46 amount=-3
kerning first=86 second=97 amount=-1
kerning first=80 second=232 amount=-1
kerning first=107 second=234 amount=-1
kerning first=70 second=244 amount=-1
kerning first=84 second=192 amount=-1
kerning first=107 second=244 amount=-1
kerning first=86 second=226 amount=-1
kerning first=70 second=249 amount=-1
kerning first=75 second=246 amount=-1
kerning first=70 second=252 amount=-1
kerning first=193 second=118 amount=-1
kerning first=87 second=232 amount=-1
kerning first=118 second=46 amount=-1
kerning first=244 second=120 amount=-1
kerning first=84 second=119 amount=-3
kerning first=89 second=193 amount=-1
kerning first=121 second=58 amount=-1
kerning first=194 second=193 amount=1
kerning first=74 second=45 amount=-1
kerning first=87 second=46 amount=-2
kerning first=70 second=242 amount=-1
kerning first=87 second=225 amount=-1
kerning first=71 second=221 amount=-1
kerning first=75 second=87 amount=-1
kerning first=212 second=221 amount=-1
kerning first=65 second=255 amount=-1
kerning first=111 second=120 amount=-1
kerning first=87 second=192 amount=-1
kerning first=192 second=192 amount=1
kerning first=75 second=213 amount=-1
kerning first=88 second=101 amount=-1
kerning first=88 second=214 amount=-1
kerning first=187 second=221 amount=-2
kerning first=82 second=221 amount=-1
kerning first=82 second=65 amount=-1
kerning first=187 second=65 amount=-1
kerning first=195 second=195 amount=1
kerning first=80 second=195 amount=-1
kerning first=208 second=89 amount=-1
kerning first=214 second=46 amount=-1
kerning first=212 second=45 amount=1
kerning first=87 second=251 amount=-1
kerning first=84 second=252 amount=-3
kerning first=76 second=255 amount=-2
kerning first=118 second=58 amount=-1
kerning first=193 second=194 amount=1
kerning first=76 second=89 amount=-3
kerning first=87 second=235 amount=-1
kerning first=82 second=251 amount=-1
kerning first=86 second=193 amount=-1
kerning first=221 second=187 amount=-1
kerning first=195 second=102 amount=-1
kerning first=120 second=232 amount=-1
kerning first=107 second=232 amount=-1
kerning first=84 second=232 amount=-3
kerning first=89 second=246 amount=-3
kerning first=221 second=244 amount=-3
kerning first=196 second=195 amount=1
kerning first=192 second=89 amount=-1
kerning first=84 second=242 amount=-2
kerning first=65 second=195 amount=1
kerning first=89 second=226 amount=-3

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,248 +0,0 @@
'use strict';
const playerArrayContains = require('./util').playerArrayContains;
const WHITESPACE = ' ';
const parseWarLog = (lineArray, war) => {
const NAME_TOO_LONG_ERROR = 'Error: ENAMETOOLONG: name too long, open \'';
const stats = {
war: war,
clean: [],
budget: [],
points: [],
kills: [],
respawn: [],
revive: [],
flag: [],
transport: [],
players: []
};
const addPlayerIfNotExists = (inputPlayer) => {
const player = getPlayerAndFractionFromString(inputPlayer);
if (player && player.name && player.fraction && !playerArrayContains(stats.players, player)) {
player['warId'] = war._id;
stats.players.push(player);
}
};
lineArray.some(line => {
/**
* sanitize nameTooLongError coming up in first line
*/
if (line.includes(NAME_TOO_LONG_ERROR)) {
line = line.substring(line.indexOf(NAME_TOO_LONG_ERROR) + NAME_TOO_LONG_ERROR.length);
}
/**
* KILLS
*/
if (line.includes('(Abschuss)') && !line.includes('Fahrzeug')) {
stats.clean.push(line);
const shooterString = line.substring(line.lastIndexOf(' von: ') + 6, line.lastIndexOf('."'));
const shooter = getPlayerAndFractionFromString(shooterString);
const targetString = line.substring(line.lastIndexOf(' --- ') + 5, line.lastIndexOf(' von:'));
const target = getPlayerAndFractionFromString(targetString);
stats.kills.push({
war: war._id,
time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]),
shooter: shooter ? shooter.name : null,
target: target ? target.name : null,
friendlyFire: shooter ? target.fraction === shooter.fraction : false,
fraction: shooter ? shooter.fraction : 'NONE'
});
}
/**
* BUDGET
*/
else if (line.includes('(Budget)')) {
stats.clean.push(line);
const budg = line.split(WHITESPACE);
if (line.includes('Startbudget')) {
stats.war['budgetBlufor'] = transformMoneyString(budg[9]);
stats.war['budgetOpfor'] = transformMoneyString(budg[12].slice(0,-1));
// this date needs to be assigned in first place !important
const dateString = budg[0].slice(0,-1).split('/');
stats.war.date = new Date(dateString[0], dateString[1], dateString[2])
} else if (line.includes('Endbudget')) {
stats.war['endBudgetBlufor'] = transformMoneyString(budg[9].substr(1));
stats.war['endBudgetOpfor'] = transformMoneyString(budg[12].slice(0,-1));
stats.war.endDate = getFullTimeDate(war.date, budg[5]);
} else {
stats.budget.push(getBudgetEntry(budg, war._id, war.date));
}
}
/**
* FLAG
*/
else if (line.includes('(Fahne)') && !line.includes('Dominator')) {
stats.clean.push(line);
const playerName = line.substring(line.lastIndexOf('rt von ') + 7).slice(0, -2);
const flagFraction = line.includes('NATO Flagge') ? 'BLUFOR' : 'OPFOR';
const capture = !!line.includes('Flagge erobert');
stats.flag.push({
war: war._id,
time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]),
player: playerName,
flagFraction: flagFraction,
capture: capture
});
}
/**
* POINTS
*/
else if (line.includes('(Punkte)')) {
stats.clean.push(line);
const pt = line.split(WHITESPACE);
if (line.includes('Endpunktestand')) {
stats.war['ptBlufor'] = parseInt(pt[9]);
stats.war['ptOpfor'] = parseInt(pt[12].slice(0, -1));
// EXIT LOOP
return true;
} else {
stats.points.push(getPointsEntry(pt, line, war._id, war.date))
}
}
/**
* RESPAWN
*/
else if (line.includes('(Respawn)')) {
const resp = line.split(WHITESPACE);
const playerName = line.substring(line.lastIndexOf('Spieler:') + 9, line.lastIndexOf('- Kosten') -1);
stats.respawn.push(getRespawnEntry(resp, playerName, war._id, war.date));
}
/**
* REVIVE
*/
else if (line.includes('(Revive)')) {
stats.clean.push(line);
const stabilized = !!line.includes('stabilisiert."');
const medicName = line.substring(line.lastIndexOf('wurde von ') + 10,
line.lastIndexOf(stabilized ? ' stabilisiert' : ' wiederbelebt'));
const medic = getPlayerAndFractionFromString(medicName);
const patientName = line.substring(line.lastIndexOf('--- ') + 4, line.lastIndexOf(' wurde von'));
const patient = getPlayerAndFractionFromString(patientName);
stats.revive.push({
war: war._id,
time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]),
stabilized: stabilized,
medic: medic.name,
patient: patient.name,
fraction: medic.fraction
});
}
/**
* TRANSPORT
*/
else if (line.includes('(Transport)')) {
stats.clean.push(line);
const driverString = line.substring(line.lastIndexOf('wurde von ') + 10, line.lastIndexOf(' eingeflogen'));
const driver = getPlayerAndFractionFromString(driverString);
const passengerString = line.substring(line.lastIndexOf('--- ') + 3, line.lastIndexOf(' wurde von'));
const passenger = getPlayerAndFractionFromString(passengerString);
const distance = parseInt(line.substring(line.lastIndexOf('eingeflogen (') + 13, line.lastIndexOf('m)') - 1));
stats.transport.push({
war: war._id,
time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]),
driver: driver ? driver.name : null,
passenger: passenger ? passenger.name : null,
fraction: driver ? driver.fraction : 'NONE',
distance: distance
});
}
/**
* PLAYERS
*/
else if (line.includes('(Fraktionsuebersicht)')) {
const playerString = line.substring(line.lastIndexOf('--- ') + 4, line.lastIndexOf(', PUID'));
addPlayerIfNotExists(playerString)
}
});
for (let i = 0; i < stats.players.length; i++) {
const playerName = stats.players[i].name;
stats.players[i]['respawn'] = stats.respawn.filter(res => res.player === playerName).length;
stats.players[i]['kill'] = stats.kills.filter(kill => kill.shooter === playerName && !kill.friendlyFire).length;
stats.players[i]['friendlyFire'] = stats.kills.filter(kill => kill.shooter === playerName && kill.friendlyFire).length;
stats.players[i]['death'] = stats.kills.filter(kill => kill.target === playerName).length;
stats.players[i]['revive'] = stats.revive.filter(rev => rev.medic === playerName && !rev.stabilized).length;
stats.players[i]['flagTouch'] = stats.flag.filter(flag => flag.player === playerName).length;
stats.players[i]['sort'] = stats.players[i]['kill'] + stats.players[i]['revive'] + stats.players[i]['flagTouch']
- stats.players[i]['friendlyFire'] - stats.players[i]['death'] - stats.players[i]['respawn']
}
stats.war.playersBlufor = stats.players.filter(player => player.fraction === 'BLUFOR').length;
stats.war.playersOpfor = stats.players.filter(player => player.fraction === 'OPFOR').length;
return stats;
};
const getRespawnEntry = (respawn, playerName, warId, warDate) => {
return {
war: warId,
time: getFullTimeDate(warDate, respawn[5]),
player: playerName
}
};
const getPointsEntry = (pt, line, warId, warDate) => {
return {
war: warId,
time: getFullTimeDate(warDate, pt[5]),
ptBlufor: parseInt(pt[10]),
ptOpfor: parseInt(pt[13].slice(0, -3)),
fraction: line.includes('Kein Dominator') ? 'NONE' : line.includes('NATO +1') ? 'BLUFOR' : 'OPFOR'
}
};
const getBudgetEntry = (budg, warId, warDate) => {
return {
war: warId,
time: getFullTimeDate(warDate, budg[5]),
fraction: budg[7] === 'NATO' ? 'BLUFOR' : 'OPFOR',
oldBudget: transformMoneyString(budg[9]),
newBudget: transformMoneyString(budg[12])
}
};
const getPlayerAndFractionFromString = (nameAndFractionString) => {
const nameArray = nameAndFractionString.split(WHITESPACE);
const fraction = nameArray[nameArray.length - 1] !== '(ENEMY)' ? nameArray[nameArray.length - 1] === '(WEST)' ? 'BLUFOR' : 'OPFOR' : undefined;
const name = nameAndFractionString.substring(0, nameAndFractionString.indexOf(nameArray[nameArray.length - 1]) - 1);
// do not return player for 'Selbstverschulden' or 'Error: No unit'
if (name && name !== 'Error: No unit') {
return {name: name, fraction: fraction};
}
};
const transformMoneyString = (budgetString) => {
if (!budgetString.includes('e+')) {
return parseInt(budgetString);
}
const budget = budgetString.split('e+');
return Math.round(parseFloat(budget[0]) * Math.pow(10, parseInt(budget[1])));
};
const getFullTimeDate = (date, timeString) => {
const returnDate = new Date(date);
const time = timeString.split(':');
returnDate.setHours(time[0]);
returnDate.setMinutes(time[1]);
return returnDate;
};
module.exports = parseWarLog;

View File

@ -1,267 +0,0 @@
"use strict";
// modules used for graphic manipulation
const jimp = require('jimp');
const imagemin = require('imagemin');
const imageminpngquant = require('imagemin-pngquant');
const SHA1 = require('node-sha1');
const async = require('async');
// Mongoose Model using mongoDB
const UserModel = require('../models/user');
const AwardingModel = require('../models/awarding');
const RankModel = require('../models/rank');
// http error code table
const codes = require('../routes/http-codes');
// standard input/output file extension
const fileExt = '.png';
const resourceDir = __dirname + '/../resource/';
let createSignature = (userId, res, next) => {
let loadedImage;
let user;
if (!String(userId).match(/^[0-9a-fA-F]{24}$/)) {
let error = new Error('no valid ID ' + userId);
error.status = codes.wrongdatatyperequest;
return next(error);
}
UserModel.findById(userId, ['username', 'rankLvl', 'squadId'])
.populate('squadId', ['name', 'fraction'])
.exec()
.then((resUser) => {
user = resUser;
let platePath;
if (resUser && resUser.squadId && resUser.squadId.fraction === 'BLUFOR') {
platePath = __dirname + '/backplate/blufor' + fileExt;
} else if (resUser && resUser.squadId && resUser.squadId.fraction === 'OPFOR') {
platePath = __dirname + '/backplate/opfor' + fileExt;
}
// kill process on undefined platePath
if (!platePath) {
throw new Error('Fraction not defined for user with id ' + userId);
}
return jimp.read(platePath)
})
.then((image) => {
loadedImage = image;
}).then(() => {
return jimp.loadFont(__dirname + '/font/DEVAJU_SANS_19.fnt');
})
.then((font) => {
loadedImage.print(font, 128, 8, user.username)
})
.then(() => {
return jimp.loadFont(__dirname + '/font/DEJAVU_SANS_13.fnt');
})
.then((font) => {
loadedImage.print(font, 128, 35, user.squadId.name);
return font;
})
.then((font) => {
let rankH, rankW, rankX, rankY;
RankModel.findOne({'level': user.rankLvl, 'fraction': user.squadId.fraction}, (err, result) => {
if (err) {
return next(err)
}
if (result) {
if (user.squadId.fraction === 'BLUFOR') {
rankW = 25;
rankH = 60;
rankX = 36;
rankY = 34;
} else {
rankW = 37;
rankH = 58;
rankX = 30;
rankY = 34;
}
jimp.read(resourceDir + 'rank/' + result._id + fileExt)
.then((rankImage) => {
rankImage.resize(rankW, rankH);
loadedImage
.print(font, 128, 55, result.name)
.composite(rankImage, rankX, rankY)
})
.then(() => {
addDecorationsAndSave(userId, loadedImage, res, next);
})
} else {
// user has not any assignable rank in his fraction at this point,
// e.g. assigned rank has been deleted or switched fraction so rankLvl is not defined
addDecorationsAndSave(userId, loadedImage, res, next);
}
})
})
.catch((err) => {
next(err);
})
};
/**
* query decorations according to user id and
* add all graphics into given image
*
* @param userId
* @param loadedImage - background image
* @param res
* @param next
*/
let addDecorationsAndSave = (userId, loadedImage, res, next) => {
const medalW = 21;
const medalH = 40;
const ribbonW = 53;
const ribbonH = 15;
let medalPx = 630;
let medalPy = 5;
let ribbonPx = 598;
let ribbonPy = 95;
AwardingModel.find({
'userId': userId,
'confirmed': 1
}, ['decorationId', 'date']).populate('decorationId', ['isMedal', 'fraction'])
.exec((err, awardings) => {
if (err) {
return next(err);
}
if (awardings.length > 0) {
//TODO: simplify this sorting hell
awardings.sort((a1, a2) => {
if (!a1.decorationId.isMedal && !a2.decorationId.isMedal) {
if (a1.decorationId.fraction === a2.decorationId.fraction) {
if (a1.date !== a2.date) {
if (a1.date < a2.date) {
return -1;
}
if (a1.date > a2.date) {
return 1;
}
}
return 0;
} else {
if (a1.decorationId.fraction === 'GLOBAL' && a2.decorationId.fraction !== 'GLOBAL') {
return -1;
}
if (a2.decorationId.fraction === 'GLOBAL' && a1.decorationId.fraction !== 'GLOBAL') {
return 1;
}
}
}
if (a1.decorationId.isMedal !== a2.decorationId.isMedal) {
if (a1.decorationId.isMedal && !a2.decorationId.isMedal) {
return 1;
}
if (!a1.decorationId.isMedal && a2.decorationId.isMedal) {
return -1;
}
}
if (a1.decorationId.isMedal && a2.decorationId.isMedal) {
if (a1.date !== a2.date) {
if (a1.date < a2.date) {
return -1;
}
if (a1.date > a2.date) {
return 1;
}
}
return 0;
}
});
// use synchronized call to keep correct order of decorations
async.eachSeries(awardings, (award, callback) => {
jimp.read(resourceDir + 'decoration/' + award.decorationId._id + fileExt).then((decorationImage) => {
if (award.decorationId.isMedal) {
decorationImage.resize(medalW, medalH);
loadedImage.composite(decorationImage, medalPx, medalPy);
if (medalPy === 5) {
medalPx = medalPx - 1 - medalW;
} else {
medalPx = medalPx + 1 + medalW;
}
if (medalPx <= 300) {
medalPy = medalPy + 3 + medalH;
}
} else {
decorationImage.resize(ribbonW, ribbonH);
loadedImage.composite(decorationImage, ribbonPx, ribbonPy);
ribbonPx = ribbonPx - 2 - ribbonW;
if (ribbonPx <= 154) {
ribbonPy = ribbonPy - 3 - ribbonH;
ribbonPx = 598;
}
}
callback();
})
}, (err) => {
if (err) {
throw err;
}
compareImagesAndSave(loadedImage, userId, res, next);
});
} else {
compareImagesAndSave(loadedImage, userId, res, next);
}
}
)
};
let compareImagesAndSave = (generatedImage, userId, res, next) => {
return jimp.read(resourceDir + 'signature/big/' + userId + fileExt)
.then((oldImage) => {
// compare hashes of image map to recognize difference
const sig1 = SHA1(generatedImage.bitmap.data);
const sig2 = SHA1(oldImage.bitmap.data);
if (sig1 !== sig2) {
saveJimpImageAndCompress(generatedImage, userId, res, next)
} else {
res.locals.items = {status: 'nothing to do'};
next();
}
})
.catch((err) => {
saveJimpImageAndCompress(generatedImage, userId, res, next);
})
};
/**
*
* Write Jimp image to file system
* Gets minified after 3 seconds timeout
*
* @param image - Jimp image
* @param userId - user id of signature owner
* @param res - express response object
* @param next - express next-function
*/
let saveJimpImageAndCompress = (image, userId, res, next) => {
image.write(resourceDir + 'signature/big/' + userId + fileExt);
setTimeout(() => {
imagemin([resourceDir + 'signature/big/' + userId + fileExt], resourceDir + 'signature/', {
plugins: [
imageminpngquant({quality: '65-80'})
]
}).then((files) => {
res.locals.items = {status: 'success'};
return next();
}).catch((error) => {
console.log(error)
})
}, 3000);
};
module.exports = createSignature;

View File

@ -4,18 +4,18 @@
cd $(dirname $0)
# array of available collection names
col=(app_user awarding campaign decoration logBudget logFlag logKill logPoints logRespawn logRevive logTransport player promotion rank squad user war )
col=(app_user awarding campaign decoration logBudget logFlag logKill logPlayerCount logPoints logRespawn logRevive logServerFps logTransport logVehicle player promotion rank squad user war)
if [ -z "$1" ]
then
DATE=$(date '+%Y-%m-%d')
mkdir -p ${DATE}/resource
cp -R ../api/resource/ ${DATE}/
cp -R ../server/resource/ ${DATE}/
else
DATE=${1}
tar -xzf ${DATE}.tar.gz
rm -rf ../api/resource
cp -Rv ${DATE}/resource ../api/
rm -rf ../server/resource
cp -Rv ${DATE}/resource ../server/
fi
for i in "${col[@]}"

30
docs/infra/3rd-party-install.sh Normal file → Executable file
View File

@ -2,13 +2,12 @@
cd $(dirname $0)
#####################################################
###### INSTALL ALL REQUIRED 3RD-PARTY SOFTWARE ######
#####################################################
# MONGO DB COMMUNITY
# NPM
# NODE
echo "########################################################"
echo "###### INSTALLING ALL REQUIRED 3RD-PARTY SOFTWARE ######"
echo "########################################################"
echo "# MONGO DB COMMUNITY"
echo "# NPM"
echo "# NODE"
# mongodb key & repo
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5
@ -26,13 +25,26 @@ sudo systemctl enable mongod.service
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
source ~/.nvm/nvm.sh
nvm install stable
sudo ln -s ~/.nvm/versions/node/$(node -v)/bin/node /usr/bin/node
sudo ln -sf ~/.nvm/versions/node/$(node -v)/bin/node /usr/bin/node
# upgrade to latest npm version
sudo npm install -g npm@latest
if [ "${1}" == "prod" ]; then
echo "########################################################"
echo "################ SETTING UP APPLICATION ################"
echo "########################################################"
cd ./../..
npm install
npm run deploy-static:prod
echo "########################################################"
echo "####### INSTALLING PM2 AND INGRAIN AUTO STARTUP ########"
echo "########################################################"
sudo npm install -g pm2
pm2 start ./../../pm2-start.json
pm2 start pm2-start.json
pm2 save
pm2 startup systemd
fi
echo " ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑"
echo "IMPORTANT: Run the sudo command printed above to finish the pm2 setup"

View File

@ -0,0 +1,27 @@
<code_scheme name="OPT-CC" version="173">
<option name="FORMATTER_TAGS_ENABLED" value="true" />
<JSCodeStyleSettings version="0">
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
</TypeScriptCodeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="RIGHT_MARGIN" value="120" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="WRAP_COMMENTS" value="true" />
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="WRAP_COMMENTS" value="true" />
</codeStyleSettings>
</code_scheme>

Binary file not shown.

565
package-lock.json generated
View File

@ -1,32 +1,29 @@
{
"name": "opt-cc",
"version": "1.6.7",
"version": "1.9.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ajv": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
"integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"requires": {
"co": "4.6.0",
"fast-deep-equal": "1.0.0",
"fast-json-stable-stringify": "2.0.0",
"json-schema-traverse": "0.3.1"
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"ansi-regex": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
"integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=",
"dev": true
},
"ansi-styles": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
"integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=",
"dev": true
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"asn1": {
"version": "0.2.3",
@ -53,29 +50,26 @@
"dev": true
},
"aws4": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
"tweetnacl": "^0.14.3"
}
},
"boom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
"dev": true,
"requires": {
"hoek": "4.2.0"
}
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
"caseless": {
"version": "0.12.0",
@ -84,23 +78,24 @@
"dev": true
},
"chalk": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
"integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"dev": true,
"requires": {
"ansi-styles": "1.1.0",
"escape-string-regexp": "1.0.5",
"has-ansi": "0.1.0",
"strip-ansi": "0.3.0",
"supports-color": "0.2.0"
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"dependencies": {
"supports-color": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
"integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
"dev": true
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@ -110,13 +105,28 @@
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true
},
"combined-stream": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
"integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
"color-convert": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz",
"integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==",
"dev": true,
"requires": {
"delayed-stream": "1.0.0"
"color-name": "1.1.1"
}
},
"color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=",
"dev": true
},
"combined-stream": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commander": {
@ -126,25 +136,26 @@
"dev": true
},
"concurrently": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.5.0.tgz",
"integrity": "sha1-jPG3cHppFqeKT/W3e7BN7FSzebI=",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.0.tgz",
"integrity": "sha512-6XiIYtYzmGEccNZFkih5JOH92jLA4ulZArAYy5j1uDSdrPLB3KzdE8GW7t2fHPcg9ry2+5LP9IEYzXzxw9lFdA==",
"dev": true,
"requires": {
"chalk": "0.5.1",
"chalk": "^2.4.1",
"commander": "2.6.0",
"date-fns": "1.29.0",
"lodash": "4.17.4",
"date-fns": "^1.23.0",
"lodash": "^4.5.1",
"read-pkg": "^3.0.0",
"rx": "2.3.24",
"spawn-command": "0.0.2-1",
"supports-color": "3.2.3",
"tree-kill": "1.2.0"
"spawn-command": "^0.0.2-1",
"supports-color": "^3.2.3",
"tree-kill": "^1.1.0"
}
},
"core-js": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
"integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=",
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
"dev": true
},
"core-util-is": {
@ -153,33 +164,13 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"cryptiles": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
"dev": true,
"requires": {
"boom": "5.2.0"
},
"dependencies": {
"boom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
"dev": true,
"requires": {
"hoek": "4.2.0"
}
}
}
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"requires": {
"assert-plus": "1.0.0"
"assert-plus": "^1.0.0"
}
},
"date-fns": {
@ -201,7 +192,16 @@
"dev": true,
"optional": true,
"requires": {
"jsbn": "0.1.1"
"jsbn": "~0.1.0"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
}
},
"escape-string-regexp": {
@ -223,9 +223,9 @@
"dev": true
},
"fast-deep-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
"dev": true
},
"fast-json-stable-stringify": {
@ -241,14 +241,14 @@
"dev": true
},
"form-data": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
"integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"dev": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.5",
"mime-types": "2.1.17"
"asynckit": "^0.4.0",
"combined-stream": "1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
@ -257,9 +257,15 @@
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"requires": {
"assert-plus": "1.0.0"
"assert-plus": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@ -272,41 +278,26 @@
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true,
"requires": {
"ajv": "5.3.0",
"har-schema": "2.0.0"
}
},
"has-ansi": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
"integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
"dev": true,
"requires": {
"ansi-regex": "0.2.1"
"ajv": "^5.1.0",
"har-schema": "^2.0.0"
}
},
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"hawk": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
"dev": true,
"requires": {
"boom": "4.3.1",
"cryptiles": "3.1.2",
"hoek": "4.2.0",
"sntp": "2.1.0"
}
},
"hoek": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
"integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
"dev": true
},
"hosted-git-info": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.1.tgz",
"integrity": "sha512-Ba4+0M4YvIDUUsprMjhVTU1yN9F2/LJSAl69ZpzaLT4l4j5mwTS6jqqW9Ojvj6lKz/veqPzpJBqGbXspOb533A==",
"dev": true
},
"http-signature": {
@ -315,9 +306,24 @@
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"jsprim": "1.4.1",
"sshpk": "1.13.1"
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-builtin-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
"dev": true,
"requires": {
"builtin-modules": "^1.0.0"
}
},
"is-typedarray": {
@ -350,11 +356,11 @@
"integrity": "sha1-M4WseQGSEwy+Iw6ALsAskhW7/to=",
"dev": true,
"requires": {
"hoek": "4.2.0",
"isemail": "2.2.1",
"items": "2.1.1",
"moment": "2.19.1",
"topo": "2.0.2"
"hoek": "4.x.x",
"isemail": "2.x.x",
"items": "2.x.x",
"moment": "2.x.x",
"topo": "2.x.x"
}
},
"jsbn": {
@ -364,6 +370,12 @@
"dev": true,
"optional": true
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@ -394,25 +406,37 @@
"verror": "1.10.0"
}
},
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0",
"strip-bom": "^3.0.0"
}
},
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"mime-db": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
"integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=",
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
"dev": true
},
"mime-types": {
"version": "2.1.17",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
"integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
"version": "2.1.18",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
"dev": true,
"requires": {
"mime-db": "1.30.0"
"mime-db": "~1.33.0"
}
},
"minimist": {
@ -422,23 +446,60 @@
"dev": true
},
"moment": {
"version": "2.19.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.19.1.tgz",
"integrity": "sha1-VtoaLRy/AdOLfhr8McELz6GSkWc=",
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=",
"dev": true
},
"normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
"dev": true,
"requires": {
"hosted-git-info": "^2.1.4",
"is-builtin-module": "^1.0.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
"dev": true
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"pify": "^3.0.0"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@ -446,39 +507,48 @@
"dev": true
},
"qs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
},
"request": {
"version": "2.83.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
"integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
"integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"dev": true,
"requires": {
"aws-sign2": "0.7.0",
"aws4": "1.6.0",
"caseless": "0.12.0",
"combined-stream": "1.0.5",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.3.1",
"har-validator": "5.0.3",
"hawk": "6.0.2",
"http-signature": "1.2.0",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.17",
"oauth-sign": "0.8.2",
"performance-now": "2.1.0",
"qs": "6.5.1",
"safe-buffer": "5.1.1",
"stringstream": "0.0.5",
"tough-cookie": "2.3.3",
"tunnel-agent": "0.6.0",
"uuid": "3.1.0"
"load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^3.0.0"
}
},
"request": {
"version": "2.87.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.6.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.5",
"extend": "~3.0.1",
"forever-agent": "~0.6.1",
"form-data": "~2.3.1",
"har-validator": "~5.0.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.17",
"oauth-sign": "~0.8.2",
"performance-now": "^2.1.0",
"qs": "~6.5.1",
"safe-buffer": "^5.1.1",
"tough-cookie": "~2.3.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.1.0"
}
},
"rx": {
@ -488,19 +558,22 @@
"dev": true
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"sntp": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
"dev": true,
"requires": {
"hoek": "4.2.0"
}
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
},
"spawn-command": {
"version": "0.0.2-1",
@ -508,44 +581,76 @@
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
"dev": true
},
"sshpk": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
"spdx-correct": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
"integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
"dev": true,
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jsbn": "0.1.1",
"tweetnacl": "0.14.5"
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
"spdx-exceptions": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
"integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
"dev": true
},
"strip-ansi": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
"integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"dev": true,
"requires": {
"ansi-regex": "0.2.1"
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
"integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
"dev": true
},
"sshpk": {
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
"integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
"dev": true,
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
"supports-color": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
"integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
"dev": true,
"requires": {
"has-flag": "1.0.0"
"has-flag": "^1.0.0"
},
"dependencies": {
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
"dev": true
}
}
},
"topo": {
@ -554,16 +659,16 @@
"integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=",
"dev": true,
"requires": {
"hoek": "4.2.0"
"hoek": "4.x.x"
}
},
"tough-cookie": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
"integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
"dev": true,
"requires": {
"punycode": "1.4.1"
"punycode": "^1.4.1"
}
},
"tree-kill": {
@ -578,7 +683,7 @@
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
@ -589,33 +694,43 @@
"optional": true
},
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
"integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "1.3.0"
"extsprintf": "^1.2.0"
}
},
"wait-on": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-2.0.2.tgz",
"integrity": "sha1-CoT9BwJMb8Joyw6r5YW+IXqvK6o=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-2.1.0.tgz",
"integrity": "sha512-hDwJ674+7dfiiK/cxtYCwPxlnjXDjto/pCz1PF02sXUhqCqCWsgvxZln0699PReWqXXgkxqkF6DDo5Rj9sjNvw==",
"dev": true,
"requires": {
"core-js": "2.5.1",
"joi": "9.2.0",
"minimist": "1.2.0",
"request": "2.83.0",
"rx": "4.1.0"
"core-js": "^2.4.1",
"joi": "^9.2.0",
"minimist": "^1.2.0",
"request": "^2.78.0",
"rx": "^4.1.0"
},
"dependencies": {
"rx": {

View File

@ -1,19 +1,23 @@
{
"name": "opt-cc",
"version": "1.6.11",
"version": "1.9.7",
"author": "Florian Hartwich <hardi@noarch.de>",
"private": true,
"scripts": {
"start": "npm run deploy-static-prod && npm start --prefix ./api",
"dev": "npm run deploy-static && npm run dev --prefix ./api",
"deploy-static": "cd ./static && $(npm bin)/ng build && ln -s ../api/resource/ ../public/resource",
"deploy-static:prod": "cd ./static && $(npm bin)/ng build --prod --aot && ln -s ../api/resource/ ../public/resource",
"postinstall": "npm install --prefix ./static && npm install --prefix ./api",
"mongodb": "mkdir -p mongodb-data && mongod --dbpath ./mongodb-data",
"test": "npm test --prefix ./api",
"e2e": "npm run deploy-static && concurrently \"npm run e2e --prefix ./api\" \"wait-on -t 60000 http://localhost:3001/ && npm run e2e --prefix ./static\" --success first --kill-others",
"start-e2e": "npm run deploy-static && npm run e2e --prefix ./api",
"test-e2e": "npm run e2e --prefix ./static"
"start": "npm run deploy-static-prod && npm start --prefix ./server",
"dev": "npm run deploy-static && npm run dev --prefix ./server",
"pre-deploy-clean": "rm -f ./public/resource",
"deploy-static": "npm run pre-deploy-clean && npm run build --prefix=static && npm run deploy-static:link-resource && npm run deploy-static:api-docs",
"deploy-static:prod": "npm run pre-deploy-clean && npm run build:prod --prefix=static && npm run deploy-static:link-resource && npm run deploy-static:api-docs",
"deploy-static:link-resource": "ln -s ../server/resource/ public/resource",
"deploy-static:api-docs": "npm run api:docs --prefix=server",
"postinstall": "npm install --prefix ./static && npm install --prefix ./server",
"lint": "npm run lint --prefix=server && npm run lint --prefix=static",
"test": "npm test --prefix ./server",
"e2e": "npm run deploy-static && concurrently \"npm run start-test --prefix ./server\" \"wait-on -t 60000 http://localhost:3001/ && npm run e2e --prefix ./static\" --success first --kill-others",
"start-e2e": "npm run deploy-static && npm run start-test --prefix ./server",
"test-e2e": "npm run e2e --prefix ./static",
"test-api": "npm run api:test-docs --prefix ./server"
},
"dependencies": {},
"devDependencies": {

View File

@ -2,7 +2,7 @@
"apps": [
{
"name": "opt-cc",
"script": "./api/server.js",
"script": "./server/server.js",
"watch": false,
"env": {
"NODE_ENV": "production",

11
server/.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,19 @@
### 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)
+ Attributes (object)
+ 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,63 @@
### Get Awardings [GET /awardings{?userId,inProgress,fractFilter,squadId}]
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`
+ squadId: `5aba54eaeadcce6332c6a774` (string, optional)
unique id of the squad
+ 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,129 @@
### 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
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4wIDDQIBeZj+RQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
AAAAFklEQVQI12NgZ2f///8/w////xkZGQEq5gYTeotA1AAAAABJRU5ErkJggg==
-----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
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4wIDDQIBeZj+RQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
AAAAFklEQVQI12NgZ2f///8/w////xkZGQEq5gYTeotA1AAAAABJRU5ErkJggg==
-----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,118 @@
### 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
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4wIDDQIBeZj+RQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
AAAAFklEQVQI12NgZ2f///8/w////xkZGQEq5gYTeotA1AAAAABJRU5ErkJggg==
-----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
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4wIDDQIBeZj+RQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
AAAAFklEQVQI12NgZ2f///8/w////xkZGQEq5gYTeotA1AAAAABJRU5ErkJggg==
-----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,112 @@
### 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
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4wIDDQIBeZj+RQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
AAAAFklEQVQI12NgZ2f///8/w////xkZGQEq5gYTeotA1AAAAABJRU5ErkJggg==
-----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
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA
B3RJTUUH4wIDDQIBeZj+RQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH
AAAAFklEQVQI12NgZ2f///8/w////xkZGQEq5gYTeotA1AAAAABJRU5ErkJggg==
-----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,96 @@
### List Users [GET /users{?q,fractFilter,squadId,decorationId,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`
+ squadId: `5aba54eaeadcce6332c6a774` (string, optional) - Field to filter by membership of certain squad
+ decorationId: `5abd3dff6e6a0334d95b8ba0` (string, optional) - Field to filter by ownership of certain decoration
+ 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

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)

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)

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,23 @@
# 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, proposer and army-user
## Properties
+ proposer (Proposer, required) - app user who requested this awarding
+ decorationId (Decoration, required) - populated decoration object that is given with the awarding
+ userId (User, required) - populated user who gets this awarding

View File

@ -0,0 +1,19 @@
# 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: WarWithPlayers (array[WarWithPlayers], required)

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,91 @@
# Log (object)
## Properties
+ _id: `` (string, required) - log entry id
+ war: `` (string, required) - warId
+ time: `` (string, required) - logging timestamp
# 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`
+ shooterVehicle: `FV-720 Mora` (string, optional) - vehicle in whiock the shooting player sat
+ targetVehicle: `Ifrit-GMG` (string, optional) - vehicle in which the target player sat
+ magazine: `30 mm APFSDS` (string, optional) - magazine name used to execute the kill
#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
+ additionalShooter: `[GNC]Paolo`, `Dominik` (array[string], required) - additional crew members of shooter vehicle
+ target: `T-100` (string, required) - name of the vehicle
+ fraction: `BLUFOR` (enum, required) - fraction of the shooter
+ Members
+ `BLUFOR`
+ `OPFOR`
+ `NONE`
+ vehicleClass: `LIGHT` (enum[string], required) - class of shot vehicle
+ Members
+ `LIGHT`
+ `HEAVY`
+ `AIR`
+ `BOAT`
+ `UNKNOWN`
+ shooterVehicle: `FV-720 Mora` (string, optional) - vehicle in whiock the shooting player sat
+ magazine: `30 mm APFSDS` (string, optional) - magazine name used to execute the kill

View File

@ -0,0 +1,39 @@
# 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
+ vehicleLight: 1 (number, required) - sum of light vehicle kills
+ vehicleHeavy: 1 (number, required) - sum of heavy vehicle kills
+ vehicleAir: 0 (number, required) - sum of air 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
+ travelDistance: 16535 (number, optional) - sum of transport meters as passenger
+ driverDistance: 1250 (number, optional) - sum of transport meters as pilot/driver
# 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 (number, optional) - unique ID for STEAM platform account
+ performance: `5abf65ae3fc5fa349ffd5cs2` (string, optional) - id of corresponding performance log entry
+ 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 returned for the highscore arrays
## Properties
+ warId: War (War, required) - war in which this player took part
+ 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,28 @@
# 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
+ fractionMappingBlufor: `BLUFOR` (enum[string], required) - display name mapping for Blufor fraction
+ fractionMappingOpfor: `OPFOR` (enum[string], required) - display name mapping for Opfor fraction
+ 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, fixed-type) - 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)

81
server/apib/dev-doc.apib Normal file
View File

@ -0,0 +1,81 @@
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
## Endpoints [/auth]
:[Gists](auth/signup.apib)
:[Gists](auth/login.apib)
# Group Admin
## Account [/account]
:[Gists](admin/account.apib)
## Commands [/cmd]
:[Gists](admin/signature.apib)
# Group Army Management
## Army [/overview]
:[Gists](army-management/army.apib)
## Awardings [/awarding]
:[Gists](army-management/awardings.apib)
## Decorations [/decorations]
:[Gists](army-management/decorations.apib)
## Promotion [/promotions]
:[Gists](army-management/promotions.apib)
## Ranks [/ranks]
:[Gists](army-management/ranks.apib)
## Squads [/squads]
:[Gists](army-management/squads.apib)
## Users [/user]
:[Gists](army-management/users.apib)
# Group Statistics
## Campaigns [/campaigns]
:[Gists](statistics/campaigns.apib)
## Logs [/logs]
:[Gists](statistics/logs.apib)
## Players [/players]
:[Gists](statistics/players.apib)
## Wars [/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,3 @@
{"_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}
{"_id":{"$oid":"5abd55ea9e32a76afef777d6"},"title":"Placeholder","timestamp":{"$date":"2018-03-30T21:08:58.123Z"},"updatedAt":{"$date":"2018-03-30T21:08:58.123Z"},"__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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":1,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":1,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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,"vehicleLight":0,"vehicleHeavy":0,"vehicleAir":0,"steamUUID":76561192214911200,"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

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