Compare commits

...

552 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
hardi a4f39dca16 Merge branch 'release/v1.6.11' of hardi/opt-cc into master 2018-02-16 16:49:03 +01:00
Florian Hartwich 82fdec3d62 update visible version # 2018-02-16 16:44:17 +01:00
Florian Hartwich ad3dd61c32 update ngx-chart for firefox cursor-pos fix; remove highscore box horizontal scrollbar as firefox fix 2018-02-16 16:43:39 +01:00
Florian Hartwich b49a17eecb Add test run todo for readme.md 2018-02-16 16:26:40 +01:00
Florian Hartwich d3f091253a revert hyperlink html change 2018-02-16 16:13:33 +01:00
Florian Hartwich 31c41f3bab replace markdown links with html for new window direct 2018-02-16 16:11:26 +01:00
Florian Hartwich 6ffc4de42a complete readme 2018-02-16 15:58:49 +01:00
Florian Hartwich bcfe6cf49a Add pm2 setup to 3rd party init 2018-02-16 15:34:31 +01:00
HardiReady 553dc492a6 Add automation 3rd party software install script 2018-02-11 12:26:53 +01:00
HardiReady eb3514c075 remove console log 2018-02-04 16:57:34 +01:00
hardi ea2c3e34ba Merge branch 'release/v1.6.10' of hardi/opt-cc into master 2018-02-04 16:38:57 +01:00
HardiReady e46896ed9b Update version 2018-02-04 16:37:15 +01:00
HardiReady b4729788ab Add ID validator; fix authentication secret usage 2018-02-04 16:36:42 +01:00
HardiReady ef65552c7b Update modules and apply logger use 2018-02-04 15:58:28 +01:00
HardiReady ebfaa7afb1 retrieve jws token from env config 2018-02-03 21:41:38 +01:00
hardi fdb81bdeda Remove old token from api config 2018-02-03 21:20:39 +01:00
hardi be824d3a24 update db schema doc 2018-02-03 17:18:47 +01:00
hardi 003cf39a9c Update 'README.md' 2018-02-03 12:55:38 +01:00
hardi 0907cb0902 Merge branch 'task/license-update' of hardi/opt-cc into master 2018-02-03 12:54:20 +01:00
hardi 38ce62f91a Update 'package.json' 2018-02-03 12:53:29 +01:00
hardi 3c60d0c9d4 Add static license file 2018-02-03 12:52:32 +01:00
hardi 13281f7f39 Add license file for API 2018-02-03 12:51:06 +01:00
hardi cd6f644fa5 Update 'README.md' 2018-02-03 12:45:55 +01:00
hardi 9fb865f4dc Merge branch 'release/v1.6.9' of hardi/opt-cc into master 2018-02-03 12:23:53 +01:00
HardiReady fcae4d57cd remove empty squads from army view; replace window.scroll with window.scrollTo() 2018-02-03 12:13:11 +01:00
HardiReady e2d522ac7c reactivate respawn parse 2018-02-03 11:34:46 +01:00
Florian Hartwich 5d1ac86b02 Fix war date and driver nptr; deactivate respawn log 2018-02-02 10:48:43 +01:00
HardiReady c4f1cc7713 remove console trace 2018-02-01 07:07:53 +01:00
HardiReady c6b9858420 Add new transport parsing to stats upload 2018-02-01 07:06:26 +01:00
HardiReady 8ae4999593 Add respawn to logparser processing 2018-01-28 17:45:54 +01:00
HardiReady 359fb4787c Fix bg change destroy condition 2018-01-21 11:50:22 +01:00
hardi 3b2325483d Merge branch 'release/v1.6.8' of hardi/opt-cc into master 2018-01-21 11:44:19 +01:00
HardiReady 5943a40576 suppress bg reloading on page change 2018-01-21 11:38:54 +01:00
HardiReady cdb63ba41b update version # 2018-01-16 19:47:07 +01:00
HardiReady 9dbef87dac Fix log parser (partially) & optimize design army overview 2018-01-16 19:46:28 +01:00
HardiReady cf7260a06d Add background for army overview 2018-01-13 15:03:12 +01:00
HardiReady 9c92c7713e Merge branch 'release/1.6.7' 2017-12-27 20:42:47 +01:00
HardiReady 4c20821fc8 fix field error for highscore player 2017-12-27 20:42:30 +01:00
hardi 3e78921372 Merge branch 'release/1.6.7' of hardi/opt-cc into master 2017-12-27 20:37:45 +01:00
HardiReady c2f2d72901 optimize highscore filter 2017-12-27 20:34:16 +01:00
HardiReady 9b9e2b9fa0 revert package-json to master 2017-12-27 20:12:24 +01:00
Florian Hartwich 1e83e0c8a8 reformat code 2017-12-27 12:50:49 +01:00
Florian Hartwich a18bedbc48 add AND filter functionality 2017-12-27 12:50:22 +01:00
Florian Hartwich 2daefe0e6c add observable for filter 2017-12-27 12:21:41 +01:00
Florian Hartwich 7239995ce3 add plazer highscore tables with filter 2017-12-26 17:09:49 +01:00
Florian Hartwich 23f1a61092 add first top5 table 2017-12-23 11:44:38 +01:00
Florian Hartwich 651401787e init data on highscore 2017-12-23 11:19:08 +01:00
Florian Hartwich d0bf863fb2 add empty Highscore page and routing 2017-12-23 10:59:27 +01:00
Florian Hartwich f72054f606 add new list entry for highscore 2017-12-23 10:41:36 +01:00
HardiReady 98412ca0ab finish basic ranking endpoint 2017-12-22 07:56:26 +01:00
hardi 43ff1e86dc Merge branch 'release/v1.6.6' of hardi/opt-cc into master 2017-12-10 16:00:57 +01:00
HardiReady 970e1fb643 Add pm2 start json 2017-12-10 15:49:57 +01:00
HardiReady 93ecfe3094 timestamp test 2017-12-10 15:43:30 +01:00
HardiReady 1b66ae83a5 use existing method for war end date; extend backup output 2017-12-10 11:24:07 +01:00
HardiReady b8ca870d8e Add scrollbar style 2017-12-10 01:04:34 +01:00
hardi 6fc0ba3a58 Merge branch 'release/v1.6.5' of hardi/opt-cc into master 2017-11-20 11:43:15 +01:00
HardiReady a32bb61020 change war list entry titzle width field 2017-11-19 15:10:14 +01:00
HardiReady 0bbda36239 remove empty column for trash symbol on missing access 2017-11-19 15:01:41 +01:00
HardiReady f749185eae rearrange header and use display dependent heights in scoreboard 2017-11-19 13:20:57 +01:00
HardiReady ca9420f3cf Use fixed position scroll for stats 2017-11-19 11:34:36 +01:00
HardiReady 4a836f4dee update ngx-datatable 2017-11-15 00:58:36 +01:00
HardiReady 7460a3cb55 reinsert rxjs functionalities import 2017-11-15 00:12:50 +01:00
hardi 407ed7b526 Merge branch 'release/v1.6.4' of hardi/opt-cc into master 2017-11-13 15:55:56 +01:00
HardiReady a588402431 remove unused css 2017-11-13 15:50:59 +01:00
HardiReady eeb166f0e9 Add campaign player detail as tab 2017-11-13 15:45:12 +01:00
HardiReady 2747ea41c6 Add player tab btn 2017-11-13 14:00:59 +01:00
HardiReady a74f1bd673 Refactor war detail components; Fix styles; Add Scoreboard additional btns 2017-11-13 13:49:47 +01:00
HardiReady cb71464462 Finish basic modulized war detail components 2017-11-13 00:06:39 +01:00
HardiReady c6d55011b6 Add extra component for fraction war stats 2017-11-12 23:32:31 +01:00
HardiReady 58bb5071a5 Mudulize war detail view 2017-11-12 19:27:26 +01:00
hardi 3dc348e5a5 Merge branch 'release/v1.6.3' of hardi/opt-cc into master 2017-11-10 15:18:28 +01:00
HardiReady d5df4b9a9f Fix player name parsing from Fraktionsuebersicht 2017-11-10 15:15:24 +01:00
HardiReady 15333858ac Merge branch 'master' of git.noarch.de:hardi/opt-cc into release/v1.6.3 2017-11-10 15:08:45 +01:00
HardiReady 668715c885 Downgrade ngx-datatable and add package-lock to git 2017-11-10 15:08:35 +01:00
HardiReady e4b3d45f13 remove logfile show in new window 2017-11-10 13:04:06 +01:00
HardiReady 99c1c69655 Remove white marks from blufor backplate 2017-11-10 01:01:55 +01:00
HardiReady 93f2e9f7fd Update signature backplate images 2017-11-09 17:07:53 +01:00
HardiReady de9504a6f3 Use fraction enum in users module 2017-11-08 19:40:51 +01:00
HardiReady 5f6be06516 Use fraction enum in squads module 2017-11-08 19:35:34 +01:00
HardiReady 67e57ac2b0 Use fraction enum in ranks module 2017-11-08 19:27:24 +01:00
HardiReady 761ac758ff Use fraction enum in decoration module 2017-11-08 19:23:15 +01:00
HardiReady 055ac797de Use fraction Enum in army component 2017-11-08 14:54:04 +01:00
HardiReady fa2c802e4e Use fraction Enum for admin panel and adjust account order 2017-11-08 14:44:32 +01:00
HardiReady 3330eaa87f Use fraction Enum for war detail 2017-11-08 14:37:13 +01:00
HardiReady 89aa585b21 Merge branch 'master' of git.noarch.de:hardi/opt-cc into release/v1.6.3 2017-11-08 14:00:46 +01:00
HardiReady fe1b4c4347 Update packages to get angular 5 working without AoT 2017-11-08 14:00:04 +01:00
HardiReady 205c4ffc3a Introduce Enum for fraction names;Add player detail tab 2017-11-07 14:02:49 +01:00
HardiReady 56a119f549 Remove console log 2017-11-06 14:24:31 +01:00
HardiReady 403b6d8f34 Use player map for scoreboard players 2017-11-06 14:22:18 +01:00
HardiReady 36a1166cae Update to angular v5.0.0 2017-11-06 09:35:33 +01:00
HardiReady cfe7f5729e Fix kill stats initialization 2017-11-04 21:16:15 +01:00
hardi 7f84b065fc Merge branch 'release/v1.6.2' of hardi/opt-cc into master 2017-11-04 21:08:17 +01:00
HardiReady 278e130614 Delete log db data on war delete 2017-11-04 21:05:41 +01:00
HardiReady ee6dc8bc5b Fix flag capture graph logic; Change initialization strategy 2017-11-04 20:46:05 +01:00
HardiReady e263ce050a simplify graph data loops 2017-11-04 15:58:48 +01:00
HardiReady 4ea1239a75 Use min interval for fraction stats 2017-11-03 21:31:03 +01:00
HardiReady 8f34d44139 Add interval dropdown 2017-11-03 14:43:04 +01:00
HardiReady 9198d59fa5 Switch to single fraction graph view 2017-11-03 12:51:58 +01:00
HardiReady 2852445edf Add both fraction values for each entry 2017-11-02 19:40:16 +01:00
HardiReady 54d2c257a7 wrap duplicated code and try to improve readability 2017-11-02 18:54:26 +01:00
HardiReady c27b14b0a5 move war date creation 2017-11-02 13:25:02 +01:00
HardiReady b1c7c705dd remove roundDomain for area chart; review date parse 2017-11-02 13:12:58 +01:00
HardiReady f393b61ca6 Merge branch 'release/1.6.1' 2017-11-02 11:50:38 +01:00
HardiReady 0e980aca52 Add timezone configuration 2017-11-02 11:50:21 +01:00
hardi 6a4ea225c4 Merge branch 'release/1.6.1' of hardi/opt-cc into master 2017-11-02 11:26:53 +01:00
HardiReady 89b9867d91 remove last curve input 2017-11-02 11:26:10 +01:00
HardiReady 9a45a43b7a remove useless date constructor 2017-11-02 11:22:27 +01:00
HardiReady b79ccbb1af rollback fract graphs to standard curve 2017-11-02 11:20:11 +01:00
HardiReady b7a0c47b32 Fix date util and log parse; Fix fract revive count 2017-11-02 11:17:16 +01:00
HardiReady c2b1b06d7b remove war submit console log 2017-10-30 09:44:49 +01:00
HardiReady e285e2b50a Fix player pie chart color & add new tables to backup 2017-10-30 09:43:12 +01:00
hardi e6f64c1cc6 Merge branch 'feature/stats-2.0' of hardi/opt-cc into master 2017-10-30 09:01:33 +01:00
HardiReady a37ca55a3b Optimize graph printing and data assignment 2017-10-30 08:59:08 +01:00
HardiReady efbc078aea Finish basic fraction war stats page 2017-10-29 17:36:55 +01:00
HardiReady 07b1c0534a Merge branch 'master' of git.noarch.de:hardi/opt-cc into feature/stats-2.0 2017-10-29 08:00:00 +01:00
HardiReady 34bf5b4f77 remove webpack from static 2017-10-29 07:25:30 +01:00
HardiReady 24f46b0f3c Add points and budget time graphs 2017-10-28 22:50:54 +02:00
HardiReady e294e3212e Add tabs 2017-10-28 20:25:58 +02:00
HardiReady 55a4662b40 fix log parser; readjust war detail view 2017-10-28 12:58:40 +02:00
HardiReady d3f3b3410a review utils 2017-10-26 19:12:54 +02:00
HardiReady 3ccdbe4da3 Add log time save with decimal 2017-10-24 20:21:06 +02:00
HardiReady b266d6f972 Add LogsService 2017-10-22 18:06:37 +02:00
HardiReady a8960506c6 simplify log router 2017-10-22 17:10:13 +02:00
HardiReady 75019a619f Fix first line parse error; Add file save; Add player sum-point-value 2017-10-22 14:13:58 +02:00
HardiReady fb5e5388f7 remove webpack installation to fix build 2017-10-22 00:17:28 +02:00
Florian Hartwich cd77c4b7f2 Add endpoints for log entries & add additional fraction fields for DOs 2017-10-21 19:49:24 +02:00
Florian Hartwich 443184027e Add mongoose schemas for logs 2017-10-21 18:23:04 +02:00
Florian Hartwich 91ebda52b0 compatible war upload page 2017-10-21 15:00:49 +02:00
Florian Hartwich 1acaba5a60 finish basci backend implementation 2017-10-21 14:54:58 +02:00
Florian Hartwich 35f6ac04b3 Finish parse tool 2017-10-21 12:11:32 +02:00
Florian Hartwich 2d9167b172 create player list & kill log parse 2017-10-21 11:32:25 +02:00
Florian Hartwich db48cd8ef6 Add revive and flag parsing 2017-10-21 10:41:49 +02:00
Florian Hartwich f1449e5047 Add parser for basic events 2017-10-20 23:42:41 +02:00
Florian Hartwich 371b62a0cc Fix top scrolling 2017-10-15 12:56:16 +02:00
558 changed files with 44080 additions and 9882 deletions

18
.gitignore vendored
View File

@ -4,13 +4,14 @@
dist/ dist/
tmp/ tmp/
etc/ etc/
server/apib/documentation.apib
# dependencies # dependencies
node_modules node_modules
package-lock.json
# IDEs and editors # IDEs and editors
/.idea .idea/
*/nbproject*
.project .project
.classpath .classpath
.c9/ .c9/
@ -43,14 +44,14 @@ testem.log
Thumbs.db Thumbs.db
.directory .directory
/public # Internal Data
/public/
resource/ server/resource/
mongodb-data/ server/apib/dredd/data/tmp-resource
backup/ backup/
!backup.sh
.idea/ # System
*/nbproject*
.npm/ .npm/
.bash_history .bash_history
.bash_logout .bash_logout
@ -58,4 +59,3 @@ backup/
.cache/motd.legal-displayed .cache/motd.legal-displayed
.profile .profile
.ssh/ .ssh/

View File

@ -1,11 +1,78 @@
# Operation Pandora Trigger Commandcenter # Operation Pandora Trigger Commandcenter
_MEAN Application_ A [MEAN Stack](http://mean.io/) application created for [https://www.opt4.net](https://www.opt4.net) Arma3 Community
## Installation ## Installation
All steps described here are working with a Debian based Linux system
### Setup required 3rd Party Software
#### Setup for Development
Run the installation script located in the docs folder:
```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!
For production setup run the script, described in _Setup for development_, adding the parameter `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.
## Development ## Development
### Run and Modify Application
**NOTE:** Do not use the execution described here in any production environment! It will make the running application highly vulnerable.
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 `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.
## Run Tests
_TODO_
## License Information ## License Information
### 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 6 Frontend (`/static`)
published under [MIT License](https://opensource.org/licenses/MIT)

View File

@ -1,20 +0,0 @@
module.exports = {
port: 8091,
secret: "$8h94j+8z4trh%nj+18tj!h4gf8zu&#418zf4zjt(16",
database: {
uri: 'mongodb://localhost:27017/',
db: 'cc',
},
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,14 +0,0 @@
"use strict";
const sortCollectionBy = (collection, key) => {
collection.sort((a, b) => {
a = a[key].toLowerCase();
b = b[key].toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
return collection;
};
exports.sortCollection = sortCollectionBy;

View File

@ -1,64 +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
}
}, {
collection: 'player',
timestamps: {createdAt: 'timestamp'}
});
// optional more indices
PlayerSchema.index({timestamp: 1});
module.exports = mongoose.model('Player', PlayerSchema);

View File

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

View File

@ -1,40 +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",
"private": true,
"scripts": {
"start": "NODE_ENV=production node server.js",
"dev": "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": "~2.2.0",
"express": "^4.16.1",
"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": "^4.12.0",
"morgan": "~1.6.1",
"multer": "^1.3.0",
"node-sha1": "^1.0.1",
"q": "^1.5.0",
"serve-favicon": "~2.3.0"
},
"devDependencies": {
"chai": "^3.5.0",
"chai-http": "^3.0.0",
"mocha": "^3.5.3",
"nodemon": "^1.12.1"
}
}

View File

@ -1,90 +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({}).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,86 +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 routerHandling = require('../middleware/router-handling');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkMT = require('../middleware/permission-check').checkMT;
// 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((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('../signature-tool/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,163 +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');
// 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((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,108 +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 = {
NATO: {
squads: []
},
CSAT: {
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);
}
const s = squad.toObject();
s.members = squadMembers;
s.memberCount = squadMembers.length;
if (s.fraction === 'BLUFOR') {
delete s.fraction;
armyOverview.NATO.squads.push(s);
countBlufor += s.members.length;
}
if (s.fraction === 'OPFOR') {
delete s.fraction;
armyOverview.CSAT.squads.push(s);
countOpfor += s.members.length;
}
callback();
});
});
}, (err) => {
if (err) {
return next(err);
}
armyOverview.NATO.memberCount = countBlufor;
armyOverview.CSAT.memberCount = countOpfor;
res.locals.items = armyOverview;
res.locals.processed = true;
next();
});
});
})
.all(
routerHandling.httpMethodNotAllowed
);
overview.use(routerHandling.emptyResponse);
module.exports = overview;

View File

@ -1,56 +0,0 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:wars');
// 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('/: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,159 +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');
// Mongoose Model using mongoDB
const RankModel = require('../models/rank');
const ranks = express.Router();
// add middleware for bonus tasks 4 and 5 to find filter and offset/limit params for GET / and GET /:id
// 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((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,57 +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')
.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,162 +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');
// 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((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,241 +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');
// 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((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,213 +0,0 @@
"use strict";
// modules
const fs = require('fs');
const mkdirp = require("mkdirp");
const {exec} = require('child_process');
const express = require('express');
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({storage: storage});
const logger = require('debug')('cc:wars');
// HTTP status codes by name
const codes = require('./http-codes');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkMT = require('../middleware/permission-check').checkMT;
const routerHandling = require('../middleware/router-handling');
// Mongoose Model using mongoDB
const CampaignModel = require('../models/campaign');
const WarModel = require('../models/war');
const PlayerModel = require('../models/player');
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) => {
let body = req.body;
let parts = body.date.split("-");
body.date = new Date(parseInt(parts[0], 10),
parseInt(parts[1], 10) - 1,
parseInt(parts[2], 10));
const war = new WarModel(body);
if (req.file) {
war.save((err, war) => {
if (err) {
return next(err);
}
const folderName = __dirname + '/../resource/logs/' + war._id;
mkdirp(folderName, function (err) {
if (err) {
return next(err);
}
fs.appendFile(folderName + '/war.log', new Buffer(req.file.buffer), (err) => {
if (err) {
return next(err);
}
exec(__dirname + '/../war-parser/run.sh ' + folderName + ' ' + war._id, (error, stdout) => {
if (error) {
return next(error);
}
exec(__dirname + '/../war-parser/clean.sh /../' + folderName + ' | tee ' + folderName + '/clean.log', (error) => {
if (error) {
return next(error);
}
let obj = JSON.parse(`${stdout}`);
for (let i = 0; i < obj.length; i++) {
if (!obj[i].fraction) {
obj.splice(i, 1);
} else if (obj[i].fraction === 'BLUFOR') {
war.playersBlufor++;
} else {
war.playersOpfor++;
}
}
WarModel.findByIdAndUpdate(war._id, war, {new: true}, (err, item) => {
if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
PlayerModel.create(obj, function (err) {
if (err) {
return next(err);
}
res.status(codes.created);
res.locals.items = war;
return next();
});
}
})
});
});
});
});
})
} else {
const err = new Error('no Logfile provided');
err.status = codes.wrongmediasend;
next(err);
}
})
.all(
routerHandling.httpMethodNotAllowed
);
wars.route('/:id')
.get((req, res, next) => {
WarModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
return next(err);
}
PlayerModel.find({warId: item._id}, {}, {sort: {kill: 'desc'}}, (err, items) => {
if (err) {
return next(err);
}
const responseObj = item.toObject();
responseObj.players = items;
res.locals.items = responseObj;
return next();
});
});
})
.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 players having this war ID as foreign key
PlayerModel.find({warId: 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,135 +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')('cc:server');
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');
// 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.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 + '/..'});
});
// (from express-generator boilerplate standard code)
// Errorhandling and requests without proper URLs ************************
// catch 404 and forward to error handler
app.use((req, res, next) => {
debug('Catching unmatched request to answer with 404');
let err = new Error('Not Found');
err.status = 404;
next(err);
});
// 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, {
useMongoClient: true
});
mongoosePromise.then((db) => {
app.listen(config.port, (err) => {
if (err !== undefined) {
debug('Error on startup, ', err);
}
else {
debug('Listening on port ' + config.port);
signatureCronJob.start();
backupCronJob.start();
}
});
})
} else {
const mongoosePromise = mongoose.connect(config.database.uri + config.test.db, {
useMongoClient: true
});
mongoosePromise.then((db) => {
app.listen(config.test.port);
console.log('Listening on port ' + config.test.port);
})
}
module.exports = app;

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,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

@ -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();
});
});
});
});

View File

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

View File

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

View File

@ -4,18 +4,18 @@
cd $(dirname $0) cd $(dirname $0)
# array of available collection names # array of available collection names
col=(app_user awarding decoration rank squad user promotion player war campaign) 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" ] if [ -z "$1" ]
then then
DATE=$(date '+%Y-%m-%d') DATE=$(date '+%Y-%m-%d')
mkdir -p ${DATE}/resource mkdir -p ${DATE}/resource
cp -R ../api/resource/ ${DATE}/ cp -R ../server/resource/ ${DATE}/
else else
DATE=${1} DATE=${1}
tar -xzf ${DATE}.tar.gz tar -xzf ${DATE}.tar.gz
rm -rf ../api/resource rm -rf ../server/resource
cp -Rv ${DATE}/resource ../api/ cp -Rv ${DATE}/resource ../server/
fi fi
for i in "${col[@]}" for i in "${col[@]}"
@ -23,6 +23,7 @@ do
# provide date for restore process, if data import is needed # provide date for restore process, if data import is needed
if [ -z "$1" ] if [ -z "$1" ]
then then
(>&2 echo -e "$(date "+%Y-%m-%dT%T.%3N%z")\tTable: ${i}")
mongoexport --db cc --collection $i --out ${DATE}/collections/${i}.json; mongoexport --db cc --collection $i --out ${DATE}/collections/${i}.json;
else else
mongoimport --db cc --collection $i --drop --file ${1}/collections/${i}.json mongoimport --db cc --collection $i --drop --file ${1}/collections/${i}.json

50
docs/infra/3rd-party-install.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
cd $(dirname $0)
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
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse" | \
sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list
# install from apt
sudo apt-get update
sudo apt-get install -y mongodb-org npm
# enable mongod for autostart
sudo systemctl enable mongod.service
# install latest node version
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
source ~/.nvm/nvm.sh
nvm install stable
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 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>

View File

@ -1,4 +1,3 @@
server { server {
listen 80; listen 80;
server_name cc.noarch.de; server_name cc.noarch.de;
@ -9,6 +8,8 @@ server {
listen 443 ssl; listen 443 ssl;
server_name cc.noarch.de; server_name cc.noarch.de;
server_tokens off;
ssl on; ssl on;
ssl_certificate /etc/letsencrypt/live/noarch.de/fullchain.pem; ssl_certificate /etc/letsencrypt/live/noarch.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/noarch.de/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/noarch.de/privkey.pem;

Binary file not shown.

745
package-lock.json generated Normal file
View File

@ -0,0 +1,745 @@
{
"name": "opt-cc",
"version": "1.9.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ajv": {
"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.0"
}
},
"ansi-styles": {
"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",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
"dev": true
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"aws4": {
"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.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.3"
}
},
"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",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"chalk": {
"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": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"dependencies": {
"supports-color": {
"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"
}
}
}
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true
},
"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": {
"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": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
"integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=",
"dev": true
},
"concurrently": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.0.tgz",
"integrity": "sha512-6XiIYtYzmGEccNZFkih5JOH92jLA4ulZArAYy5j1uDSdrPLB3KzdE8GW7t2fHPcg9ry2+5LP9IEYzXzxw9lFdA==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
"commander": "2.6.0",
"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.1.0"
}
},
"core-js": {
"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": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"date-fns": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
"integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==",
"dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"dev": true,
"optional": true,
"requires": {
"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": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
"dev": true
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
},
"fast-deep-equal": {
"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": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true
},
"form-data": {
"version": "2.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.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"requires": {
"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",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true,
"requires": {
"ajv": "^5.1.0",
"har-schema": "^2.0.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"hoek": {
"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": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
"isemail": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz",
"integrity": "sha1-A1PT2aYpUQgMJiwqoKQrjqjp4qY=",
"dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"items": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/items/-/items-2.1.1.tgz",
"integrity": "sha1-i9FtnIOxlSneWuoyGsqtp4NkoZg=",
"dev": true
},
"joi": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-9.2.0.tgz",
"integrity": "sha1-M4WseQGSEwy+Iw6ALsAskhW7/to=",
"dev": true,
"requires": {
"hoek": "4.x.x",
"isemail": "2.x.x",
"items": "2.x.x",
"moment": "2.x.x",
"topo": "2.x.x"
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"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",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
"dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"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.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"mime-db": {
"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.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.33.0"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"moment": {
"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",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
"dev": true
},
"qs": {
"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
},
"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": {
"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": {
"version": "2.3.24",
"resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz",
"integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=",
"dev": true
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"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",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
"dev": true
},
"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": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"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
},
"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": {
"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"
},
"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": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz",
"integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=",
"dev": true,
"requires": {
"hoek": "4.x.x"
}
},
"tough-cookie": {
"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"
}
},
"tree-kill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz",
"integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==",
"dev": true
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true,
"optional": true
},
"uuid": {
"version": "3.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",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"wait-on": {
"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.4.1",
"joi": "^9.2.0",
"minimist": "^1.2.0",
"request": "^2.78.0",
"rx": "^4.1.0"
},
"dependencies": {
"rx": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
"integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=",
"dev": true
}
}
}
}
}

View File

@ -1,19 +1,23 @@
{ {
"name": "opt-cc", "name": "opt-cc",
"version": "1.5.4", "version": "1.9.7",
"license": "MIT", "author": "Florian Hartwich <hardi@noarch.de>",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "npm run deploy-static-prod && npm start --prefix ./api", "start": "npm run deploy-static-prod && npm start --prefix ./server",
"dev": "npm run deploy-static && npm run dev --prefix ./api", "dev": "npm run deploy-static && npm run dev --prefix ./server",
"deploy-static": "cd ./static && $(npm bin)/ng build && ln -s ../api/resource/ ../public/resource", "pre-deploy-clean": "rm -f ./public/resource",
"deploy-static-prod": "cd ./static && $(npm bin)/ng build --prod --aot && ln -s ../api/resource/ ../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",
"postinstall": "npm install --prefix ./static && npm install --prefix ./api", "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",
"mongodb": "mkdir -p mongodb-data && mongod --dbpath ./mongodb-data", "deploy-static:link-resource": "ln -s ../server/resource/ public/resource",
"test": "npm test --prefix ./api", "deploy-static:api-docs": "npm run api:docs --prefix=server",
"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", "postinstall": "npm install --prefix ./static && npm install --prefix ./server",
"start-e2e": "npm run deploy-static && npm run e2e --prefix ./api", "lint": "npm run lint --prefix=server && npm run lint --prefix=static",
"test-e2e": "npm run e2e --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": {}, "dependencies": {},
"devDependencies": { "devDependencies": {

15
pm2-start.json Normal file
View File

@ -0,0 +1,15 @@
{
"apps": [
{
"name": "opt-cc",
"script": "./server/server.js",
"watch": false,
"env": {
"NODE_ENV": "production",
"DEBUG": "cc:*",
"TZ": "Europe/Berlin",
"JWS_SECRET": "secret"
}
}
]
}

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
}],
}
};

431
server/LICENSE.txt Normal file
View File

@ -0,0 +1,431 @@
Attribution-ShareAlike 4.0 International
applies to:
- Express server setup (modified), pagination and mongoDB connection setup, Copyright (c) Johannes Konert
- Express endpoints, signature image builder and Arma3 RPT log parser (c) 2018 "HardiReady" Florian Hartwich
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.

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

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