Compare commits

..

209 Commits

Author SHA1 Message Date
niggl 88844e1a44 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 15:53:45 +00:00
niggl e76a9cef95 Merge pull request 'Release 0.7.1' (#173) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #173
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-26 15:52:41 +00:00
niggl 3ef3a94b20 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-26 14:24:07 +00:00
niggl 135852eb9a 🚀Bumped version to v0.7.1
continuous-integration/drone/push Build is passing
2021-03-26 15:23:05 +01:00
niggl 963253cbc8 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 14:22:55 +00:00
niggl 539a6509b1 Merge pull request 'RESPONSERUNNERCARD fix bugfix/171-responserunnercards' (#172) from bugfix/171-responserunnercards into dev
continuous-integration/drone/push Build is failing
Thx @philipp :)

Reviewed-on: #172
2021-03-26 14:22:15 +00:00
niggl f3d73d5346 Tests now keep the group
continuous-integration/drone/pr Build is passing
ref #171
2021-03-26 15:17:05 +01:00
niggl f159252651 Revert "Set timeout even higher b/c sqlite just kills itself during these tests"
continuous-integration/drone/pr Build is failing
This reverts commit 6ab60998d4.
2021-03-26 15:14:17 +01:00
niggl 6ab60998d4 Set timeout even higher b/c sqlite just kills itself during these tests
continuous-integration/drone/pr Build is failing
ref #171
2021-03-26 15:13:31 +01:00
niggl 30d220bc36 Adjusted jest timeout to mitigate sqlite from invalidateing all tests⏱
continuous-integration/drone/pr Build is failing
ref #171
2021-03-26 15:06:20 +01:00
niggl 24aff3bac4 Now resolveing runnercards
ref #171
2021-03-26 14:56:21 +01:00
niggl ce63043887 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-23 17:50:46 +00:00
niggl e40017a6b8 Merge pull request 'Release 0.7.0' (#170) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #170
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-23 17:49:45 +00:00
niggl e843a464e7 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is failing
2021-03-23 17:44:04 +00:00
niggl d0ae50d557 🚀Bumped version to v0.7.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-03-23 18:42:16 +01:00
niggl 7a49e7c5c9 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-23 17:42:14 +00:00
niggl 1dd64204cc Merge pull request 'Bulk card creation feature/168-runnercards_bulk' (#169) from feature/168-runnercards_bulk into dev
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
Reviewed-on: #169
2021-03-23 17:41:31 +00:00
niggl 438ff0fc3f Added bulk card creation tests
continuous-integration/drone/pr Build is passing
ref #168
2021-03-23 18:19:49 +01:00
niggl c1bbda51f0 Added new "bulk" endpoint
ref #168
2021-03-23 18:16:55 +01:00
niggl 4705a39aab 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-19 16:41:43 +00:00
niggl 4d721f62d9 Merge pull request 'Release 0.6.4' (#167) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #167
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-19 16:40:45 +00:00
niggl b0328ffdaf 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-19 16:34:14 +00:00
niggl 031cede542 🚀Bumped version to v0.6.4
continuous-integration/drone/push Build is passing
2021-03-19 17:33:17 +01:00
niggl 3c69f8c4a8 Adjsuted endpoint 2021-03-19 17:32:46 +01:00
niggl cc6568c381 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-18 15:54:33 +00:00
niggl a3a1395a46 Merge pull request 'Release 0.6.3' (#165) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #165
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-18 15:53:42 +00:00
niggl b08acc6660 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-18 15:11:01 +00:00
niggl 7a303c2b2c 🚀Bumped version to v0.6.3
continuous-integration/drone/push Build is passing
2021-03-18 16:10:02 +01:00
niggl 3f9a7049e3 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-18 15:09:50 +00:00
niggl 6249419fae Merge pull request 'TrackScan Update bug 🐞bugfix/163-trackscan_updates' (#164) from bugfix/163-trackscan_updates into dev
continuous-integration/drone/push Build is failing
Reviewed-on: #164
2021-03-18 15:09:08 +00:00
niggl f347b7ad49 Updated tests 🧪
continuous-integration/drone/pr Build is passing
ref #163
2021-03-18 14:05:13 +01:00
niggl 74faec85c8 Merge branch 'bugfix/163-trackscan_updates' of git.odit.services:lfk/backend into bugfix/163-trackscan_updates 2021-03-18 14:00:16 +01:00
niggl fbdadbef1f The basic bugfix 🐞
ref #163
2021-03-18 14:00:14 +01:00
niggl c87c97c90f The basic bugfix 🐞
REF '!&§
2021-03-18 14:00:04 +01:00
niggl a6bca59ffe 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 18:44:42 +00:00
niggl 732a1b88d9 Merge pull request 'Release 0.6.2' (#162) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #162
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-17 18:43:45 +00:00
niggl 4c960feeb2 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-17 18:42:50 +00:00
niggl 72fee96a08 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build is passing
2021-03-17 19:41:41 +01:00
niggl fcb43f92b0 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 18:41:40 +00:00
niggl 5ba8f1dd44 🚀Bumped version to v0.6.2 2021-03-17 19:41:35 +01:00
niggl 3d3790c2eb Merge pull request 'Bugfixes for trackscans feature/160-responseTrackScan_total_distance' (#161) from feature/160-responseTrackScan_total_distance into dev
continuous-integration/drone/push Build is failing
Reviewed-on: #161
2021-03-17 18:40:47 +00:00
niggl 1fa3fa75ee Fixed wrong error type 👀👀
continuous-integration/drone/pr Build is passing
ref #160
2021-03-17 19:37:35 +01:00
niggl c8882ae6a1 Removed duplicate openapi declarations 🗑
continuous-integration/drone/pr Build is failing
ref #160
2021-03-17 19:33:44 +01:00
niggl 673e896aa3 Added missing discription
ref #160
2021-03-17 19:31:09 +01:00
niggl 0ed7f78b2c Fixed missing renameing🛠
ref #160
2021-03-17 19:30:08 +01:00
niggl 1d38d308ad Changed the method of getting a parameter from the headers🛠
ref #160
2021-03-17 19:29:12 +01:00
niggl d709ee7479 Now defining security per endpoint 🔐
ref #160
2021-03-17 19:25:49 +01:00
niggl aae042c041 Now auto-etting the station token🔥🔥🔥
ref #160
2021-03-17 19:24:25 +01:00
niggl ca7a84eb3e Merge branch 'feature/160-responseTrackScan_total_distance' of git.odit.services:lfk/backend into feature/160-responseTrackScan_total_distance 2021-03-17 19:22:09 +01:00
niggl 1f32ed0727 Marked station as optional (quality of life improvements incoming)
ref #160
2021-03-17 19:22:04 +01:00
niggl 289f9e2196 Added comments✏
ref #160
2021-03-17 19:21:35 +01:00
niggl 937a9fad4d Added comments✏
ref #150
2021-03-17 19:21:29 +01:00
niggl 7c3a1b8fff Merge branch 'dev' into feature/160-responseTrackScan_total_distance 2021-03-17 19:16:54 +01:00
niggl a8ea4fa659 Fixed trackscan vaildation
ref #160
2021-03-17 19:16:42 +01:00
niggl c1dd4518d1 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 17:59:10 +00:00
niggl bdc7bb67e7 Merge pull request 'Release v0.6.0' (#159) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #159
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-17 17:58:20 +00:00
niggl 54988ba0fe 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-17 16:41:01 +00:00
niggl ce3ca9f1c8 🚀Bumped version to v0.6.1
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-03-17 17:40:13 +01:00
niggl 46b7aceb0b Scanauth return objects 2021-03-17 17:40:01 +01:00
niggl 486e450a58 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-17 16:13:23 +00:00
niggl 623b5a1873 🚀Bumped version to v0.6.0
continuous-integration/drone/push Build is passing
2021-03-17 17:12:01 +01:00
niggl a7958eecd6 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 15:52:53 +00:00
niggl 13e839902c Merge pull request 'Scanstation "me" endpoint feature/157-scanstation_me' (#158) from feature/157-scanstation_me into dev
continuous-integration/drone/push Build is passing
Reviewed-on: #158
2021-03-17 15:52:02 +00:00
niggl 94001a48f1 Updated description
continuous-integration/drone/pr Build is passing
ref #157
2021-03-17 16:50:48 +01:00
niggl 2cb7ec7317 As requested by @philpp
continuous-integration/drone/pr Build is passing
ref #157
2021-03-17 16:49:19 +01:00
niggl 757332ed2b Added tests for the new endpoint
continuous-integration/drone/pr Build is passing
ref #157
2021-03-17 11:51:31 +01:00
niggl 8ba7ee1d48 Now adding station id to headers of request for scan auth
ref #157
2021-03-17 11:45:40 +01:00
niggl c5178e0181 Added scanstation me endpoint
ref #157
2021-03-17 11:44:54 +01:00
niggl a1a94ec9da 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-15 15:03:59 +00:00
niggl f7af777104 Applied Docker MTU fix 🛠
continuous-integration/drone/push Build is passing
2021-03-15 16:03:15 +01:00
niggl 076aa87dba 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-12 19:21:08 +00:00
niggl ca6fa633a1 Revert "Switched normal images to chached registry"
continuous-integration/drone/push Build was killed
This reverts commit cba4455d53.
2021-03-12 20:20:46 +01:00
niggl 641e2aed52 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build was killed
2021-03-12 20:09:36 +01:00
niggl cba4455d53 Switched normal images to chached registry 2021-03-12 20:09:26 +01:00
niggl d5930f7c46 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-12 18:03:04 +00:00
niggl 5541ae6ebd Updated ci with new kubernetes secrets 🚀🚀🚀
continuous-integration/drone/push Build was killed
ref odit/org#12
2021-03-12 19:02:45 +01:00
niggl 6c43872198 Changed ci pipeline type to kubernetes
continuous-integration/drone/push Build encountered an error
ref odit/org#12 (comment)
2021-03-10 20:07:46 +01:00
niggl e4ed20da3e 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-06 13:22:37 +00:00
niggl cb6e78fc17 Merge pull request 'selfservice forgotten mails feature/154-selfservice_forgotten' (#155) from feature/154-selfservice_forgotten into dev
continuous-integration/drone/push Build is passing
Reviewed-on: #155
2021-03-06 13:22:16 +00:00
niggl bf1ec976e3 Added selfservice forgott positive tests
continuous-integration/drone/pr Build was killed
ref #154
2021-03-06 14:12:46 +01:00
niggl d0a7e34de8 Added all "negative" tests
ref #154
2021-03-06 14:08:39 +01:00
niggl 08957d4dc2 Updated to new responsetype
ref #154
2021-03-06 14:02:58 +01:00
niggl 1d762f5662 Renamed test
ref #154
2021-03-06 13:57:25 +01:00
niggl a95a9b4ec4 Added first selfservice forgotten test
ref #154
2021-03-06 13:56:55 +01:00
niggl e5dab3469c Changed endpoint url to avoid conflicts
ref #154
2021-03-06 13:56:34 +01:00
niggl c01233b4d6 Added console logging when a testing env get's discovered
ref #154
2021-03-06 13:51:24 +01:00
niggl 92920273be Adjusted tests for the new testing env
ref #154
2021-03-06 13:51:07 +01:00
niggl 6bb3ae8ba9 Mailer now ignores mailing erros when env is set to test
ref #154
2021-03-06 13:44:10 +01:00
niggl cedc1750c2 Added readme description for testing env
ref #154
2021-03-06 13:42:10 +01:00
niggl 3f372123fd Added testing env check
ref #154
2021-03-06 13:41:10 +01:00
niggl a3437475ca Runner controller now uses the Mailer functions
ref #154
2021-03-06 13:38:37 +01:00
niggl 83765136cc Added mailer functions
ref #154
2021-03-06 13:36:12 +01:00
niggl e26b7d4923 Implemented the "real" errors
ref #154
2021-03-06 13:32:36 +01:00
niggl e7f0cb45c9 Added not found error logic
ref #154
2021-03-06 13:29:44 +01:00
niggl ffcd45e572 Updated request timeout
ref #154
2021-03-06 13:24:43 +01:00
niggl d7099717c2 Created basic endpoint for user forgotten mails
ref #154
2021-03-06 13:23:01 +01:00
niggl 66d6023335 Added last reset requested timestamp to runners
ref #154
2021-03-06 13:15:27 +01:00
niggl 5f5c8a061e 📖New license file version [CI SKIP] [skip ci] 2021-03-04 16:27:55 +00:00
niggl bf71e35ecd 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-04 16:23:18 +00:00
niggl 64da0eadb3 Merge pull request 'Alpha Release 0.5.0' (#153) from dev into main
continuous-integration/drone/tag Build is failing
continuous-integration/drone/push Build was killed
Reviewed-on: #153
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-04 16:22:36 +00:00
niggl 52728290b4 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-03-04 16:07:53 +00:00
niggl 3f2a2d2929 🚀Bumped version to v0.5.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build was killed
2021-03-04 17:03:51 +01:00
niggl f1d85cfb85 🚀Bumped version to v0.4.7 2021-03-04 17:03:41 +01:00
niggl 15356c1030 Merge pull request 'Features for the new selfservice feature/151-selfservice_scans_mails' (#152) from feature/151-selfservice_scans_mails into dev
continuous-integration/drone/push Build was killed
Reviewed-on: #152
2021-03-04 16:03:04 +00:00
niggl 82c65b632c Added scans returns 200 test
continuous-integration/drone/pr Build is passing
ref #151
2021-03-04 16:40:05 +01:00
niggl ae7d617690 Updated auth reset test for new mailer
ref #151
2021-03-04 16:35:30 +01:00
niggl bf6b70106e Now generateing bs mailer config in test env
ref #151
2021-03-04 16:32:06 +01:00
niggl 33310cdb44 Merge branch 'feature/151-selfservice_scans_mails' of git.odit.services:lfk/backend into feature/151-selfservice_scans_mails 2021-03-04 16:29:46 +01:00
niggl db58a280b3 Updated readme env section
ref #151
2021-03-04 16:29:44 +01:00
niggl 149f3a83b2 Updated readme env section
ref #151
2021-03-04 16:28:13 +01:00
niggl a5d2a6ecd3 Added locale to pw reset endpoint
ref #151
2021-03-04 16:25:23 +01:00
niggl bb9bad6d90 Now checking for mails being set
ref #151
2021-03-04 16:19:19 +01:00
niggl ada679823c Removed useless functions and updated comments
ref #151
2021-03-04 16:13:36 +01:00
niggl 9a1678acf0 Now using mailer as static funtion
ref #151
2021-03-04 16:11:35 +01:00
niggl 485c247cd3 Removed (now useless) mail controller
ref #151
2021-03-04 16:11:21 +01:00
niggl ddea02db57 Added new mailer settings to config
ref #151
2021-03-04 16:11:05 +01:00
niggl 1551a444ba Added the new mailer code
ref #151
2021-03-04 16:10:43 +01:00
niggl f289afd8bc Updated mail errors
ref #151
2021-03-04 16:10:26 +01:00
niggl a9e06c9055 Promoted axios to dependency
ref #151
2021-03-04 15:56:24 +01:00
niggl c2fdfeed4f Removed mail templates
ref #151
2021-03-04 15:54:47 +01:00
niggl 0342757d92 Removed mail config
ref #151
2021-03-04 15:54:27 +01:00
niggl 5833f4218f Removed nodemailer from backend
ref #151
2021-03-04 15:52:20 +01:00
niggl 0fcc729b56 Removed old mailer code
ref #151
2021-03-04 15:51:05 +01:00
niggl a2c97a11a3 Laptime is now a part of the response
ref #151
2021-03-01 16:59:36 +01:00
niggl aa833736d3 Trackscans now have a laptime that get's calculated on creation
ref #151
2021-03-01 16:58:34 +01:00
niggl 771a205fe6 Added new selfservice scans endpoint
ref #151
2021-03-01 16:49:37 +01:00
niggl 6074ac5b3a Added selfservice scan response class
ref #151
2021-03-01 16:47:54 +01:00
niggl 030b2255d4 Added another resonse type
ref #151
2021-03-01 16:47:05 +01:00
niggl f7f6df41ff Added new selfservice response type
ref #151
2021-03-01 16:36:36 +01:00
niggl be397c8899 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-26 16:50:45 +00:00
niggl dd3c9275d6 Merge pull request 'Alpha Release 0.4.6' (#148) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #148
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-26 16:50:03 +00:00
niggl 764b7ffe00 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-26 16:48:33 +00:00
niggl d870b2fd01 Merge pull request 'Fixed wrong body acceptance type' (#150) from bugfix/146-usergroup_update into dev
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #150
2021-02-26 16:48:16 +00:00
niggl aaec09d2ab Fixed wrong body acceptance type
continuous-integration/drone/pr Build is passing
ref #146
2021-02-26 17:32:26 +01:00
niggl bce8811925 📖New license file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-25 19:41:04 +00:00
niggl 3afc207903 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-25 19:39:39 +00:00
niggl fca997beb8 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-02-25 20:39:21 +01:00
niggl 39ebfbf0b6 Pinned package version to avoid dependency conflicts 📌
yeet
2021-02-25 20:39:16 +01:00
niggl 3736b29e54 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-25 19:33:24 +00:00
niggl b4c9369a53 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is passing
2021-02-25 20:33:04 +01:00
niggl 5d6c8c957a Quick bugfix 2021-02-25 20:33:00 +01:00
niggl 09fe47b9aa 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-25 19:30:37 +00:00
niggl b4acd157fc 🚀Bumped version to v0.4.6
continuous-integration/drone/push Build is failing
2021-02-25 20:30:16 +01:00
niggl b1fced7764 📖New license file version [CI SKIP] [skip ci] 2021-02-24 20:00:08 +00:00
niggl c0cafb4d51 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-24 19:59:05 +00:00
niggl 45d61b487e Merge pull request 'New org selfservice endpoint feature/146-more_selfservice_endpoints' (#147) from feature/146-more_selfservice_endpoints into dev
continuous-integration/drone/push Build is failing
Reviewed-on: #147
2021-02-24 19:58:47 +00:00
niggl 28ef139a70 Added tests for the new org selfservice endpoints
continuous-integration/drone/pr Build is passing
ref #146
2021-02-24 18:32:56 +01:00
niggl 656f63dfd5 Added selfservice org info endpoint
ref #146
2021-02-24 18:23:08 +01:00
niggl ba3b5eeefc Added selfservice org response model
ref #146
2021-02-24 18:20:31 +01:00
niggl ba396e0eba Added selfservice team response model
ref #146
2021-02-24 18:18:10 +01:00
niggl 3c11d88557 Added new response types
ref #146
2021-02-24 18:17:30 +01:00
niggl 305fa0078d 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 18:25:18 +00:00
niggl a46d14278b Merge pull request 'Alpha release 0.4.5' (#145) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #145
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-09 18:24:30 +00:00
niggl 680ae8ebbb 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-09 18:19:21 +00:00
niggl cc869f69ad 🚀Bumped version to v0.4.5
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-02-09 19:18:46 +01:00
niggl b9aac71676 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-09 18:18:22 +00:00
niggl a30a342e00 Merge pull request 'usergroups/permissions endpoint feature/143-usergroup_permissions_endpoint' (#144) from feature/143-usergroup_permissions_endpoint into dev
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
Reviewed-on: #144
2021-02-09 18:18:04 +00:00
niggl bdcfce88cb Now all /usergroups endpoints return ResponseUserGroup
continuous-integration/drone/pr Build is passing
ref #143
2021-02-09 18:27:11 +01:00
niggl dd81f4c7e4 Merge branch 'feature/143-usergroup_permissions_endpoint' of git.odit.services:lfk/backend into feature/143-usergroup_permissions_endpoint
# Conflicts:
#	src/models/responses/ResponseUserGroup.ts
2021-02-09 18:26:33 +01:00
niggl 416f2a1366 The ResponseUserGroup now returns their permisssions as a string array
ref #143
2021-02-09 18:26:22 +01:00
niggl 5e353db206 The ResponseUserGroup now returns their permisssions as a string array
ref #143
2021-02-09 18:26:16 +01:00
niggl 0c9867d706 Implemented /groups/permissions endpoint
ref #143
2021-02-09 18:18:00 +01:00
niggl 8379c3e29c 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-09 16:47:57 +00:00
niggl c4edccace7 Merge pull request 'Alpha release 0.4.4' (#142) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #142
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-09 16:47:31 +00:00
niggl 74de6559d7 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-08 16:34:37 +00:00
niggl a6f73c733c 🚀Bumped version to v0.4.4
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-02-08 17:34:03 +01:00
niggl ca3d093e54 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-08 16:33:37 +00:00
niggl 28cfbaa662 Merge pull request 'Expanded runner response feature/140-runner_group_parent' (#141) from feature/140-runner_group_parent into dev
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
Reviewed-on: #141
2021-02-08 16:33:21 +00:00
niggl 90e1ad7db7 Adjusted test for the new response depth
continuous-integration/drone/pr Build is passing
ref #140
2021-02-08 16:51:09 +01:00
niggl 906a1dc9e7 The group/runners endpoints now also deliver the runner's group's parentGroup
ref #140
2021-02-08 16:48:20 +01:00
niggl 5872c6335b Adjusted test for the new response depth
ref #140
2021-02-08 16:46:07 +01:00
niggl 701706c028 Now loading runner's group's parentgroup with every runner controller request
ref #140
2021-02-08 16:40:17 +01:00
niggl 09bbc70f5f 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-07 12:43:57 +00:00
niggl dd9cb6d3ef Merge pull request 'Alpha Release 0.4.3' (#139) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #139
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-07 12:43:30 +00:00
niggl 23c732b690 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-07 12:40:54 +00:00
niggl 656d564baa 🚀Bumped version to v0.4.3
continuous-integration/drone/push Build is passing
2021-02-07 13:40:32 +01:00
philipp f3f5cb462e 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 12:38:54 +00:00
philipp 9959172f2a Merge pull request 'Bugfix for @lfk/frontend/#43' (#138) from bugfix/118-encode_jwt_in_mail into dev
continuous-integration/drone/push Build is passing
Reviewed-on: #138
2021-02-07 12:38:34 +00:00
niggl 8f0a396dd0 Bugfix for @lfk/frontend/#43
continuous-integration/drone/pr Build is passing
2021-02-07 13:37:01 +01:00
niggl a18d4d3cee 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-02 15:02:34 +00:00
niggl 390b36dfd4 Merge pull request 'Alpha Release 0.4.2' (#137) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #137
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-02 15:02:08 +00:00
niggl 3b718f3ce5 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-02-02 14:46:03 +00:00
niggl 321b20b073 🚀Bumped version to v0.4.2
continuous-integration/drone/push Build is passing
2021-02-02 15:45:41 +01:00
niggl f7a0ec7174 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-02 14:45:15 +00:00
niggl 110a84783e Merge pull request 'Imprint&Privacy Links feature/135-imprint_and_privacy' (#136) from feature/135-imprint_and_privacy into dev
continuous-integration/drone/push Build is failing
Reviewed-on: #136
2021-02-02 14:45:00 +00:00
niggl 333e806da4 Added documentation about the new env vars to the readme
continuous-integration/drone/pr Build is passing
ref #135
2021-02-02 15:10:04 +01:00
niggl f4f621973a Added imprint and privacy to the api spec
ref #135
2021-02-02 15:07:33 +01:00
niggl bcad691045 Added new url env vars to config
ref #135
2021-02-02 15:04:09 +01:00
niggl 74791df68b 📖New license file version [CI SKIP] [skip ci] 2021-01-30 17:13:12 +00:00
niggl 8425043099 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 17:12:25 +00:00
niggl 74b982afba fixed license-exporter call
continuous-integration/drone/push Build is passing
2021-01-30 18:12:09 +01:00
niggl 3aefa75412 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 16:00:16 +00:00
niggl 71cab4e836 Merge pull request 'Alpha Release 0.4.1' (#134) from dev into main
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #134
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-01-30 15:59:51 +00:00
niggl 4e10077901 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing
2021-01-30 15:54:03 +00:00
niggl c32fa93673 🚀Bumped version to v0.4.1
continuous-integration/drone/push Build is passing
2021-01-30 16:53:38 +01:00
niggl 3d1baae0cc Dependency bump🔝 [skip ci] 2021-01-30 16:53:21 +01:00
niggl 94dd7963b7 Deleted useless file [ci skip] 2021-01-30 16:44:39 +01:00
niggl 7ba67b9dca 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 15:40:39 +00:00
niggl 6e5f1bd5ff Merge pull request 'Response object types feature/132-object_types' (#133) from feature/132-object_types into dev
continuous-integration/drone/push Build is passing
Reviewed-on: #133
2021-01-30 15:40:24 +00:00
niggl 60ee6ebc1e Merge branch 'dev' into feature/132-object_types
continuous-integration/drone/pr Build is passing
2021-01-30 15:32:20 +00:00
niggl 02295346da 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 15:27:16 +00:00
niggl ff7406e71a Cleaned up realations regarding response classes
continuous-integration/drone/pr Build is passing
ref #132
2021-01-30 16:19:42 +01:00
niggl 8dc2810c0c Adjusted tests for the new responseType parameter (part 3)
ref #132
2021-01-30 16:19:20 +01:00
niggl ff8af090e3 Adjusted tests for the new responseType parameter (part 2)
ref #132
2021-01-30 15:39:39 +01:00
niggl bcc15e4286 Adjusted tests for the new responseType parameter (part 1)
ref #132
2021-01-29 18:46:15 +01:00
niggl 2a87819486 Fixed typos and missing types
ref #132
2021-01-29 17:06:57 +01:00
niggl 9d5e486c6d Implemented the interface in all responses
refr #132
2021-01-29 17:06:43 +01:00
niggl e44cc4c4cb Added a Response interface
ref #132
2021-01-29 16:49:19 +01:00
niggl 581ca5ff6c Added Responseobjecttype enum
ref #132
2021-01-29 16:45:23 +01:00
124 changed files with 1800 additions and 6708 deletions
+37 -12
View File
@@ -1,5 +1,27 @@
---
kind: secret
name: docker_username
get:
path: odit-registry-builder
name: username
---
kind: secret
name: docker_password
get:
path: odit-registry-builder
name: password
---
kind: secret
name: git_ssh
get:
path: odit-git-bot
name: sshkey
---
kind: pipeline
type: kubernetes
name: tests:node_latest
clone:
disable: true
@@ -20,7 +42,7 @@ trigger:
---
kind: pipeline
type: docker
type: kubernetes
name: build:dev
clone:
disable: true
@@ -36,13 +58,14 @@ steps:
depends_on: [clone]
settings:
username:
from_secret: DOCKER_REGISTRY_USER
from_secret: docker_username
password:
from_secret: DOCKER_REGISTRY_PASSWORD
from_secret: docker_password
repo: registry.odit.services/lfk/backend
tags:
- dev
registry: registry.odit.services
mtu: 1000
- name: run changelog export
depends_on: ["clone"]
image: node:latest
@@ -58,7 +81,7 @@ steps:
author_email: bot@odit.services
remote: git@git.odit.services:lfk/backend.git
ssh_key:
from_secret: GITLAB_SSHKEY
from_secret: git_ssh
- name: run full license export
depends_on: ["clone"]
image: node:14.15.1-alpine3.12
@@ -76,7 +99,7 @@ steps:
remote: git@git.odit.services:lfk/backend.git
skip_verify: true
ssh_key:
from_secret: GITLAB_SSHKEY
from_secret: git_ssh
trigger:
@@ -87,7 +110,7 @@ trigger:
---
kind: pipeline
type: docker
type: kubernetes
name: build:latest
clone:
disable: true
@@ -105,13 +128,14 @@ steps:
image: plugins/docker
settings:
username:
from_secret: DOCKER_REGISTRY_USER
from_secret: docker_username
password:
from_secret: DOCKER_REGISTRY_PASSWORD
from_secret: docker_password
repo: registry.odit.services/lfk/backend
tags:
- latest
registry: registry.odit.services
mtu: 1000
- name: push merge to repo
depends_on: ["clone"]
image: appleboy/drone-git-push
@@ -120,7 +144,7 @@ steps:
commit: false
remote: git@git.odit.services:lfk/backend.git
ssh_key:
from_secret: GITLAB_SSHKEY
from_secret: git_ssh
trigger:
branch:
@@ -130,7 +154,7 @@ trigger:
---
kind: pipeline
type: docker
type: kubernetes
name: build:tags
steps:
@@ -139,13 +163,14 @@ steps:
depends_on: [clone]
settings:
username:
from_secret: DOCKER_REGISTRY_USER
from_secret: docker_username
password:
from_secret: DOCKER_REGISTRY_PASSWORD
from_secret: docker_password
repo: registry.odit.services/lfk/backend
tags:
- '${DRONE_TAG}'
registry: registry.odit.services
mtu: 1000
- name: trigger node lib build
image: idcooldi/drone-webhook
settings:
+277 -8
View File
@@ -2,20 +2,282 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v0.7.1](https://git.odit.services/lfk/backend/compare/v0.7.0...v0.7.1)
- Merge pull request 'Release 0.7.1' (#173) from dev into main [`e76a9ce`](https://git.odit.services/lfk/backend/commit/e76a9cef956b00de7bbb11b6d863d4f33e3d5a34)
- Revert "Set timeout even higher b/c sqlite just kills itself during these tests" [`f159252`](https://git.odit.services/lfk/backend/commit/f159252651942e442026dbcaae09b242e05d8204)
- Set timeout even higher b/c sqlite just kills itself during these tests [`6ab6099`](https://git.odit.services/lfk/backend/commit/6ab60998d4f716aded93bb3b5d15594fc5e0434a)
- Adjusted jest timeout to mitigate sqlite from invalidateing all tests⏱ [`30d220b`](https://git.odit.services/lfk/backend/commit/30d220bc36a28f224406e49ed27ff3f6b4f409e9)
- 🧾New changelog file version [CI SKIP] [skip ci] [`963253c`](https://git.odit.services/lfk/backend/commit/963253cbc84ed07af13ed0925952ec1b7dcc53ad)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3ef3a94`](https://git.odit.services/lfk/backend/commit/3ef3a94b20c1abf6fd2f19472e5f448b4c72bd7f)
- 🚀Bumped version to v0.7.1 [`135852e`](https://git.odit.services/lfk/backend/commit/135852eb9a91010a4ab972ba9efc7b71dfe4d68f)
- Merge pull request 'RESPONSERUNNERCARD fix bugfix/171-responserunnercards' (#172) from bugfix/171-responserunnercards into dev [`539a650`](https://git.odit.services/lfk/backend/commit/539a6509b17cfd373eef8e443eaa7d41168ac7a9)
- Now resolveing runnercards [`24aff3b`](https://git.odit.services/lfk/backend/commit/24aff3bac458a9886ca40163484bc72733dc766a)
- Tests now keep the group [`f3d73d5`](https://git.odit.services/lfk/backend/commit/f3d73d53467a4d00011d280c24e1e12fbb8e443d)
- 🧾New changelog file version [CI SKIP] [skip ci] [`ce63043`](https://git.odit.services/lfk/backend/commit/ce63043887769e1f92a8c064d6647e0deb81b7fa)
#### [v0.7.0](https://git.odit.services/lfk/backend/compare/v0.6.4...v0.7.0)
> 23 March 2021
- Merge pull request 'Release 0.7.0' (#170) from dev into main [`e40017a`](https://git.odit.services/lfk/backend/commit/e40017a6b88d83d5bfc57ff4603abeaca7a9a37b)
- Added bulk card creation tests [`438ff0f`](https://git.odit.services/lfk/backend/commit/438ff0fc3f246f83b1fa04cb11828f4a61dfcd1e)
- Added new "bulk" endpoint [`c1bbda5`](https://git.odit.services/lfk/backend/commit/c1bbda51f067cbd9ac1a9a5378ae3f5d7b9f4eca)
- 🧾New changelog file version [CI SKIP] [skip ci] [`7a49e7c`](https://git.odit.services/lfk/backend/commit/7a49e7c5c98eb23af1cd0d2084914641e9a1bf90)
- 🧾New changelog file version [CI SKIP] [skip ci] [`e843a46`](https://git.odit.services/lfk/backend/commit/e843a464e747c0d41280484cb54495cb2de2a9e8)
- 🚀Bumped version to v0.7.0 [`d0ae50d`](https://git.odit.services/lfk/backend/commit/d0ae50d5579e969ad33d6b9cfd66dac7fa472223)
- Merge pull request 'Bulk card creation feature/168-runnercards_bulk' (#169) from feature/168-runnercards_bulk into dev [`1dd6420`](https://git.odit.services/lfk/backend/commit/1dd64204cc63fb1a8a4a4aa503c21da42945eafd)
- 🧾New changelog file version [CI SKIP] [skip ci] [`4705a39`](https://git.odit.services/lfk/backend/commit/4705a39aabaad894d332a5062df03840c23c6bfa)
#### [v0.6.4](https://git.odit.services/lfk/backend/compare/v0.6.3...v0.6.4)
> 19 March 2021
- Merge pull request 'Release 0.6.4' (#167) from dev into main [`4d721f6`](https://git.odit.services/lfk/backend/commit/4d721f62d9a5f6a1361ef2811a3a2ff63011b2ad)
- 🧾New changelog file version [CI SKIP] [skip ci] [`b0328ff`](https://git.odit.services/lfk/backend/commit/b0328ffdaffc8ef2e6e01e808c29748f58f42cac)
- 🧾New changelog file version [CI SKIP] [skip ci] [`cc6568c`](https://git.odit.services/lfk/backend/commit/cc6568c3810fed3ff2597df0db73a6ca9e072413)
- 🚀Bumped version to v0.6.4 [`031cede`](https://git.odit.services/lfk/backend/commit/031cede5426742dc3c2b9dc6b049951d7c14871c)
- Adjsuted endpoint [`3c69f8c`](https://git.odit.services/lfk/backend/commit/3c69f8c4a824e588977b06dbb45119cccb03c6bc)
#### [v0.6.3](https://git.odit.services/lfk/backend/compare/v0.6.2...v0.6.3)
> 18 March 2021
- Merge pull request 'Release 0.6.3' (#165) from dev into main [`a3a1395`](https://git.odit.services/lfk/backend/commit/a3a1395a46d7970cff1b8cc2e84306a97791ed88)
- The basic bugfix 🐞 [`fbdadbe`](https://git.odit.services/lfk/backend/commit/fbdadbef1f9eb835e1914e8d3770cca836b4c443)
- The basic bugfix 🐞 [`c87c97c`](https://git.odit.services/lfk/backend/commit/c87c97c90f5e1229f92671b1f2ebe1fa0d2307cd)
- Updated tests 🧪 [`f347b7a`](https://git.odit.services/lfk/backend/commit/f347b7ad4982ed3760117c08e11dca5c3f72d495)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3f9a704`](https://git.odit.services/lfk/backend/commit/3f9a7049e31a6948125a07e847233b804f27ba31)
- 🧾New changelog file version [CI SKIP] [skip ci] [`b08acc6`](https://git.odit.services/lfk/backend/commit/b08acc666035ed766cc6ccfa9a410a54db4d7321)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a6bca59`](https://git.odit.services/lfk/backend/commit/a6bca59ffe06a37f03af21500c442cebeaa74c7e)
- 🚀Bumped version to v0.6.3 [`7a303c2`](https://git.odit.services/lfk/backend/commit/7a303c2b2c267d6dd566b1470649e65bc1c1b2ee)
- Merge pull request 'TrackScan Update bug 🐞bugfix/163-trackscan_updates' (#164) from bugfix/163-trackscan_updates into dev [`6249419`](https://git.odit.services/lfk/backend/commit/6249419fae22e0203c046c1a3cd82c07f94f510c)
#### [v0.6.2](https://git.odit.services/lfk/backend/compare/v0.6.1...v0.6.2)
> 17 March 2021
- Merge pull request 'Release 0.6.2' (#162) from dev into main [`732a1b8`](https://git.odit.services/lfk/backend/commit/732a1b88d916720ea82cd4b192fc696640ade2aa)
- 🧾New changelog file version [CI SKIP] [skip ci] [`fcb43f9`](https://git.odit.services/lfk/backend/commit/fcb43f92b0b7a8fa2ed3772357c3eab8e6564eef)
- Fixed trackscan vaildation [`a8ea4fa`](https://git.odit.services/lfk/backend/commit/a8ea4fa659732ca2c922fc3c75d2238be2feb5c7)
- Added comments✏ [`289f9e2`](https://git.odit.services/lfk/backend/commit/289f9e219692789f86c631f52c67b578216acb48)
- Added comments✏ [`937a9fa`](https://git.odit.services/lfk/backend/commit/937a9fad4d8914b83fc6300f776c0720b756a9f4)
- Removed duplicate openapi declarations 🗑 [`c8882ae`](https://git.odit.services/lfk/backend/commit/c8882ae6a18188a9c98a237dd594548ebac6f460)
- Now defining security per endpoint 🔐 [`d709ee7`](https://git.odit.services/lfk/backend/commit/d709ee74795b785599cda50b4351bd566a0b8573)
- Changed the method of getting a parameter from the headers🛠 [`1d38d30`](https://git.odit.services/lfk/backend/commit/1d38d308ad8ae00d67c2b807b584da4f00bd9a58)
- Now auto-etting the station token🔥🔥🔥 [`aae042c`](https://git.odit.services/lfk/backend/commit/aae042c041e325626b89b146d005e900bd880453)
- Marked station as optional (quality of life improvements incoming) [`1f32ed0`](https://git.odit.services/lfk/backend/commit/1f32ed0727cb1117e5d201b5530b2f2d7f0323d8)
- 🧾New changelog file version [CI SKIP] [skip ci] [`4c960fe`](https://git.odit.services/lfk/backend/commit/4c960feeb22f819d1c618ced73f5799a3c7e4f00)
- Fixed missing renameing🛠 [`0ed7f78`](https://git.odit.services/lfk/backend/commit/0ed7f78b2c284909d47fa0533424c279adef0ba3)
- 🧾New changelog file version [CI SKIP] [skip ci] [`c1dd451`](https://git.odit.services/lfk/backend/commit/c1dd4518d128edd8b8e36981a513744471241a25)
- 🚀Bumped version to v0.6.2 [`5ba8f1d`](https://git.odit.services/lfk/backend/commit/5ba8f1dd4451c1a1b38fdd36cf632c9e6efa829c)
- Merge pull request 'Bugfixes for trackscans feature/160-responseTrackScan_total_distance' (#161) from feature/160-responseTrackScan_total_distance into dev [`3d3790c`](https://git.odit.services/lfk/backend/commit/3d3790c2eb6a92bb5b1d2c7e44c75aef4e1b015f)
- Fixed wrong error type 👀👀 [`1fa3fa7`](https://git.odit.services/lfk/backend/commit/1fa3fa75ee447b9919585e02c7997e3f1de9c8a7)
- Added missing discription [`673e896`](https://git.odit.services/lfk/backend/commit/673e896aa3dc853b301a2e560e785c464a449b6f)
#### [v0.6.1](https://git.odit.services/lfk/backend/compare/v0.6.0...v0.6.1)
> 17 March 2021
#### [v0.6.0](https://git.odit.services/lfk/backend/compare/v0.5.0...v0.6.0)
> 17 March 2021
- Merge pull request 'Release v0.6.0' (#159) from dev into main [`bdc7bb6`](https://git.odit.services/lfk/backend/commit/bdc7bb67e7e21769d95a762c3b6dfbf82e7e38d0)
- 📖New license file version [CI SKIP] [skip ci] [`5f5c8a0`](https://git.odit.services/lfk/backend/commit/5f5c8a061eb94361e4cd02e9a6469194a9092513)
- As requested by @philpp [`2cb7ec7`](https://git.odit.services/lfk/backend/commit/2cb7ec7317d8a48364261506facb2c11c7cf895f)
- Updated ci with new kubernetes secrets 🚀🚀🚀 [`5541ae6`](https://git.odit.services/lfk/backend/commit/5541ae6ebd7f36f4482ae752f358102a18b95de0)
- Added selfservice forgott positive tests [`bf1ec97`](https://git.odit.services/lfk/backend/commit/bf1ec976e3732b6ac052a55a51ee2ee18a8b1d3d)
- Added all "negative" tests [`d0a7e34`](https://git.odit.services/lfk/backend/commit/d0a7e34de8095fca282adefff01fa5f72e7cdba3)
- Added mailer functions [`8376513`](https://git.odit.services/lfk/backend/commit/83765136ccacd82ba6a8f9fb43eed78191ee0aa5)
- Added tests for the new endpoint [`757332e`](https://git.odit.services/lfk/backend/commit/757332ed2b3325d8730ef1b284ac6ba40356df93)
- 🧾New changelog file version [CI SKIP] [skip ci] [`e4ed20d`](https://git.odit.services/lfk/backend/commit/e4ed20da3e0a9e32a2e4664d50f316f9131564f0)
- Added first selfservice forgotten test [`a95a9b4`](https://git.odit.services/lfk/backend/commit/a95a9b4ec4a3012a91f6f622cfb9f5bff3376344)
- Created basic endpoint for user forgotten mails [`d709971`](https://git.odit.services/lfk/backend/commit/d7099717c2eee8aaf1b580345717cc5acc06dbd2)
- Implemented the "real" errors [`e26b7d4`](https://git.odit.services/lfk/backend/commit/e26b7d4923777a3013368e29c122709de7e1d9da)
- Runner controller now uses the Mailer functions [`a343747`](https://git.odit.services/lfk/backend/commit/a3437475caf6b435ae4bdf6d48aeb7da7d43b25f)
- Added scanstation me endpoint [`c5178e0`](https://git.odit.services/lfk/backend/commit/c5178e01814cedaa4402773b10f24d186714c1d2)
- 🧾New changelog file version [CI SKIP] [skip ci] [`54988ba`](https://git.odit.services/lfk/backend/commit/54988ba0fe012ce87d44c9068f7546a9be73723c)
- Added last reset requested timestamp to runners [`66d6023`](https://git.odit.services/lfk/backend/commit/66d6023335c7a9d1a145c4189b610940ef5a525a)
- Scanauth return objects [`46b7ace`](https://git.odit.services/lfk/backend/commit/46b7aceb0b86b03688faf0ec6661e4c9fbc6115c)
- Revert "Switched normal images to chached registry" [`ca6fa63`](https://git.odit.services/lfk/backend/commit/ca6fa633a156a265d8f643a5f23090b6ab32260d)
- Switched normal images to chached registry [`cba4455`](https://git.odit.services/lfk/backend/commit/cba4455d53f9a39b6f9993c36b5abd281201dfa1)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a7958ee`](https://git.odit.services/lfk/backend/commit/a7958eecd65116ab937f640cbebcae1962cb86c8)
- 🧾New changelog file version [CI SKIP] [skip ci] [`076aa87`](https://git.odit.services/lfk/backend/commit/076aa87dba1d6fc544e76c16f99c64d37fc82ea0)
- 🧾New changelog file version [CI SKIP] [skip ci] [`486e450`](https://git.odit.services/lfk/backend/commit/486e450a58d3671dc867ae1a99d052d9fe814c1a)
- Updated request timeout [`ffcd45e`](https://git.odit.services/lfk/backend/commit/ffcd45e5724fccdec9b1dbc48f1320525dcd7288)
- Added testing env check [`3f37212`](https://git.odit.services/lfk/backend/commit/3f372123fd2e1fae467e9cb20985de1eeb9f6a57)
- 🚀Bumped version to v0.6.1 [`ce3ca9f`](https://git.odit.services/lfk/backend/commit/ce3ca9f1c86a6fe72e4dd77e3a0d60bf1e1bf542)
- 🚀Bumped version to v0.6.0 [`623b5a1`](https://git.odit.services/lfk/backend/commit/623b5a1873afa73a984251543995b7da1cfdb5c9)
- Merge pull request 'Scanstation "me" endpoint feature/157-scanstation_me' (#158) from feature/157-scanstation_me into dev [`13e8399`](https://git.odit.services/lfk/backend/commit/13e839902c063057e902fdb52b403be081d1667e)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a1a94ec`](https://git.odit.services/lfk/backend/commit/a1a94ec9dafecd9b4c453cc8cfe32c2e90acccf5)
- 🧾New changelog file version [CI SKIP] [skip ci] [`d5930f7`](https://git.odit.services/lfk/backend/commit/d5930f7c46f4fc8ed56b6eeec9f784d435fd3b2b)
- Changed ci pipeline type to kubernetes [`6c43872`](https://git.odit.services/lfk/backend/commit/6c43872198c3dba44b3af3a7cfc7b628d5b304a3)
- Mailer now ignores mailing erros when env is set to test [`6bb3ae8`](https://git.odit.services/lfk/backend/commit/6bb3ae8ba992bd6c4d5809d75a264c710999cdcf)
- 🧾New changelog file version [CI SKIP] [skip ci] [`bf71e35`](https://git.odit.services/lfk/backend/commit/bf71e35ecd333d888d63213d69b04fc681a9d0bd)
- Adjusted tests for the new testing env [`9292027`](https://git.odit.services/lfk/backend/commit/92920273bec409563d1e38ea27f4d30f893598e8)
- Applied Docker MTU fix 🛠 [`f7af777`](https://git.odit.services/lfk/backend/commit/f7af77710421d7aae5efb048e0622cd067fc20eb)
- Updated description [`94001a4`](https://git.odit.services/lfk/backend/commit/94001a48f1b314e91ea5ec982e5585124f9541b6)
- Now adding station id to headers of request for scan auth [`8ba7ee1`](https://git.odit.services/lfk/backend/commit/8ba7ee1d481e44e686489e237980b21aaaf6071c)
- Merge pull request 'selfservice forgotten mails feature/154-selfservice_forgotten' (#155) from feature/154-selfservice_forgotten into dev [`cb6e78f`](https://git.odit.services/lfk/backend/commit/cb6e78fc176ec9efe94311b64286020b3c5bf633)
- Changed endpoint url to avoid conflicts [`e5dab34`](https://git.odit.services/lfk/backend/commit/e5dab3469c3cef6298fc8deb1192a38f7d18406b)
- Added console logging when a testing env get's discovered [`c01233b`](https://git.odit.services/lfk/backend/commit/c01233b4d663aefece26dbb86f8b6bcd5c916325)
- Added not found error logic [`e7f0cb4`](https://git.odit.services/lfk/backend/commit/e7f0cb45c9ac3aa06e2a57786aa1cc51c9d66598)
- Updated to new responsetype [`08957d4`](https://git.odit.services/lfk/backend/commit/08957d4dc2951cfeec56a54680c2ae4ef1525ab2)
- Added readme description for testing env [`cedc175`](https://git.odit.services/lfk/backend/commit/cedc1750c21ad256c3337f293f06e894e2c2ef9f)
- Renamed test [`1d762f5`](https://git.odit.services/lfk/backend/commit/1d762f56628eff47f4e1a910c7152bd0158283bd)
#### [v0.5.0](https://git.odit.services/lfk/backend/compare/v0.4.6...v0.5.0)
> 4 March 2021
- Merge pull request 'Alpha Release 0.5.0' (#153) from dev into main [`64da0ea`](https://git.odit.services/lfk/backend/commit/64da0eadb313f3bd3ae20a66bcaf4401528008d9)
- Removed mail templates [`c2fdfee`](https://git.odit.services/lfk/backend/commit/c2fdfeed4f5fc454b02bc4b198965889c173bbaa)
- Removed mail config [`0342757`](https://git.odit.services/lfk/backend/commit/0342757d929b12635c88e74f17495df656865b1a)
- Added selfservice scan response class [`6074ac5`](https://git.odit.services/lfk/backend/commit/6074ac5b3a8e43fd98394c1fb70c6e1dea8fcd5e)
- Removed old mailer code [`0fcc729`](https://git.odit.services/lfk/backend/commit/0fcc729b56430f0fdb56242857aa1d883d5a4866)
- 🧾New changelog file version [CI SKIP] [skip ci] [`5272829`](https://git.odit.services/lfk/backend/commit/52728290b477d3f90ee7c14e0d438c4c74415322)
- Added the new mailer code [`1551a44`](https://git.odit.services/lfk/backend/commit/1551a444babc025cde6e894c66d2be2c84ab26da)
- Removed (now useless) mail controller [`485c247`](https://git.odit.services/lfk/backend/commit/485c247cd3305c4c4422d5582b1d61cc7af84989)
- Trackscans now have a laptime that get's calculated on creation [`aa83373`](https://git.odit.services/lfk/backend/commit/aa833736d32993b1656abeeb02a4f8b021ec6252)
- Removed useless functions and updated comments [`ada6798`](https://git.odit.services/lfk/backend/commit/ada679823cda8bc31d45c0ff6905f3d270cfd729)
- Added new selfservice scans endpoint [`771a205`](https://git.odit.services/lfk/backend/commit/771a205fe634fc5c07e794b3245c59483ff14bd8)
- Updated mail errors [`f289afd`](https://git.odit.services/lfk/backend/commit/f289afd8bc47f6eae9f12f765322b2db974ba918)
- Laptime is now a part of the response [`a2c97a1`](https://git.odit.services/lfk/backend/commit/a2c97a11a3dc82543076e3844f20d1218943bbf9)
- Updated readme env section [`db58a28`](https://git.odit.services/lfk/backend/commit/db58a280b3792b768eb2b1c82a76d9a9836978b1)
- Added locale to pw reset endpoint [`a5d2a6e`](https://git.odit.services/lfk/backend/commit/a5d2a6ecd31dc9c186d4201aef5c52e34cbef3b5)
- Now using mailer as static funtion [`9a1678a`](https://git.odit.services/lfk/backend/commit/9a1678acf0929dab9f84bd2c6a961b52e36172ce)
- Updated readme env section [`149f3a8`](https://git.odit.services/lfk/backend/commit/149f3a83b2e9d59bfbf36c7ea9e27bc7f514856d)
- Now checking for mails being set [`bb9bad6`](https://git.odit.services/lfk/backend/commit/bb9bad6d90370e768d4baffaae23ec756cc8353b)
- Updated auth reset test for new mailer [`ae7d617`](https://git.odit.services/lfk/backend/commit/ae7d6176902699f82ea127194908ee360233e7b4)
- Added scans returns 200 test [`82c65b6`](https://git.odit.services/lfk/backend/commit/82c65b632cdf44165b083494702b836c74e46a41)
- 🚀Bumped version to v0.4.7 [`f1d85cf`](https://git.odit.services/lfk/backend/commit/f1d85cfb855c2aae581ade69751b3969ce38f020)
- Now generateing bs mailer config in test env [`bf6b701`](https://git.odit.services/lfk/backend/commit/bf6b70106eb735d9ad6f6ad89f09194680af5ae1)
- Added new mailer settings to config [`ddea02d`](https://git.odit.services/lfk/backend/commit/ddea02db574cc348685558f3fa3ecc84adbd6b65)
- 🚀Bumped version to v0.5.0 [`3f2a2d2`](https://git.odit.services/lfk/backend/commit/3f2a2d292979c7f8162d92465b60b220f2634e7a)
- Merge pull request 'Features for the new selfservice feature/151-selfservice_scans_mails' (#152) from feature/151-selfservice_scans_mails into dev [`15356c1`](https://git.odit.services/lfk/backend/commit/15356c1030988d03e3739f3ffe770669789759f2)
- 🧾New changelog file version [CI SKIP] [skip ci] [`be397c8`](https://git.odit.services/lfk/backend/commit/be397c8899d5b4406c17e8f9951555c54f852901)
- Promoted axios to dependency [`a9e06c9`](https://git.odit.services/lfk/backend/commit/a9e06c905537b6da24706389e304e825a33a28ad)
- Removed nodemailer from backend [`5833f42`](https://git.odit.services/lfk/backend/commit/5833f4218f9a4c97b69021814df92470a1816917)
- Added another resonse type [`030b225`](https://git.odit.services/lfk/backend/commit/030b2255d42aab21d8974fc3a7235285934d53b7)
- Added new selfservice response type [`f7f6df4`](https://git.odit.services/lfk/backend/commit/f7f6df41ff74708482db3ea2db717ffb562131c0)
#### [v0.4.6](https://git.odit.services/lfk/backend/compare/v0.4.5...v0.4.6)
> 26 February 2021
- Merge pull request 'Alpha Release 0.4.6' (#148) from dev into main [`dd3c927`](https://git.odit.services/lfk/backend/commit/dd3c9275d60cb5bb1a40fbe91f666f17a8e0c8d3)
- Added tests for the new org selfservice endpoints [`28ef139`](https://git.odit.services/lfk/backend/commit/28ef139a70e0c063982b2eb9167b7abe41db1621)
- Added selfservice org response model [`ba3b5ee`](https://git.odit.services/lfk/backend/commit/ba3b5eeefc45f9bd94aef24f9f509f6835f5ea7c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`764b7ff`](https://git.odit.services/lfk/backend/commit/764b7ffe00086248e1f1cccb265ca920a568c0a0)
- Merge pull request 'Fixed wrong body acceptance type' (#150) from bugfix/146-usergroup_update into dev [`d870b2f`](https://git.odit.services/lfk/backend/commit/d870b2fd01b11b1732fcbb6feecaf6a6155fa702)
- Added selfservice team response model [`ba396e0`](https://git.odit.services/lfk/backend/commit/ba396e0eba15647b3004437a5a9949c7a69e828d)
- 📖New license file version [CI SKIP] [skip ci] [`bce8811`](https://git.odit.services/lfk/backend/commit/bce8811925e7f77c64fc507d55335ac45b0e5572)
- 📖New license file version [CI SKIP] [skip ci] [`b1fced7`](https://git.odit.services/lfk/backend/commit/b1fced77640b6c26438331474f368f2b0708b672)
- Added selfservice org info endpoint [`656f63d`](https://git.odit.services/lfk/backend/commit/656f63dfd5fdbe13554fc98440e416be7e56d909)
- 🧾New changelog file version [CI SKIP] [skip ci] [`c0cafb4`](https://git.odit.services/lfk/backend/commit/c0cafb4d510116773fed12592cad1efc2ef09f38)
- 🧾New changelog file version [CI SKIP] [skip ci] [`09fe47b`](https://git.odit.services/lfk/backend/commit/09fe47b9aaac47b65d4e910ef89d558c47fd7364)
- Fixed wrong body acceptance type [`aaec09d`](https://git.odit.services/lfk/backend/commit/aaec09d2ab08a76e9d367fdfefc01cea5588f1b9)
- Pinned package version to avoid dependency conflicts 📌 [`39ebfbf`](https://git.odit.services/lfk/backend/commit/39ebfbf0b633ecc479a33fdf851cd6550616bfee)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3736b29`](https://git.odit.services/lfk/backend/commit/3736b29e5435abb05de03e5d99d9adb438cd7d7e)
- 🧾New changelog file version [CI SKIP] [skip ci] [`305fa00`](https://git.odit.services/lfk/backend/commit/305fa0078d44b39b0391e84ba67b048285cf77b9)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3afc207`](https://git.odit.services/lfk/backend/commit/3afc207903c9cf1e62e6f4a62601b4213f608192)
- Quick bugfix [`5d6c8c9`](https://git.odit.services/lfk/backend/commit/5d6c8c957acd098a20e674ce5529f60cbc9f4151)
- 🚀Bumped version to v0.4.6 [`b4acd15`](https://git.odit.services/lfk/backend/commit/b4acd157fc075154a60946c1ee8876ee5f5dfbee)
- Merge pull request 'New org selfservice endpoint feature/146-more_selfservice_endpoints' (#147) from feature/146-more_selfservice_endpoints into dev [`45d61b4`](https://git.odit.services/lfk/backend/commit/45d61b487e8e6fdd8e00c184a08c9d6e34a1b6bf)
- Added new response types [`3c11d88`](https://git.odit.services/lfk/backend/commit/3c11d88557a2612bf4320ff669323bc048634e94)
#### [v0.4.5](https://git.odit.services/lfk/backend/compare/v0.4.4...v0.4.5)
> 9 February 2021
- Merge pull request 'Alpha release 0.4.5' (#145) from dev into main [`a46d142`](https://git.odit.services/lfk/backend/commit/a46d14278b9a084ca54f8f90e5e70b04739c2dd7)
- 🚀Bumped version to v0.4.5 [`cc869f6`](https://git.odit.services/lfk/backend/commit/cc869f69add1f1a175ff94510d52888f81bccb69)
- 🧾New changelog file version [CI SKIP] [skip ci] [`680ae8e`](https://git.odit.services/lfk/backend/commit/680ae8ebbb39d103085fe1fe8781d71b3c3ed055)
- 🧾New changelog file version [CI SKIP] [skip ci] [`b9aac71`](https://git.odit.services/lfk/backend/commit/b9aac7167681ff0945e538dd177abd6f97771bf2)
- Merge pull request 'usergroups/permissions endpoint feature/143-usergroup_permissions_endpoint' (#144) from feature/143-usergroup_permissions_endpoint into dev [`a30a342`](https://git.odit.services/lfk/backend/commit/a30a342e00ba944f8014044bba28141c0657a17f)
- Implemented /groups/permissions endpoint [`0c9867d`](https://git.odit.services/lfk/backend/commit/0c9867d70616615c8f3c72bbec37a4441e4868ef)
- Now all /usergroups endpoints return ResponseUserGroup [`bdcfce8`](https://git.odit.services/lfk/backend/commit/bdcfce88cbe069f9ba1925fcaac06367a109d2b7)
- The ResponseUserGroup now returns their permisssions as a string array [`416f2a1`](https://git.odit.services/lfk/backend/commit/416f2a1366c570998011d022ebd7f5f44276b2c9)
- The ResponseUserGroup now returns their permisssions as a string array [`5e353db`](https://git.odit.services/lfk/backend/commit/5e353db2061c30b4d10965c47f0dcbecb7f59fc5)
- 🧾New changelog file version [CI SKIP] [skip ci] [`8379c3e`](https://git.odit.services/lfk/backend/commit/8379c3e29c45f0d7c4c84bce1f3abc718158fa84)
#### [v0.4.4](https://git.odit.services/lfk/backend/compare/v0.4.3...v0.4.4)
> 9 February 2021
- Merge pull request 'Alpha release 0.4.4' (#142) from dev into main [`c4edcca`](https://git.odit.services/lfk/backend/commit/c4edccace78765dd5caa0f0e79c52f07c8a3568e)
- 🧾New changelog file version [CI SKIP] [skip ci] [`ca3d093`](https://git.odit.services/lfk/backend/commit/ca3d093e54bfaaa77c97e96738a74eeb25aee440)
- Now loading runner's group's parentgroup with every runner controller request [`701706c`](https://git.odit.services/lfk/backend/commit/701706c0289b357439608b4e2eaa66c617d16e9d)
- 🧾New changelog file version [CI SKIP] [skip ci] [`74de655`](https://git.odit.services/lfk/backend/commit/74de6559d7c5e8c6d257d41dc91396b53bf0c071)
- The group/runners endpoints now also deliver the runner's group's parentGroup [`906a1dc`](https://git.odit.services/lfk/backend/commit/906a1dc9e79ea4eb298a561cf98e6ae42b3ae4ec)
- 🚀Bumped version to v0.4.4 [`a6f73c7`](https://git.odit.services/lfk/backend/commit/a6f73c733c8cfc8d84beb7e0bbd5bcd1313df9d0)
- Merge pull request 'Expanded runner response feature/140-runner_group_parent' (#141) from feature/140-runner_group_parent into dev [`28cfbaa`](https://git.odit.services/lfk/backend/commit/28cfbaa6624d0bc65e2a9b72ffed17060e828735)
- 🧾New changelog file version [CI SKIP] [skip ci] [`09bbc70`](https://git.odit.services/lfk/backend/commit/09bbc70f5fd1f026148be07fe889a6907bc3f75a)
- Adjusted test for the new response depth [`90e1ad7`](https://git.odit.services/lfk/backend/commit/90e1ad7db72732d13002c87461c33560b74befa6)
- Adjusted test for the new response depth [`5872c63`](https://git.odit.services/lfk/backend/commit/5872c6335be573d849cdc3746b261c6cf476c3de)
#### [v0.4.3](https://git.odit.services/lfk/backend/compare/v0.4.2...v0.4.3)
> 7 February 2021
- Merge pull request 'Alpha Release 0.4.3' (#139) from dev into main [`dd9cb6d`](https://git.odit.services/lfk/backend/commit/dd9cb6d3ef60cb118391abc2ba17fd0f83db0b1c)
- 🚀Bumped version to v0.4.3 [`656d564`](https://git.odit.services/lfk/backend/commit/656d564baa8c8bf1f63523b0301ad9ff23e08aa4)
- Bugfix for @lfk/frontend/#43 [`8f0a396`](https://git.odit.services/lfk/backend/commit/8f0a396dd07937fb7ccfb345d1acbac86eb5d9bb)
- 🧾New changelog file version [CI SKIP] [skip ci] [`f3f5cb4`](https://git.odit.services/lfk/backend/commit/f3f5cb462e4ecf932ad55eb519815222b4e5dd17)
- 🧾New changelog file version [CI SKIP] [skip ci] [`23c732b`](https://git.odit.services/lfk/backend/commit/23c732b6905cc9f6479a53a7744b72d01e345ecb)
- Merge pull request 'Bugfix for @lfk/frontend/#43' (#138) from bugfix/118-encode_jwt_in_mail into dev [`9959172`](https://git.odit.services/lfk/backend/commit/9959172f2ae11cbb7a2b8e97bad432956fc70a80)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a18d4d3`](https://git.odit.services/lfk/backend/commit/a18d4d3cee58f8eb9dc428b051a2394bd3ece5c2)
#### [v0.4.2](https://git.odit.services/lfk/backend/compare/v0.4.1...v0.4.2)
> 2 February 2021
- Merge pull request 'Alpha Release 0.4.2' (#137) from dev into main [`390b36d`](https://git.odit.services/lfk/backend/commit/390b36dfd46cf8829126581bd62dd3d4ce8558fa)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3b718f3`](https://git.odit.services/lfk/backend/commit/3b718f3ce58f12007b6068e5db00a00dbe1c5398)
- 🧾New changelog file version [CI SKIP] [skip ci] [`f7a0ec7`](https://git.odit.services/lfk/backend/commit/f7a0ec7174521b1863a4bc58c7ff2b86cafdee66)
- 🚀Bumped version to v0.4.2 [`321b20b`](https://git.odit.services/lfk/backend/commit/321b20b073b6debd605a92544779d0dfc0449f10)
- Merge pull request 'Imprint&Privacy Links feature/135-imprint_and_privacy' (#136) from feature/135-imprint_and_privacy into dev [`110a847`](https://git.odit.services/lfk/backend/commit/110a84783e023407cbcf81506deb7cc204db9659)
- 📖New license file version [CI SKIP] [skip ci] [`74791df`](https://git.odit.services/lfk/backend/commit/74791df68b40355e1d1a1f7f5ae4f64422571dc9)
- 🧾New changelog file version [CI SKIP] [skip ci] [`8425043`](https://git.odit.services/lfk/backend/commit/84250430996920ada15af23b68756daba8f99257)
- Added new url env vars to config [`bcad691`](https://git.odit.services/lfk/backend/commit/bcad691045d00c9630bedb0936c123610b655946)
- fixed license-exporter call [`74b982a`](https://git.odit.services/lfk/backend/commit/74b982afba3ec82a038c4748420920732fe32a51)
- Added documentation about the new env vars to the readme [`333e806`](https://git.odit.services/lfk/backend/commit/333e806da42d7654e2b9fc13abae984726c689e7)
- Added imprint and privacy to the api spec [`f4f6219`](https://git.odit.services/lfk/backend/commit/f4f621973aa98645dee3d43252bb18f125087c54)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3aefa75`](https://git.odit.services/lfk/backend/commit/3aefa754128fc58a8200b280ee036c49cdaaac4a)
#### [v0.4.1](https://git.odit.services/lfk/backend/compare/v0.4.0...v0.4.1)
> 30 January 2021
- Merge pull request 'Alpha Release 0.4.1' (#134) from dev into main [`71cab4e`](https://git.odit.services/lfk/backend/commit/71cab4e836dca2e2072b62676337f5f90796f105)
- Deleted useless file [ci skip] [`94dd796`](https://git.odit.services/lfk/backend/commit/94dd7963b73d98089f086175ed79002f59195c26)
- Implemented the interface in all responses [`9d5e486`](https://git.odit.services/lfk/backend/commit/9d5e486c6ddb4db87d36409fbd8bca1bf9659e9f)
- Adjusted tests for the new responseType parameter (part 1) [`bcc15e4`](https://git.odit.services/lfk/backend/commit/bcc15e42863b641b97cd03440f141332e112c889)
- Cleaned up realations regarding response classes [`ff7406e`](https://git.odit.services/lfk/backend/commit/ff7406e71a4a76670d381b415bfb66f602e1206b)
- Added Responseobjecttype enum [`581ca5f`](https://git.odit.services/lfk/backend/commit/581ca5ff6c67ed1c701c06532671441293ee0706)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0229534`](https://git.odit.services/lfk/backend/commit/02295346dad109745faa492dc968abbc98522804)
- Dependency bump🔝 [skip ci] [`3d1baae`](https://git.odit.services/lfk/backend/commit/3d1baae0cce26b0c1f6948778b15f4b0097a77a9)
- 🧾New changelog file version [CI SKIP] [skip ci] [`7ba67b9`](https://git.odit.services/lfk/backend/commit/7ba67b9dcab7692c8c1e548ccbc3895eb084eedd)
- Adjusted tests for the new responseType parameter (part 3) [`8dc2810`](https://git.odit.services/lfk/backend/commit/8dc2810c0c4097e26bf1b517bb9d0b102494f6c1)
- Added a Response interface [`e44cc4c`](https://git.odit.services/lfk/backend/commit/e44cc4c4cbecdf7c8d90f0af73fffd8b01eba61e)
- 🧾New changelog file version [CI SKIP] [skip ci] [`4e10077`](https://git.odit.services/lfk/backend/commit/4e100779017c5ce542b59cf2156bd6ab5fdceed2)
- Adjusted tests for the new responseType parameter (part 2) [`ff8af09`](https://git.odit.services/lfk/backend/commit/ff8af090e3ea1760fb6110284cff870a0b4cbf84)
- 🚀Bumped version to v0.4.1 [`c32fa93`](https://git.odit.services/lfk/backend/commit/c32fa93673010ab18e009ab24fc139ed4f67310e)
- Merge pull request 'Response object types feature/132-object_types' (#133) from feature/132-object_types into dev [`6e5f1bd`](https://git.odit.services/lfk/backend/commit/6e5f1bd5ff217a88ec2dfaf154e5e26dee588e12)
- Fixed typos and missing types [`2a87819`](https://git.odit.services/lfk/backend/commit/2a878194865d406123805659c011c329c955f669)
#### [v0.4.0](https://git.odit.services/lfk/backend/compare/v0.3.1...v0.4.0)
> 30 January 2021
- Merge pull request 'Alpha Release 0.4.0' (#131) from dev into main [`c4ea808`](https://git.odit.services/lfk/backend/commit/c4ea808e06bdc68add5636346f99a7d292852ab0)
- Added pw reset template provided by @philipp [`c116338`](https://git.odit.services/lfk/backend/commit/c116338cd74cf726362f8fa0ae5eea7ec9fabac4)
- Added test mail templates [`8270029`](https://git.odit.services/lfk/backend/commit/827002989ee6e3f0d776b5b55b8fb6d65f10c898)
- 🧾New changelog file version [CI SKIP] [skip ci] [`09b24aa`](https://git.odit.services/lfk/backend/commit/09b24aa6094a980debf4428a1c32583cdb7b606f)
- Table fix [`1f0c842`](https://git.odit.services/lfk/backend/commit/1f0c842d9e086456f1ae0f6908e474258a04beb4)
- 🧾New changelog file version [CI SKIP] [skip ci] [`fea4857`](https://git.odit.services/lfk/backend/commit/fea485768570eb5de2bbd2783e339794a67db2de)
- 🧾New changelog file version [CI SKIP] [skip ci] [`e07f258`](https://git.odit.services/lfk/backend/commit/e07f258a315898d1183c311e7fcd8f65a415504c)
- 🚀Bumped version to v0.4.0 [`e5f4f6e`](https://git.odit.services/lfk/backend/commit/e5f4f6ee590e0885d6eef9151ce7eb76578b70ca)
- Merge pull request 'Implemented testmail endpoint feature/124-testmail' (#130) from feature/124-testmail into dev [`f9e75d0`](https://git.odit.services/lfk/backend/commit/f9e75d06b8ee8ff79f60fb384cb2c35ccf19811d)
- Merge pull request 'Email Basics feature/118-emails' (#128) from feature/118-emails into dev [`348e6cd`](https://git.odit.services/lfk/backend/commit/348e6cdec7411345953243edfb5322a17ad7614d)
- Merge pull request 'Mail+Env documentation feature/123-mail_documentation' (#129) from feature/123-mail_documentation into dev [`61bbeb0`](https://git.odit.services/lfk/backend/commit/61bbeb0d8f3fd6bfafb65bd11eb4c076a27b4a53)
- Added pw reset template provided by @philipp [`c116338`](https://git.odit.services/lfk/backend/commit/c116338cd74cf726362f8fa0ae5eea7ec9fabac4)
- Implemented automatic ci env generation [`536de2a`](https://git.odit.services/lfk/backend/commit/536de2a3199b1befed54b6fe520a2e3fcefe0d90)
- Implemented a basic mailer with reset link sending [`6379753`](https://git.odit.services/lfk/backend/commit/637975305f1adf9bf505507790638cf1e229cfb1)
- Table fix [`1f0c842`](https://git.odit.services/lfk/backend/commit/1f0c842d9e086456f1ae0f6908e474258a04beb4)
- Implemented the test-mail endpoint via a new mailcontroller [`54ed313`](https://git.odit.services/lfk/backend/commit/54ed313342a72b029b9545bc5ea193e3f0c2166d)
- Added documentation for the env vars [`13ccab5`](https://git.odit.services/lfk/backend/commit/13ccab5e289d0a629cefb7fe281a85a46058ae97)
- Added comments [`9bd7636`](https://git.odit.services/lfk/backend/commit/9bd7636a23b5a9662ea2b965d2a2407727a188fb)
@@ -24,8 +286,15 @@ All notable changes to this project will be documented in this file. Dates are d
- Implementes mail sending on pw reset request [`e26744b`](https://git.odit.services/lfk/backend/commit/e26744b7925d32d65ef4cc3911651758cfc9274f)
- Added a txt variant of the pw-reset mail [`d3647e3`](https://git.odit.services/lfk/backend/commit/d3647e339990d989dbca4d91aa8c3fe5789dd24a)
- Changed order [`583a4bc`](https://git.odit.services/lfk/backend/commit/583a4bc0dd0de8026bb2eb6a9b0c31f59344e813)
- 🧾New changelog file version [CI SKIP] [skip ci] [`fea4857`](https://git.odit.services/lfk/backend/commit/fea485768570eb5de2bbd2783e339794a67db2de)
- Translated the pw reset mail to english [`5cade25`](https://git.odit.services/lfk/backend/commit/5cade25eeb137eb5890b3fd450646acfbdff2e8b)
- The auth tests now use mail to identify the user [`c43334b`](https://git.odit.services/lfk/backend/commit/c43334bf96901bfd5116301ff7cf4b2ae1dfcbd3)
- 🧾New changelog file version [CI SKIP] [skip ci] [`e07f258`](https://git.odit.services/lfk/backend/commit/e07f258a315898d1183c311e7fcd8f65a415504c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`b972395`](https://git.odit.services/lfk/backend/commit/b972395ae8bb06184baa56296a3b8bfc16ae96a7)
- 🚀Bumped version to v0.4.0 [`e5f4f6e`](https://git.odit.services/lfk/backend/commit/e5f4f6ee590e0885d6eef9151ce7eb76578b70ca)
- Merge pull request 'Implemented testmail endpoint feature/124-testmail' (#130) from feature/124-testmail into dev [`f9e75d0`](https://git.odit.services/lfk/backend/commit/f9e75d06b8ee8ff79f60fb384cb2c35ccf19811d)
- Merge pull request 'Email Basics feature/118-emails' (#128) from feature/118-emails into dev [`348e6cd`](https://git.odit.services/lfk/backend/commit/348e6cdec7411345953243edfb5322a17ad7614d)
- Merge pull request 'Mail+Env documentation feature/123-mail_documentation' (#129) from feature/123-mail_documentation into dev [`61bbeb0`](https://git.odit.services/lfk/backend/commit/61bbeb0d8f3fd6bfafb65bd11eb4c076a27b4a53)
- Added a test mail sending function [`b94179e`](https://git.odit.services/lfk/backend/commit/b94179e3caaf4be0654ca3372f57a490fb32e208)
- Added the first mail error [`c418603`](https://git.odit.services/lfk/backend/commit/c4186034233a296b5971fbef16e7ef6809fbac51)
- Now also sending txt mail body [`b92f633`](https://git.odit.services/lfk/backend/commit/b92f633d68604636cecc5e9fdd0d6990b9cb83fe)
+7 -7
View File
@@ -49,7 +49,7 @@ yarn docs
## ENV Vars
> You can provide them via .env file or docker env vars.
> You can use the `test:ci:generate_env` package script to generate a example env (uses [ethereal.email](https://ethereal.email) as the mailserver).
> You can use the `test:ci:generate_env` package script to generate a example env (uses bs data as test server and ignores the errors).
| Name | Type | Default | Description
| - | - | - | -
@@ -60,15 +60,15 @@ yarn docs
| DB_USER | String | N/A | The user for accessing the db
| DB_PASSWORD | String | N/A | The user's password for accessing the db
| DB_NAME | String | N/A | The db's name
| NODE_ENV | String | dev | The apps env - influences debug info.
| NODE_ENV | String | dev | The apps env - influences debug info. Also when the env is set to "test", mailing errors get ignored.
| POSTALCODE_COUNTRYCODE | String/CountryCode | N/A | The countrycode used to validate address's postal codes
| PHONE_COUNTRYCODE | String/CountryCode | null (international) | The countrycode used to validate phone numers
| SEED_TEST_DATA | Boolean | False | If you want the app to seed some example data set this to true
| MAIL_SERVER | String | N/A | The smtp server's ip-address/fqdn
| MAIL_PORT | String | N/A | The smtp server's port
| MAIL_USER | String | N/A | The username for sending mails
| MAIL_PASSWORD | String | N/A | The user's password for sending mails
| MAIL_FROM | String | N/A | The from-address for sending mails
| MAILER_URL | String(Url) | N/A | The mailer's base url (no trailing slash)
| MAILER_KEY | String | N/A | The mailer's api key.
| IMPRINT_URL | String(Url) | /imprint | The link to a imprint page for the system (Defaults to the frontend's imprint)
| PRIVACY_URL | String(Url) | /privacy | The link to a privacy page for the system (Defaults to the frontend's privacy page)
## Recommended Editor
+123 -44
View File
@@ -1,3 +1,32 @@
# @odit/class-validator-jsonschema
**Author**: Aleksi Pekkala <aleksipekkala@gmail.com>
**Repo**: git@github.com:epiphone/class-validator-jsonschema.git
**License**: MIT
**Description**: Convert class-validator-decorated classes into JSON schema
## License Text
MIT License
Copyright (c) 2017 Aleksi Pekkala
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# argon2
**Author**: Ranieri Althoff <ranisalt+argon2@gmail.com>
**Repo**: [object Object]
@@ -28,6 +57,33 @@ SOFTWARE.
# axios
**Author**: Matt Zabriskie
**Repo**: [object Object]
**License**: MIT
**Description**: Promise based HTTP client for the browser and node.js
## License Text
Copyright (c) 2014-present Matt Zabriskie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# body-parser
**Author**: undefined
**Repo**: expressjs/body-parser
@@ -88,22 +144,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# class-validator
**Author**: [object Object]
**Author**: TypeStack contributors
**Repo**: [object Object]
**License**: MIT
**Description**: Class-based validation with Typescript / ES6 / ES5 using decorators or validation schemas. Supports both node.js and browser
**Description**: Decorator-based property validation for classes.
## License Text
# class-validator-jsonschema
**Author**: Aleksi Pekkala <aleksipekkala@gmail.com>
**Repo**: git@github.com:epiphone/class-validator-jsonschema.git
**License**: MIT
**Description**: Convert class-validator-decorated classes into JSON schema
## License Text
MIT License
The MIT License
Copyright (c) 2017 Aleksi Pekkala
Copyright (c) 2015-2020 TypeStack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -112,17 +161,16 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# consola
**Author**: undefined
@@ -332,6 +380,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# libphonenumber-js
**Author**: catamphetamine <purecatamphetamine@gmail.com>
**Repo**: [object Object]
**License**: MIT
**Description**: A simpler (and smaller) rewrite of Google Android's libphonenumber library in javascript
## License Text
(The MIT License)
Copyright (c) 2016 @catamphetamine <purecatamphetamine@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# mysql
**Author**: Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)
**Repo**: mysqljs/mysql
@@ -594,7 +671,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**Author**: ODIT.Services
**Repo**: [object Object]
**License**: MIT
**Description**: A simple license crawler
**Description**: A simple license crawler for crediting open source work
## License Text
MIT License Copyright (c) 2020 ODIT.Services (info@odit.services)
@@ -820,33 +897,6 @@ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SOFTWARE
# axios
**Author**: Matt Zabriskie
**Repo**: [object Object]
**License**: MIT
**Description**: Promise based HTTP client for the browser and node.js
## License Text
Copyright (c) 2014-present Matt Zabriskie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# cp-cli
**Author**: undefined
**Repo**: [object Object]
@@ -934,6 +984,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# release-it
**Author**: [object Object]
**Repo**: [object Object]
**License**: MIT
**Description**: Generic CLI tool to automate versioning and package publishing related tasks.
## License Text
MIT License
Copyright (c) 2018 Lars Kappert
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# rimraf
**Author**: Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)
**Repo**: git://github.com/isaacs/rimraf.git
+11 -13
View File
@@ -1,6 +1,6 @@
{
"name": "@odit/lfk-backend",
"version": "0.4.0",
"version": "0.7.1",
"main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend",
"author": {
@@ -24,6 +24,7 @@
"dependencies": {
"@odit/class-validator-jsonschema": "2.1.1",
"argon2": "^0.27.1",
"axios": "^0.21.1",
"body-parser": "^1.19.0",
"class-transformer": "0.3.1",
"class-validator": "^0.13.1",
@@ -35,15 +36,14 @@
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"libphonenumber-js": "^1.9.7",
"libphonenumber-js": "^1.9.9",
"mysql": "^2.18.1",
"nodemailer": "^6.4.17",
"pg": "^8.5.1",
"reflect-metadata": "^0.1.13",
"routing-controllers": "^0.9.0-alpha.6",
"routing-controllers": "0.9.0-alpha.6",
"routing-controllers-openapi": "^2.2.0",
"sqlite3": "5.0.0",
"typeorm": "^0.2.29",
"typeorm": "^0.2.30",
"typeorm-routing-controllers-extensions": "^0.2.0",
"typeorm-seeding": "^1.6.1",
"uuid": "^8.3.2",
@@ -53,22 +53,20 @@
"@odit/license-exporter": "^0.0.9",
"@types/cors": "^2.8.9",
"@types/csvtojson": "^1.1.5",
"@types/express": "^4.17.9",
"@types/jest": "^26.0.16",
"@types/express": "^4.17.11",
"@types/jest": "^26.0.20",
"@types/jsonwebtoken": "^8.5.0",
"@types/node": "^14.14.20",
"@types/nodemailer": "^6.4.0",
"@types/node": "^14.14.22",
"@types/uuid": "^8.3.0",
"axios": "^0.21.1",
"cp-cli": "^2.0.0",
"jest": "^26.6.3",
"nodemon": "^2.0.7",
"release-it": "^14.2.2",
"rimraf": "^3.0.2",
"start-server-and-test": "^1.11.7",
"ts-jest": "^26.4.4",
"ts-jest": "^26.5.0",
"ts-node": "^9.1.1",
"typedoc": "^0.20.14",
"typedoc": "^0.20.19",
"typescript": "^4.1.3"
},
"scripts": {
@@ -82,7 +80,7 @@
"test:ci": "npm run test:ci:generate_env && npm run test:ci:run",
"seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed",
"openapi:export": "ts-node scripts/openapi_export.ts",
"licenses:export": "license-exporter --md",
"licenses:export": "license-exporter --markdown",
"release": "release-it --only-version"
},
"release-it": {
+10 -23
View File
@@ -1,15 +1,8 @@
import consola from "consola";
import fs from "fs";
import nodemailer from "nodemailer";
nodemailer.createTestAccount((err, account) => {
if (err) {
console.error('Failed to create a testing account. ' + err.message);
return process.exit(1);
}
const env = `
const env = `
APP_PORT=4010
DB_TYPE=sqlite
DB_HOST=bla
@@ -17,21 +10,15 @@ DB_PORT=bla
DB_USER=bla
DB_PASSWORD=bla
DB_NAME=./test.sqlite
NODE_ENV=dev
NODE_ENV=test
POSTALCODE_COUNTRYCODE=DE
SEED_TEST_DATA=true
MAIL_SERVER=${account.smtp.host}
MAIL_PORT=${account.smtp.port}
MAIL_USER=${account.user}
MAIL_PASSWORD=${account.pass}
MAIL_FROM=${account.user}`
try {
fs.writeFileSync("./.env", env, { encoding: "utf-8" });
consola.success("Exported ci env to .env");
} catch (error) {
consola.error("Couldn't export the ci env");
}
});
MAILER_URL=https://dev.lauf-fuer-kaya.de/mailer
MAILER_KEY=asdasd`;
try {
fs.writeFileSync("./.env", env, { encoding: "utf-8" });
consola.success("Exported ci env to .env");
} catch (error) {
consola.error("Couldn't export the ci env");
}
+1 -1
View File
@@ -42,7 +42,7 @@ export function generateSpec(storage: MetadataArgsStorage, schemas) {
}
},
info: {
description: "The the backend API for the LfK! runner system.",
description: `The the backend API for the LfK! runner system. <br>[Imprint](${config.imprint_url}) & [Privacy](${config.privacy_url})`,
title: "LfK! Backend API",
version: config.version
},
+3
View File
@@ -20,6 +20,9 @@ const app = createExpressServer({
async function main() {
await loaders(app);
if (config.testing) {
consola.info("🛠[config]: Discovered testing env. Mailing errors will get ignored!")
}
app.listen(config.internal_port, () => {
consola.success(
`⚡️[server]: Server is running at http://localhost:${config.internal_port}`
+9 -6
View File
@@ -6,17 +6,17 @@ configDotenv();
export const config = {
internal_port: parseInt(process.env.APP_PORT) || 4010,
development: process.env.NODE_ENV === "production",
testing: process.env.NODE_ENV === "test",
jwt_secret: process.env.JWT_SECRET || "secretjwtsecret",
phone_validation_countrycode: getPhoneCodeLocale(),
postalcode_validation_countrycode: getPostalCodeLocale(),
version: process.env.VERSION || require('../package.json').version,
seedTestData: getDataSeeding(),
app_url: process.env.APP_URL || "http://localhost:4010",
mail_server: process.env.MAIL_SERVER,
mail_port: Number(process.env.MAIL_PORT) || 25,
mail_user: process.env.MAIL_USER,
mail_password: process.env.MAIL_PASSWORD,
mail_from: process.env.MAIL_FROM
app_url: process.env.APP_URL || "http://localhost:8080",
privacy_url: process.env.PRIVACY_URL || "/privacy",
imprint_url: process.env.IMPRINT_URL || "/imprint",
mailer_url: process.env.MAILER_URL || "",
mailer_key: process.env.MAILER_KEY || ""
}
let errors = 0
if (typeof config.internal_port !== "number") {
@@ -25,6 +25,9 @@ if (typeof config.internal_port !== "number") {
if (typeof config.development !== "boolean") {
errors++
}
if (config.mailer_url == "" || config.mailer_key == "") {
errors++;
}
function getPhoneCodeLocale(): CountryCode {
return (process.env.PHONE_COUNTRYCODE as CountryCode);
}
+106 -110
View File
@@ -1,110 +1,106 @@
import { Body, CookieParam, JsonController, Param, Post, Req, Res } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { IllegalJWTError, InvalidCredentialsError, JwtNotProvidedError, PasswordNeededError, RefreshTokenCountInvalidError, UsernameOrEmailNeededError } from '../errors/AuthError';
import { UserNotFoundError } from '../errors/UserErrors';
import { Mailer } from '../mailer';
import { CreateAuth } from '../models/actions/create/CreateAuth';
import { CreateResetToken } from '../models/actions/create/CreateResetToken';
import { HandleLogout } from '../models/actions/HandleLogout';
import { RefreshAuth } from '../models/actions/RefreshAuth';
import { ResetPassword } from '../models/actions/ResetPassword';
import { ResponseAuth } from '../models/responses/ResponseAuth';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { Logout } from '../models/responses/ResponseLogout';
@JsonController('/auth')
export class AuthController {
private mailer: Mailer;
constructor() {
this.mailer = new Mailer();
}
@Post("/login")
@ResponseSchema(ResponseAuth)
@ResponseSchema(InvalidCredentialsError)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@ResponseSchema(PasswordNeededError)
@ResponseSchema(InvalidCredentialsError)
@OpenAPI({ description: 'Login with your username/email and password. <br> You will receive: \n * access token (use it as a bearer token) \n * refresh token (will also be sent as a cookie)' })
async login(@Body({ validate: true }) createAuth: CreateAuth, @Res() response: any) {
let auth;
try {
auth = await createAuth.toAuth();
response.cookie('lfk_backend__refresh_token', auth.refresh_token, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
response.cookie('lfk_backend__refresh_token_expires_at', auth.refresh_token_expires_at, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
return response.send(auth)
} catch (error) {
throw error;
}
}
@Post("/logout")
@ResponseSchema(Logout)
@ResponseSchema(InvalidCredentialsError)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@ResponseSchema(PasswordNeededError)
@ResponseSchema(InvalidCredentialsError)
@OpenAPI({ description: 'Logout using your refresh token. <br> This instantly invalidates all your access and refresh tokens.', security: [{ "RefreshTokenCookie": [] }] })
async logout(@Body({ validate: true }) handleLogout: HandleLogout, @CookieParam("lfk_backend__refresh_token") refresh_token: string, @Res() response: any) {
if (refresh_token && refresh_token.length != 0 && handleLogout.token == undefined) {
handleLogout.token = refresh_token;
}
let logout;
try {
logout = await handleLogout.logout()
await response.cookie('lfk_backend__refresh_token', "expired", { expires: new Date(Date.now()), httpOnly: true });
response.cookie('lfk_backend__refresh_token_expires_at', "expired", { expires: new Date(Date.now()), httpOnly: true });
} catch (error) {
throw error;
}
return response.send(logout)
}
@Post("/refresh")
@ResponseSchema(ResponseAuth)
@ResponseSchema(JwtNotProvidedError)
@ResponseSchema(IllegalJWTError)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(RefreshTokenCountInvalidError)
@OpenAPI({ description: 'Refresh your access and refresh tokens using a valid refresh token. <br> You will receive: \n * access token (use it as a bearer token) \n * refresh token (will also be sent as a cookie)', security: [{ "RefreshTokenCookie": [] }] })
async refresh(@Body({ validate: true }) refreshAuth: RefreshAuth, @CookieParam("lfk_backend__refresh_token") refresh_token: string, @Res() response: any, @Req() req: any) {
if (refresh_token && refresh_token.length != 0 && refreshAuth.token == undefined) {
refreshAuth.token = refresh_token;
}
let auth;
try {
auth = await refreshAuth.toAuth();
response.cookie('lfk_backend__refresh_token', auth.refresh_token, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
response.cookie('lfk_backend__refresh_token_expires_at', auth.refresh_token_expires_at, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
} catch (error) {
throw error;
}
return response.send(auth)
}
@Post("/reset")
@ResponseSchema(ResponseEmpty, { statusCode: 200 })
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@ResponseSchema(UsernameOrEmailNeededError, { statusCode: 406 })
@OpenAPI({ description: "Request a password reset token. <br> This will provide you with a reset token that you can use by posting to /api/auth/reset/{token}." })
async getResetToken(@Body({ validate: true }) passwordReset: CreateResetToken) {
const reset_token: String = await passwordReset.toResetToken();
await this.mailer.sendResetMail(passwordReset.email, reset_token);
return new ResponseEmpty();
}
@Post("/reset/:token")
@ResponseSchema(ResponseAuth)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@OpenAPI({ description: "Reset a user's utilising a valid password reset token. <br> This will set the user's password to the one you provided in the body. <br> To get a reset token post to /api/auth/reset with your username." })
async resetPassword(@Param("token") token: string, @Body({ validate: true }) passwordReset: ResetPassword) {
passwordReset.resetToken = token;
return await passwordReset.resetPassword();
}
}
import { Body, CookieParam, JsonController, Param, Post, QueryParam, Req, Res } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { IllegalJWTError, InvalidCredentialsError, JwtNotProvidedError, PasswordNeededError, RefreshTokenCountInvalidError, UsernameOrEmailNeededError } from '../errors/AuthError';
import { MailSendingError } from '../errors/MailErrors';
import { UserNotFoundError } from '../errors/UserErrors';
import { Mailer } from '../mailer';
import { CreateAuth } from '../models/actions/create/CreateAuth';
import { CreateResetToken } from '../models/actions/create/CreateResetToken';
import { HandleLogout } from '../models/actions/HandleLogout';
import { RefreshAuth } from '../models/actions/RefreshAuth';
import { ResetPassword } from '../models/actions/ResetPassword';
import { ResponseAuth } from '../models/responses/ResponseAuth';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { Logout } from '../models/responses/ResponseLogout';
@JsonController('/auth')
export class AuthController {
@Post("/login")
@ResponseSchema(ResponseAuth)
@ResponseSchema(InvalidCredentialsError)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@ResponseSchema(PasswordNeededError)
@ResponseSchema(InvalidCredentialsError)
@OpenAPI({ description: 'Login with your username/email and password. <br> You will receive: \n * access token (use it as a bearer token) \n * refresh token (will also be sent as a cookie)' })
async login(@Body({ validate: true }) createAuth: CreateAuth, @Res() response: any) {
let auth;
try {
auth = await createAuth.toAuth();
response.cookie('lfk_backend__refresh_token', auth.refresh_token, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
response.cookie('lfk_backend__refresh_token_expires_at', auth.refresh_token_expires_at, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
return response.send(auth)
} catch (error) {
throw error;
}
}
@Post("/logout")
@ResponseSchema(Logout)
@ResponseSchema(InvalidCredentialsError)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@ResponseSchema(PasswordNeededError)
@ResponseSchema(InvalidCredentialsError)
@OpenAPI({ description: 'Logout using your refresh token. <br> This instantly invalidates all your access and refresh tokens.', security: [{ "RefreshTokenCookie": [] }] })
async logout(@Body({ validate: true }) handleLogout: HandleLogout, @CookieParam("lfk_backend__refresh_token") refresh_token: string, @Res() response: any) {
if (refresh_token && refresh_token.length != 0 && handleLogout.token == undefined) {
handleLogout.token = refresh_token;
}
let logout;
try {
logout = await handleLogout.logout()
await response.cookie('lfk_backend__refresh_token', "expired", { expires: new Date(Date.now()), httpOnly: true });
response.cookie('lfk_backend__refresh_token_expires_at', "expired", { expires: new Date(Date.now()), httpOnly: true });
} catch (error) {
throw error;
}
return response.send(logout)
}
@Post("/refresh")
@ResponseSchema(ResponseAuth)
@ResponseSchema(JwtNotProvidedError)
@ResponseSchema(IllegalJWTError)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(RefreshTokenCountInvalidError)
@OpenAPI({ description: 'Refresh your access and refresh tokens using a valid refresh token. <br> You will receive: \n * access token (use it as a bearer token) \n * refresh token (will also be sent as a cookie)', security: [{ "RefreshTokenCookie": [] }] })
async refresh(@Body({ validate: true }) refreshAuth: RefreshAuth, @CookieParam("lfk_backend__refresh_token") refresh_token: string, @Res() response: any, @Req() req: any) {
if (refresh_token && refresh_token.length != 0 && refreshAuth.token == undefined) {
refreshAuth.token = refresh_token;
}
let auth;
try {
auth = await refreshAuth.toAuth();
response.cookie('lfk_backend__refresh_token', auth.refresh_token, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
response.cookie('lfk_backend__refresh_token_expires_at', auth.refresh_token_expires_at, { expires: new Date(auth.refresh_token_expires_at * 1000), httpOnly: true });
} catch (error) {
throw error;
}
return response.send(auth)
}
@Post("/reset")
@ResponseSchema(ResponseEmpty, { statusCode: 200 })
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@ResponseSchema(UsernameOrEmailNeededError, { statusCode: 406 })
@ResponseSchema(MailSendingError, { statusCode: 500 })
@OpenAPI({ description: "Request a password reset token. <br> This will provide you with a reset token that you can use by posting to /api/auth/reset/{token}." })
async getResetToken(@Body({ validate: true }) passwordReset: CreateResetToken, @QueryParam("locale") locale: string = "en") {
const reset_token: string = await passwordReset.toResetToken();
await Mailer.sendResetMail(passwordReset.email, reset_token, locale);
return new ResponseEmpty();
}
@Post("/reset/:token")
@ResponseSchema(ResponseAuth)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@OpenAPI({ description: "Reset a user's utilising a valid password reset token. <br> This will set the user's password to the one you provided in the body. <br> To get a reset token post to /api/auth/reset with your username." })
async resetPassword(@Param("token") token: string, @Body({ validate: true }) passwordReset: ResetPassword) {
passwordReset.resetToken = token;
return await passwordReset.resetPassword();
}
}
+2 -2
View File
@@ -78,7 +78,7 @@ export class DonationController {
async postDistance(@Body({ validate: true }) createDonation: CreateDistanceDonation) {
let donation = await createDonation.toEntity();
donation = await this.distanceDonationRepository.save(donation);
return (await this.donationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] })).toResponse();
return (await this.distanceDonationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] })).toResponse();
}
@Put('/fixed/:id')
@@ -124,7 +124,7 @@ export class DonationController {
}
await this.distanceDonationRepository.save(await donation.update(oldDonation));
return (await this.donationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] })).toResponse();
return (await this.distanceDonationRepository.findOne({ id: donation.id }, { relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] })).toResponse();
}
@Delete('/:id')
-26
View File
@@ -1,26 +0,0 @@
import { Authorized, JsonController, Post } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { config } from '../config';
import { Mailer } from '../mailer';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
@JsonController('/mails')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class MailController {
private mailer: Mailer;
constructor() {
this.mailer = new Mailer();
}
@Post('/test')
@Authorized(["MAIL:CREATE"])
@ResponseSchema(ResponseEmpty, { statusCode: 200 })
@OpenAPI({ description: 'Sends a test email to the configured from-address.' })
async get() {
await this.mailer.sendTestMail(config.mail_from);
return new ResponseEmpty();
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ export class MeController {
return new ResponseUser(user);
}
@Get('/')
@Get('/permissions')
@ResponseSchema(ResponseUserPermissions)
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@OnUndefined(UserNotFoundError)
+19 -4
View File
@@ -28,7 +28,7 @@ export class RunnerCardController {
@OpenAPI({ description: 'Lists all card.' })
async getAll() {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
const cards = await this.cardRepository.find({ relations: ['runner'] });
const cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'] });
cards.forEach(card => {
responseCards.push(new ResponseRunnerCard(card));
});
@@ -42,11 +42,26 @@ export class RunnerCardController {
@OnUndefined(RunnerCardNotFoundError)
@OpenAPI({ description: "Lists all information about the card whose id got provided." })
async getOne(@Param('id') id: number) {
let card = await this.cardRepository.findOne({ id: id }, { relations: ['runner'] });
let card = await this.cardRepository.findOne({ id: id }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] });
if (!card) { throw new RunnerCardNotFoundError(); }
return card.toResponse();
}
@Post('/bulk')
@Authorized("CARD:CREATE")
@ResponseSchema(ResponseEmpty, { statusCode: 200 })
@OpenAPI({ description: "Create blank cards in bulk. <br> Just provide the count as a query param and wait for the 200 response." })
async postBlancoBulk(@QueryParam("count") count: number) {
let createPromises = new Array<any>();
for (let index = 0; index < count; index++) {
createPromises.push(this.cardRepository.save({ runner: null, enabled: true }))
}
await Promise.all(createPromises);
let response = new ResponseEmpty();
response.response = `Created ${count} new blanco cards.`
return response;
}
@Post()
@Authorized("CARD:CREATE")
@ResponseSchema(ResponseRunnerCard)
@@ -55,7 +70,7 @@ export class RunnerCardController {
async post(@Body({ validate: true }) createCard: CreateRunnerCard) {
let card = await createCard.toEntity();
card = await this.cardRepository.save(card);
return (await this.cardRepository.findOne({ id: card.id }, { relations: ['runner'] })).toResponse();
return (await this.cardRepository.findOne({ id: card.id }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] })).toResponse();
}
@Put('/:id')
@@ -77,7 +92,7 @@ export class RunnerCardController {
}
await this.cardRepository.save(await card.update(oldCard));
return (await this.cardRepository.findOne({ id: id }, { relations: ['runner'] })).toResponse();
return (await this.cardRepository.findOne({ id: id }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] })).toResponse();
}
@Delete('/:id')
+5 -5
View File
@@ -32,7 +32,7 @@ export class RunnerController {
@OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' })
async getAll() {
let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
const runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'scans.track', 'cards'] });
const runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards'] });
runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner));
});
@@ -46,7 +46,7 @@ export class RunnerController {
@OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all information about the runner whose id got provided.' })
async getOne(@Param('id') id: number) {
let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'scans.track', 'cards'] })
let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards'] })
if (!runner) { throw new RunnerNotFoundError(); }
return new ResponseRunner(runner);
}
@@ -91,7 +91,7 @@ export class RunnerController {
}
runner = await this.runnerRepository.save(runner)
return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'scans.track', 'cards'] }));
return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards'] }));
}
@Put('/:id')
@@ -112,7 +112,7 @@ export class RunnerController {
}
await this.runnerRepository.save(await runner.update(oldRunner));
return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'scans.track', 'cards'] }));
return new ResponseRunner(await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards'] }));
}
@Delete('/:id')
@@ -125,7 +125,7 @@ export class RunnerController {
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let runner = await this.runnerRepository.findOne({ id: id });
if (!runner) { return null; }
const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'scans.track', 'cards'] });
const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards'] });
if (!runner) {
throw new RunnerNotFoundError();
@@ -58,8 +58,8 @@ export class RunnerOrganizationController {
async getRunners(@Param('id') id: number, @QueryParam('onlyDirect') onlyDirect: boolean) {
let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
let runners: Runner[];
if (!onlyDirect) { runners = (await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.scans', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.group', 'teams.runners.scans', 'teams.runners.scans.track'] })).allRunners; }
else { runners = (await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.scans', 'runners.scans.track'] })).runners; }
if (!onlyDirect) { runners = (await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.group.parentGroup', 'runners.scans', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.group', 'teams.runners.group.parentGroup', 'teams.runners.scans', 'teams.runners.scans.track'] })).allRunners; }
else { runners = (await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.group.parentGroup', 'runners.scans', 'runners.scans.track'] })).runners; }
runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner));
});
+95 -7
View File
@@ -1,24 +1,34 @@
import { Request } from "express";
import * as jwt from "jsonwebtoken";
import { Body, Get, JsonController, OnUndefined, Param, Post } from 'routing-controllers';
import { Body, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { config } from '../config';
import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError';
import { RunnerEmailNeededError, RunnerNotFoundError } from '../errors/RunnerErrors';
import { MailSendingError } from '../errors/MailErrors';
import { RunnerEmailNeededError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors';
import { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors';
import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
import { JwtCreator } from '../jwtcreator';
import { Mailer } from '../mailer';
import ScanAuth from '../middlewares/ScanAuth';
import { CreateSelfServiceCitizenRunner } from '../models/actions/create/CreateSelfServiceCitizenRunner';
import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServiceRunner';
import { Runner } from '../models/entities/Runner';
import { RunnerGroup } from '../models/entities/RunnerGroup';
import { RunnerOrganization } from '../models/entities/RunnerOrganization';
import { ScanStation } from '../models/entities/ScanStation';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseScanStation } from '../models/responses/ResponseScanStation';
import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation';
import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner';
import { ResponseSelfServiceScan } from '../models/responses/ResponseSelfServiceScan';
@JsonController('/runners')
@JsonController()
export class RunnerSelfServiceController {
private runnerRepository: Repository<Runner>;
private orgRepository: Repository<RunnerOrganization>;
private stationRepository: Repository<ScanStation>;
/**
* Gets the repository of this controller's model/entity.
@@ -26,9 +36,10 @@ export class RunnerSelfServiceController {
constructor() {
this.runnerRepository = getConnectionManager().get().getRepository(Runner);
this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization);
this.stationRepository = getConnectionManager().get().getRepository(ScanStation);
}
@Get('/me/:jwt')
@Get('/runners/me/:jwt')
@ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError)
@@ -37,7 +48,59 @@ export class RunnerSelfServiceController {
return (new ResponseSelfServiceRunner(await this.getRunner(token)));
}
@Post('/register')
@Get('/runners/me/:jwt/scans')
@ResponseSchema(ResponseSelfServiceScan, { isArray: true })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all your (runner) scans. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please contact support.' })
async getScans(@Param('jwt') token: string) {
const scans = (await this.getRunner(token)).scans;
let responseScans = new Array<ResponseSelfServiceScan>()
for (let scan of scans) {
responseScans.push(new ResponseSelfServiceScan(scan));
}
return responseScans;
}
@Get('/stations/me')
@UseBefore(ScanAuth)
@ResponseSchema(ResponseScanStation)
@ResponseSchema(ScanStationNotFoundError, { statusCode: 404 })
@OnUndefined(ScanStationNotFoundError)
@OpenAPI({ description: 'Lists basic information about the station whose token got provided. <br> This includes it\'s associated track.', security: [{ "StationApiToken": [] }] })
async getStationMe(@Req() req: Request) {
let scan = await this.stationRepository.findOne({ id: parseInt(req.headers["station_id"].toString()) }, { relations: ['track'] })
if (!scan) { throw new ScanStationNotFoundError(); }
return scan.toResponse();
}
@Post('/runners/forgot')
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(ResponseEmpty)
@OpenAPI({ description: 'Use this endpoint to reuqest a new selfservice token/link to be sent to your mail address (rate limited to one mail every 24hrs).' })
async requestNewToken(@QueryParam('mail') mail: string) {
if (!mail) {
throw new RunnerNotFoundError();
}
const runner = await this.runnerRepository.findOne({ email: mail });
if (!runner) { throw new RunnerNotFoundError(); }
if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 60 * 24)) { throw new RunnerSelfserviceTimeoutError(); }
const token = JwtCreator.createSelfService(runner);
try {
await Mailer.sendSelfserviceForgottenMail(runner.email, token, "en")
} catch (error) {
throw new MailSendingError();
}
runner.resetRequestedTimestamp = Math.floor(Date.now() / 1000);
await this.runnerRepository.save(runner);
return { token };
}
@Post('/runners/register')
@ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerEmailNeededError, { statusCode: 406 })
@OpenAPI({ description: 'Create a new selfservice runner in the citizen org. <br> This endpoint shoud be used to allow "everyday citizen" to register themselves. <br> You have to provide a mail address, b/c the future we\'ll implement email verification.' })
@@ -47,10 +110,17 @@ export class RunnerSelfServiceController {
runner = await this.runnerRepository.save(runner);
let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }));
response.token = JwtCreator.createSelfService(runner);
try {
await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, "en")
} catch (error) {
throw new MailSendingError();
}
return response;
}
@Post('/register/:token')
@Post('/runners/register/:token')
@ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Create a new selfservice runner in a provided org. <br> The orgs get provided and authorized via api tokens that can be optained via the /organizations endpoint.' })
@@ -62,9 +132,27 @@ export class RunnerSelfServiceController {
let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }));
response.token = JwtCreator.createSelfService(runner);
try {
await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, "en")
} catch (error) {
throw new MailSendingError();
}
return response;
}
@Get('/organizations/selfservice/:token')
@ResponseSchema(ResponseSelfServiceOrganisation, { isArray: false })
@ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Get the basic info and teams for a org.' })
async getSelfserviceOrg(@Param('token') token: string) {
const orgid = (await this.getOrgansisation(token)).id;
const org = await this.orgRepository.findOne({ id: orgid }, { relations: ['teams'] })
return new ResponseSelfServiceOrganisation(<RunnerOrganization>org);
}
/**
* Get's a runner by a provided jwt token.
* @param token The runner jwt provided by the runner to identitfy themselves.
+1 -1
View File
@@ -55,7 +55,7 @@ export class RunnerTeamController {
@OpenAPI({ description: 'Lists all runners from this team. <br> This includes the runner\'s group and distance ran.' })
async getRunners(@Param('id') id: number) {
let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
const runners = (await this.runnerTeamRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.scans', 'runners.scans.track'] })).runners;
const runners = (await this.runnerTeamRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.group.parentGroup', 'runners.scans', 'runners.scans.track'] })).runners;
runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner));
});
+16 -12
View File
@@ -1,4 +1,5 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam, UseBefore } from 'routing-controllers';
import { Request } from "express";
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam, Req, UseBefore } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
@@ -14,7 +15,6 @@ import { TrackScan } from '../models/entities/TrackScan';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseScan } from '../models/responses/ResponseScan';
import { ResponseTrackScan } from '../models/responses/ResponseTrackScan';
@JsonController('/scans')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class ScanController {
@@ -36,7 +36,7 @@ export class ScanController {
@OpenAPI({ description: 'Lists all scans (normal or track) from all runners. <br> This includes the scan\'s runner\'s distance ran.' })
async getAll() {
let responseScans: ResponseScan[] = new Array<ResponseScan>();
const scans = await this.scanRepository.find({ relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] });
const scans = await this.scanRepository.find({ relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] });
scans.forEach(scan => {
responseScans.push(scan.toResponse());
});
@@ -51,7 +51,7 @@ export class ScanController {
@OnUndefined(ScanNotFoundError)
@OpenAPI({ description: 'Lists all information about the scan whose id got provided. This includes the scan\'s runner\'s distance ran.' })
async getOne(@Param('id') id: number) {
let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })
let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] })
if (!scan) { throw new ScanNotFoundError(); }
return scan.toResponse();
}
@@ -60,22 +60,26 @@ export class ScanController {
@UseBefore(ScanAuth)
@ResponseSchema(ResponseScan)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Create a new scan (not track scan - use /scans/trackscans instead). <br> Please rmemember to provide the scan\'s runner\'s id and distance.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
@OpenAPI({ description: 'Create a new scan (not track scan - use /scans/trackscans instead). <br> Please rmemember to provide the scan\'s runner\'s id and distance.', security: [{ "StationApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async post(@Body({ validate: true }) createScan: CreateScan) {
let scan = await createScan.toEntity();
scan = await this.scanRepository.save(scan);
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Post("/trackscans")
@UseBefore(ScanAuth)
@ResponseSchema(ResponseTrackScan)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Create a new track scan (for "normal" scans use /scans instead). <br> Please remember that to provide the scan\'s card\'s station\'s id.', security: [{ "ScanApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async postTrackScans(@Body({ validate: true }) createScan: CreateTrackScan) {
@OpenAPI({ description: 'Create a new track scan (for "normal" scans use /scans instead). <br> Please remember that to provide the scan\'s card\'s station\'s id.', security: [{ "StationApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async postTrackScans(@Body({ validate: true }) createScan: CreateTrackScan, @Req() req: Request) {
const station_id = req.headers["station_id"];
if (station_id) {
createScan.station = parseInt(station_id.toString());
}
let scan = await createScan.toEntity();
scan = await this.trackScanRepository.save(scan);
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
return (await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Put('/:id')
@@ -97,7 +101,7 @@ export class ScanController {
}
await this.scanRepository.save(await scan.update(oldScan));
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Put('/trackscans/:id')
@@ -120,7 +124,7 @@ export class ScanController {
}
await this.trackScanRepository.save(await scan.update(oldScan));
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] })).toResponse();
return (await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] })).toResponse();
}
@Delete('/:id')
@@ -132,7 +136,7 @@ export class ScanController {
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let scan = await this.scanRepository.findOne({ id: id });
if (!scan) { return null; }
const responseScan = await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] });
const responseScan = await this.scanRepository.findOne({ id: scan.id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] });
await this.scanRepository.delete(scan);
return responseScan.toResponse();
+29 -11
View File
@@ -1,13 +1,13 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { EntityFromBody } from 'typeorm-routing-controllers-extensions';
import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors';
import { CreateUserGroup } from '../models/actions/create/CreateUserGroup';
import { UpdateUserGroup } from '../models/actions/update/UpdateUserGroup';
import { UserGroup } from '../models/entities/UserGroup';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseUserGroup } from '../models/responses/ResponseUserGroup';
import { ResponseUserGroupPermissions } from '../models/responses/ResponseUserGroupPermissions';
import { PermissionController } from './PermissionController';
@@ -25,20 +25,37 @@ export class UserGroupController {
@Get()
@Authorized("USERGROUP:GET")
@ResponseSchema(UserGroup, { isArray: true })
@ResponseSchema(ResponseUserGroup, { isArray: true })
@OpenAPI({ description: 'Lists all groups. <br> The information provided might change while the project continues to evolve.' })
getAll() {
return this.userGroupsRepository.find({ relations: ["permissions"] });
async getAll() {
let responseGroups: ResponseUserGroup[] = new Array<ResponseUserGroup>();
const groups = await this.userGroupsRepository.find({ relations: ['permissions'] });
groups.forEach(group => {
responseGroups.push(group.toResponse());
});
return responseGroups;
}
@Get('/:id')
@Authorized("USERGROUP:GET")
@ResponseSchema(UserGroup)
@ResponseSchema(ResponseUserGroup)
@ResponseSchema(UserGroupNotFoundError, { statusCode: 404 })
@OnUndefined(UserGroupNotFoundError)
@OpenAPI({ description: 'Lists all information about the group whose id got provided. <br> The information provided might change while the project continues to evolve.' })
getOne(@Param('id') id: number) {
return this.userGroupsRepository.findOne({ id: id }, { relations: ["permissions"] });
async getOne(@Param('id') id: number) {
return await (await (this.userGroupsRepository.findOne({ id: id }, { relations: ["permissions"] }))).toResponse();
}
@Get('/:id/permissions')
@Authorized("USERGROUP:GET")
@ResponseSchema(ResponseUserGroupPermissions)
@ResponseSchema(UserGroupNotFoundError, { statusCode: 404 })
@OnUndefined(UserGroupNotFoundError)
@OpenAPI({ description: 'Lists all permissions granted to the group as permission response objects.' })
async getPermissions(@Param('id') id: number) {
let group = await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions', 'permissions.principal'] })
if (!group) { throw new UserGroupNotFoundError(); }
return new ResponseUserGroupPermissions(group);
}
@Post()
@@ -54,7 +71,8 @@ export class UserGroupController {
throw error;
}
return this.userGroupsRepository.save(userGroup);
userGroup = await this.userGroupsRepository.save(userGroup);
return (await (this.userGroupsRepository.findOne({ id: userGroup.id }, { relations: ["permissions"] }))).toResponse();
}
@Put('/:id')
@@ -63,7 +81,7 @@ export class UserGroupController {
@ResponseSchema(UserGroupNotFoundError, { statusCode: 404 })
@ResponseSchema(UserGroupIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: "Update the group whose id you provided. <br> To change the permissions granted to the group please use /api/permissions instead. <br> Please remember that ids can't be changed." })
async put(@Param('id') id: number, @EntityFromBody() updateGroup: UpdateUserGroup) {
async put(@Param('id') id: number, @Body({ validate: true }) updateGroup: UpdateUserGroup) {
let oldGroup = await this.userGroupsRepository.findOne({ id: id });
if (!oldGroup) {
@@ -75,7 +93,7 @@ export class UserGroupController {
}
await this.userGroupsRepository.save(await updateGroup.update(oldGroup));
return (await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions', 'groups'] })).toResponse();
return (await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions'] })).toResponse();
}
@Delete('/:id')
@@ -85,7 +103,7 @@ export class UserGroupController {
@OnUndefined(204)
@OpenAPI({ description: 'Delete the group whose id you provided. <br> If there are any permissions directly granted to the group they will get deleted as well. <br> Users associated with this group won\'t get deleted - just deassociated. <br> If no group with this id exists it will just return 204(no content).' })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let group = await this.userGroupsRepository.findOne({ id: id }, { relations: ["permissions"] });
let group = await this.userGroupsRepository.findOne({ id: id });
if (!group) { return null; }
const responseGroup = await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions'] });
+9 -4
View File
@@ -1,12 +1,17 @@
import { IsString } from 'class-validator'
import { IsString } from 'class-validator';
import { InternalServerError } from 'routing-controllers';
/**
* Error to throw when a permission couldn't be found.
*/
export class MailServerConfigError extends Error {
export class MailSendingError extends InternalServerError {
@IsString()
name = "MailServerConfigError"
name = "MailSendingError"
@IsString()
message = "The SMTP server you provided couldn't be reached!"
message = "We had a problem sending the mail!"
constructor() {
super("We had a problem sending the mail!");
}
}
+11
View File
@@ -46,6 +46,17 @@ export class RunnerEmailNeededError extends NotAcceptableError {
message = "Citizenrunners have to provide an email address for verification and contacting."
}
/**
* Error to throw when a runner already requested a new selfservice link in the last 24hrs.
*/
export class RunnerSelfserviceTimeoutError extends NotAcceptableError {
@IsString()
name = "RunnerSelfserviceTimeoutError"
@IsString()
message = "You can only reqest a new token every 24hrs."
}
/**
* Error to throw when a runner still has distance donations associated.
*/
+43 -58
View File
@@ -1,79 +1,64 @@
import fs from "fs";
import nodemailer from 'nodemailer';
import { MailOptions } from 'nodemailer/lib/json-transport';
import Mail from 'nodemailer/lib/mailer';
import axios from 'axios';
import { config } from './config';
import { MailServerConfigError } from './errors/MailErrors';
import { MailSendingError } from './errors/MailErrors';
/**
* This class is responsible for all things mail sending.
* This uses the mail emplates from src/static/mail_templates
* This uses axios to communicate with the mailer api (https://git.odit.services/lfk/mailer).
*/
export class Mailer {
private transport: Mail;
public static base: string = config.mailer_url;
public static key: string = config.mailer_key;
public static testing: boolean = config.testing;
/**
* The class's default constructor.
* Creates the transporter and tests the connection.
*/
constructor() {
this.transport = nodemailer.createTransport({
host: config.mail_server,
port: config.mail_port,
auth: {
user: config.mail_user,
pass: config.mail_password
}
});
this.transport.verify(function (error, success) {
if (error) {
throw new MailServerConfigError();
}
});
}
/**
* Function for sending a test mail from the test mail template.
* Function for sending a password reset mail.
* @param to_address The address the mail will be sent to. Should always get pulled from a user object.
* @param token The requested password reset token - will be combined with the app_url to generate a password reset link.
*/
public async sendResetMail(to_address: string, token: String) {
const reset_link = `${config.app_url}/reset/${token}`
const body_html = fs.readFileSync(__dirname + '/static/mail_templates/pw-reset.html', { encoding: 'utf8' }).replace("{{reset_link}}", reset_link).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`);
const body_txt = fs.readFileSync(__dirname + '/static/mail_templates/pw-reset.html', { encoding: 'utf8' }).replace("{{reset_link}}", reset_link).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`);
const mail: MailOptions = {
to: to_address,
subject: "LfK! Password Reset",
text: body_txt,
html: body_html
};
await this.sendMail(mail);
public static async sendResetMail(to_address: string, token: string, locale: string = "en") {
try {
await axios.post(`${Mailer.base}/reset?locale=${locale}&key=${Mailer.key}`, {
address: to_address,
resetKey: token
});
} catch (error) {
if (Mailer.testing) { return true; }
throw new MailSendingError();
}
}
/**
* Function for sending a test mail from the test mail template.
* @param to_address The address the test mail will be sent to - this is the configured from-address by default.
* Function for sending a runner selfservice welcome mail.
* @param to_address The address the mail will be sent to. Should always get pulled from a runner object.
* @param token The requested selfservice token - will be combined with the app_url to generate a selfservice profile link.
*/
public async sendTestMail(to_address: string = config.mail_from) {
const body_html = fs.readFileSync(__dirname + '/static/mail_templates/test.html', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`);
const body_txt = fs.readFileSync(__dirname + '/static/mail_templates/test.txt', { encoding: 'utf8' }).replace("{{recipient_mail}}", to_address).replace("{{copyright_owner}}", "LfK!").replace("{{link_imprint}}", `${config.app_url}/imprint`).replace("{{link_privacy}}", `${config.app_url}/privacy`);
const mail: MailOptions = {
to: to_address,
subject: "LfK! Test Mail",
text: body_txt,
html: body_html
};
await this.sendMail(mail);
public static async sendSelfserviceWelcomeMail(to_address: string, token: string, locale: string = "en") {
try {
await axios.post(`${Mailer.base}/registration?locale=${locale}&key=${Mailer.key}`, {
address: to_address,
selfserviceToken: token
});
} catch (error) {
if (Mailer.testing) { return true; }
throw new MailSendingError();
}
}
/**
* Wrapper function for sending a mail via this object's transporter.
* @param mail MailOptions object containing the
* Function for sending a runner selfservice link forgotten mail.
* @param to_address The address the mail will be sent to. Should always get pulled from a runner object.
* @param token The requested selfservice token - will be combined with the app_url to generate a selfservice profile link.
*/
public async sendMail(mail: MailOptions) {
mail.from = config.mail_from;
await this.transport.sendMail(mail);
public static async sendSelfserviceForgottenMail(to_address: string, token: string, locale: string = "en") {
try {
await axios.post(`${Mailer.base}/registration_forgot?locale=${locale}&key=${Mailer.key}`, {
address: to_address,
selfserviceToken: token
});
} catch (error) {
if (Mailer.testing) { return true; }
throw new MailSendingError();
}
}
}
+7 -7
View File
@@ -15,14 +15,14 @@ import authchecker from './authchecker';
const ScanAuth = async (req: Request, res: Response, next: () => void) => {
let provided_token: string = req.headers["authorization"];
if (provided_token == "" || provided_token === undefined || provided_token === null) {
res.status(401).send("No api token provided.");
res.status(401).send({ http_code: 401, short: "no_token", message: "No api token provided." });
return;
}
try {
provided_token = provided_token.replace("Bearer ", "");
} catch (error) {
res.status(401).send("No valid jwt or api token provided.");
res.status(401).send({ http_code: 401, short: "no_token", message: "No valid jwt or api token provided." });
return;
}
@@ -32,7 +32,7 @@ const ScanAuth = async (req: Request, res: Response, next: () => void) => {
}
finally {
if (prefix == "" || prefix == undefined || prefix == null) {
res.status(401).send("Api token non-existent or invalid syntax.");
res.status(401).send({ http_code: 401, short: "invalid_token", message: "Api token non-existent or invalid syntax." });
return;
}
}
@@ -46,7 +46,7 @@ const ScanAuth = async (req: Request, res: Response, next: () => void) => {
}
finally {
if (user_authorized == false) {
res.status(401).send("Api token non-existent or invalid syntax.");
res.status(401).send({ http_code: 401, short: "invalid_token", message: "Api token non-existent or invalid syntax." });
return;
}
else {
@@ -56,13 +56,13 @@ const ScanAuth = async (req: Request, res: Response, next: () => void) => {
}
else {
if (station.enabled == false) {
res.status(401).send("Station disabled.");
res.status(401).send({ http_code: 401, short: "station_disabled", message: "Station is disabled." });
}
if (!(await argon2.verify(station.key, provided_token))) {
res.status(401).send("Api token invalid.");
res.status(401).send({ http_code: 401, short: "invalid_token", message: "Api token non-existent or invalid syntax." });
return;
}
req.headers["station_id"] = station.id.toString();
next();
}
}
@@ -23,7 +23,7 @@ export class CreateResetToken {
/**
* Create a password reset token based on this.
*/
public async toResetToken(): Promise<any> {
public async toResetToken(): Promise<string> {
if (!this.email) {
throw new UserEmailNeededError();
}
@@ -37,7 +37,7 @@ export class CreateResetToken {
await getConnectionManager().get().getRepository(User).save(found_user);
//Create the reset token
let reset_token = JwtCreator.createReset(found_user);
let reset_token: string = JwtCreator.createReset(found_user);
return reset_token;
}
+32 -11
View File
@@ -1,4 +1,5 @@
import { IsInt, IsPositive } from 'class-validator';
import { IsInt, IsOptional, IsPositive } from 'class-validator';
import { BadRequestError } from 'routing-controllers';
import { getConnection } from 'typeorm';
import { RunnerCardNotFoundError } from '../../../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../../../errors/RunnerErrors';
@@ -22,10 +23,12 @@ export class CreateTrackScan {
/**
* The scanning station's id that created the scan.
* Mainly used for logging and traceing back scans (or errors).
* You don't have to provide the station if you're authenticateing via a scanstation token (The server takes care of it for you).
*/
@IsInt()
@IsPositive()
station: number;
@IsOptional()
station?: number;
/**
* Creates a new Track entity from this.
@@ -44,11 +47,15 @@ export class CreateTrackScan {
}
newScan.timestamp = Math.round(new Date().getTime() / 1000);
newScan.valid = await this.validateScan(newScan);
newScan = await this.validateScan(newScan);
return newScan;
}
/**
* Get's a runnerCard entity via the provided id.
* @returns The runnerCard whom's id you provided.
*/
public async getCard(): Promise<RunnerCard> {
const track = await getConnection().getRepository(RunnerCard).findOne({ id: this.card }, { relations: ["runner"] });
if (!track) {
@@ -57,7 +64,14 @@ export class CreateTrackScan {
return track;
}
/**
* Get's a scanstation entity via the provided id.
* @returns The scanstation whom's id you provided.
*/
public async getStation(): Promise<ScanStation> {
if (!this.station) {
throw new BadRequestError("You are missing the station's id!")
}
const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }, { relations: ["track"] });
if (!station) {
throw new ScanStationNotFoundError();
@@ -65,15 +79,22 @@ export class CreateTrackScan {
return station;
}
public async validateScan(scan: TrackScan): Promise<boolean> {
/**
* Validates the scan and sets it's lap time;
* @param scan The scan you want to validate
* @returns The validated scan with it's laptime set.
*/
public async validateScan(scan: TrackScan): Promise<TrackScan> {
const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner, valid: true }, relations: ["track"] });
if (scans.length == 0) { return true; }
const newestScan = scans[scans.length - 1];
if ((scan.timestamp - newestScan.timestamp) > scan.track.minimumLapTime) {
return true;
if (scans.length == 0) {
scan.lapTime = 0;
scan.valid = true;
}
return false;
else {
const newestScan = scans[scans.length - 1];
scan.lapTime = scan.timestamp - newestScan.timestamp;
scan.valid = (scan.lapTime > scan.track.minimumLapTime);
}
return scan;
}
}
+9 -10
View File
@@ -1,9 +1,9 @@
import { IsBoolean, IsInt, IsOptional, IsPositive } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerNotFoundError } from '../../../errors/RunnerErrors';
import { ScanStationNotFoundError } from '../../../errors/ScanStationErrors';
import { TrackNotFoundError } from '../../../errors/TrackErrors';
import { Runner } from '../../entities/Runner';
import { ScanStation } from '../../entities/ScanStation';
import { Track } from '../../entities/Track';
import { TrackScan } from '../../entities/TrackScan';
/**
@@ -38,7 +38,7 @@ export abstract class UpdateTrackScan {
*/
@IsInt()
@IsPositive()
public station: number;
public track: number;
/**
* Update a TrackScan entity based on this.
@@ -47,8 +47,7 @@ export abstract class UpdateTrackScan {
public async update(scan: TrackScan): Promise<TrackScan> {
scan.valid = this.valid;
scan.runner = await this.getRunner();
scan.station = await this.getStation();
scan.track = scan.station.track;
scan.track = await this.getTrack();
return scan;
}
@@ -67,11 +66,11 @@ export abstract class UpdateTrackScan {
/**
* Gets a runner based on the runner id provided via this.runner.
*/
public async getStation(): Promise<ScanStation> {
const station = await getConnection().getRepository(ScanStation).findOne({ id: this.station }, { relations: ['track'] });
if (!station) {
throw new ScanStationNotFoundError();
public async getTrack(): Promise<Track> {
const track = await getConnection().getRepository(Track).findOne({ id: this.track });
if (!track) {
throw new TrackNotFoundError();
}
return station;
return track;
}
}
+11 -2
View File
@@ -1,5 +1,5 @@
import { IsInt, IsNotEmpty } from "class-validator";
import { ChildEntity, ManyToOne, OneToMany } from "typeorm";
import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { ChildEntity, Column, ManyToOne, OneToMany } from "typeorm";
import { ResponseRunner } from '../responses/ResponseRunner';
import { DistanceDonation } from "./DistanceDonation";
import { Participant } from "./Participant";
@@ -43,6 +43,15 @@ export class Runner extends Participant {
@OneToMany(() => Scan, scan => scan.runner, { nullable: true })
scans: Scan[];
/**
* The last time the runner requested a selfservice link.
* Used to prevent spamming of the selfservice link forgotten route.
*/
@Column({ nullable: true, unique: false })
@IsString()
@IsOptional()
resetRequestedTimestamp?: number;
/**
* Returns all valid scans associated with this runner.
* This is implemented here to avoid duplicate code in other files.
+10
View File
@@ -2,6 +2,8 @@ import {
IsInt,
IsNotEmpty,
IsNumber,
IsPositive
} from "class-validator";
import { ChildEntity, Column, ManyToOne } from "typeorm";
@@ -59,6 +61,14 @@ export class TrackScan extends Scan {
@IsInt()
timestamp: number;
/**
* The scan's lap time.
* This simply get's calculated from the last lap time;
*/
@Column()
@IsNumber()
lapTime: number;
/**
* Turns this entity into it's response class.
*/
+1 -2
View File
@@ -4,7 +4,6 @@ import {
IsString
} from "class-validator";
import { ChildEntity, Column } from "typeorm";
import { ResponsePrincipal } from '../responses/ResponsePrincipal';
import { ResponseUserGroup } from '../responses/ResponseUserGroup';
import { Principal } from './Principal';
@@ -34,7 +33,7 @@ export class UserGroup extends Principal {
/**
* Turns this entity into it's response class.
*/
public toResponse(): ResponsePrincipal {
public toResponse(): ResponseUserGroup {
return new ResponseUserGroup(this);
}
}
+38
View File
@@ -0,0 +1,38 @@
/**
* This enum contains all object types/entities a response can contain.
*/
export enum ResponseObjectType {
AUTH = 'AUTH',
DISTANCEDONATION = 'DISTANCEDONATION',
DONATION = 'DONATION',
DONOR = 'DONOR',
EMPTY = 'EMPTY',
GROUPCONTACT = 'GROUPCONTACT',
LOGOUT = 'LOGOUT',
PARTICIPANT = 'PARTICIPANT',
PERMISSION = 'PERMISSION',
PRINCIPAL = 'PRINCIPAL',
RUNNER = 'RUNNER',
RUNNERCARD = 'RUNNERCARD',
RUNNERGROUP = 'RUNNERGROUP',
RUNNERORGANIZATION = 'RUNNERORGANIZATION',
RUNNERTEAM = 'RUNNERTEAM',
SCAN = 'SCAN',
SCANSTATION = 'SCANSTATION',
SELFSERVICEDONATION = 'SELFSERVICEDONATION',
SELFSERVICERUNNER = 'SELFSERVICRUNNER',
SELFSERVICESCAN = 'SELFSERVICESCAN',
SELFSERVICETRACKSCAN = 'SELFSERVICETRACKSCAN',
SELFSERVICETEAM = 'SELFSERVICETEAM',
SELFSERVICEORGANIZATION = 'SELFSERVICEORGANIZATION',
STATS = 'STATS',
STATSCLIENT = 'STATSCLIENT',
STATSORGANIZATION = 'STATSORGANIZATION',
STATSRUNNER = 'STATSRUNNER',
STATSTEAM = 'STATSTEAM',
TRACK = 'TRACK',
TRACKSCAN = 'TRACKSCAN',
USER = 'USER',
USERGROUP = 'USERGROUP',
USERPERMISSIONS = 'USERPERMISSIONS',
}
+13
View File
@@ -0,0 +1,13 @@
import { ResponseObjectType } from '../enums/ResponseObjectType';
/**
* Defines the repsonse interface.
* This forces all response classes to implement the interfaces properties.
*/
export interface IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType;
}
+10 -1
View File
@@ -1,9 +1,18 @@
import { IsInt, IsString } from 'class-validator';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the repsonse auth.
*/
export class ResponseAuth {
export class ResponseAuth implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.AUTH;
/**
* The access_token - JWT shortterm access token.
*/
@@ -1,12 +1,19 @@
import { IsInt, IsObject, IsPositive } from 'class-validator';
import { DistanceDonation } from '../entities/DistanceDonation';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseDonation } from './ResponseDonation';
import { ResponseRunner } from './ResponseRunner';
/**
* Defines the distance donation response.
*/
export class ResponseDistanceDonation extends ResponseDonation {
export class ResponseDistanceDonation extends ResponseDonation implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.DISTANCEDONATION;
/**
* The donation's associated runner.
@@ -29,7 +36,7 @@ export class ResponseDistanceDonation extends ResponseDonation {
*/
public constructor(donation: DistanceDonation) {
super(donation);
this.runner = donation.runner.toResponse();
if (donation.runner) { this.runner = donation.runner.toResponse(); }
this.amountPerDistance = donation.amountPerDistance;
}
}
+10 -1
View File
@@ -1,11 +1,20 @@
import { IsInt, IsNotEmpty, IsPositive } from "class-validator";
import { Donation } from '../entities/Donation';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseDonor } from './ResponseDonor';
/**
* Defines the donation response.
*/
export class ResponseDonation {
export class ResponseDonation implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.DONATION;
/**
* The donation's id.
*/
+8 -1
View File
@@ -2,12 +2,19 @@ import {
IsBoolean, IsInt
} from "class-validator";
import { Donor } from '../entities/Donor';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseParticipant } from './ResponseParticipant';
/**
* Defines the donor response.
*/
export class ResponseDonor extends ResponseParticipant {
export class ResponseDonor extends ResponseParticipant implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.DONOR;
/**
* Does this donor need a receipt?
+9 -1
View File
@@ -1,9 +1,17 @@
import { IsString } from 'class-validator';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines a empty response object.
*/
export class ResponseEmpty {
export class ResponseEmpty implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.EMPTY;
@IsString()
response: string = "nothing here"
}
+13 -3
View File
@@ -1,12 +1,20 @@
import { IsInt, IsObject, IsString } from "class-validator";
import { Address } from '../entities/Address';
import { GroupContact } from '../entities/GroupContact';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
/**
* Defines the group contact response.
*/
export class ResponseGroupContact {
export class ResponseGroupContact implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.GROUPCONTACT;
/**
* The contact's id.
*/
@@ -69,8 +77,10 @@ export class ResponseGroupContact {
this.email = contact.email;
this.address = contact.address;
this.groups = new Array<ResponseRunnerGroup>();
for (let group of contact.groups) {
this.groups.push(group.toResponse());
if (contact.groups) {
for (let group of contact.groups) {
this.groups.push(group.toResponse());
}
}
}
}
+9 -1
View File
@@ -1,9 +1,17 @@
import { IsString } from 'class-validator';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the logout response.
*/
export class Logout {
export class Logout implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.LOGOUT;
/**
* The logout's timestamp.
*/
+9 -1
View File
@@ -1,11 +1,19 @@
import { IsInt, IsObject, IsOptional, IsString } from "class-validator";
import { Address } from '../entities/Address';
import { Participant } from '../entities/Participant';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the participant response.
*/
export abstract class ResponseParticipant {
export abstract class ResponseParticipant implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.PARTICIPANT;
/**
* The participant's id.
*/
+9 -1
View File
@@ -7,12 +7,20 @@ import {
import { Permission } from '../entities/Permission';
import { PermissionAction } from '../enums/PermissionAction';
import { PermissionTarget } from '../enums/PermissionTargets';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponsePrincipal } from './ResponsePrincipal';
/**
* Defines the permission response.
*/
export class ResponsePermission {
export class ResponsePermission implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.PERMISSION;
/**
* The permission's id.
*/
+9 -1
View File
@@ -2,11 +2,19 @@ import {
IsInt
} from "class-validator";
import { Principal } from '../entities/Principal';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the principal response.
*/
export abstract class ResponsePrincipal {
export abstract class ResponsePrincipal implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.PRINCIPAL;
/**
* The principal's id.
+11 -4
View File
@@ -3,13 +3,20 @@ import {
IsObject
} from "class-validator";
import { Runner } from '../entities/Runner';
import { RunnerGroup } from '../entities/RunnerGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseParticipant } from './ResponseParticipant';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
/**
* Defines the runner response.
*/
export class ResponseRunner extends ResponseParticipant {
export class ResponseRunner extends ResponseParticipant implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.RUNNER;
/**
* The runner's currently ran distance in meters.
@@ -21,7 +28,7 @@ export class ResponseRunner extends ResponseParticipant {
* The runner's group.
*/
@IsObject()
group: RunnerGroup;
group: ResponseRunnerGroup;
/**
* Creates a ResponseRunner object from a runner.
@@ -31,6 +38,6 @@ export class ResponseRunner extends ResponseParticipant {
super(runner);
if (!runner.scans) { this.distance = 0 }
else { this.distance = runner.validScans.reduce((sum, current) => sum + current.distance, 0); }
this.group = runner.group;
if (runner.group) { this.group = runner.group.toResponse(); }
}
}
+9 -1
View File
@@ -1,11 +1,19 @@
import { IsBoolean, IsEAN, IsInt, IsNotEmpty, IsObject, IsString } from "class-validator";
import { RunnerCard } from '../entities/RunnerCard';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunner } from './ResponseRunner';
/**
* Defines the runner card response.
*/
export class ResponseRunnerCard {
export class ResponseRunnerCard implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.RUNNERCARD;
/**
* The card's id.
*/
+12 -4
View File
@@ -1,11 +1,19 @@
import { IsInt, IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator";
import { GroupContact } from '../entities/GroupContact';
import { RunnerGroup } from '../entities/RunnerGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseGroupContact } from './ResponseGroupContact';
/**
* Defines the runnerGroup response.
*/
export abstract class ResponseRunnerGroup {
export abstract class ResponseRunnerGroup implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.RUNNERGROUP;
/**
* The runnerGroup's id.
*/
@@ -26,7 +34,7 @@ export abstract class ResponseRunnerGroup {
*/
@IsObject()
@IsOptional()
contact?: GroupContact;
contact?: ResponseGroupContact;
/**
* Creates a ResponseRunnerGroup object from a runnerGroup.
@@ -35,6 +43,6 @@ export abstract class ResponseRunnerGroup {
public constructor(group: RunnerGroup) {
this.id = group.id;
this.name = group.name;
this.contact = group.contact;
if (group.contact) { this.contact = group.contact.toResponse(); };
}
}
@@ -11,13 +11,20 @@ import {
} from "class-validator";
import { Address } from '../entities/Address';
import { RunnerOrganization } from '../entities/RunnerOrganization';
import { RunnerTeam } from '../entities/RunnerTeam';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
import { ResponseRunnerTeam } from './ResponseRunnerTeam';
/**
* Defines the runnerOrganization response.
*/
export class ResponseRunnerOrganization extends ResponseRunnerGroup {
export class ResponseRunnerOrganization extends ResponseRunnerGroup implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.RUNNERORGANIZATION;
/**
* The runnerOrganization's address.
@@ -30,7 +37,7 @@ export class ResponseRunnerOrganization extends ResponseRunnerGroup {
* The runnerOrganization associated teams.
*/
@IsArray()
teams: RunnerTeam[];
teams: ResponseRunnerTeam[];
/**
* The organization's registration key.
@@ -55,7 +62,13 @@ export class ResponseRunnerOrganization extends ResponseRunnerGroup {
public constructor(org: RunnerOrganization) {
super(org);
this.address = org.address;
this.teams = org.teams;
this.teams = new Array<ResponseRunnerTeam>();
if (org.teams) {
for (let team of org.teams) {
this.teams.push(team.toResponse());
}
}
if (!org.key) { this.registrationEnabled = false; }
else { this.registrationKey = Buffer.from(org.key).toString('base64'); }
}
+9 -2
View File
@@ -1,12 +1,19 @@
import { IsNotEmpty, IsObject } from "class-validator";
import { RunnerTeam } from '../entities/RunnerTeam';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
import { ResponseRunnerOrganization } from './ResponseRunnerOrganization';
/**
* Defines the runnerTeam response.
*/
export class ResponseRunnerTeam extends ResponseRunnerGroup {
export class ResponseRunnerTeam extends ResponseRunnerGroup implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.RUNNERTEAM;
/**
* The runnerTeam's parent group (organization).
@@ -21,6 +28,6 @@ export class ResponseRunnerTeam extends ResponseRunnerGroup {
*/
public constructor(team: RunnerTeam) {
super(team);
this.parentGroup = team.parentGroup.toResponse();
if (team.parentGroup) { this.parentGroup = team.parentGroup.toResponse(); }
}
}
+10 -2
View File
@@ -1,11 +1,19 @@
import { IsBoolean, IsInt, IsNotEmpty, IsPositive } from "class-validator";
import { Scan } from '../entities/Scan';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunner } from './ResponseRunner';
/**
* Defines the scan response.
*/
export class ResponseScan {
export class ResponseScan implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SCAN;
/**
* The scans's id.
*/
@@ -39,7 +47,7 @@ export class ResponseScan {
*/
public constructor(scan: Scan) {
this.id = scan.id;
this.runner = scan.runner.toResponse();
if (scan.runner) { this.runner = scan.runner.toResponse(); }
this.distance = scan.distance;
this.valid = scan.valid;
}
+10 -2
View File
@@ -11,12 +11,20 @@ import {
IsString
} from "class-validator";
import { ScanStation } from '../entities/ScanStation';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseTrack } from './ResponseTrack';
/**
* Defines the statsClient response.
*/
export class ResponseScanStation {
export class ResponseScanStation implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SCANSTATION;
/**
* The client's id.
*/
@@ -64,7 +72,7 @@ export class ResponseScanStation {
this.description = station.description;
this.prefix = station.prefix;
this.key = "Only visible on creation.";
this.track = station.track;
if (station.track) { this.track = station.track.toResponse(); }
this.enabled = station.enabled;
}
}
@@ -1,11 +1,19 @@
import { IsInt, IsNotEmpty, IsPositive } from 'class-validator';
import { DistanceDonation } from '../entities/DistanceDonation';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the runner selfservice donation response.
* Why? B/C runner's are not allowed to view all information available to admin users.
*/
export class ResponseSelfServiceDonation {
export class ResponseSelfServiceDonation implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SELFSERVICEDONATION;
/**
* The donation's donor.
*/
@@ -0,0 +1,38 @@
import { IsArray, IsNotEmpty, IsString } from 'class-validator';
import { RunnerOrganization } from '../entities/RunnerOrganization';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseSelfServiceTeam } from './ResponseSelfServiceTeam';
/**
* Defines the runner selfservice organization response.
* Why? B/C runner's are not allowed to view all information available to admin users.
*/
export class ResponseSelfServiceOrganisation implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SELFSERVICEORGANIZATION;
/**
* The org's name.
*/
@IsNotEmpty()
@IsString()
name: string;
/**
* The org's teams (just containing name and id).
*/
@IsArray()
teams: ResponseSelfServiceTeam[];
public constructor(org: RunnerOrganization) {
this.name = org.name;
this.teams = new Array<ResponseSelfServiceTeam>();
for (let team of org.teams) {
this.teams.push(new ResponseSelfServiceTeam(team));
}
}
}
@@ -3,6 +3,8 @@ import { DistanceDonation } from '../entities/DistanceDonation';
import { Runner } from '../entities/Runner';
import { RunnerGroup } from '../entities/RunnerGroup';
import { RunnerTeam } from '../entities/RunnerTeam';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseParticipant } from './ResponseParticipant';
import { ResponseSelfServiceDonation } from './ResponseSelfServiceDonation';
@@ -10,7 +12,12 @@ import { ResponseSelfServiceDonation } from './ResponseSelfServiceDonation';
* Defines the runner selfservice response.
* Why? B/C runner's are not allowed to view all information available to admin users.
*/
export class ResponseSelfServiceRunner extends ResponseParticipant {
export class ResponseSelfServiceRunner extends ResponseParticipant implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SELFSERVICERUNNER;
/**
* The runner's currently ran distance in meters.
@@ -0,0 +1,57 @@
import { IsBoolean, IsInt, IsNotEmpty, IsPositive } from "class-validator";
import { Scan } from '../entities/Scan';
import { TrackScan } from '../entities/TrackScan';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the scan selfservice response.
*/
export class ResponseSelfServiceScan implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SELFSERVICESCAN;
/**
* The scans's id (for sorting).
*/
@IsInt()
id: number;
/**
* Is the scan valid (for fraud reasons).
* The determination of validity will work differently for every child class.
*/
@IsBoolean()
valid: boolean = true;
/**
* The scans's length/distance in meters.
*/
@IsInt()
@IsPositive()
distance: number;
/**
* The scans's lap time (0 if non is availdable).
*/
@IsInt()
@IsNotEmpty()
lapTime: number = 0;
/**
* Creates a ResponseScan object from a scan.
* @param scan The scan the response shall be build for.
*/
public constructor(scan: Scan | TrackScan) {
this.id = scan.id;
this.distance = scan.distance;
this.valid = scan.valid;
if (scan instanceof TrackScan) {
this.lapTime = scan.lapTime;
this.responseType = ResponseObjectType.SELFSERVICETRACKSCAN;
}
}
}
@@ -0,0 +1,36 @@
import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator';
import { RunnerTeam } from '../entities/RunnerTeam';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the runner selfservice team response.
* Why? B/C runner's are not allowed to view all information available to admin users.
*/
export class ResponseSelfServiceTeam implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SELFSERVICETEAM;
/**
* The team's name.
*/
@IsNotEmpty()
@IsString()
name: string;
/**
* The team's id.
* Will be used to insert runners it into that team.
*/
@IsInt()
@IsPositive()
id: number;
public constructor(team: RunnerTeam) {
this.name = team.name;
this.id = team.id;
}
}
+9 -1
View File
@@ -7,12 +7,20 @@ import { RunnerOrganization } from '../entities/RunnerOrganization';
import { RunnerTeam } from '../entities/RunnerTeam';
import { Scan } from '../entities/Scan';
import { User } from '../entities/User';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the stats response.
* The stats response calculates some basic stats for a dashboard or public display.
*/
export class ResponseStats {
export class ResponseStats implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.STATS;
/**
* The amount of runners registered in the system.
*/
+9 -1
View File
@@ -8,11 +8,19 @@ import {
IsString
} from "class-validator";
import { StatsClient } from '../entities/StatsClient';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the statsClient response.
*/
export class ResponseStatsClient {
export class ResponseStatsClient implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.STATSCLIENT;
/**
* The client's id.
*/
@@ -4,12 +4,20 @@ import {
IsString
} from "class-validator";
import { RunnerOrganization } from '../entities/RunnerOrganization';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the org stats response.
* This differs from the normal org responce.
*/
export class ResponseStatsOrgnisation {
export class ResponseStatsOrgnisation implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.STATSORGANIZATION;
/**
* The orgs's id.
*/
+12 -4
View File
@@ -4,13 +4,21 @@ import {
IsString
} from "class-validator";
import { Runner } from '../entities/Runner';
import { RunnerGroup } from '../entities/RunnerGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
/**
* Defines the runner stats response.
* This differs from the normal runner responce.
*/
export class ResponseStatsRunner {
export class ResponseStatsRunner implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.STATSRUNNER;
/**
* The runner's id.
*/
@@ -51,7 +59,7 @@ export class ResponseStatsRunner {
* The runner's group.
*/
@IsObject()
group: RunnerGroup;
group: ResponseRunnerGroup;
/**
* Creates a new runner stats response from a runner
@@ -64,6 +72,6 @@ export class ResponseStatsRunner {
this.lastname = runner.lastname;
this.distance = runner.distance;
this.donationAmount = runner.distanceDonationAmount;
this.group = runner.group;
this.group = runner.group.toResponse();
}
}
+12 -4
View File
@@ -3,14 +3,22 @@ import {
IsObject,
IsString
} from "class-validator";
import { RunnerGroup } from '../entities/RunnerGroup';
import { RunnerTeam } from '../entities/RunnerTeam';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
/**
* Defines the team stats response.
* This differs from the normal team responce.
*/
export class ResponseStatsTeam {
export class ResponseStatsTeam implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.STATSTEAM;
/**
* The team's id.
*/
@@ -39,7 +47,7 @@ export class ResponseStatsTeam {
* The teams's parent group.
*/
@IsObject()
parent: RunnerGroup;
parent: ResponseRunnerGroup;
/**
* Creates a new team stats response from a team
@@ -48,7 +56,7 @@ export class ResponseStatsTeam {
public constructor(team: RunnerTeam) {
this.name = team.name;
this.id = team.id;
this.parent = team.parentGroup;
this.parent = team.parentGroup.toResponse();
this.distance = team.distance;
this.donationAmount = team.distanceDonationAmount;
}
+9 -1
View File
@@ -1,11 +1,19 @@
import { IsInt, IsOptional, IsString } from "class-validator";
import { TrackLapTimeCantBeNegativeError } from '../../errors/TrackErrors';
import { Track } from '../entities/Track';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the track response.
*/
export class ResponseTrack {
export class ResponseTrack implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.TRACK;
/**
* The track's id.
*/
+22 -6
View File
@@ -1,5 +1,7 @@
import { IsDateString, IsNotEmpty } from "class-validator";
import { IsDateString, IsNotEmpty, IsNumber } from "class-validator";
import { TrackScan } from '../entities/TrackScan';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseRunnerCard } from './ResponseRunnerCard';
import { ResponseScan } from './ResponseScan';
import { ResponseScanStation } from './ResponseScanStation';
@@ -8,10 +10,16 @@ import { ResponseTrack } from './ResponseTrack';
/**
* Defines the trackScan response.
*/
export class ResponseTrackScan extends ResponseScan {
export class ResponseTrackScan extends ResponseScan implements IResponse {
/**
* The scan's associated track.
*/
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.TRACKSCAN;
/**
* The scan's associated track.
*/
@IsNotEmpty()
track: ResponseTrack;
@@ -34,6 +42,13 @@ export class ResponseTrackScan extends ResponseScan {
@IsNotEmpty()
timestamp: number;
/**
* The scan's lap time.
* This simply get's calculated from the last lap time;
*/
@IsNumber()
lapTime: number;
/**
* Creates a ResponseTrackScan object from a scan.
* @param scan The trackSscan the response shall be build for.
@@ -41,9 +56,10 @@ export class ResponseTrackScan extends ResponseScan {
public constructor(scan: TrackScan) {
super(scan);
this.track = new ResponseTrack(scan.track);
this.card = scan.card.toResponse();
this.station = scan.station.toResponse();
if (scan.card) { scan.card.toResponse(); }
if (scan.station) { scan.station.toResponse(); }
this.timestamp = scan.timestamp;
this.distance = scan.distance;
this.lapTime = scan.lapTime;
}
}
+17 -6
View File
@@ -6,13 +6,21 @@ import {
IsString
} from "class-validator";
import { User } from '../entities/User';
import { UserGroup } from '../entities/UserGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponsePrincipal } from './ResponsePrincipal';
import { ResponseUserGroup } from './ResponseUserGroup';
/**
* Defines the user response.
*/
export class ResponseUser extends ResponsePrincipal {
export class ResponseUser extends ResponsePrincipal implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.USER;
/**
* The user's first name.
*/
@@ -66,7 +74,7 @@ export class ResponseUser extends ResponsePrincipal {
*/
@IsArray()
@IsOptional()
groups: UserGroup[];
groups: ResponseUserGroup[];
/**
* The user's permissions.
@@ -90,10 +98,13 @@ export class ResponseUser extends ResponsePrincipal {
this.username = user.username;
this.enabled = user.enabled;
this.profilePic = user.profilePic;
this.groups = user.groups;
this.groups = new Array<ResponseUserGroup>();
this.permissions = user.allPermissions;
if (this.groups) {
this.groups.forEach(function (g) { delete g.permissions });
if (user.groups) {
for (let group of user.groups) {
delete group.permissions;
this.groups.push(group.toResponse());
}
}
}
}
+15 -4
View File
@@ -1,12 +1,19 @@
import { IsArray, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { Permission } from '../entities/Permission';
import { UserGroup } from '../entities/UserGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponsePrincipal } from './ResponsePrincipal';
/**
* Defines the userGroup response.
*/
export class ResponseUserGroup extends ResponsePrincipal {
export class ResponseUserGroup extends ResponsePrincipal implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.USERGROUP;
/**
* The userGroup's name.
*/
@@ -26,7 +33,7 @@ export class ResponseUserGroup extends ResponsePrincipal {
*/
@IsArray()
@IsOptional()
permissions: Permission[];
permissions: string[] = new Array<string>();
/**
* Creates a ResponseUserGroup object from a userGroup.
@@ -36,6 +43,10 @@ export class ResponseUserGroup extends ResponsePrincipal {
super(group);
this.name = group.name;
this.description = group.description;
this.permissions = group.permissions;
if (group.permissions) {
for (let permission of group.permissions) {
this.permissions.push(permission.toString());
}
}
}
}
@@ -0,0 +1,43 @@
import {
IsArray,
IsOptional
} from "class-validator";
import { UserGroup } from '../entities/UserGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponsePermission } from './ResponsePermission';
/**
* Defines the group permission response (get /api/groups/:id/permissions).
*/
export class ResponseUserGroupPermissions implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.USERPERMISSIONS;
/**
* The permissions directly granted to the group.
*/
@IsArray()
@IsOptional()
directlyGranted: ResponsePermission[] = new Array<ResponsePermission>();
/**
* Is just here for compatability.
*/
@IsArray()
@IsOptional()
inherited: ResponsePermission[] = new Array<ResponsePermission>();
/**
* Creates a ResponseUserPermissions object from a group.
* @param group The group the response shall be build for.
*/
public constructor(group: UserGroup) {
for (let permission of group.permissions) {
this.directlyGranted.push(permission.toResponse());
}
}
}
@@ -5,12 +5,20 @@ import {
IsOptional
} from "class-validator";
import { User } from '../entities/User';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponsePermission } from './ResponsePermission';
/**
* Defines the user permission response (get /api/users/:id/permissions).
*/
export class ResponseUserPermissions {
export class ResponseUserPermissions implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.USERPERMISSIONS;
/**
* The permissions directly granted to the user.
*/
View File
-384
View File
@@ -1,384 +0,0 @@
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>LfK! - Passwort zurücksetzen</title> <!-- The title tag shows in email notifications, like Android 4.4. -->
<meta charset="utf-8"> <!-- utf-8 works for most cases -->
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
<meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no"> <!-- Tell iOS not to automatically link certain text strings. -->
<!-- CSS Reset : BEGIN -->
<style>
/* What it does: Remove spaces around the email design added by some email clients. */
/* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
html,
body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
/* What it does: Stops email clients resizing small text. */
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
/* What it does: Centers email on Android 4.4 */
div[style*="margin: 16px 0"] {
margin:0 !important;
}
/* What it does: Stops Outlook from adding extra spacing to tables. */
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
/* What it does: Fixes webkit padding issue. */
table {
border: 0;
border-spacing: 0;
border-collapse: collapse
}
/* What it does: Forces Samsung Android mail clients to use the entire viewport. */
#MessageViewBody,
#MessageWebViewDiv{
width: 100% !important;
}
/* What it does: Uses a better rendering method when resizing images in IE. */
img {
-ms-interpolation-mode:bicubic;
}
/* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
a {
text-decoration: none;
}
/* What it does: A work-around for email clients automatically linking certain text strings. */
/* iOS */
a[x-apple-data-detectors],
.unstyle-auto-detected-links a,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
u + #body a, /* Gmail */
#MessageViewBody a /* Samsung Mail */
{
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
/* What it does: Prevents Gmail from changing the text color in conversation threads. */
.im {
color: inherit !important;
}
/* What it does: Prevents Gmail from displaying an download button on large, non-linked images. */
.a6S {
display: none !important;
opacity: 0.01 !important;
}
/* If the above doesn't work, add a .g-img class to any image in question. */
img.g-img + div {
display:none !important;
}
/* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */
/* Create one of these media queries for each additional viewport size you'd like to fix */
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u ~ div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u ~ div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u ~ div .email-container {
min-width: 414px !important;
}
}
</style>
<!-- What it does: Helps DPI scaling in Outlook 2007-2013 -->
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!-- CSS Reset : END -->
<!-- Progressive Enhancements : BEGIN -->
<style>
/* What it does: Hover styles for buttons and tags */
.s-btn__primary:hover {
background: #0077CC !important;
border-color: #0077CC !important;
}
.s-btn__white:hover {
background: #EFF0F1 !important;
border-color: #EFF0F1 !important;
}
.s-btn__outlined:hover {
background: rgba(0,119,204,.05) !important;
color: #005999 !important;
}
.s-tag:hover,
.post-tag:hover {
border-color: #cee0ed !important;
background: #cee0ed !important;
}
/* What it does: Styles markdown links that we can't write inline CSS for. */
.has-markdown a,
.has-markdown a:visited {
color: #0077CC !important;
text-decoration: none !important;
}
/* What it does: Styles markdown code blocks that we can't write inline CSS for. */
code {
padding: 1px 5px;
background-color: #EFF0F1;
color: #242729;
font-size: 13px;
line-height: inherit;
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, sans-serif;
}
pre {
margin: 0 0 15px;
line-height: 17px;
background-color: #EFF0F1;
padding: 4px 8px;
border-radius: 3px;
overflow-x: auto;
}
pre code {
margin: 0 0 15px;
padding: 0;
line-height: 17px;
background-color: none;
}
/* What it does: Styles markdown blockquotes that we can't write inline CSS for. */
blockquote {
margin: 0 0 15px;
padding: 4px 10px;
background-color: #FFF8DC;
border-left: 2px solid #ffeb8e;
}
blockquote p {
padding: 4px 0;
margin: 0;
overflow-wrap: break-word;
}
/* What it does: Rounds corners in email clients that support it */
.bar {
border-radius: 5px;
}
.btr {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.bbr {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
@media screen and (max-width: 680px) {
/* What it does: Forces table cells into full-width rows. */
.stack-column,
.stack-column-center {
display: block !important;
width: 100% !important;
max-width: 100% !important;
direction: ltr !important;
}
/* And center justify these ones. */
.stack-column-center {
text-align: center !important;
}
/* Hides things in small viewports. */
.hide-on-mobile {
display: none !important;
max-height: 0 !important;
overflow: hidden !important;
visibility: hidden !important;
}
/* What it does: Utility classes to reduce spacing for smaller viewports. */
.sm-p-none {padding: 0 !important;}
.sm-pt-none {padding-top: 0 !important;}
.sm-pb-none {padding-bottom: 0 !important;}
.sm-pr-none {padding-right: 0 !important;}
.sm-pl-none {padding-left: 0 !important;}
.sm-px-none {padding-left: 0 !important; padding-right: 0 !important;}
.sm-py-none {padding-top: 0 !important; padding-bottom: 0 !important;}
.sm-p {padding: 20px !important;}
.sm-pt {padding-top: 20px !important;}
.sm-pb {padding-bottom: 20px !important;}
.sm-pr {padding-right: 20px !important;}
.sm-pl {padding-left: 20px !important;}
.sm-px {padding-left: 20px !important; padding-right: 20px !important;}
.sm-py {padding-top: 20px !important; padding-bottom: 20px !important;}
.sm-mb {margin-bottom: 20px !important;}
/* What it does: Utility classes to kill border radius for smaller viewports. Used mainly on the email's main container(s). */
.bar,
.btr,
.bbr {
border-top-left-radius: 0;
border-top-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
</style>
<!-- Progressive Enhancements : END -->
</head>
<!--
The email background color is defined in three places, just below. If you change one, remember to change the others.
1. body tag: for most email clients
2. center tag: for Gmail and Inbox mobile apps and web versions of Gmail, GSuite, Inbox, Yahoo, AOL, Libero, Comcast, freenet, Mail.ru, Orange.fr
3. mso conditional: For Windows 10 Mail
-->
<body width="100%" style="margin: 0; padding: 0 !important; background: #f3f3f5; mso-line-height-rule: exactly;">
<center style="width: 100%; background: #f3f3f5;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f3f5;">
<tr>
<td>
<![endif]-->
<!-- Visually Hidden Preview Text : BEGIN -->
<div style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
LfK! - Password reset
</div>
<!-- Visually Hidden Preview Text : END -->
<div class="email-container" style="max-width: 680px; margin: 0 auto;">
<!--[if mso]>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
<tr>
<td>
<![endif]-->
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="max-width: 680px; width:100%">
<tr>
<td style="padding: 30px; background-color: #ffffff;" class="sm-p bar">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tr>
<td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
<h1 style="font-weight: bold; font-size: 27px; line-height: 27px; color: #0C0D0E; margin: 0 0 15px 0;">LfK!</h1>
</td>
</tr>
<tr>
<td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
<h1 style="font-weight: bold; font-size: 21px; line-height: 21px; color: #0C0D0E; margin: 0 0 15px 0;">Password reset</h1>
<p style="margin: 0 0 15px;" class="has-markdown">A password reset for your account got requested.<br><b>If you didn't request the reset please ignore this mail.</b><br>Your password won't be changed until you click the reset link below and set a new one.</p>
</td>
</tr>
<!-- Button Row : BEGIN -->
<tr>
<td>
<!-- Button : BEGIN -->
<table align="left" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="s-btn s-btn__primary" style="border-radius: 4px; background: #0095ff;">
<a class="s-btn s-btn__primary" href="{{reset_link}}" target="_parent" style="background: #0095FF; border: 1px solid #0077cc; box-shadow: inset 0 1px 0 0 rgba(102,191,255,.75); font-family: arial, sans-serif; font-size: 17px; line-height: 17px; color: #ffffff; text-align: center; text-decoration: none; padding: 13px 17px; display: block; border-radius: 4px; white-space: nowrap;">Reset password</a>
</td>
</tr>
</table>
<!-- Button : END -->
</td>
</tr>
<!-- Button Row : END -->
</table>
</td>
</tr>
<!-----------------------------
EMAIL BODY : END
------------------------------>
<!-- Footer : BEGIN -->
<tr>
<td style="padding: 30px;" class="sm-p">
<table align="left" border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<!-- Subscription Info : BEGIN -->
<tr>
<td style="padding-bottom: 10px; font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
Copyright © {{copyright_owner}}. All rights reserved.
</td>
</tr>
<tr>
<td style="font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
<a href="{{link_imprint}}"
style="color: #9199A1; text-decoration: underline;">Imprint</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{link_privacy}}" style="color: #9199A1; text-decoration: underline;">Privacy</a>
</td>
</tr>
<!-- Subscription Info : BEGIN -->
<!-- HR line : BEGIN -->
<tr>
<td style="padding: 30px 0;" width="100%" class="sm-py">
<table aria-hidden="true" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%">
<tr>
<td height="1" width="100%" style="font-size: 0; line-height: 0; border-top: 1px solid #D6D8DB;">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<!-- HR line : END -->
<tr>
<td style="padding-bottom: 5px; font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">This mail was sent to <strong>{{recipient_mail}}</strong> because someone request a password reset for a account linked to the mail address.</td>
</tr>
<!-- Sender Info : END -->
</table>
</td>
</tr>
<!-- Footer : END -->
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</html>
-12
View File
@@ -1,12 +0,0 @@
LfK! - Password reset.
A password reset for your account got requested
If you didn't request the reset please ignore this mail
Your password won't be changed until you click the reset link below and set a new one.
Reset: {{reset_link}}
Copyright © {{copyright_owner}}. All rights reserved.
Imprint: {{link_imprint}} | Privacy: {{link_privacy}}
This mail was sent to {{recipient_mail}} because someone request a password reset for a account linked to the mail address.
-369
View File
@@ -1,369 +0,0 @@
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>LfK! - Mail test</title> <!-- The title tag shows in email notifications, like Android 4.4. -->
<meta charset="utf-8"> <!-- utf-8 works for most cases -->
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
<meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no"> <!-- Tell iOS not to automatically link certain text strings. -->
<!-- CSS Reset : BEGIN -->
<style>
/* What it does: Remove spaces around the email design added by some email clients. */
/* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
html,
body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
/* What it does: Stops email clients resizing small text. */
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
/* What it does: Centers email on Android 4.4 */
div[style*="margin: 16px 0"] {
margin:0 !important;
}
/* What it does: Stops Outlook from adding extra spacing to tables. */
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
/* What it does: Fixes webkit padding issue. */
table {
border: 0;
border-spacing: 0;
border-collapse: collapse
}
/* What it does: Forces Samsung Android mail clients to use the entire viewport. */
#MessageViewBody,
#MessageWebViewDiv{
width: 100% !important;
}
/* What it does: Uses a better rendering method when resizing images in IE. */
img {
-ms-interpolation-mode:bicubic;
}
/* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
a {
text-decoration: none;
}
/* What it does: A work-around for email clients automatically linking certain text strings. */
/* iOS */
a[x-apple-data-detectors],
.unstyle-auto-detected-links a,
.aBn {
border-bottom: 0 !important;
cursor: default !important;
color: inherit !important;
text-decoration: none !important;
font-size: inherit !important;
font-family: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
}
u + #body a, /* Gmail */
#MessageViewBody a /* Samsung Mail */
{
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
/* What it does: Prevents Gmail from changing the text color in conversation threads. */
.im {
color: inherit !important;
}
/* What it does: Prevents Gmail from displaying an download button on large, non-linked images. */
.a6S {
display: none !important;
opacity: 0.01 !important;
}
/* If the above doesn't work, add a .g-img class to any image in question. */
img.g-img + div {
display:none !important;
}
/* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */
/* Create one of these media queries for each additional viewport size you'd like to fix */
/* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
@media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
u ~ div .email-container {
min-width: 320px !important;
}
}
/* iPhone 6, 6S, 7, 8, and X */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u ~ div .email-container {
min-width: 375px !important;
}
}
/* iPhone 6+, 7+, and 8+ */
@media only screen and (min-device-width: 414px) {
u ~ div .email-container {
min-width: 414px !important;
}
}
</style>
<!-- What it does: Helps DPI scaling in Outlook 2007-2013 -->
<!--[if gte mso 9]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!-- CSS Reset : END -->
<!-- Progressive Enhancements : BEGIN -->
<style>
/* What it does: Hover styles for buttons and tags */
.s-btn__primary:hover {
background: #0077CC !important;
border-color: #0077CC !important;
}
.s-btn__white:hover {
background: #EFF0F1 !important;
border-color: #EFF0F1 !important;
}
.s-btn__outlined:hover {
background: rgba(0,119,204,.05) !important;
color: #005999 !important;
}
.s-tag:hover,
.post-tag:hover {
border-color: #cee0ed !important;
background: #cee0ed !important;
}
/* What it does: Styles markdown links that we can't write inline CSS for. */
.has-markdown a,
.has-markdown a:visited {
color: #0077CC !important;
text-decoration: none !important;
}
/* What it does: Styles markdown code blocks that we can't write inline CSS for. */
code {
padding: 1px 5px;
background-color: #EFF0F1;
color: #242729;
font-size: 13px;
line-height: inherit;
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, sans-serif;
}
pre {
margin: 0 0 15px;
line-height: 17px;
background-color: #EFF0F1;
padding: 4px 8px;
border-radius: 3px;
overflow-x: auto;
}
pre code {
margin: 0 0 15px;
padding: 0;
line-height: 17px;
background-color: none;
}
/* What it does: Styles markdown blockquotes that we can't write inline CSS for. */
blockquote {
margin: 0 0 15px;
padding: 4px 10px;
background-color: #FFF8DC;
border-left: 2px solid #ffeb8e;
}
blockquote p {
padding: 4px 0;
margin: 0;
overflow-wrap: break-word;
}
/* What it does: Rounds corners in email clients that support it */
.bar {
border-radius: 5px;
}
.btr {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.bbr {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
@media screen and (max-width: 680px) {
/* What it does: Forces table cells into full-width rows. */
.stack-column,
.stack-column-center {
display: block !important;
width: 100% !important;
max-width: 100% !important;
direction: ltr !important;
}
/* And center justify these ones. */
.stack-column-center {
text-align: center !important;
}
/* Hides things in small viewports. */
.hide-on-mobile {
display: none !important;
max-height: 0 !important;
overflow: hidden !important;
visibility: hidden !important;
}
/* What it does: Utility classes to reduce spacing for smaller viewports. */
.sm-p-none {padding: 0 !important;}
.sm-pt-none {padding-top: 0 !important;}
.sm-pb-none {padding-bottom: 0 !important;}
.sm-pr-none {padding-right: 0 !important;}
.sm-pl-none {padding-left: 0 !important;}
.sm-px-none {padding-left: 0 !important; padding-right: 0 !important;}
.sm-py-none {padding-top: 0 !important; padding-bottom: 0 !important;}
.sm-p {padding: 20px !important;}
.sm-pt {padding-top: 20px !important;}
.sm-pb {padding-bottom: 20px !important;}
.sm-pr {padding-right: 20px !important;}
.sm-pl {padding-left: 20px !important;}
.sm-px {padding-left: 20px !important; padding-right: 20px !important;}
.sm-py {padding-top: 20px !important; padding-bottom: 20px !important;}
.sm-mb {margin-bottom: 20px !important;}
/* What it does: Utility classes to kill border radius for smaller viewports. Used mainly on the email's main container(s). */
.bar,
.btr,
.bbr {
border-top-left-radius: 0;
border-top-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
</style>
<!-- Progressive Enhancements : END -->
</head>
<!--
The email background color is defined in three places, just below. If you change one, remember to change the others.
1. body tag: for most email clients
2. center tag: for Gmail and Inbox mobile apps and web versions of Gmail, GSuite, Inbox, Yahoo, AOL, Libero, Comcast, freenet, Mail.ru, Orange.fr
3. mso conditional: For Windows 10 Mail
-->
<body width="100%" style="margin: 0; padding: 0 !important; background: #f3f3f5; mso-line-height-rule: exactly;">
<center style="width: 100%; background: #f3f3f5;">
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f3f5;">
<tr>
<td>
<![endif]-->
<!-- Visually Hidden Preview Text : BEGIN -->
<div style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
LfK! - Mail test
</div>
<!-- Visually Hidden Preview Text : END -->
<div class="email-container" style="max-width: 680px; margin: 0 auto;">
<!--[if mso]>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
<tr>
<td>
<![endif]-->
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="max-width: 680px; width:100%">
<tr>
<td style="padding: 30px; background-color: #ffffff;" class="sm-p bar">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tr>
<td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
<h1 style="font-weight: bold; font-size: 27px; line-height: 27px; color: #0C0D0E; margin: 0 0 15px 0;">LfK!</h1>
</td>
</tr>
<tr>
<td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
<h1 style="font-weight: bold; font-size: 21px; line-height: 21px; color: #0C0D0E; margin: 0 0 15px 0;">Test mail</h1>
<p style="margin: 0 0 15px;" class="has-markdown">This is a test mail triggered by an admin in the LfK! backend.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-----------------------------
EMAIL BODY : END
------------------------------>
<!-- Footer : BEGIN -->
<tr>
<td style="padding: 30px;" class="sm-p">
<table align="left" border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<!-- Subscription Info : BEGIN -->
<tr>
<td style="padding-bottom: 10px; font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
Copyright © {{copyright_owner}}. All rights reserved.
</td>
</tr>
<tr>
<td style="font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
<a href="{{link_imprint}}"
style="color: #9199A1; text-decoration: underline;">Imprint</a>&nbsp;&nbsp;&nbsp;&nbsp;
<a href="{{link_privacy}}" style="color: #9199A1; text-decoration: underline;">Privacy</a>
</td>
</tr>
<!-- Subscription Info : BEGIN -->
<!-- HR line : BEGIN -->
<tr>
<td style="padding: 30px 0;" width="100%" class="sm-py">
<table aria-hidden="true" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%">
<tr>
<td height="1" width="100%" style="font-size: 0; line-height: 0; border-top: 1px solid #D6D8DB;">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<!-- HR line : END -->
<tr>
<td style="padding-bottom: 5px; font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">This mail was sent to <strong>{{recipient_mail}}</strong> because someone request a mail test for this mail address.</td>
</tr>
<!-- Sender Info : END -->
</table>
</td>
</tr>
<!-- Footer : END -->
</table>
</div>
<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</html>
-8
View File
@@ -1,8 +0,0 @@
LfK! - Mail test.
This is a test mail triggered by an admin in the LfK! backend.
Copyright © {{copyright_owner}}. All rights reserved.
Imprint: {{link_imprint}} | Privacy: {{link_privacy}}
This mail was sent to {{recipient_mail}} because someone requested a mail test for this mail address.
+1
View File
@@ -5,6 +5,7 @@ const base = "http://localhost:" + config.internal_port
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
axios_config = {
validateStatus: undefined
};
+1
View File
@@ -8,6 +8,7 @@ const axios_config = {
};;
beforeAll(async () => {
jest.setTimeout(20000);
const res_login = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
await axios.post(base + '/api/users', {
"firstname": "demo_logout",
+1
View File
@@ -8,6 +8,7 @@ const axios_config = {
};;
beforeAll(async () => {
jest.setTimeout(20000);
const res_login = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
await axios.post(base + '/api/users', {
"firstname": "demo_refresh",
+3 -2
View File
@@ -8,6 +8,7 @@ const axios_config = {
};;
beforeAll(async () => {
jest.setTimeout(20000);
const res_login = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
await axios.post(base + '/api/users', {
"firstname": "demo_reset",
@@ -35,8 +36,8 @@ beforeAll(async () => {
describe('POST /api/auth/reset valid', () => {
let reset_token;
it('valid reset token request should return 200', async () => {
const res1 = await axios.post(base + '/api/auth/reset', { email: "demo_reset1@dev.lauf-fuer-kaya.de" });
it('valid reset token request should return 200 (500 w/o correct auth)', async () => {
const res1 = await axios.post(base + '/api/auth/reset', { email: "demo_reset1@dev.lauf-fuer-kaya.de" }, axios_config);
reset_token = res1.data.resetToken;
expect(res1.status).toEqual(200);
});
+38 -8
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -34,7 +35,8 @@ describe('POST /api/cards successfully (without runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a disabled card should return 200', async () => {
@@ -47,7 +49,8 @@ describe('POST /api/cards successfully (without runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": false
"enabled": false,
"responseType": "RUNNERCARD"
});
});
it('creating a enabled card should return 200', async () => {
@@ -60,7 +63,8 @@ describe('POST /api/cards successfully (without runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true
"enabled": true,
"responseType": "RUNNERCARD"
});
});
});
@@ -82,7 +86,6 @@ describe('POST /api/cards successfully (with runner)', () => {
"lastname": "last",
"group": added_org.id
}, axios_config);
delete res2.data.group;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -97,7 +100,8 @@ describe('POST /api/cards successfully (with runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a card with runner (no optional params) should return 200', async () => {
@@ -110,7 +114,8 @@ describe('POST /api/cards successfully (with runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a enabled card with runner should return 200', async () => {
@@ -124,7 +129,8 @@ describe('POST /api/cards successfully (with runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a disabled card with runner should return 200', async () => {
@@ -138,7 +144,31 @@ describe('POST /api/cards successfully (with runner)', () => {
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": false
"enabled": false,
"responseType": "RUNNERCARD"
});
});
});
// ---------------
describe('POST /api/cards/bulk successfully', () => {
it('creating a single new bulk card should return 200', async () => {
const res = await axios.post(base + '/api/cards/bulk?count=1', {}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('creating 50 new bulk card should return 200', async () => {
const res = await axios.post(base + '/api/cards/bulk?count=50', {}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('creating 250 new bulk card should return 200', async () => {
const res = await axios.post(base + '/api/cards/bulk?count=250', {}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('creating 2000 new bulk card should return 200', async () => {
const res = await axios.post(base + '/api/cards/bulk?count=2000', {}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
});
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+9 -6
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -63,7 +64,6 @@ describe('adding + updating card.runner successfully', () => {
"lastname": "last",
"group": added_org.id
}, axios_config);
delete res2.data.group;
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -74,7 +74,6 @@ describe('adding + updating card.runner successfully', () => {
"lastname": "last",
"group": added_org.id
}, axios_config);
delete res2.data.group;
added_runner2 = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
@@ -96,7 +95,8 @@ describe('adding + updating card.runner successfully', () => {
"id": added_card.id,
"runner": added_runner,
"enabled": true,
"code": added_card.code
"code": added_card.code,
"responseType": "RUNNERCARD"
});
});
it('valid runner update (change runner) should return 200', async () => {
@@ -110,7 +110,8 @@ describe('adding + updating card.runner successfully', () => {
"id": added_card.id,
"runner": added_runner2,
"enabled": true,
"code": added_card.code
"code": added_card.code,
"responseType": "RUNNERCARD"
});
});
});
@@ -142,7 +143,8 @@ describe('adding + updating other values successfully', () => {
"id": added_card.id,
"runner": null,
"enabled": false,
"code": added_card.code
"code": added_card.code,
"responseType": "RUNNERCARD"
});
});
it('valid enable update should return 200', async () => {
@@ -156,7 +158,8 @@ describe('adding + updating other values successfully', () => {
"id": added_card.id,
"runner": null,
"enabled": true,
"code": added_card.code
"code": added_card.code,
"responseType": "RUNNERCARD"
});
});
});
+9 -3
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -136,6 +137,7 @@ describe('POST /api/contacts working (with group)', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.groups[0].teams;
expect(res.data).toEqual({
"firstname": "first",
"middlename": null,
@@ -149,7 +151,8 @@ describe('POST /api/contacts working (with group)', () => {
"city": null,
"country": null
},
"groups": [added_org]
"groups": [added_org],
"responseType": "GROUPCONTACT"
});
});
it('creating a new contact with a valid team should return 200', async () => {
@@ -174,7 +177,8 @@ describe('POST /api/contacts working (with group)', () => {
"city": null,
"country": null
},
"groups": [added_team]
"groups": [added_team],
"responseType": "GROUPCONTACT"
});
});
it('creating a new contact with a valid org and team should return 200', async () => {
@@ -187,6 +191,7 @@ describe('POST /api/contacts working (with group)', () => {
expect(res.headers['content-type']).toContain("application/json");
added_contact = res.data
delete res.data.id;
delete res.data.groups[0].teams;
expect(res.data).toEqual({
"firstname": "first",
"middlename": null,
@@ -200,7 +205,8 @@ describe('POST /api/contacts working (with group)', () => {
"city": null,
"country": null
},
"groups": [added_org, added_team]
"groups": [added_org, added_team],
"responseType": "GROUPCONTACT"
});
});
it('checking if the added team\'s contact is the new contact should return 200', async () => {
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+1
View File
@@ -5,6 +5,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+15 -6
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -86,7 +87,8 @@ describe('Update contact group after adding (should work)', () => {
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_contact = res.data
added_contact = res.data;
delete res.data.groups[0].teams;
expect(res.data).toEqual({
"id": res.data.id,
"firstname": "first",
@@ -101,7 +103,8 @@ describe('Update contact group after adding (should work)', () => {
"city": null,
"country": null
},
"groups": [added_org]
"groups": [added_org],
"responseType": "GROUPCONTACT"
});
});
it('valid group update to single team should return 200', async () => {
@@ -127,7 +130,8 @@ describe('Update contact group after adding (should work)', () => {
"city": null,
"country": null
},
"groups": [added_team]
"groups": [added_team],
"responseType": "GROUPCONTACT"
});
});
it('valid group update to org and team should return 200', async () => {
@@ -139,6 +143,7 @@ describe('Update contact group after adding (should work)', () => {
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.groups[0].teams;
expect(res.data).toEqual({
"id": res.data.id,
"firstname": "first",
@@ -153,7 +158,8 @@ describe('Update contact group after adding (should work)', () => {
"city": null,
"country": null
},
"groups": [added_org, added_team]
"groups": [added_org, added_team],
"responseType": "GROUPCONTACT"
});
});
it('valid group update to none should return 200', async () => {
@@ -179,7 +185,8 @@ describe('Update contact group after adding (should work)', () => {
"city": null,
"country": null
},
"groups": []
"groups": [],
"responseType": "GROUPCONTACT"
});
});
});
@@ -206,6 +213,7 @@ describe('Update contact group invalid after adding (should fail)', () => {
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
added_contact = res.data
delete res.data.groups[0].teams;
expect(res.data).toEqual({
"id": res.data.id,
"firstname": "first",
@@ -220,7 +228,8 @@ describe('Update contact group invalid after adding (should fail)', () => {
"city": null,
"country": null
},
"groups": [added_org]
"groups": [added_org],
"responseType": "GROUPCONTACT"
});
});
it('invalid group update to single team should return 404', async () => {
+5 -2
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -179,7 +180,8 @@ describe('POST /api/donations/fixed successfully', () => {
expect(res.headers['content-type']).toContain("application/json");
expect(res.data).toEqual({
"donor": added_donor,
"amount": 1000
"amount": 1000,
"responseType": "DONATION"
});
});
});
@@ -230,7 +232,8 @@ describe('POST /api/donations/distance successfully', () => {
"donor": added_donor,
"amountPerDistance": 100,
"runner": added_runner,
"amount": 0
"amount": 0,
"responseType": "DISTANCEDONATION"
})
});
});
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+1 -1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -323,7 +324,6 @@ describe('adding + updating distance donation valid', () => {
"amountPerDistance": 69,
"donor": added_donor.id
}, axios_config);
delete res.data.runner.group;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
expect(res.data.runner).toEqual(added_runner2);
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+1
View File
@@ -5,6 +5,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
-22
View File
@@ -1,22 +0,0 @@
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
let axios_config;
beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('POST /mails/test valid', () => {
it('test mail request should return 200', async () => {
const res1 = await axios.post(base + '/api/mails/test', null, axios_config);
});
});
+5 -4
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -55,7 +56,6 @@ describe('adding + getting from all orgs', () => {
delete added_org.id
expect(added_org).toEqual({
"name": "test123",
"contact": null,
"address": {
"address1": null,
"address2": null,
@@ -64,7 +64,8 @@ describe('adding + getting from all orgs', () => {
"postalcode": null,
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
})
});
});
@@ -89,7 +90,6 @@ describe('adding + getting explicitly', () => {
delete added_org2.id
expect(added_org2).toEqual({
"name": "test123",
"contact": null,
"address": {
"address1": null,
"address2": null,
@@ -98,7 +98,8 @@ describe('adding + getting explicitly', () => {
"postalcode": null,
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
})
});
});
+4 -3
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -43,7 +44,6 @@ describe('adding + deletion (successfull)', () => {
delete added_org2.id
expect(added_org2).toEqual({
"name": "test123",
"contact": null,
"address": {
"address1": null,
"address2": null,
@@ -52,7 +52,8 @@ describe('adding + deletion (successfull)', () => {
"postalcode": null,
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('check if org really was deleted', async () => {
@@ -127,7 +128,6 @@ describe('adding + deletion with teams still existing (with force)', () => {
delete added_org2.teams;
expect(added_org2).toEqual({
"name": "test123",
"contact": null,
"address": {
"address1": null,
"address2": null,
@@ -136,6 +136,7 @@ describe('adding + deletion with teams still existing (with force)', () => {
"postalcode": null,
},
"registrationEnabled": false,
"responseType": "RUNNERORGANIZATION"
});
});
it('check if org really was deleted', async () => {
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+17 -16
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -41,7 +42,6 @@ describe('adding + updating name', () => {
delete added_org2.id
expect(added_org2).toEqual({
"name": "testlelele",
"contact": null,
"address": {
"address1": null,
"address2": null,
@@ -50,7 +50,8 @@ describe('adding + updating name', () => {
"postalcode": null,
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
})
});
});
@@ -109,7 +110,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": "Test1",
"address2": null,
@@ -118,7 +118,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": "90174"
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('updateing address\'s first line should return 200', async () => {
@@ -139,7 +140,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": "Test2",
"address2": null,
@@ -148,7 +148,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": "90174"
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('updateing address\'s second line should return 200', async () => {
@@ -169,7 +170,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": "Test2",
"address2": "Test3",
@@ -178,7 +178,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": "90174"
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('updateing address\'s city should return 200', async () => {
@@ -199,7 +200,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": "Test2",
"address2": "Test3",
@@ -208,7 +208,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": "90174"
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('updateing address\'s country should return 200', async () => {
@@ -229,7 +230,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": "Test2",
"address2": "Test3",
@@ -238,7 +238,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": "90174"
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('updateing address\'s postal code should return 200', async () => {
@@ -259,7 +260,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": "Test2",
"address2": "Test3",
@@ -268,7 +268,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": "91065"
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
it('removing org\'s address should return 200', async () => {
@@ -282,7 +283,6 @@ describe('adding + updateing address valid)', () => {
expect(res.data).toEqual({
"id": added_org_id,
"name": "testlelele",
"contact": null,
"address": {
"address1": null,
"address2": null,
@@ -291,7 +291,8 @@ describe('adding + updateing address valid)', () => {
"postalcode": null
},
"registrationEnabled": false,
"teams": []
"teams": [],
"responseType": "RUNNERORGANIZATION"
});
});
});
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
+2 -1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -48,7 +49,7 @@ describe('adding org', () => {
delete deleted_team.parentGroup;
expect(deleted_team).toEqual({
"name": "test123",
"contact": null
"responseType": "RUNNERTEAM"
});
});
it('check if team really was deleted', async () => {
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {
@@ -127,6 +128,7 @@ describe('add+update parent org (valid)', () => {
delete added_org2.registrationEnabled;
delete res4.data.parentGroup.key;
delete res4.data.parentGroup.registrationEnabled;
delete res4.data.parentGroup.teams;
expect(res4.data.parentGroup).toEqual(added_org2)
});
});
+1
View File
@@ -6,6 +6,7 @@ let access_token;
let axios_config;
beforeAll(async () => {
jest.setTimeout(20000);
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"];
axios_config = {

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