Compare commits

...

458 Commits
v0.3.0 ... main

Author SHA1 Message Date
Philipp Dormann 5ef3b6eb97
merge dev to main (#208)
Co-authored-by: Nicolai Ort <info@nicolai-ort.com>
Reviewed-on: #208
Co-authored-by: Philipp Dormann <philipp@philippdormann.de>
Co-committed-by: Philipp Dormann <philipp@philippdormann.de>
2023-11-06 17:18:48 +00:00
Nicolai Ort e98e7717aa
Merge pull request 'Releases 0.13.2 & 0.13.3' (#203) from dev into main
continuous-integration/drone/push Build is failing Details
Reviewed-on: #203
Reviewed-by: Philipp Dormann <philipp@noreply.git.odit.services>
2023-02-15 13:59:07 +00:00
Nicolai Ort 3bac75e7ab
🚀Bumped version to v0.13.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-15 14:55:41 +01:00
Nicolai Ort d05eddcae1
Merge pull request 'feature/201-no_citizen-deletion' (#202) from feature/201-no_citizen-deletion into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #202
2023-02-15 13:54:54 +00:00
Nicolai Ort d5c689d693
Updated tests
continuous-integration/drone/pr Build is passing Details
ref #201
2023-02-15 14:35:58 +01:00
Nicolai Ort 8fedd4ef3b
Added delete check for citizen org
ref #201
2023-02-15 14:34:12 +01:00
Philipp Dormann e8b2e6f261
🚀Bumped version to v0.13.2
continuous-integration/drone/push Build is passing Details
2023-02-03 16:12:20 +01:00
Philipp Dormann 39f3b0e01f
Merge pull request 'move selfservice magic link endpoint to 15min rate limit' (#200) from feature/runner-selfservice-login-link-rate-limit into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #200
2023-02-03 15:09:34 +00:00
Philipp Dormann edaf255e8f
move to 15min limit
continuous-integration/drone/pr Build is passing Details
2023-02-03 14:12:28 +01:00
Philipp Dormann 41c4ed4d0f
Merge pull request 'Releases 0.12.0 and 0.13.0' (#199) from dev into main
continuous-integration/drone/push Build is passing Details
Reviewed-on: #199
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2023-02-03 13:04:39 +00:00
Nicolai Ort f2bd88aadf
🚀Bumped version to v0.13.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-02-02 16:16:50 +01:00
Nicolai Ort 67a3661448
Updated description 2023-02-02 16:16:36 +01:00
Nicolai Ort 0c763a2dfd
🚀Bumped version to v0.13.0
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2023-02-02 12:58:19 +01:00
Nicolai Ort a7297ff933
Moved changelog generation to package script 2023-02-02 12:58:06 +01:00
Nicolai Ort 4cdba8bc77
Updated readme 2023-02-02 12:56:23 +01:00
Nicolai Ort 77c6303014
Moved license and changelog export to releaseit hooks 2023-02-02 12:55:42 +01:00
Nicolai Ort 2b641faa29 📖New license file version [CI SKIP] [skip ci] 2023-02-02 11:52:17 +00:00
Nicolai Ort 9fa8b93c08 🧾New changelog file version [CI SKIP] [skip ci] 2023-02-02 11:51:42 +00:00
Nicolai Ort 4b676bc853
Merge pull request 'feature/197-duplicate_runner_mail' (#198) from feature/197-duplicate_runner_mail into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #198
2023-02-02 11:51:23 +00:00
Nicolai Ort 4433ddb1e1
Updated logo url
continuous-integration/drone/pr Build is passing Details
2023-02-02 11:24:04 +01:00
Nicolai Ort 39aa7598b7
Updated tests for new login in selfservice
continuous-integration/drone/pr Build is passing Details
ref #197
2023-02-02 11:18:45 +01:00
Nicolai Ort 19a290c3a9
Fixed typo 2023-02-02 11:17:18 +01:00
Nicolai Ort 9bc80aac8a
Updated selfservice tests to prevent email duplication
ref #197
2023-02-02 11:14:48 +01:00
Nicolai Ort e184673963
Added faker for testing
ref #197
2023-02-02 11:10:04 +01:00
Nicolai Ort 68cd746a9f
Added selfservice runner create check to prevent duplicate email
ref #197
2023-02-02 11:08:36 +01:00
Nicolai Ort 69651d9f6c
Rename selfservice forgot to login
ref #197
2023-02-02 11:03:12 +01:00
Nicolai Ort 6fd246f43c 📖New license file version [CI SKIP] [skip ci] 2023-02-02 09:24:02 +00:00
Nicolai Ort ae14d6c74f 🧾New changelog file version [CI SKIP] [skip ci] 2023-02-02 09:23:34 +00:00
Nicolai Ort 2fa56b82d1
Add git for changelog fun
continuous-integration/drone/push Build is passing Details
2023-02-02 10:23:11 +01:00
Nicolai Ort 9cc66eebdf
depends_on: ["clone"]
continuous-integration/drone/push Build is failing Details
2023-02-02 10:21:19 +01:00
Nicolai Ort 4c10e20b91
🚀Bumped version to v0.12.0
continuous-integration/drone/push Build is failing Details
2023-02-02 10:19:57 +01:00
Nicolai Ort 9217421221
Enabled tag via release script 2023-02-02 10:19:30 +01:00
Nicolai Ort 4570845b3e
Pinned pnpm for builds 2023-02-02 10:18:08 +01:00
Nicolai Ort 0e78951300
Drone -> Kaniko based builds 2023-02-02 10:15:46 +01:00
Nicolai Ort 6ad56b3126
Drone images to odit registry 2023-02-02 10:13:04 +01:00
Nicolai Ort d95c6d3365
Bumped container base images 2023-02-02 10:10:48 +01:00
Nicolai Ort 1f2c8abb22
Ignore pnpm lock 2023-02-02 10:08:22 +01:00
Nicolai Ort a6d5693ccd
Pinned versions 2023-02-02 10:05:23 +01:00
Nicolai Ort 31b258b4ce 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 18:21:52 +00:00
Nicolai Ort f19f2808d8 Merge pull request 'Release 0.11.1' (#196) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #196
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-22 18:21:16 +00:00
Nicolai Ort 3b9cd2e1bb 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-04-22 17:59:02 +00:00
Nicolai Ort 95320ca1bc 🚀Bumped version to v0.11.1
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is passing Details
2021-04-22 19:57:31 +02:00
Nicolai Ort f2d127fc98 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-04-22 17:53:09 +00:00
Nicolai Ort eb526fb57f Added fix for the appended 2
continuous-integration/drone/push Build is passing Details
2021-04-22 19:52:32 +02:00
Nicolai Ort 348fe52c42 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-22 17:42:09 +00:00
Nicolai Ort eef0fa6952 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build is passing Details
2021-04-22 19:41:21 +02:00
Nicolai Ort 8a82e059b7 Now prefixing runnercards with 2 2021-04-22 19:41:18 +02:00
Nicolai Ort 2229cdf20d 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-14 17:05:15 +00:00
Nicolai Ort 3220b194d4 Merge pull request 'Release 0.11.0' (#195) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #195
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-14 17:04:29 +00:00
Nicolai Ort 278c4a6a41 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is failing Details
2021-04-14 16:59:20 +00:00
Nicolai Ort ec50ac31c4 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-04-14 18:58:24 +02:00
Nicolai Ort a2f0d814fc 📖New license file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-04-14 16:58:11 +00:00
Nicolai Ort 6468b35708 Merge branch 'dev' of git.odit.services:lfk/backend into dev 2021-04-14 18:57:45 +02:00
Nicolai Ort 3558e99090 🚀Bumped version to v0.11.0 2021-04-14 18:57:26 +02:00
Nicolai Ort 520608aef0 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-14 16:57:03 +00:00
Nicolai Ort 6df5f634f3 Merge pull request 'Donation payment management feature/193-donation_payments' (#194) from feature/193-donation_payments into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #194
2021-04-14 16:56:22 +00:00
Nicolai Ort da266a8dd6 Fixed spelling
continuous-integration/drone/pr Build is passing Details
ref #193
2021-04-14 18:54:02 +02:00
Nicolai Ort 8ae4b85827 Added payedDonationAmount to donor and responsedonor
continuous-integration/drone/pr Build is passing Details
ref #193
2021-04-14 18:49:44 +02:00
Nicolai Ort 8fe3243693 Saved missing file
continuous-integration/drone/pr Build is passing Details
ref #193
2021-04-14 18:47:10 +02:00
Nicolai Ort 49b174f29f No longer answering with null, but 0
continuous-integration/drone/pr Build is failing Details
ref #193
2021-04-14 18:42:38 +02:00
Nicolai Ort 30c6d3d8db Added status to tests
continuous-integration/drone/pr Build was killed Details
ref #193
2021-04-14 18:41:20 +02:00
Nicolai Ort 6c14ed9c89 Added mssing check to tests
ref #193
2021-04-14 18:36:59 +02:00
Nicolai Ort 01ed51489e Updated tests
continuous-integration/drone/pr Build is failing Details
ref #193
2021-04-14 18:34:15 +02:00
Nicolai Ort 0636616dad Marked payedAmount as optional during creation and/or update
ref #193
2021-04-14 18:29:40 +02:00
Nicolai Ort 34dbaaafe0 Responses now contain the donation status
ref #193
2021-04-14 18:28:08 +02:00
Nicolai Ort b4c31ee9b5 Added donation status enum
ref #193
2021-04-14 18:25:42 +02:00
Nicolai Ort 99307423c5 Added payed amount to update classes
ref #193
2021-04-14 18:19:26 +02:00
Nicolai Ort 71542bc388 Added payed amount to crealte classes
ref #193
2021-04-14 18:17:26 +02:00
Nicolai Ort d64f470b60 Added payed amount to response class
ref #193
2021-04-14 18:15:37 +02:00
Nicolai Ort b8fbb72fa0 Added payed amount fileld to donation class
ref #193
2021-04-14 18:12:45 +02:00
Nicolai Ort 0c61ff457d 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-07 17:28:37 +00:00
Nicolai Ort 1d82f65b0d Merge pull request 'Release 0.10.2' (#192) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is failing Details
Reviewed-on: #192
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-07 17:27:47 +00:00
Nicolai Ort 610988ec16 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-04-07 17:20:21 +00:00
Nicolai Ort 6e236ede14 🚀Bumped version to v0.10.2
continuous-integration/drone/push Build is passing Details
2021-04-07 19:18:21 +02:00
Nicolai Ort b7ad5d3a31 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-07 17:17:14 +00:00
Nicolai Ort a694ad225c Merge pull request 'stats/runners/laptime feature/190-runners_laptime' (#191) from feature/190-runners_laptime into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #191
2021-04-07 17:16:33 +00:00
Nicolai Ort 5633e85f41 added new ci secret
continuous-integration/drone/pr Build is passing Details
ref #190
2021-04-07 18:13:00 +02:00
Nicolai Ort 95e1eec313 Removed all useless console.logs
continuous-integration/drone/pr Build is passing Details
ref #190
2021-04-07 16:41:10 +02:00
Nicolai Ort 377d5dadb2 Potential fix for all remaining errors
continuous-integration/drone/pr Build is passing Details
ref #190
2021-04-07 16:38:20 +02:00
Nicolai Ort 4a294b1e17 Now resolving all relations for orgs by distance
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-07 16:34:09 +02:00
Nicolai Ort 720774fcf4 Added temp console log
ref #190
2021-04-07 16:31:36 +02:00
Nicolai Ort dcdbdd15ac Ptotential fix for stats failing
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-07 16:28:48 +02:00
Nicolai Ort 132b48cf2a Removed console log for passing tests
ref #190
2021-04-07 16:26:12 +02:00
Nicolai Ort 23bd432c5f Resolved missing parentgroup relation
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-07 16:23:13 +02:00
Nicolai Ort 71b33ab05b Removed console logs for now working tests
ref #190
2021-04-07 16:21:01 +02:00
Nicolai Ort 87f444c30d At least one fewer test should fail now
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-07 16:17:37 +02:00
Nicolai Ort 4a73eab134 Added temp console log for ci debugging
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-07 16:10:36 +02:00
Nicolai Ort f8baca5ab2 Updated default docker-compose
ref #190
2021-04-07 16:10:22 +02:00
Nicolai Ort 10221b9f2e Pinned testing container tag to prod container tag
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-07 16:06:06 +02:00
Nicolai Ort 1d8c8c8e9c Removed console log
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 10:45:06 +02:00
Nicolai Ort 4603a84f16 Reverted temp bugfix
ref #190
2021-04-06 10:43:54 +02:00
Nicolai Ort 2cd8f3f7f3 Merge branch 'feature/190-runners_laptime' of git.odit.services:lfk/backend into feature/190-runners_laptime
continuous-integration/drone/pr Build is failing Details
2021-04-06 10:15:35 +02:00
Nicolai Ort 107eeeae7f Merge branch 'feature/190-runners_laptime' of git.odit.services:lfk/backend into feature/190-runners_laptime 2021-04-06 10:15:32 +02:00
Nicolai Ort b8767b8bd4 Merge branch 'feature/190-runners_laptime' of git.odit.services:lfk/backend into feature/190-runners_laptime
continuous-integration/drone/pr Build is failing Details
2021-04-06 10:08:00 +02:00
Nicolai Ort bf686e89e0 Temp test logging workaround
ref #190
2021-04-06 10:07:59 +02:00
Nicolai Ort 6163f0a90b Temp test logging workaround
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 10:05:05 +02:00
Nicolai Ort 8f0f795a70 Tried workaround for no availdable stats
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:34:12 +02:00
Nicolai Ort 22cae39bd3 Added temp console log for test
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:29:23 +02:00
Nicolai Ort 0b07a53ed2 Temp disabled runners by donations test
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:27:22 +02:00
Nicolai Ort d4a02e7db2 Added orgs by donations stats tests
ref #190
2021-04-06 09:26:19 +02:00
Nicolai Ort b9a7dc84f0 Added teams stats endpoint tests
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:21:23 +02:00
Nicolai Ort 7111068361 Added runners stats tests
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:20:09 +02:00
Nicolai Ort 63964fbf2c Removed test for content type
continuous-integration/drone/pr Build is passing Details
ref #190
2021-04-06 09:14:45 +02:00
Nicolai Ort cbcb829fbd Fixed typo in test
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:12:03 +02:00
Nicolai Ort 057ae0d797 Added first selfservice test
continuous-integration/drone/pr Build is failing Details
ref #190
2021-04-06 09:09:01 +02:00
Nicolai Ort 257f320ee3 Now resolving all missing relations
ref #190
2021-04-06 09:02:07 +02:00
Nicolai Ort 7b15c2d88b Fixed sorting
ref #190
2021-04-06 08:58:22 +02:00
Nicolai Ort 988f17a795 Fixed sorting algo
ref #190
2021-04-06 08:56:34 +02:00
Nicolai Ort 4471e57438 First try of the laptime sort
ref #190
2021-04-06 08:44:14 +02:00
Nicolai Ort 51daf969cf Added min laptime to StatsRunner
ref #190
2021-04-06 08:14:02 +02:00
Nicolai Ort cb71fcd13b Added basic laptime endpoint
ref #190
2021-04-06 08:13:43 +02:00
Nicolai Ort a6a526dc5d Fixed top-ten bein top 9
ref #190
2021-04-05 17:47:51 +02:00
Nicolai Ort dd6d799c84 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-03 16:25:14 +00:00
Nicolai Ort e89e07d0fc Merge pull request 'Release 0.10.1' (#189) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #189
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-03 16:24:25 +00:00
Nicolai Ort c28843c405 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-04-03 16:17:51 +00:00
Nicolai Ort 4834a6698b Removed duplicate openapi statement
continuous-integration/drone/push Build is passing Details
2021-04-03 18:16:56 +02:00
Nicolai Ort 69afd4d587 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-03 16:15:38 +00:00
Nicolai Ort 24d152fdc8 🚀Bumped version to v0.10.1
continuous-integration/drone/push Build was killed Details
2021-04-03 18:14:47 +02:00
Nicolai Ort 4279e43743 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-03 16:14:17 +00:00
Nicolai Ort d837654617 Merge pull request 'Selfservice donations reformatting feature/187-selfservice_donation' (#188) from feature/187-selfservice_donation into dev
continuous-integration/drone/push Build was killed Details
Reviewed-on: #188
2021-04-03 16:13:34 +00:00
Nicolai Ort 0767943721 Switched selfservice donation.donor from string to object
continuous-integration/drone/pr Build is passing Details
ref #187
2021-04-03 17:07:44 +02:00
Nicolai Ort ca87774767 Adjusted runner property names
ref #187
2021-04-03 17:06:54 +02:00
Nicolai Ort f693f2cde9 Added new responsetype for new class
ref #187
2021-04-03 17:05:58 +02:00
Nicolai Ort d70c5b1bbc New class: ResponseSelfServiceDonor
ref #187
2021-04-03 17:05:10 +02:00
Nicolai Ort 71e3d0efe2 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-01 16:39:21 +00:00
Nicolai Ort b517dff8a8 Merge pull request 'Release 0.10.0' (#186) from dev into main
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #186
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-01 16:38:30 +00:00
Nicolai Ort 114c246ace 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-04-01 16:31:25 +00:00
Nicolai Ort d7703c9e07 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build is passing Details
2021-04-01 18:30:38 +02:00
Nicolai Ort dc3071f7d2 🚀Bumped version to v0.10.0 2021-04-01 18:30:30 +02:00
Nicolai Ort 5fb355f450 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-01 16:30:20 +00:00
Nicolai Ort 33c13de32c Merge pull request 'Mail locales feature/184-mail_locales' (#185) from feature/184-mail_locales into dev
continuous-integration/drone/push Build was killed Details
Reviewed-on: #185
2021-04-01 16:29:39 +00:00
Nicolai Ort 1be073a4fa Added locale to mail related user endpoints
continuous-integration/drone/pr Build is passing Details
ref #184
2021-04-01 18:25:09 +02:00
Nicolai Ort b0d8249452 Merge branch 'feature/184-mail_locales' of git.odit.services:lfk/backend into feature/184-mail_locales 2021-04-01 18:23:21 +02:00
Nicolai Ort 7af883f271 Added locale to mail related runner endpoints
ref #184
2021-04-01 18:23:19 +02:00
Nicolai Ort f5433076b0 Added locale to mail related runner endpoints
ref #84
2021-04-01 18:23:15 +02:00
Nicolai Ort 6aafe4a6ae 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 16:43:52 +00:00
Nicolai Ort bdeeb03645 Merge pull request 'Release 0.9.2' (#183) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #183
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-29 16:42:59 +00:00
Nicolai Ort 675c8762e8 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-29 16:32:26 +00:00
Nicolai Ort 89e392473c 🚀Bumped version to v0.9.2
continuous-integration/drone/push Build is passing Details
2021-03-29 18:31:40 +02:00
Nicolai Ort 6c9b91d75a Fixed bug in return creation 2021-03-29 18:31:27 +02:00
Nicolai Ort 8c00aefd6c 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 16:13:02 +00:00
Nicolai Ort 3afd785a54 Merge pull request 'Release v0.9.1' (#182) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #182
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-29 16:12:14 +00:00
Nicolai Ort 8099999e2c 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-29 15:49:57 +00:00
Nicolai Ort a139554e05 🚀Bumped version to v0.9.1
continuous-integration/drone/push Build is passing Details
2021-03-29 17:48:53 +02:00
Nicolai Ort 0290b0e5f5 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 15:48:47 +00:00
Nicolai Ort 0f7fa990d4 Merge pull request 'Return cards generated in bulk feature/180-blank_generation_return' (#181) from feature/180-blank_generation_return into dev
continuous-integration/drone/push Build is failing Details
Reviewed-on: #181
2021-03-29 15:48:05 +00:00
Nicolai Ort 2f568c9cb8 Fixed copy-paste oversight
continuous-integration/drone/pr Build is passing Details
ref #180
2021-03-28 18:54:16 +02:00
Nicolai Ort 1cb2dc9d53 Added test for returnCards=true array length
continuous-integration/drone/pr Build is failing Details
ref #180
2021-03-28 18:47:32 +02:00
Nicolai Ort 6005b0661f Added test for single card generation with returnCards=true
ref #180
2021-03-28 18:46:25 +02:00
Nicolai Ort 5a36c8dcae Added query param to return created runenrcards
ref #180
2021-03-28 18:44:21 +02:00
Nicolai Ort 58f4d2151f 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 20:36:54 +00:00
Nicolai Ort 95135ddc89 Merge pull request 'Release 0.9.0' (#179) from dev into main
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #179
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-26 20:36:02 +00:00
Nicolai Ort a7fe1e1759 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-26 20:32:56 +00:00
Nicolai Ort 56a5f41686 🚀Bumped version to v0.9.0
continuous-integration/drone/push Build is passing Details
2021-03-26 21:32:11 +01:00
Nicolai Ort c23b4d907f 🚀Bumped version to v0.8.0 2021-03-26 21:32:02 +01:00
Nicolai Ort bd7b81efe7 📖New license file version [CI SKIP] [skip ci] 2021-03-26 20:31:19 +00:00
Nicolai Ort 274a146b9b 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 20:30:16 +00:00
Nicolai Ort 5a3fc5b2bd Merge pull request 'Password security feature/99-password_checks' (#177) from feature/99-password_checks into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #177
2021-03-26 20:29:35 +00:00
Nicolai Ort 070560e863 Fixed test params
continuous-integration/drone/pr Build is passing Details
ref #99
2021-03-26 21:24:53 +01:00
Nicolai Ort 536900091a Fixed empty object getting called
continuous-integration/drone/pr Build is failing Details
ref #99
2021-03-26 21:19:58 +01:00
Nicolai Ort 8154e715bb Now forceing user deletion in tests
continuous-integration/drone/pr Build is failing Details
ref #99
2021-03-26 21:13:17 +01:00
Nicolai Ort 4c6665062f Reenabled user tests
continuous-integration/drone/pr Build is failing Details
ref #99
2021-03-26 21:08:50 +01:00
Nicolai Ort cb3ea9b1eb Fixed pw not getting hashed currectly;
continuous-integration/drone/pr Build is passing Details
ref #99
2021-03-26 21:06:42 +01:00
Nicolai Ort 7a64f23937 Moved to tmp files to better check for other problems
continuous-integration/drone/pr Build is failing Details
ref #99
2021-03-26 20:57:42 +01:00
Nicolai Ort 96ba25ec6c No longer using createuser in seeding process
continuous-integration/drone/pr Build is failing Details
ref #99
2021-03-26 20:52:58 +01:00
Nicolai Ort e6a8ebcb5b Added user deletion tests
continuous-integration/drone/pr Build was killed Details
ref #99
2021-03-26 20:44:28 +01:00
Nicolai Ort 888cab5898 Added user creation invalid tests
ref #99
2021-03-26 20:41:36 +01:00
Nicolai Ort 383a8095b8 Added user creation valid tests
ref #99
2021-03-26 20:41:25 +01:00
Nicolai Ort 63f6526e4f Updated auth test to comply with the new pw requirements
ref #99
2021-03-26 20:28:08 +01:00
Nicolai Ort b24e24ff7d Added pw errors to user controller
ref #99
2021-03-26 20:24:08 +01:00
Nicolai Ort 9ce35d8eb7 Added pw errors to me controller
ref #99
2021-03-26 20:23:29 +01:00
Nicolai Ort 48a87e8936 Now checking password rules on user update
ref #99
2021-03-26 20:19:23 +01:00
Nicolai Ort b8c28ebb08 Formatting
ref #99
2021-03-26 20:18:39 +01:00
Nicolai Ort 5daaa3a73c Now checking password rules on user creation
ref #99
2021-03-26 20:18:08 +01:00
Nicolai Ort 24c38cce26 Added password errors
ref #99
2021-03-26 20:17:00 +01:00
Nicolai Ort bd00f4f8d5 Added password checker dependency
ref #99
2021-03-26 20:11:22 +01:00
Nicolai Ort 03d76e6d0b 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 16:35:23 +00:00
Nicolai Ort 3f8e8ce3a6 Merge pull request 'Release 0.8.0' (#176) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #176
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-26 16:34:31 +00:00
Nicolai Ort c9bd6de476 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-26 16:19:28 +00:00
Nicolai Ort e702118d4d Merge pull request 'Selfservice deletion feature/174-selfservice_deletion' (#175) from feature/174-selfservice_deletion into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #175
2021-03-26 16:18:28 +00:00
Nicolai Ort 97159dd9f8 Removed param from test
continuous-integration/drone/pr Build is passing Details
ref #174
2021-03-26 16:56:45 +01:00
Nicolai Ort 942d9dbc76 Merge branch 'dev' into feature/174-selfservice_deletion
continuous-integration/drone/pr Build was killed Details
2021-03-26 16:54:34 +01:00
Nicolai Ort 88844e1a44 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 15:53:45 +00:00
Nicolai Ort e76a9cef95 Merge pull request 'Release 0.7.1' (#173) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #173
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-26 15:52:41 +00:00
Nicolai Ort 20aeed8778 Added tests for the new endpoint
continuous-integration/drone/pr Build is failing Details
ref #174
2021-03-26 16:50:12 +01:00
Nicolai Ort ccb7ae29a3 Fixed response bug
ref #174
2021-03-26 16:45:30 +01:00
Nicolai Ort dcb12b0ac2 Added selfservice deletion endpoint
ref #174
2021-03-26 16:42:14 +01:00
Nicolai Ort dd1258333e Updated old hint
ref #174
2021-03-26 16:42:01 +01:00
Nicolai Ort 3ef3a94b20 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-26 14:24:07 +00:00
Nicolai Ort 135852eb9a 🚀Bumped version to v0.7.1
continuous-integration/drone/push Build is passing Details
2021-03-26 15:23:05 +01:00
Nicolai Ort 963253cbc8 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-26 14:22:55 +00:00
Nicolai Ort 539a6509b1 Merge pull request 'RESPONSERUNNERCARD fix bugfix/171-responserunnercards' (#172) from bugfix/171-responserunnercards into dev
continuous-integration/drone/push Build is failing Details
Thx @philipp :)

Reviewed-on: #172
2021-03-26 14:22:15 +00:00
Nicolai Ort f3d73d5346 Tests now keep the group
continuous-integration/drone/pr Build is passing Details
ref #171
2021-03-26 15:17:05 +01:00
Nicolai Ort f159252651 Revert "Set timeout even higher b/c sqlite just kills itself during these tests"
continuous-integration/drone/pr Build is failing Details
This reverts commit 6ab60998d4.
2021-03-26 15:14:17 +01:00
Nicolai Ort 6ab60998d4 Set timeout even higher b/c sqlite just kills itself during these tests
continuous-integration/drone/pr Build is failing Details
ref #171
2021-03-26 15:13:31 +01:00
Nicolai Ort 30d220bc36 Adjusted jest timeout to mitigate sqlite from invalidateing all tests⏱
continuous-integration/drone/pr Build is failing Details
ref #171
2021-03-26 15:06:20 +01:00
Nicolai Ort 24aff3bac4 Now resolveing runnercards
ref #171
2021-03-26 14:56:21 +01:00
Nicolai Ort ce63043887 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-23 17:50:46 +00:00
Nicolai Ort e40017a6b8 Merge pull request 'Release 0.7.0' (#170) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #170
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-23 17:49:45 +00:00
Nicolai Ort e843a464e7 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is failing Details
2021-03-23 17:44:04 +00:00
Nicolai Ort d0ae50d557 🚀Bumped version to v0.7.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-03-23 18:42:16 +01:00
Nicolai Ort 7a49e7c5c9 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-23 17:42:14 +00:00
Nicolai Ort 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 Details
continuous-integration/drone/push Build is failing Details
Reviewed-on: #169
2021-03-23 17:41:31 +00:00
Nicolai Ort 438ff0fc3f Added bulk card creation tests
continuous-integration/drone/pr Build is passing Details
ref #168
2021-03-23 18:19:49 +01:00
Nicolai Ort c1bbda51f0 Added new "bulk" endpoint
ref #168
2021-03-23 18:16:55 +01:00
Nicolai Ort 4705a39aab 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-19 16:41:43 +00:00
Nicolai Ort 4d721f62d9 Merge pull request 'Release 0.6.4' (#167) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #167
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-19 16:40:45 +00:00
Nicolai Ort b0328ffdaf 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-19 16:34:14 +00:00
Nicolai Ort 031cede542 🚀Bumped version to v0.6.4
continuous-integration/drone/push Build is passing Details
2021-03-19 17:33:17 +01:00
Nicolai Ort 3c69f8c4a8 Adjsuted endpoint 2021-03-19 17:32:46 +01:00
Nicolai Ort cc6568c381 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-18 15:54:33 +00:00
Nicolai Ort a3a1395a46 Merge pull request 'Release 0.6.3' (#165) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #165
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-18 15:53:42 +00:00
Nicolai Ort b08acc6660 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-18 15:11:01 +00:00
Nicolai Ort 7a303c2b2c 🚀Bumped version to v0.6.3
continuous-integration/drone/push Build is passing Details
2021-03-18 16:10:02 +01:00
Nicolai Ort 3f9a7049e3 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-18 15:09:50 +00:00
Nicolai Ort 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 Details
Reviewed-on: #164
2021-03-18 15:09:08 +00:00
Nicolai Ort f347b7ad49 Updated tests 🧪
continuous-integration/drone/pr Build is passing Details
ref #163
2021-03-18 14:05:13 +01:00
Nicolai Ort 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
Nicolai Ort fbdadbef1f The basic bugfix 🐞
ref #163
2021-03-18 14:00:14 +01:00
Nicolai Ort c87c97c90f The basic bugfix 🐞
REF '!&§
2021-03-18 14:00:04 +01:00
Nicolai Ort a6bca59ffe 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 18:44:42 +00:00
Nicolai Ort 732a1b88d9 Merge pull request 'Release 0.6.2' (#162) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #162
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-17 18:43:45 +00:00
Nicolai Ort 4c960feeb2 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-17 18:42:50 +00:00
Nicolai Ort 72fee96a08 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build is passing Details
2021-03-17 19:41:41 +01:00
Nicolai Ort fcb43f92b0 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 18:41:40 +00:00
Nicolai Ort 5ba8f1dd44 🚀Bumped version to v0.6.2 2021-03-17 19:41:35 +01:00
Nicolai Ort 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 Details
Reviewed-on: #161
2021-03-17 18:40:47 +00:00
Nicolai Ort 1fa3fa75ee Fixed wrong error type 👀👀
continuous-integration/drone/pr Build is passing Details
ref #160
2021-03-17 19:37:35 +01:00
Nicolai Ort c8882ae6a1 Removed duplicate openapi declarations 🗑
continuous-integration/drone/pr Build is failing Details
ref #160
2021-03-17 19:33:44 +01:00
Nicolai Ort 673e896aa3 Added missing discription
ref #160
2021-03-17 19:31:09 +01:00
Nicolai Ort 0ed7f78b2c Fixed missing renameing🛠
ref #160
2021-03-17 19:30:08 +01:00
Nicolai Ort 1d38d308ad Changed the method of getting a parameter from the headers🛠
ref #160
2021-03-17 19:29:12 +01:00
Nicolai Ort d709ee7479 Now defining security per endpoint 🔐
ref #160
2021-03-17 19:25:49 +01:00
Nicolai Ort aae042c041 Now auto-etting the station token🔥🔥🔥
ref #160
2021-03-17 19:24:25 +01:00
Nicolai Ort 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
Nicolai Ort 1f32ed0727 Marked station as optional (quality of life improvements incoming)
ref #160
2021-03-17 19:22:04 +01:00
Nicolai Ort 289f9e2196 Added comments✏
ref #160
2021-03-17 19:21:35 +01:00
Nicolai Ort 937a9fad4d Added comments✏
ref #150
2021-03-17 19:21:29 +01:00
Nicolai Ort 7c3a1b8fff Merge branch 'dev' into feature/160-responseTrackScan_total_distance 2021-03-17 19:16:54 +01:00
Nicolai Ort a8ea4fa659 Fixed trackscan vaildation
ref #160
2021-03-17 19:16:42 +01:00
Nicolai Ort c1dd4518d1 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 17:59:10 +00:00
Nicolai Ort bdc7bb67e7 Merge pull request 'Release v0.6.0' (#159) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #159
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-17 17:58:20 +00:00
Nicolai Ort 54988ba0fe 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-17 16:41:01 +00:00
Nicolai Ort ce3ca9f1c8 🚀Bumped version to v0.6.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-03-17 17:40:13 +01:00
Nicolai Ort 46b7aceb0b Scanauth return objects 2021-03-17 17:40:01 +01:00
Nicolai Ort 486e450a58 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-17 16:13:23 +00:00
Nicolai Ort 623b5a1873 🚀Bumped version to v0.6.0
continuous-integration/drone/push Build is passing Details
2021-03-17 17:12:01 +01:00
Nicolai Ort a7958eecd6 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-17 15:52:53 +00:00
Nicolai Ort 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 Details
Reviewed-on: #158
2021-03-17 15:52:02 +00:00
Nicolai Ort 94001a48f1 Updated description
continuous-integration/drone/pr Build is passing Details
ref #157
2021-03-17 16:50:48 +01:00
Nicolai Ort 2cb7ec7317 As requested by @philpp
continuous-integration/drone/pr Build is passing Details
ref #157
2021-03-17 16:49:19 +01:00
Nicolai Ort 757332ed2b Added tests for the new endpoint
continuous-integration/drone/pr Build is passing Details
ref #157
2021-03-17 11:51:31 +01:00
Nicolai Ort 8ba7ee1d48 Now adding station id to headers of request for scan auth
ref #157
2021-03-17 11:45:40 +01:00
Nicolai Ort c5178e0181 Added scanstation me endpoint
ref #157
2021-03-17 11:44:54 +01:00
Nicolai Ort a1a94ec9da 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-15 15:03:59 +00:00
Nicolai Ort f7af777104 Applied Docker MTU fix 🛠
continuous-integration/drone/push Build is passing Details
2021-03-15 16:03:15 +01:00
Nicolai Ort 076aa87dba 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-12 19:21:08 +00:00
Nicolai Ort ca6fa633a1 Revert "Switched normal images to chached registry"
continuous-integration/drone/push Build was killed Details
This reverts commit cba4455d53.
2021-03-12 20:20:46 +01:00
Nicolai Ort 641e2aed52 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build was killed Details
2021-03-12 20:09:36 +01:00
Nicolai Ort cba4455d53 Switched normal images to chached registry 2021-03-12 20:09:26 +01:00
Nicolai Ort d5930f7c46 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-12 18:03:04 +00:00
Nicolai Ort 5541ae6ebd Updated ci with new kubernetes secrets 🚀🚀🚀
continuous-integration/drone/push Build was killed Details
ref odit/org#12
2021-03-12 19:02:45 +01:00
Nicolai Ort 6c43872198 Changed ci pipeline type to kubernetes
continuous-integration/drone/push Build encountered an error Details
ref odit/org#12 (comment)
2021-03-10 20:07:46 +01:00
Nicolai Ort e4ed20da3e 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-06 13:22:37 +00:00
Nicolai Ort 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 Details
Reviewed-on: #155
2021-03-06 13:22:16 +00:00
Nicolai Ort bf1ec976e3 Added selfservice forgott positive tests
continuous-integration/drone/pr Build was killed Details
ref #154
2021-03-06 14:12:46 +01:00
Nicolai Ort d0a7e34de8 Added all "negative" tests
ref #154
2021-03-06 14:08:39 +01:00
Nicolai Ort 08957d4dc2 Updated to new responsetype
ref #154
2021-03-06 14:02:58 +01:00
Nicolai Ort 1d762f5662 Renamed test
ref #154
2021-03-06 13:57:25 +01:00
Nicolai Ort a95a9b4ec4 Added first selfservice forgotten test
ref #154
2021-03-06 13:56:55 +01:00
Nicolai Ort e5dab3469c Changed endpoint url to avoid conflicts
ref #154
2021-03-06 13:56:34 +01:00
Nicolai Ort c01233b4d6 Added console logging when a testing env get's discovered
ref #154
2021-03-06 13:51:24 +01:00
Nicolai Ort 92920273be Adjusted tests for the new testing env
ref #154
2021-03-06 13:51:07 +01:00
Nicolai Ort 6bb3ae8ba9 Mailer now ignores mailing erros when env is set to test
ref #154
2021-03-06 13:44:10 +01:00
Nicolai Ort cedc1750c2 Added readme description for testing env
ref #154
2021-03-06 13:42:10 +01:00
Nicolai Ort 3f372123fd Added testing env check
ref #154
2021-03-06 13:41:10 +01:00
Nicolai Ort a3437475ca Runner controller now uses the Mailer functions
ref #154
2021-03-06 13:38:37 +01:00
Nicolai Ort 83765136cc Added mailer functions
ref #154
2021-03-06 13:36:12 +01:00
Nicolai Ort e26b7d4923 Implemented the "real" errors
ref #154
2021-03-06 13:32:36 +01:00
Nicolai Ort e7f0cb45c9 Added not found error logic
ref #154
2021-03-06 13:29:44 +01:00
Nicolai Ort ffcd45e572 Updated request timeout
ref #154
2021-03-06 13:24:43 +01:00
Nicolai Ort d7099717c2 Created basic endpoint for user forgotten mails
ref #154
2021-03-06 13:23:01 +01:00
Nicolai Ort 66d6023335 Added last reset requested timestamp to runners
ref #154
2021-03-06 13:15:27 +01:00
Nicolai Ort 5f5c8a061e 📖New license file version [CI SKIP] [skip ci] 2021-03-04 16:27:55 +00:00
Nicolai Ort bf71e35ecd 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-04 16:23:18 +00:00
Nicolai Ort 64da0eadb3 Merge pull request 'Alpha Release 0.5.0' (#153) from dev into main
continuous-integration/drone/tag Build is failing Details
continuous-integration/drone/push Build was killed Details
Reviewed-on: #153
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-04 16:22:36 +00:00
Nicolai Ort 52728290b4 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-03-04 16:07:53 +00:00
Nicolai Ort 3f2a2d2929 🚀Bumped version to v0.5.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build was killed Details
2021-03-04 17:03:51 +01:00
Nicolai Ort f1d85cfb85 🚀Bumped version to v0.4.7 2021-03-04 17:03:41 +01:00
Nicolai Ort 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 Details
Reviewed-on: #152
2021-03-04 16:03:04 +00:00
Nicolai Ort 82c65b632c Added scans returns 200 test
continuous-integration/drone/pr Build is passing Details
ref #151
2021-03-04 16:40:05 +01:00
Nicolai Ort ae7d617690 Updated auth reset test for new mailer
ref #151
2021-03-04 16:35:30 +01:00
Nicolai Ort bf6b70106e Now generateing bs mailer config in test env
ref #151
2021-03-04 16:32:06 +01:00
Nicolai Ort 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
Nicolai Ort db58a280b3 Updated readme env section
ref #151
2021-03-04 16:29:44 +01:00
Nicolai Ort 149f3a83b2 Updated readme env section
ref #151
2021-03-04 16:28:13 +01:00
Nicolai Ort a5d2a6ecd3 Added locale to pw reset endpoint
ref #151
2021-03-04 16:25:23 +01:00
Nicolai Ort bb9bad6d90 Now checking for mails being set
ref #151
2021-03-04 16:19:19 +01:00
Nicolai Ort ada679823c Removed useless functions and updated comments
ref #151
2021-03-04 16:13:36 +01:00
Nicolai Ort 9a1678acf0 Now using mailer as static funtion
ref #151
2021-03-04 16:11:35 +01:00
Nicolai Ort 485c247cd3 Removed (now useless) mail controller
ref #151
2021-03-04 16:11:21 +01:00
Nicolai Ort ddea02db57 Added new mailer settings to config
ref #151
2021-03-04 16:11:05 +01:00
Nicolai Ort 1551a444ba Added the new mailer code
ref #151
2021-03-04 16:10:43 +01:00
Nicolai Ort f289afd8bc Updated mail errors
ref #151
2021-03-04 16:10:26 +01:00
Nicolai Ort a9e06c9055 Promoted axios to dependency
ref #151
2021-03-04 15:56:24 +01:00
Nicolai Ort c2fdfeed4f Removed mail templates
ref #151
2021-03-04 15:54:47 +01:00
Nicolai Ort 0342757d92 Removed mail config
ref #151
2021-03-04 15:54:27 +01:00
Nicolai Ort 5833f4218f Removed nodemailer from backend
ref #151
2021-03-04 15:52:20 +01:00
Nicolai Ort 0fcc729b56 Removed old mailer code
ref #151
2021-03-04 15:51:05 +01:00
Nicolai Ort a2c97a11a3 Laptime is now a part of the response
ref #151
2021-03-01 16:59:36 +01:00
Nicolai Ort aa833736d3 Trackscans now have a laptime that get's calculated on creation
ref #151
2021-03-01 16:58:34 +01:00
Nicolai Ort 771a205fe6 Added new selfservice scans endpoint
ref #151
2021-03-01 16:49:37 +01:00
Nicolai Ort 6074ac5b3a Added selfservice scan response class
ref #151
2021-03-01 16:47:54 +01:00
Nicolai Ort 030b2255d4 Added another resonse type
ref #151
2021-03-01 16:47:05 +01:00
Nicolai Ort f7f6df41ff Added new selfservice response type
ref #151
2021-03-01 16:36:36 +01:00
Nicolai Ort be397c8899 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-26 16:50:45 +00:00
Nicolai Ort dd3c9275d6 Merge pull request 'Alpha Release 0.4.6' (#148) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #148
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-26 16:50:03 +00:00
Nicolai Ort 764b7ffe00 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-26 16:48:33 +00:00
Nicolai Ort d870b2fd01 Merge pull request 'Fixed wrong body acceptance type' (#150) from bugfix/146-usergroup_update into dev
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #150
2021-02-26 16:48:16 +00:00
Nicolai Ort aaec09d2ab Fixed wrong body acceptance type
continuous-integration/drone/pr Build is passing Details
ref #146
2021-02-26 17:32:26 +01:00
Nicolai Ort bce8811925 📖New license file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-25 19:41:04 +00:00
Nicolai Ort 3afc207903 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-25 19:39:39 +00:00
Nicolai Ort fca997beb8 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-02-25 20:39:21 +01:00
Nicolai Ort 39ebfbf0b6 Pinned package version to avoid dependency conflicts 📌
yeet
2021-02-25 20:39:16 +01:00
Nicolai Ort 3736b29e54 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-25 19:33:24 +00:00
Nicolai Ort b4c9369a53 Merge branch 'dev' of git.odit.services:lfk/backend into dev
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is passing Details
2021-02-25 20:33:04 +01:00
Nicolai Ort 5d6c8c957a Quick bugfix 2021-02-25 20:33:00 +01:00
Nicolai Ort 09fe47b9aa 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-25 19:30:37 +00:00
Nicolai Ort b4acd157fc 🚀Bumped version to v0.4.6
continuous-integration/drone/push Build is failing Details
2021-02-25 20:30:16 +01:00
Nicolai Ort b1fced7764 📖New license file version [CI SKIP] [skip ci] 2021-02-24 20:00:08 +00:00
Nicolai Ort c0cafb4d51 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-24 19:59:05 +00:00
Nicolai Ort 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 Details
Reviewed-on: #147
2021-02-24 19:58:47 +00:00
Nicolai Ort 28ef139a70 Added tests for the new org selfservice endpoints
continuous-integration/drone/pr Build is passing Details
ref #146
2021-02-24 18:32:56 +01:00
Nicolai Ort 656f63dfd5 Added selfservice org info endpoint
ref #146
2021-02-24 18:23:08 +01:00
Nicolai Ort ba3b5eeefc Added selfservice org response model
ref #146
2021-02-24 18:20:31 +01:00
Nicolai Ort ba396e0eba Added selfservice team response model
ref #146
2021-02-24 18:18:10 +01:00
Nicolai Ort 3c11d88557 Added new response types
ref #146
2021-02-24 18:17:30 +01:00
Nicolai Ort 305fa0078d 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-09 18:25:18 +00:00
Nicolai Ort a46d14278b Merge pull request 'Alpha release 0.4.5' (#145) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #145
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-09 18:24:30 +00:00
Nicolai Ort 680ae8ebbb 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-09 18:19:21 +00:00
Nicolai Ort cc869f69ad 🚀Bumped version to v0.4.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-02-09 19:18:46 +01:00
Nicolai Ort b9aac71676 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-09 18:18:22 +00:00
Nicolai Ort 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 Details
continuous-integration/drone/push Build is failing Details
Reviewed-on: #144
2021-02-09 18:18:04 +00:00
Nicolai Ort bdcfce88cb Now all /usergroups endpoints return ResponseUserGroup
continuous-integration/drone/pr Build is passing Details
ref #143
2021-02-09 18:27:11 +01:00
Nicolai Ort 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
Nicolai Ort 416f2a1366 The ResponseUserGroup now returns their permisssions as a string array
ref #143
2021-02-09 18:26:22 +01:00
Nicolai Ort 5e353db206 The ResponseUserGroup now returns their permisssions as a string array
ref #143
2021-02-09 18:26:16 +01:00
Nicolai Ort 0c9867d706 Implemented /groups/permissions endpoint
ref #143
2021-02-09 18:18:00 +01:00
Nicolai Ort 8379c3e29c 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-09 16:47:57 +00:00
Nicolai Ort c4edccace7 Merge pull request 'Alpha release 0.4.4' (#142) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #142
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-09 16:47:31 +00:00
Nicolai Ort 74de6559d7 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-08 16:34:37 +00:00
Nicolai Ort a6f73c733c 🚀Bumped version to v0.4.4
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2021-02-08 17:34:03 +01:00
Nicolai Ort ca3d093e54 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-08 16:33:37 +00:00
Nicolai Ort 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 Details
continuous-integration/drone/push Build is failing Details
Reviewed-on: #141
2021-02-08 16:33:21 +00:00
Nicolai Ort 90e1ad7db7 Adjusted test for the new response depth
continuous-integration/drone/pr Build is passing Details
ref #140
2021-02-08 16:51:09 +01:00
Nicolai Ort 906a1dc9e7 The group/runners endpoints now also deliver the runner's group's parentGroup
ref #140
2021-02-08 16:48:20 +01:00
Nicolai Ort 5872c6335b Adjusted test for the new response depth
ref #140
2021-02-08 16:46:07 +01:00
Nicolai Ort 701706c028 Now loading runner's group's parentgroup with every runner controller request
ref #140
2021-02-08 16:40:17 +01:00
Nicolai Ort 09bbc70f5f 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-07 12:43:57 +00:00
Nicolai Ort dd9cb6d3ef Merge pull request 'Alpha Release 0.4.3' (#139) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #139
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-07 12:43:30 +00:00
Nicolai Ort 23c732b690 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-07 12:40:54 +00:00
Nicolai Ort 656d564baa 🚀Bumped version to v0.4.3
continuous-integration/drone/push Build is passing Details
2021-02-07 13:40:32 +01:00
Philipp Dormann f3f5cb462e 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-07 12:38:54 +00:00
Philipp Dormann 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 Details
Reviewed-on: #138
2021-02-07 12:38:34 +00:00
Nicolai Ort 8f0a396dd0 Bugfix for @lfk/frontend/#43
continuous-integration/drone/pr Build is passing Details
2021-02-07 13:37:01 +01:00
Nicolai Ort a18d4d3cee 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-02 15:02:34 +00:00
Nicolai Ort 390b36dfd4 Merge pull request 'Alpha Release 0.4.2' (#137) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #137
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-02-02 15:02:08 +00:00
Nicolai Ort 3b718f3ce5 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-02-02 14:46:03 +00:00
Nicolai Ort 321b20b073 🚀Bumped version to v0.4.2
continuous-integration/drone/push Build is passing Details
2021-02-02 15:45:41 +01:00
Nicolai Ort f7a0ec7174 🧾New changelog file version [CI SKIP] [skip ci] 2021-02-02 14:45:15 +00:00
Nicolai Ort 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 Details
Reviewed-on: #136
2021-02-02 14:45:00 +00:00
Nicolai Ort 333e806da4 Added documentation about the new env vars to the readme
continuous-integration/drone/pr Build is passing Details
ref #135
2021-02-02 15:10:04 +01:00
Nicolai Ort f4f621973a Added imprint and privacy to the api spec
ref #135
2021-02-02 15:07:33 +01:00
Nicolai Ort bcad691045 Added new url env vars to config
ref #135
2021-02-02 15:04:09 +01:00
Nicolai Ort 74791df68b 📖New license file version [CI SKIP] [skip ci] 2021-01-30 17:13:12 +00:00
Nicolai Ort 8425043099 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 17:12:25 +00:00
Nicolai Ort 74b982afba fixed license-exporter call
continuous-integration/drone/push Build is passing Details
2021-01-30 18:12:09 +01:00
Nicolai Ort 3aefa75412 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 16:00:16 +00:00
Nicolai Ort 71cab4e836 Merge pull request 'Alpha Release 0.4.1' (#134) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #134
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-01-30 15:59:51 +00:00
Nicolai Ort 4e10077901 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-01-30 15:54:03 +00:00
Nicolai Ort c32fa93673 🚀Bumped version to v0.4.1
continuous-integration/drone/push Build is passing Details
2021-01-30 16:53:38 +01:00
Nicolai Ort 3d1baae0cc Dependency bump🔝 [skip ci] 2021-01-30 16:53:21 +01:00
Nicolai Ort 94dd7963b7 Deleted useless file [ci skip] 2021-01-30 16:44:39 +01:00
Nicolai Ort 7ba67b9dca 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 15:40:39 +00:00
Nicolai Ort 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 Details
Reviewed-on: #133
2021-01-30 15:40:24 +00:00
Nicolai Ort 60ee6ebc1e Merge branch 'dev' into feature/132-object_types
continuous-integration/drone/pr Build is passing Details
2021-01-30 15:32:20 +00:00
Nicolai Ort 02295346da 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-30 15:27:16 +00:00
Nicolai Ort c4ea808e06 Merge pull request 'Alpha Release 0.4.0' (#131) from dev into main
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #131
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-01-30 15:26:48 +00:00
Nicolai Ort ff7406e71a Cleaned up realations regarding response classes
continuous-integration/drone/pr Build is passing Details
ref #132
2021-01-30 16:19:42 +01:00
Nicolai Ort 8dc2810c0c Adjusted tests for the new responseType parameter (part 3)
ref #132
2021-01-30 16:19:20 +01:00
Nicolai Ort ff8af090e3 Adjusted tests for the new responseType parameter (part 2)
ref #132
2021-01-30 15:39:39 +01:00
Nicolai Ort bcc15e4286 Adjusted tests for the new responseType parameter (part 1)
ref #132
2021-01-29 18:46:15 +01:00
Nicolai Ort 2a87819486 Fixed typos and missing types
ref #132
2021-01-29 17:06:57 +01:00
Nicolai Ort 9d5e486c6d Implemented the interface in all responses
refr #132
2021-01-29 17:06:43 +01:00
Nicolai Ort e44cc4c4cb Added a Response interface
ref #132
2021-01-29 16:49:19 +01:00
Nicolai Ort 581ca5ff6c Added Responseobjecttype enum
ref #132
2021-01-29 16:45:23 +01:00
Nicolai Ort b972395ae8 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-01-27 16:54:23 +00:00
Nicolai Ort e5f4f6ee59 🚀Bumped version to v0.4.0
continuous-integration/drone/push Build is passing Details
2021-01-27 17:53:58 +01:00
Nicolai Ort fea4857685 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-27 16:52:45 +00:00
Nicolai Ort f9e75d06b8 Merge pull request 'Implemented testmail endpoint feature/124-testmail' (#130) from feature/124-testmail into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #130
2021-01-27 16:52:28 +00:00
Nicolai Ort 38223b194b Merge branch 'dev' into feature/124-testmail
continuous-integration/drone/pr Build is passing Details
2021-01-27 17:47:29 +01:00
Nicolai Ort 09b24aa609 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-27 16:47:07 +00:00
Nicolai Ort 348e6cdec7 Merge pull request 'Email Basics feature/118-emails' (#128) from feature/118-emails into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #128
2021-01-27 16:46:34 +00:00
Nicolai Ort bd1813a0e8 Merge branch 'dev' into feature/118-emails
continuous-integration/drone/pr Build is passing Details
2021-01-27 16:45:17 +00:00
Nicolai Ort e07f258a31 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-27 16:44:56 +00:00
Nicolai Ort 61bbeb0d8f Merge pull request 'Mail+Env documentation feature/123-mail_documentation' (#129) from feature/123-mail_documentation into dev
continuous-integration/drone/push Build is passing Details
Reviewed-on: #129
2021-01-27 16:44:33 +00:00
Nicolai Ort 650a55e586 Merge branch 'dev' into feature/123-mail_documentation
continuous-integration/drone/pr Build is passing Details
2021-01-27 16:44:19 +00:00
Nicolai Ort 2071c4db33 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-27 16:43:56 +00:00
Nicolai Ort 80e606aa96 Merge branch 'dev' into feature/123-mail_documentation
continuous-integration/drone/pr Build is passing Details
2021-01-27 16:43:54 +00:00
Nicolai Ort 20f960ed67 Merge pull request 'Alpha Release 0.3.1' (#127) from dev into main
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is passing Details
Reviewed-on: #127
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-01-27 16:43:26 +00:00
Nicolai Ort e6fe8fcd58 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-01-27 16:32:04 +00:00
Nicolai Ort 870fd47c83 Merge pull request 'new advanced endpoints feature/125-team_runner' (#126) from feature/125-team_runner into dev
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
Reviewed-on: #126
2021-01-27 16:31:45 +00:00
Nicolai Ort 644045db44 Merge branch 'dev' into feature/125-team_runner
continuous-integration/drone/pr Build is passing Details
2021-01-27 16:31:16 +00:00
Nicolai Ort 8611fcb849 Merge branch 'dev' into feature/124-testmail
continuous-integration/drone/pr Build is passing Details
2021-01-27 11:08:37 +00:00
Nicolai Ort 08e6e59655 Removed the duplicate env copy/create from ci tests
continuous-integration/drone/pr Build is passing Details
ref #124
2021-01-27 12:06:00 +01:00
Nicolai Ort ae74b3963f Added test mail sending test
continuous-integration/drone/pr Build is passing Details
ref #124
2021-01-27 12:03:02 +01:00
Nicolai Ort 54ed313342 Implemented the test-mail endpoint via a new mailcontroller
ref #124
2021-01-27 11:59:32 +01:00
Nicolai Ort ad4b903c25 Added a Mail permisssion target
ref #124
2021-01-27 11:53:33 +01:00
Nicolai Ort 9bd7636a23 Added comments
ref #124
2021-01-27 11:37:46 +01:00
Nicolai Ort b94179e3ca Added a test mail sending function
ref #124
2021-01-27 11:28:27 +01:00
Nicolai Ort 827002989e Added test mail templates
ref #124
2021-01-27 11:28:02 +01:00
Nicolai Ort eeff67c192 Merge branch 'dev' into feature/123-mail_documentation
continuous-integration/drone/pr Build is passing Details
2021-01-26 19:42:59 +00:00
Nicolai Ort 583a4bc0dd Changed order
continuous-integration/drone/pr Build is passing Details
ref #123
2021-01-26 20:41:44 +01:00
Nicolai Ort 53fcff77d0 Added a hint to ethereal.email
ref #123
2021-01-26 20:41:08 +01:00
Nicolai Ort 1f0c842d9e Table fix
ref #123
2021-01-26 20:39:38 +01:00
Nicolai Ort 13ccab5e28 Added documentation for the env vars
ref #123
2021-01-26 20:38:53 +01:00
Nicolai Ort b5018eb114 Added the basics about mail templates to the readme
ref #123
2021-01-26 20:26:25 +01:00
Nicolai Ort aedfcfcc83 🧾New changelog file version [CI SKIP] [skip ci]
continuous-integration/drone/pr Build is passing Details
2021-01-26 19:15:59 +00:00
Nicolai Ort db0876015b 🚀Bumped version to v0.3.1
continuous-integration/drone/push Build is passing Details
2021-01-26 20:15:35 +01:00
Nicolai Ort 69417e93c0 Added get runners by team test
continuous-integration/drone/pr Build is passing Details
ref #125
2021-01-26 20:13:39 +01:00
Nicolai Ort f71a22f4dd Added get runners by org test
ref #125
2021-01-26 20:12:14 +01:00
Nicolai Ort 570c34bed0 Created the organizations/runners endpoint
ref #125
2021-01-26 20:06:54 +01:00
Nicolai Ort 7be2971a9e Created the runnerTeam/runners endpoint
ref #125
2021-01-26 19:57:35 +01:00
Nicolai Ort b92f633d68 Now also sending txt mail body
continuous-integration/drone/pr Build is passing Details
ref #118
2021-01-26 18:45:19 +01:00
Nicolai Ort d3647e3399 Added a txt variant of the pw-reset mail
ref #118
2021-01-26 18:41:50 +01:00
Nicolai Ort 389e423850 Cleaned up the replacements
ref #118
2021-01-26 18:41:21 +01:00
Nicolai Ort 46af786516 Fixed missing app_url protocol
ref #118
2021-01-26 18:31:34 +01:00
Nicolai Ort b4c117b7dc Fixed wrong file location
ref #118
2021-01-26 18:31:17 +01:00
Nicolai Ort 5cade25eeb Translated the pw reset mail to english
ref #118
2021-01-26 18:17:52 +01:00
Nicolai Ort fb77f4d798 Renamed the template
ref #118
2021-01-26 18:09:27 +01:00
Nicolai Ort c116338cd7 Added pw reset template provided by @philipp
ref #118
2021-01-26 18:09:00 +01:00
Nicolai Ort 979d36ea91 Password reset now enforces email
ref #118
2021-01-26 18:07:56 +01:00
Nicolai Ort c43334bf96 The auth tests now use mail to identify the user
ref #118
2021-01-26 18:07:42 +01:00
Nicolai Ort 71c4caae8b Removed bs console.log
ref #118
2021-01-26 17:55:41 +01:00
Nicolai Ort 536de2a319 Implemented automatic ci env generation
ref #118
2021-01-26 17:54:25 +01:00
Nicolai Ort e26744b792 Implementes mail sending on pw reset request
ref #118
2021-01-26 17:35:03 +01:00
Nicolai Ort d02e9dec56 Removed tests working directly with the old pw-reset response
ref #118
2021-01-26 17:28:20 +01:00
Nicolai Ort 637975305f Implemented a basic mailer with reset link sending
ref #118
2021-01-26 17:21:18 +01:00
Nicolai Ort c418603423 Added the first mail error
ref #118
2021-01-26 17:20:55 +01:00
Nicolai Ort 78d2ac3027 Added nodemailer types
ref #118
2021-01-26 17:20:44 +01:00
Nicolai Ort 470703c4de Added env vars
ref #118
2021-01-26 17:20:33 +01:00
Nicolai Ort e260e16d66 Merge branch 'feature/118-emails' of git.odit.services:lfk/backend into feature/118-emails 2021-01-26 16:10:26 +01:00
Nicolai Ort 6b0155f014 Added a folder for the mail templates
ref #118
2021-01-26 16:10:24 +01:00
Nicolai Ort 33890b544b Added a folder for the mail templates
ref #118
2021-01-26 16:10:02 +01:00
Nicolai Ort d7ea928714 Added mail env vars
ref #118
2021-01-26 16:09:11 +01:00
Nicolai Ort 908ac4f1ce Added nodemailer dependecy
ref #118
2021-01-26 16:06:47 +01:00
Nicolai Ort cf012c0b7e Added a barebones class for handleing mail stuff
ref #118
2021-01-26 16:05:55 +01:00
Nicolai Ort 71898d576c 🧾New changelog file version [CI SKIP] [skip ci] 2021-01-24 17:58:01 +00:00
151 changed files with 11996 additions and 6461 deletions

View File

@ -1,6 +1,42 @@
---
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: secret
name: ci_token
get:
path: odit-ci-bot
name: apikey
---
kind: secret
name: npm_url
get:
path: odit-npm-cache
name: url
---
kind: pipeline
name: tests:node_latest
type: kubernetes
name: tests:node
clone:
disable: true
steps:
@ -9,19 +45,22 @@ steps:
commands:
- git clone $DRONE_REMOTE_URL .
- git checkout $DRONE_SOURCE_BRANCH
- mv .env.ci .env
- name: run tests
image: node:latest
image: registry.odit.services/hub/library/node:19.5.0-alpine3.16
commands:
- yarn
- yarn test:ci
- npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
- pnpm i
- pnpm test:ci
environment:
NPM_REGISTRY_URL:
from_secret: npm_url
trigger:
event:
- pull_request
---
kind: pipeline
type: docker
type: kubernetes
name: build:dev
clone:
disable: true
@ -33,52 +72,21 @@ steps:
- git clone $DRONE_REMOTE_URL .
- git checkout dev
- name: build dev
image: plugins/docker
depends_on: [clone]
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
from_secret: DOCKER_REGISTRY_USER
from_secret: docker_username
password:
from_secret: DOCKER_REGISTRY_PASSWORD
repo: registry.odit.services/lfk/backend
from_secret: docker_password
build_args:
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/backend
tags:
- dev
cache: true
registry: registry.odit.services
- name: run changelog export
depends_on: ["clone"]
image: node:latest
commands:
- npx auto-changelog --commit-limit false -p -u --hide-credit
- name: push new changelog to repo
depends_on: ["run changelog export"]
image: appleboy/drone-git-push
settings:
branch: dev
commit: true
commit_message: 🧾New changelog file version [CI SKIP] [skip ci]
author_email: bot@odit.services
remote: git@git.odit.services:lfk/backend.git
ssh_key:
from_secret: GITLAB_SSHKEY
- name: run full license export
depends_on: ["clone"]
image: node:14.15.1-alpine3.12
commands:
- yarn
- yarn licenses:export
- name: push new licenses file to repo
depends_on: ["run full license export"]
image: appleboy/drone-git-push
settings:
branch: dev
commit: true
commit_message: 📖New license file version [CI SKIP] [skip ci]
author_email: bot@odit.services
remote: git@git.odit.services:lfk/backend.git
skip_verify: true
ssh_key:
from_secret: GITLAB_SSHKEY
trigger:
branch:
@ -88,7 +96,7 @@ trigger:
---
kind: pipeline
type: docker
type: kubernetes
name: build:latest
clone:
disable: true
@ -103,15 +111,19 @@ steps:
- git checkout main
- name: build latest
depends_on: ["clone"]
image: plugins/docker
image: registry.odit.services/library/drone-kaniko
settings:
username:
from_secret: DOCKER_REGISTRY_USER
from_secret: docker_username
password:
from_secret: DOCKER_REGISTRY_PASSWORD
repo: registry.odit.services/lfk/backend
from_secret: docker_password
build_args:
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/backend
tags:
- latest
cache: true
registry: registry.odit.services
- name: push merge to repo
depends_on: ["clone"]
@ -121,7 +133,7 @@ steps:
commit: false
remote: git@git.odit.services:lfk/backend.git
ssh_key:
from_secret: GITLAB_SSHKEY
from_secret: git_ssh
trigger:
branch:
@ -131,34 +143,32 @@ trigger:
---
kind: pipeline
type: docker
type: kubernetes
name: build:tags
steps:
- name: build $DRONE_TAG
image: plugins/docker
depends_on: [clone]
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
from_secret: DOCKER_REGISTRY_USER
from_secret: docker_username
password:
from_secret: DOCKER_REGISTRY_PASSWORD
repo: registry.odit.services/lfk/backend
from_secret: docker_password
build_args:
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/backend
tags:
- '${DRONE_TAG}'
- "${DRONE_TAG}"
cache: true
registry: registry.odit.services
- name: trigger node lib build
image: idcooldi/drone-webhook
settings:
urls: https://ci.odit.services/api/repos/lfk/lfk-client-node/builds?SOURCE_TAG=${DRONE_TAG}
bearer:
from_secret: BOT_DRONE_KEY
- name: trigger js lib build
image: idcooldi/drone-webhook
settings:
urls: https://ci.odit.services/api/repos/lfk/lfk-client-js/builds?SOURCE_TAG=${DRONE_TAG}
bearer:
from_secret: BOT_DRONE_KEY
from_secret: ci_token
trigger:
event:
- tag
- tag

2
.gitignore vendored
View File

@ -135,4 +135,4 @@ build
/docs
lib
/oss-attribution
*.tmp
*.tmp

View File

@ -2,29 +2,773 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v1.1.3](https://git.odit.services/lfk/backend/compare/v1.1.2...v1.1.3)
- feat(orgs): Also resolve child-teams' distances and add them to org total [`8d94186`](https://git.odit.services/lfk/backend/commit/8d9418635d3e381c0f55a2521a3334ba497c169a)
- fix(orgs): Removed unused log [`f2832a2`](https://git.odit.services/lfk/backend/commit/f2832a2daecc7bc7bbee4d4fceeab8db194730cf)
#### [v1.1.2](https://git.odit.services/lfk/backend/compare/v1.1.1...v1.1.2)
> 10 May 2023
- 🚀Bumped version to v1.1.2 [`0d21596`](https://git.odit.services/lfk/backend/commit/0d21596e2b64a99258d4925ae2ad627d5cdbd984)
- feat(groups): Resolve the total group distance on group get single (aka get org and get team) [`245827e`](https://git.odit.services/lfk/backend/commit/245827e9c659cf76183dc33ab253becc22ddf032)
- chore(package): Formatting [`4608a36`](https://git.odit.services/lfk/backend/commit/4608a36df6b187520ca0c331b8dce615205257be)
#### [v1.1.1](https://git.odit.services/lfk/backend/compare/v1.1.0...v1.1.1)
> 19 April 2023
- feat(donors): Resolve donations with donors via pagination [`12a9ae2`](https://git.odit.services/lfk/backend/commit/12a9ae24933117acb3ff9815a7d72abca5eea7a7)
- 🚀Bumped version to v1.1.1 [`cb1305a`](https://git.odit.services/lfk/backend/commit/cb1305aa77c36aa9d7900f09e7413bc6d45f2c89)
#### [v1.1.0](https://git.odit.services/lfk/backend/compare/v1.0.1...v1.1.0)
> 19 April 2023
- feat(stats): Added donation count and donor count to stats [`6f39ac4`](https://git.odit.services/lfk/backend/commit/6f39ac42dafc2a589bbb2256b0417f3e774ae174)
- 🚀Bumped version to v1.1.0 [`b9fe9f1`](https://git.odit.services/lfk/backend/commit/b9fe9f1c24653b91255a6dbbdc32c30b1b411eeb)
- Added average donation per distance to stats [`fe59e3a`](https://git.odit.services/lfk/backend/commit/fe59e3a557903cf555d4c50098e935c49ca1fac4)
- Added hints [`b25b0db`](https://git.odit.services/lfk/backend/commit/b25b0db76071ef8d50cc60e950a399dc060a2a9f)
- Added calls to controller [`6ee5328`](https://git.odit.services/lfk/backend/commit/6ee5328dbc404603d19db3a5173ae4def560a9c9)
- Formatting [`42c23a5`](https://git.odit.services/lfk/backend/commit/42c23a5883dacda4e0147842d448b3ad35b197b1)
#### [v1.0.1](https://git.odit.services/lfk/backend/compare/v1.0.0...v1.0.1)
> 18 April 2023
- fix(pagination) page=0 resulted in false thx JS [`fcee390`](https://git.odit.services/lfk/backend/commit/fcee3909f4c4664115cc7ecb94f30e0dd8e78ce0)
- 🚀Bumped version to v1.0.1 [`301f334`](https://git.odit.services/lfk/backend/commit/301f33467489a8533bdac11fbd10efd1b791f5e3)
### [v1.0.0](https://git.odit.services/lfk/backend/compare/v0.15.4...v1.0.0)
> 18 April 2023
- 🚀Bumped version to v1.0.0 [`f0e20e4`](https://git.odit.services/lfk/backend/commit/f0e20e413014fe446c97754d2765cdad92c2cc3b)
- Merge pull request 'feature/205-pagination' (#206) from feature/205-pagination into dev [`80de188`](https://git.odit.services/lfk/backend/commit/80de188565523d642407612272432ef07672b890)
- Added pagination for runner orgs [`538622a`](https://git.odit.services/lfk/backend/commit/538622aa1841e27256f304e15b4204c2f6d24d76)
- RunnerTeam Pagination [`0fa663a`](https://git.odit.services/lfk/backend/commit/0fa663a34104d438dd8fc9ab02458fdf289329f8)
- users pagination [`244da61`](https://git.odit.services/lfk/backend/commit/244da618926377f58bb12dbbd89b7bb39d84596e)
- Track pagination [`2a72aea`](https://git.odit.services/lfk/backend/commit/2a72aea10ef940fbdd4a9e6137b22933fdec7734)
- usergroup pagination [`513d7f6`](https://git.odit.services/lfk/backend/commit/513d7f6fbaebe39beab6ec95e6e42eb10c62296d)
- statsclient pagination [`71ebce6`](https://git.odit.services/lfk/backend/commit/71ebce6f8eebf110bb973a53b91dd6a49e1def99)
- scanstation pagination [`f60025b`](https://git.odit.services/lfk/backend/commit/f60025b6de79b0f5f89995bf59260194f5de9af0)
- Get all pagination for permissions [`86a21db`](https://git.odit.services/lfk/backend/commit/86a21dbfa4b50d8e80c611ea6e3eabfc2b8ae365)
- Pagination for group contacts [`1e9e24d`](https://git.odit.services/lfk/backend/commit/1e9e24d99d75ce6dc846ff662e62c886646ea974)
- Added pagination for get all donors [`4493c0e`](https://git.odit.services/lfk/backend/commit/4493c0e3d9beebbf7f601b39e1a2579771b4d152)
- Added pagination for donations [`f5d48fc`](https://git.odit.services/lfk/backend/commit/f5d48fc638080c9333efe474d86f131794c809af)
- Added pagination for runnercards [`b35a2dd`](https://git.odit.services/lfk/backend/commit/b35a2dd2fab708253373b3326f11ab574be18371)
- Added pagination for runners [`d873674`](https://git.odit.services/lfk/backend/commit/d873674819e6cb33cf89da4f8fdc30a0b41707e4)
- Added pagination for get all scans [`37b2ac9`](https://git.odit.services/lfk/backend/commit/37b2ac974b2276efd13538c127ba5ddda2537fe3)
- Updated test for attribute [`2f305e1`](https://git.odit.services/lfk/backend/commit/2f305e127c75e9e6ff8e9fc0cfc10cc3db44759d)
- Formatting [`a28ffe0`](https://git.odit.services/lfk/backend/commit/a28ffe06e5f3f69e4af6fdf0c66c9a1dfda10cfa)
#### [v0.15.4](https://git.odit.services/lfk/backend/compare/v0.15.3...v0.15.4)
> 15 April 2023
- Fixed possible null [`0f0c3c7`](https://git.odit.services/lfk/backend/commit/0f0c3c7214f357d991518aafd015ffc4d387ce59)
- 🚀Bumped version to v0.15.4 [`81aed1d`](https://git.odit.services/lfk/backend/commit/81aed1de40166f4cefabdb478d7638017127b25c)
#### [v0.15.3](https://git.odit.services/lfk/backend/compare/v0.15.2...v0.15.3)
> 15 April 2023
- Faster stats (not including donations) [`b2ac70e`](https://git.odit.services/lfk/backend/commit/b2ac70e0aec1064e54a5043a104e7892984b2338)
- 🚀Bumped version to v0.15.3 [`3909ed3`](https://git.odit.services/lfk/backend/commit/3909ed34f739e9fee90828f16757c75da90bab0f)
#### [v0.15.2](https://git.odit.services/lfk/backend/compare/v0.15.1...v0.15.2)
> 15 April 2023
- 🚀Bumped version to v0.15.2 [`5f17e7f`](https://git.odit.services/lfk/backend/commit/5f17e7f783a7e8e2efc8f7dbbf2c98bcd1d80240)
- Don't resolve runner group and parten with get all card requests [`2d8f752`](https://git.odit.services/lfk/backend/commit/2d8f7528d98144832e7609f5aa6fac8de4723c4a)
- Resolve groups again for card generation [`a5a56a2`](https://git.odit.services/lfk/backend/commit/a5a56a263a01dbd911a799ab57084166e17b80ac)
#### [v0.15.1](https://git.odit.services/lfk/backend/compare/v0.15.0...v0.15.1)
> 15 April 2023
- 🚀Bumped version to v0.15.1 [`9581185`](https://git.odit.services/lfk/backend/commit/9581185b24039338e7f238ecdcc3881bb5203759)
- Faster trackscan creation by only loading the latest scan [`e9914e3`](https://git.odit.services/lfk/backend/commit/e9914e317b7fd78863cfd8549bad65da9292b7ca)
- Log batch time in mass scan script [`2905884`](https://git.odit.services/lfk/backend/commit/2905884c024d7f275b3ad2c2858a2f0911adb95b)
- Dont load cards with get all runners request [`702070d`](https://git.odit.services/lfk/backend/commit/702070da669cc605b93e6f5b62d712c28f079dd0)
#### [v0.15.0](https://git.odit.services/lfk/backend/compare/v0.14.6...v0.15.0)
> 15 April 2023
- Added test script for creating mass scans [`8007117`](https://git.odit.services/lfk/backend/commit/80071174342d87199fcbd981cd8c92300b0a51e4)
- 🚀Bumped version to v0.15.0 [`cc89ba8`](https://git.odit.services/lfk/backend/commit/cc89ba8afb3120569613a889baf962555612e95a)
- Get all scans speed improvement [`23fa78e`](https://git.odit.services/lfk/backend/commit/23fa78eb9dcc01ecc036347f6703aacc0d163d7d)
- More scan request optimizations [`7c4ff42`](https://git.odit.services/lfk/backend/commit/7c4ff42a3b3e7b186e16c85a97d9ecc854a32cb0)
#### [v0.14.6](https://git.odit.services/lfk/backend/compare/v0.14.5...v0.14.6)
> 15 April 2023
- 🚀Bumped version to v0.14.6 [`3b3e689`](https://git.odit.services/lfk/backend/commit/3b3e68900beca16cfff88dbef22540f77750d29b)
- Missing orm file [`3ff666f`](https://git.odit.services/lfk/backend/commit/3ff666fd3e84ac8cf41b30e9e17082b10548d55b)
#### [v0.14.5](https://git.odit.services/lfk/backend/compare/v0.14.4...v0.14.5)
> 15 April 2023
- 🚀Bumped version to v0.14.5 [`4e44350`](https://git.odit.services/lfk/backend/commit/4e4435010fd7095e3b9742e207cba1b68cd6da3b)
- Entrypoint fix [`de9af5a`](https://git.odit.services/lfk/backend/commit/de9af5a90907dcfc9bfb1d5a56420eed8bb59922)
- Fixed copy [`ac631f0`](https://git.odit.services/lfk/backend/commit/ac631f0af467446552478873b7b4802a9310f865)
#### [v0.14.4](https://git.odit.services/lfk/backend/compare/v0.14.3...v0.14.4)
> 15 April 2023
- Switched ci over to pnpm + cache [`6275aaa`](https://git.odit.services/lfk/backend/commit/6275aaa326f1c02c8dd42aa31608978408c44ab7)
- 🚀Bumped version to v0.14.4 [`6bbdd5b`](https://git.odit.services/lfk/backend/commit/6bbdd5bb04a1c38e4b3a150db24b76e9c96490dd)
- Back to ean13 based codes [`a8fc755`](https://git.odit.services/lfk/backend/commit/a8fc7558408b97da4b2c469ae5e73ab502b4fda0)
- install prod in first step [`d027439`](https://git.odit.services/lfk/backend/commit/d02743984dfea8057be3081bd3a32a8f67e610aa)
- Switched dockerfile to pnpm 8 with cache [`93d43b7`](https://git.odit.services/lfk/backend/commit/93d43b76843d7cb411f37fd2066c6a5364c05415)
- COPY by stage name [`a64f6c9`](https://git.odit.services/lfk/backend/commit/a64f6c9822af2b927e91b0b55f1f50176de30169)
- pinned pnpm version [`2a94bfa`](https://git.odit.services/lfk/backend/commit/2a94bfa6227d14f635b5fc2789b59c36d490937e)
- custom pnpm cache [`85dc344`](https://git.odit.services/lfk/backend/commit/85dc3444acc677ddd242f9f2543ce477fe427a7c)
- added missing ci env [`734c826`](https://git.odit.services/lfk/backend/commit/734c826face58dd5c3bb2607bda6e7f6d051012e)
- pinned pnpm to 8 [`27e74e8`](https://git.odit.services/lfk/backend/commit/27e74e824cd1e23d4d53c1a983a1668dd87f5d59)
- coherent baseimage [`b5c0a28`](https://git.odit.services/lfk/backend/commit/b5c0a288ac3c020f5d753c558aee160fea0bae14)
- bumped final pnpm version [`33b25c9`](https://git.odit.services/lfk/backend/commit/33b25c9743abb7cefb3538f08cc2f78a646905c8)
#### [v0.14.3](https://git.odit.services/lfk/backend/compare/v0.14.2...v0.14.3)
> 18 March 2023
- 🚀Bumped version to v0.14.3 [`16ce0a8`](https://git.odit.services/lfk/backend/commit/16ce0a848050b74c4b6dd93f17e5a6e9024cdb7d)
- Adjusted modulo for new fixed card length [`9a8d618`](https://git.odit.services/lfk/backend/commit/9a8d618ae4584640e8be1ce9fe4bddd2ef7a92ae)
#### [v0.14.2](https://git.odit.services/lfk/backend/compare/v0.14.1...v0.14.2)
> 18 March 2023
- 🚀Bumped version to v0.14.2 [`38da2d3`](https://git.odit.services/lfk/backend/commit/38da2d33187f4b24eef878642e153663ecd95de1)
- Back to modulo [`068deb4`](https://git.odit.services/lfk/backend/commit/068deb4960bd16decf99887ffbda7a7d3dd9ff0b)
#### [v0.14.1](https://git.odit.services/lfk/backend/compare/v0.14.0...v0.14.1)
> 18 March 2023
- 🚀Bumped version to v0.14.1 [`13f093b`](https://git.odit.services/lfk/backend/commit/13f093bb6138a498f93a05ef6dd812ae92f2676a)
- Switched from card prefix replacement via modulo to regex [`6289f30`](https://git.odit.services/lfk/backend/commit/6289f307400aacaa9cfe03f3024c1e0d5554d4f2)
#### [v0.14.0](https://git.odit.services/lfk/backend/compare/v0.13.3...v0.14.0)
> 15 March 2023
- 🚀Bumped version to v0.14.0 [`6ff764b`](https://git.odit.services/lfk/backend/commit/6ff764bc340ca25b3bdd62c6892259e228723973)
- Updated default length [`ea87cc7`](https://git.odit.services/lfk/backend/commit/ea87cc793b163bf0d4405a25bbe83fbc8e31c206)
- breaking(runnercards): shorter runnercard codes (padding to 12 was a bit tooo ambitious) [`ffee887`](https://git.odit.services/lfk/backend/commit/ffee887ddf6a71102ee39533d7cd504d1fd6698f)
- Removed sqlite journal [`92517e3`](https://git.odit.services/lfk/backend/commit/92517e365393f4baac3814f5668874b5752dc7c8)
#### [v0.13.3](https://git.odit.services/lfk/backend/compare/v0.13.2...v0.13.3)
> 15 February 2023
- 🚀Bumped version to v0.13.3 [`3bac75e`](https://git.odit.services/lfk/backend/commit/3bac75e7ab9f16ecab1fbfa9915a7edb923883f6)
- Merge pull request 'feature/201-no_citizen-deletion' (#202) from feature/201-no_citizen-deletion into dev [`d05eddc`](https://git.odit.services/lfk/backend/commit/d05eddcae198427ce9a334096563b3aadcff2b56)
- Updated tests [`d5c689d`](https://git.odit.services/lfk/backend/commit/d5c689d6937288df7dca14ce26fbbd4f46a8752a)
- Added delete check for citizen org [`8fedd4e`](https://git.odit.services/lfk/backend/commit/8fedd4ef3bdd48dc42abc1d53006eefc145175e3)
#### [v0.13.2](https://git.odit.services/lfk/backend/compare/v0.13.1...v0.13.2)
> 3 February 2023
- 🚀Bumped version to v0.13.2 [`e8b2e6f`](https://git.odit.services/lfk/backend/commit/e8b2e6f26140a18c06b017e4461742d7e7942f08)
- Merge pull request 'move selfservice magic link endpoint to 15min rate limit' (#200) from feature/runner-selfservice-login-link-rate-limit into dev [`39f3b0e`](https://git.odit.services/lfk/backend/commit/39f3b0e01f03bfbcfcb0ea08d697268ce068e63d)
- move to 15min limit [`edaf255`](https://git.odit.services/lfk/backend/commit/edaf255e8f609185dcd6c2c0cd2e8b007b785e0c)
- Merge pull request 'Releases 0.12.0 and 0.13.0' (#199) from dev into main [`41c4ed4`](https://git.odit.services/lfk/backend/commit/41c4ed4d0faaed382801bbe480f31dafa6f3912d)
#### [v0.13.1](https://git.odit.services/lfk/backend/compare/v0.13.0...v0.13.1)
> 2 February 2023
- 🚀Bumped version to v0.13.1 [`f2bd88a`](https://git.odit.services/lfk/backend/commit/f2bd88aadfcb6ffa0485ea6afac8c7664a37f5f4)
- Updated description [`67a3661`](https://git.odit.services/lfk/backend/commit/67a36614485b2ea83c2de41e0684708b95a05b32)
#### [v0.13.0](https://git.odit.services/lfk/backend/compare/v0.12.0...v0.13.0)
> 2 February 2023
- Added faker for testing [`e184673`](https://git.odit.services/lfk/backend/commit/e1846739638905aab6ba7e059fd2cbf8ff467bf3)
- 📖New license file version [CI SKIP] [skip ci] [`2b641fa`](https://git.odit.services/lfk/backend/commit/2b641faa29c47d95f69983770dc4ab37e674604f)
- 🚀Bumped version to v0.13.0 [`0c763a2`](https://git.odit.services/lfk/backend/commit/0c763a2dfd39607b480d9aff7d3c883791f41700)
- Updated selfservice tests to prevent email duplication [`9bc80aa`](https://git.odit.services/lfk/backend/commit/9bc80aac8aab9b4dedc26c9bc3ce705d7fe9c0bf)
- Moved license and changelog export to releaseit hooks [`77c6303`](https://git.odit.services/lfk/backend/commit/77c6303014578edbbadeeaa790f7974bde2a9764)
- Updated readme [`4cdba8b`](https://git.odit.services/lfk/backend/commit/4cdba8bc77ce543f6fb636711b8728bce794eac7)
- 🧾New changelog file version [CI SKIP] [skip ci] [`ae14d6c`](https://git.odit.services/lfk/backend/commit/ae14d6c74f9205440b41ca5fdbd052ca449148fc)
- Added selfservice runner create check to prevent duplicate email [`68cd746`](https://git.odit.services/lfk/backend/commit/68cd746a9f3360b3630a9ba570213d2aa62497b4)
- Updated tests for new login in selfservice [`39aa759`](https://git.odit.services/lfk/backend/commit/39aa7598b7cd0ecb0f077f50ebdd31c6e205f06d)
- 🧾New changelog file version [CI SKIP] [skip ci] [`9fa8b93`](https://git.odit.services/lfk/backend/commit/9fa8b93c08ee52335b18e743f9d205b19e6095c6)
- Moved changelog generation to package script [`a7297ff`](https://git.odit.services/lfk/backend/commit/a7297ff933ae1372a9d508cdae1a54d2ebbcc647)
- Merge pull request 'feature/197-duplicate_runner_mail' (#198) from feature/197-duplicate_runner_mail into dev [`4b676bc`](https://git.odit.services/lfk/backend/commit/4b676bc85336c2d494e9e74823d38deec5cc0400)
- Updated logo url [`4433ddb`](https://git.odit.services/lfk/backend/commit/4433ddb1e15a35481728670e22049200644bf337)
- depends_on: ["clone"] [`9cc66ee`](https://git.odit.services/lfk/backend/commit/9cc66eebdfe8e7a2888bbc97197d1756ff44de30)
- Fixed typo [`19a290c`](https://git.odit.services/lfk/backend/commit/19a290c3a931ead0d9ae9ebb0985bfbaac54df59)
- Rename selfservice forgot to login [`69651d9`](https://git.odit.services/lfk/backend/commit/69651d9f6cd826b6d4720f164897a2a72a57c851)
- 📖New license file version [CI SKIP] [skip ci] [`6fd246f`](https://git.odit.services/lfk/backend/commit/6fd246f43cb3f4d0ccb6e017ee699889ba17daac)
- Add git for changelog fun [`2fa56b8`](https://git.odit.services/lfk/backend/commit/2fa56b82d1e082a1deae943e5fca5101f24e3ef5)
#### [v0.12.0](https://git.odit.services/lfk/backend/compare/v0.11.1...v0.12.0)
> 2 February 2023
- Pinned versions [`a6d5693`](https://git.odit.services/lfk/backend/commit/a6d5693ccdeb25b15a09af8f7438142114268807)
- Drone -&gt; Kaniko based builds [`0e78951`](https://git.odit.services/lfk/backend/commit/0e789513008085d0db94fc3b2dd9e74a5e583049)
- Drone images to odit registry [`6ad56b3`](https://git.odit.services/lfk/backend/commit/6ad56b31269bf19a740c1b6b1a303a8a9d7d59d0)
- Bumped container base images [`d95c6d3`](https://git.odit.services/lfk/backend/commit/d95c6d33657f6aa977a8ebfefad7e199bb1cc9c3)
- Enabled tag via release script [`9217421`](https://git.odit.services/lfk/backend/commit/92174212213f874e41c9472a927bcf87b963ac94)
- Pinned pnpm for builds [`4570845`](https://git.odit.services/lfk/backend/commit/4570845b3e1bd00c228fe1b09b658c24e20aba7f)
- 🚀Bumped version to v0.12.0 [`4c10e20`](https://git.odit.services/lfk/backend/commit/4c10e20b91a8101ee37b230373ceb3e024582b41)
- Ignore pnpm lock [`1f2c8ab`](https://git.odit.services/lfk/backend/commit/1f2c8abb22f3ff1e61b7350b517bd699c3e315f6)
- 🧾New changelog file version [CI SKIP] [skip ci] [`31b258b`](https://git.odit.services/lfk/backend/commit/31b258b4ce82213144160a4233b7fd127e456776)
#### [v0.11.1](https://git.odit.services/lfk/backend/compare/v0.11.0...v0.11.1)
> 22 April 2021
- Merge pull request 'Release 0.11.1' (#196) from dev into main [`f19f280`](https://git.odit.services/lfk/backend/commit/f19f2808d88414f1877c01f10996dac68b6f9617)
- 🧾New changelog file version [CI SKIP] [skip ci] [`2229cdf`](https://git.odit.services/lfk/backend/commit/2229cdf20db1a98f9f76a99fa9d3f463cdf6d804)
- 🧾New changelog file version [CI SKIP] [skip ci] [`348fe52`](https://git.odit.services/lfk/backend/commit/348fe52c42cfa32239b703041820f725e147154e)
- Now prefixing runnercards with 2 [`8a82e05`](https://git.odit.services/lfk/backend/commit/8a82e059b74ceabf43c9cbfe9c9b89ef6ce15a28)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3b9cd2e`](https://git.odit.services/lfk/backend/commit/3b9cd2e1bbbe8e69c3883233a98f286d768c2b79)
- Added fix for the appended 2 [`eb526fb`](https://git.odit.services/lfk/backend/commit/eb526fb57faf631fd6e84af99af738ab1b3481c7)
- 🚀Bumped version to v0.11.1 [`95320ca`](https://git.odit.services/lfk/backend/commit/95320ca1bccc2886553accea6a428aadffda0a27)
- 🧾New changelog file version [CI SKIP] [skip ci] [`f2d127f`](https://git.odit.services/lfk/backend/commit/f2d127fc98d75ce658424624abd382c087737ca0)
#### [v0.11.0](https://git.odit.services/lfk/backend/compare/v0.10.2...v0.11.0)
> 14 April 2021
- Merge pull request 'Release 0.11.0' (#195) from dev into main [`3220b19`](https://git.odit.services/lfk/backend/commit/3220b194d4c704835d6d106ec4d9d54a17a38b62)
- Fixed spelling [`da266a8`](https://git.odit.services/lfk/backend/commit/da266a8dd68dbb575997ae343624982b690486ec)
- Updated tests [`01ed514`](https://git.odit.services/lfk/backend/commit/01ed51489eb92fff907d46a930ecf0b0eb5cad2b)
- 🧾New changelog file version [CI SKIP] [skip ci] [`520608a`](https://git.odit.services/lfk/backend/commit/520608aef05b21f4daadf55cfc8caddba06b8f01)
- Added payedDonationAmount to donor and responsedonor [`8ae4b85`](https://git.odit.services/lfk/backend/commit/8ae4b8582749332f4fb081eee0c520293347001f)
- Responses now contain the donation status [`34dbaaa`](https://git.odit.services/lfk/backend/commit/34dbaaafe0422234848eabe3f52b26879c9e5a49)
- 🧾New changelog file version [CI SKIP] [skip ci] [`278c4a6`](https://git.odit.services/lfk/backend/commit/278c4a6a415434487a92ff66f8114bb2547aac48)
- Marked payedAmount as optional during creation and/or update [`0636616`](https://git.odit.services/lfk/backend/commit/0636616dad5afb41ffe47a857d91ac75b4f2f20a)
- Added payed amount fileld to donation class [`b8fbb72`](https://git.odit.services/lfk/backend/commit/b8fbb72fa0b659c9acc406c72a8a59c2174351b4)
- Added status to tests [`30c6d3d`](https://git.odit.services/lfk/backend/commit/30c6d3d8db9fe37a51e596a73add8b87e8616e54)
- Added payed amount to response class [`d64f470`](https://git.odit.services/lfk/backend/commit/d64f470b608b3f179ec77da0210de51c328ef3f2)
- 📖New license file version [CI SKIP] [skip ci] [`a2f0d81`](https://git.odit.services/lfk/backend/commit/a2f0d814fc782ad440500e7d6ec779b6ab7f0ac6)
- 🚀Bumped version to v0.11.0 [`3558e99`](https://git.odit.services/lfk/backend/commit/3558e9909088647bd4f1f4334f50c07a5ef00214)
- Merge pull request 'Donation payment management feature/193-donation_payments' (#194) from feature/193-donation_payments into dev [`6df5f63`](https://git.odit.services/lfk/backend/commit/6df5f634f3123e04c015889573ccc5674a8bab27)
- Added payed amount to crealte classes [`71542bc`](https://git.odit.services/lfk/backend/commit/71542bc3887b97c15436d03280e49f7b3f0fcb06)
- Added donation status enum [`b4c31ee`](https://git.odit.services/lfk/backend/commit/b4c31ee9b5b35d6e11b07f50f3d30ca12e0f7728)
- Added payed amount to update classes [`9930742`](https://git.odit.services/lfk/backend/commit/99307423c533f8cde847b59a80bffc2ff42c9769)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0c61ff4`](https://git.odit.services/lfk/backend/commit/0c61ff457d02f750efa457dd75464187683b037a)
- Added mssing check to tests [`6c14ed9`](https://git.odit.services/lfk/backend/commit/6c14ed9c89eadc1a10db8c912d8ea2711a518766)
- No longer answering with null, but 0 [`49b174f`](https://git.odit.services/lfk/backend/commit/49b174f29f63e963e600d74b6923a20211d832eb)
- Saved missing file [`8fe3243`](https://git.odit.services/lfk/backend/commit/8fe32436935d7cd6c17eae1e138383d3b714e1ba)
#### [v0.10.2](https://git.odit.services/lfk/backend/compare/v0.10.1...v0.10.2)
> 7 April 2021
- Merge pull request 'Release 0.10.2' (#192) from dev into main [`1d82f65`](https://git.odit.services/lfk/backend/commit/1d82f65b0d3a32d10c1a10c991353c18696d58bf)
- Added first selfservice test [`057ae0d`](https://git.odit.services/lfk/backend/commit/057ae0d79758cd627d6d128406a0d201b6b7ad9b)
- 🧾New changelog file version [CI SKIP] [skip ci] [`b7ad5d3`](https://git.odit.services/lfk/backend/commit/b7ad5d3a31b8b4f5960852d3ac38af133719ebcd)
- First try of the laptime sort [`4471e57`](https://git.odit.services/lfk/backend/commit/4471e57438582d55ff846fd69c2cfcc26b40df2a)
- Potential fix for all remaining errors [`377d5da`](https://git.odit.services/lfk/backend/commit/377d5dadb2a14cb2d70e0b2dc77026f51b3fb51c)
- At least one fewer test should fail now [`87f444c`](https://git.odit.services/lfk/backend/commit/87f444c30d69d65a9f918c63631a859a389eeee3)
- Tried workaround for no availdable stats [`8f0f795`](https://git.odit.services/lfk/backend/commit/8f0f795a709db216396998b68b8bbd64ff4d44ff)
- Reverted temp bugfix [`4603a84`](https://git.odit.services/lfk/backend/commit/4603a84f16fb53a14d1792447100f5b470969dd0)
- Fixed sorting algo [`988f17a`](https://git.odit.services/lfk/backend/commit/988f17a795bb2d867e9d1d8e78051dff1a14ec30)
- Added runners stats tests [`7111068`](https://git.odit.services/lfk/backend/commit/7111068361e00cc1308664a3ae650a56e28c015c)
- Added basic laptime endpoint [`cb71fcd`](https://git.odit.services/lfk/backend/commit/cb71fcd13bc61e6214e2fd7b70e72094749463d3)
- Added orgs by donations stats tests [`d4a02e7`](https://git.odit.services/lfk/backend/commit/d4a02e7db2ff4976be21605e31aac2f3c82a49c0)
- Added teams stats endpoint tests [`b9a7dc8`](https://git.odit.services/lfk/backend/commit/b9a7dc84f05441445453193974b2a793b5197fa5)
- Now resolving all missing relations [`257f320`](https://git.odit.services/lfk/backend/commit/257f320ee3bf6429c4314c64023520366f9f730b)
- Added min laptime to StatsRunner [`51daf96`](https://git.odit.services/lfk/backend/commit/51daf969cf74792b2c2f2f16ce4359d9fca47bc8)
- Fixed sorting [`7b15c2d`](https://git.odit.services/lfk/backend/commit/7b15c2d88b14e7279aad97b0c950202ddb5acaaa)
- Fixed top-ten bein top 9 [`a6a526d`](https://git.odit.services/lfk/backend/commit/a6a526dc5d8b1613ea34e82e477081349e764aec)
- added new ci secret [`5633e85`](https://git.odit.services/lfk/backend/commit/5633e85f41cb69b10fd8a86f57f1bd2f50848f7b)
- Added temp console log for test [`22cae39`](https://git.odit.services/lfk/backend/commit/22cae39bd351ca285880e50187ea0d46a7a26437)
- 🧾New changelog file version [CI SKIP] [skip ci] [`610988e`](https://git.odit.services/lfk/backend/commit/610988ec16b8df61cca61cf2252a469d30318d81)
- Added temp console log for ci debugging [`4a73eab`](https://git.odit.services/lfk/backend/commit/4a73eab134c3a9f58771be996bc8811b62cf378e)
- Temp disabled runners by donations test [`0b07a53`](https://git.odit.services/lfk/backend/commit/0b07a53ed209c6193ead3c4d199545e22333ab32)
- Updated default docker-compose [`f8baca5`](https://git.odit.services/lfk/backend/commit/f8baca5ab2c56b906751bc7edd71477456ad91f3)
- 🧾New changelog file version [CI SKIP] [skip ci] [`dd6d799`](https://git.odit.services/lfk/backend/commit/dd6d799c847fc96aec1be8f2667ad371890076fb)
- Resolved missing parentgroup relation [`23bd432`](https://git.odit.services/lfk/backend/commit/23bd432c5f33a0863217120d97e2e4ea52a08baf)
- Removed console logs for now working tests [`71b33ab`](https://git.odit.services/lfk/backend/commit/71b33ab05b53b62c8b271bd2995c94b2fc212dfd)
- Fixed typo in test [`cbcb829`](https://git.odit.services/lfk/backend/commit/cbcb829fbde3a4a5e7f94de5dcf24d854c5fc257)
- Ptotential fix for stats failing [`dcdbdd1`](https://git.odit.services/lfk/backend/commit/dcdbdd15acfe6eef4220b7ed66db60d78107d1f9)
- 🚀Bumped version to v0.10.2 [`6e236ed`](https://git.odit.services/lfk/backend/commit/6e236ede145e164ee84543fb62404b4776550973)
- Merge pull request 'stats/runners/laptime feature/190-runners_laptime' (#191) from feature/190-runners_laptime into dev [`a694ad2`](https://git.odit.services/lfk/backend/commit/a694ad225c68fa23152402acba871c857433cc70)
- Removed all useless console.logs [`95e1eec`](https://git.odit.services/lfk/backend/commit/95e1eec313a79458dd75307a9d0f8319af0d0904)
- Pinned testing container tag to prod container tag [`10221b9`](https://git.odit.services/lfk/backend/commit/10221b9f2e4493080f3ff095d9772bcfd0ac50eb)
- Now resolving all relations for orgs by distance [`4a294b1`](https://git.odit.services/lfk/backend/commit/4a294b1e17c44294274b06748ec8141812c2d217)
- Added temp console log [`720774f`](https://git.odit.services/lfk/backend/commit/720774fcf47c38601ab88d5d74cfcd0e47b21acf)
- Removed console log for passing tests [`132b48c`](https://git.odit.services/lfk/backend/commit/132b48cf2a9e990a5e830c744ed8244bd25e8b3a)
- Removed console log [`1d8c8c8`](https://git.odit.services/lfk/backend/commit/1d8c8c8e9cefa58449f7abb2481d9396fe37ba20)
- Temp test logging workaround [`bf686e8`](https://git.odit.services/lfk/backend/commit/bf686e89e02998ccc80c838ef890c736c252634c)
- Temp test logging workaround [`6163f0a`](https://git.odit.services/lfk/backend/commit/6163f0a90b3721d3a1488f89cbb39ddff7152241)
- Removed test for content type [`63964fb`](https://git.odit.services/lfk/backend/commit/63964fbf2c41d9b90f995f056e9db65ab07d54a8)
#### [v0.10.1](https://git.odit.services/lfk/backend/compare/v0.10.0...v0.10.1)
> 3 April 2021
- Merge pull request 'Release 0.10.1' (#189) from dev into main [`e89e07d`](https://git.odit.services/lfk/backend/commit/e89e07d0fc99f14148b01204fb8ed39e2da77e38)
- 🧾New changelog file version [CI SKIP] [skip ci] [`69afd4d`](https://git.odit.services/lfk/backend/commit/69afd4d5877401eb46df430f43a7feb273abda1e)
- 🚀Bumped version to v0.10.1 [`24d152f`](https://git.odit.services/lfk/backend/commit/24d152fdc8fe17fffa2f2a718d7145ba8a91d79c)
- New class: ResponseSelfServiceDonor [`d70c5b1`](https://git.odit.services/lfk/backend/commit/d70c5b1bbc9f02782f8755b6929e2d3458e10221)
- 🧾New changelog file version [CI SKIP] [skip ci] [`4279e43`](https://git.odit.services/lfk/backend/commit/4279e4374304887e8db40eab77763b20bbce91a1)
- Removed duplicate openapi statement [`4834a66`](https://git.odit.services/lfk/backend/commit/4834a6698b0958602421c1478a95fec7edda910b)
- Switched selfservice donation.donor from string to object [`0767943`](https://git.odit.services/lfk/backend/commit/0767943721b6964d542f580c541e744f86444ac6)
- Adjusted runner property names [`ca87774`](https://git.odit.services/lfk/backend/commit/ca87774767807a2c4bc869b0de95cc73832a8405)
- 🧾New changelog file version [CI SKIP] [skip ci] [`71e3d0e`](https://git.odit.services/lfk/backend/commit/71e3d0efe2cbde47aea0f26cb5a8b5cd3312707d)
- 🧾New changelog file version [CI SKIP] [skip ci] [`c28843c`](https://git.odit.services/lfk/backend/commit/c28843c405dc4fd06a10f0fb85814acede15a769)
- Merge pull request 'Selfservice donations reformatting feature/187-selfservice_donation' (#188) from feature/187-selfservice_donation into dev [`d837654`](https://git.odit.services/lfk/backend/commit/d837654617f7de5d055ffb06c65e2cd52f65c604)
- Added new responsetype for new class [`f693f2c`](https://git.odit.services/lfk/backend/commit/f693f2cde9a04147155aea4de5d52e1d19d722ca)
#### [v0.10.0](https://git.odit.services/lfk/backend/compare/v0.9.2...v0.10.0)
> 1 April 2021
- Merge pull request 'Release 0.10.0' (#186) from dev into main [`b517dff`](https://git.odit.services/lfk/backend/commit/b517dff8a82c960836d9f0be90fd89f3ba2fae7d)
- 🚀Bumped version to v0.10.0 [`dc3071f`](https://git.odit.services/lfk/backend/commit/dc3071f7d2be298f0bb02d86ec67ed1125cd3b49)
- Added locale to mail related runner endpoints [`7af883f`](https://git.odit.services/lfk/backend/commit/7af883f27198206af542bcaff4686221d3788e87)
- Added locale to mail related runner endpoints [`f543307`](https://git.odit.services/lfk/backend/commit/f5433076b01c743ed9af085fccadb8f1edc26419)
- 🧾New changelog file version [CI SKIP] [skip ci] [`5fb355f`](https://git.odit.services/lfk/backend/commit/5fb355f450f19e96d3671b1a46e94d564495942b)
- 🧾New changelog file version [CI SKIP] [skip ci] [`114c246`](https://git.odit.services/lfk/backend/commit/114c246aceba566cc0dd6daab51a77b951b031cc)
- Merge pull request 'Mail locales feature/184-mail_locales' (#185) from feature/184-mail_locales into dev [`33c13de`](https://git.odit.services/lfk/backend/commit/33c13de32c68a3d9e87e4fd9ad12a815ed8c9fde)
- Added locale to mail related user endpoints [`1be073a`](https://git.odit.services/lfk/backend/commit/1be073a4fa39f0332a46f567ee6af10a9137844c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`6aafe4a`](https://git.odit.services/lfk/backend/commit/6aafe4a6ae7d253ab39220e551c52ae067cc481a)
#### [v0.9.2](https://git.odit.services/lfk/backend/compare/v0.9.1...v0.9.2)
> 29 March 2021
- Merge pull request 'Release 0.9.2' (#183) from dev into main [`bdeeb03`](https://git.odit.services/lfk/backend/commit/bdeeb036459c2a2131e843d8a5a6b338e0ba46ea)
- 🧾New changelog file version [CI SKIP] [skip ci] [`675c876`](https://git.odit.services/lfk/backend/commit/675c8762e8e4cf28d2f334d5ab2e1cb6b594e33c)
- Fixed bug in return creation [`6c9b91d`](https://git.odit.services/lfk/backend/commit/6c9b91d75a0d08fc4ab0e72c7a09bd0133566368)
- 🧾New changelog file version [CI SKIP] [skip ci] [`8c00aef`](https://git.odit.services/lfk/backend/commit/8c00aefd6ce3723d9f83d1c94e6491d5d597391f)
- 🚀Bumped version to v0.9.2 [`89e3924`](https://git.odit.services/lfk/backend/commit/89e392473c52a3f328545699a0f4df89be33ba89)
#### [v0.9.1](https://git.odit.services/lfk/backend/compare/v0.9.0...v0.9.1)
> 29 March 2021
- Merge pull request 'Release v0.9.1' (#182) from dev into main [`3afd785`](https://git.odit.services/lfk/backend/commit/3afd785a54fac91c12af789af19b45e6124e0e39)
- 🚀Bumped version to v0.9.1 [`a139554`](https://git.odit.services/lfk/backend/commit/a139554e059e9a10acb1733ce1a82b610cc99269)
- 🧾New changelog file version [CI SKIP] [skip ci] [`8099999`](https://git.odit.services/lfk/backend/commit/8099999e2cdfc8046f9ff4a90681281b671e402d)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0290b0e`](https://git.odit.services/lfk/backend/commit/0290b0e5f531364d37d8157e639614cf5a6b4189)
- Merge pull request 'Return cards generated in bulk feature/180-blank_generation_return' (#181) from feature/180-blank_generation_return into dev [`0f7fa99`](https://git.odit.services/lfk/backend/commit/0f7fa990d473ce2dce032c47c39f79c1d0e8df90)
- Added query param to return created runenrcards [`5a36c8d`](https://git.odit.services/lfk/backend/commit/5a36c8dcae3d79b3b05ffb30a7ebb0d31dc8183a)
- 🧾New changelog file version [CI SKIP] [skip ci] [`58f4d21`](https://git.odit.services/lfk/backend/commit/58f4d2151f459bc72692cc70e02a59b77abfb9f0)
- Added test for returnCards=true array length [`1cb2dc9`](https://git.odit.services/lfk/backend/commit/1cb2dc9d53b530435f5798f9cdf7ee866eb7416e)
- Added test for single card generation with returnCards=true [`6005b06`](https://git.odit.services/lfk/backend/commit/6005b0661f1d5c461bb102e243cc209a8adc21fa)
- Fixed copy-paste oversight [`2f568c9`](https://git.odit.services/lfk/backend/commit/2f568c9cb8ae39ce40ec8df6d9acbaf0d5ae1a26)
#### [v0.9.0](https://git.odit.services/lfk/backend/compare/v0.8.0...v0.9.0)
> 26 March 2021
- Merge pull request 'Release 0.9.0' (#179) from dev into main [`95135dd`](https://git.odit.services/lfk/backend/commit/95135ddc893dcf64be67b47b0ef2b0d9041253bd)
- Reenabled user tests [`4c66650`](https://git.odit.services/lfk/backend/commit/4c6665062fe6717242e43b58e66c1f1d030c018d)
- Moved to tmp files to better check for other problems [`7a64f23`](https://git.odit.services/lfk/backend/commit/7a64f2393783f97a9729356bc1dfd831927dd312)
- Added user creation invalid tests [`888cab5`](https://git.odit.services/lfk/backend/commit/888cab5898caf9e552c421346934bf90f717a653)
- Updated auth test to comply with the new pw requirements [`63f6526`](https://git.odit.services/lfk/backend/commit/63f6526e4f59621edbf1fad59fc569b4bd6acbf2)
- Added user deletion tests [`e6a8ebc`](https://git.odit.services/lfk/backend/commit/e6a8ebcb5b4f430254da4afe159141b21d8da0ed)
- Added user creation valid tests [`383a809`](https://git.odit.services/lfk/backend/commit/383a8095b8286d51fb2fb24ae2fd0156230e56ab)
- 📖New license file version [CI SKIP] [skip ci] [`bd7b81e`](https://git.odit.services/lfk/backend/commit/bd7b81efe795c02512c87f3b5dd5eec796580144)
- Added password errors [`24c38cc`](https://git.odit.services/lfk/backend/commit/24c38cce26da41ccf375e1ccf04afa1868aad8df)
- 🧾New changelog file version [CI SKIP] [skip ci] [`274a146`](https://git.odit.services/lfk/backend/commit/274a146b9bccfe5e1a879ca137ebb4f51eaa5d57)
- Fixed test params [`070560e`](https://git.odit.services/lfk/backend/commit/070560e8632e833dd26505c02ccb2474462b63ac)
- No longer using createuser in seeding process [`96ba25e`](https://git.odit.services/lfk/backend/commit/96ba25ec6c6c397cd2aa322afa79024395f658fe)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a7fe1e1`](https://git.odit.services/lfk/backend/commit/a7fe1e175918edd7a98983ece570b47075e85e9a)
- 🚀Bumped version to v0.8.0 [`c23b4d9`](https://git.odit.services/lfk/backend/commit/c23b4d907f20ed7af37a6de6ea4c61433e30b29b)
- 🚀Bumped version to v0.9.0 [`56a5f41`](https://git.odit.services/lfk/backend/commit/56a5f4168621263daeab5d2fda97b944cdc6ab31)
- Merge pull request 'Password security feature/99-password_checks' (#177) from feature/99-password_checks into dev [`5a3fc5b`](https://git.odit.services/lfk/backend/commit/5a3fc5b2bd06b3e26177d017d3503f4f627be3f2)
- Added pw errors to user controller [`b24e24f`](https://git.odit.services/lfk/backend/commit/b24e24ff7dd75d972cdab0fd1e2fe6c532ca2b2f)
- Now checking password rules on user creation [`5daaa3a`](https://git.odit.services/lfk/backend/commit/5daaa3a73c4eca2817d67e226679d125928a3645)
- Now checking password rules on user update [`48a87e8`](https://git.odit.services/lfk/backend/commit/48a87e8936e13c48f4baa3f4b10f781ad2f55a44)
- Fixed pw not getting hashed currectly; [`cb3ea9b`](https://git.odit.services/lfk/backend/commit/cb3ea9b1ebb82c650abd83d4be8629cfe29a5b21)
- Added pw errors to me controller [`9ce35d8`](https://git.odit.services/lfk/backend/commit/9ce35d8eb78a01f40af8c70e640eca3bcb142304)
- Now forceing user deletion in tests [`8154e71`](https://git.odit.services/lfk/backend/commit/8154e715bbf18938bd5d1031656a88d39231fa81)
- Added password checker dependency [`bd00f4f`](https://git.odit.services/lfk/backend/commit/bd00f4f8d585fb6878874810f7de0b8b9f3950d5)
- Fixed empty object getting called [`5369000`](https://git.odit.services/lfk/backend/commit/536900091afd7366128f21058490d0d4f15c6c89)
- 🧾New changelog file version [CI SKIP] [skip ci] [`03d76e6`](https://git.odit.services/lfk/backend/commit/03d76e6d0bc5b4655f7f441232681c9462815526)
- Formatting [`b8c28eb`](https://git.odit.services/lfk/backend/commit/b8c28ebb0808395218b5fb9031f477ae1d48e65e)
#### [v0.8.0](https://git.odit.services/lfk/backend/compare/v0.7.1...v0.8.0)
> 26 March 2021
- Merge pull request 'Release 0.8.0' (#176) from dev into main [`3f8e8ce`](https://git.odit.services/lfk/backend/commit/3f8e8ce3a66a943801c0c8e17885e71feeee744f)
- 🧾New changelog file version [CI SKIP] [skip ci] [`c9bd6de`](https://git.odit.services/lfk/backend/commit/c9bd6de4762fec04e1e02cd3b667838d05ef39a7)
- Merge pull request 'Selfservice deletion feature/174-selfservice_deletion' (#175) from feature/174-selfservice_deletion into dev [`e702118`](https://git.odit.services/lfk/backend/commit/e702118d4d80e362e41bb88c74343d50530d1338)
- Added tests for the new endpoint [`20aeed8`](https://git.odit.services/lfk/backend/commit/20aeed87780247dc6401bba725801fc1874e50b5)
- Removed param from test [`97159dd`](https://git.odit.services/lfk/backend/commit/97159dd9f81aed080c174a3eb8da9e66dfea9b10)
- Added selfservice deletion endpoint [`dcb12b0`](https://git.odit.services/lfk/backend/commit/dcb12b0ac289f8df148ba10ae6389727c16f53fd)
- 🧾New changelog file version [CI SKIP] [skip ci] [`88844e1`](https://git.odit.services/lfk/backend/commit/88844e1a44d87a7dc253bf9aedf2fb3f6cdd1cfe)
- Fixed response bug [`ccb7ae2`](https://git.odit.services/lfk/backend/commit/ccb7ae29a39387c0f2762861565dc22996a2493a)
- Updated old hint [`dd12583`](https://git.odit.services/lfk/backend/commit/dd1258333ef67243f8a8df97c176ec5a054a5e3b)
#### [v0.7.1](https://git.odit.services/lfk/backend/compare/v0.7.0...v0.7.1)
> 26 March 2021
- 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)
- 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)
- Added test mail sending test [`ae74b39`](https://git.odit.services/lfk/backend/commit/ae74b3963fddb847aed4a828031b93b26cf551db)
- Password reset now enforces email [`979d36e`](https://git.odit.services/lfk/backend/commit/979d36ea9147dc575e9e989f6833388828285176)
- 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)
- Removed tests working directly with the old pw-reset response [`d02e9de`](https://git.odit.services/lfk/backend/commit/d02e9dec5637aedefdf2ed3cd2c6d73216b6464b)
- Added the basics about mail templates to the readme [`b5018eb`](https://git.odit.services/lfk/backend/commit/b5018eb11492884db9f4ec969c767c3cce53f105)
- Cleaned up the replacements [`389e423`](https://git.odit.services/lfk/backend/commit/389e423850d68a5fe440b62413a6c662353ac9c6)
- Added mail env vars [`d7ea928`](https://git.odit.services/lfk/backend/commit/d7ea928714f94814695cbd2815c8730df58033f6)
- Added a barebones class for handleing mail stuff [`cf012c0`](https://git.odit.services/lfk/backend/commit/cf012c0b7efffb81b03497a04b0fdad0423c72f7)
- Added a Mail permisssion target [`ad4b903`](https://git.odit.services/lfk/backend/commit/ad4b903c258820f14df28d56b12e099075ca7d78)
- Added env vars [`470703c`](https://git.odit.services/lfk/backend/commit/470703c4de954da94726879becd57986b59e1f69)
- 🧾New changelog file version [CI SKIP] [skip ci] [`2071c4d`](https://git.odit.services/lfk/backend/commit/2071c4db33bbb9fd41ef650b409cac789732225f)
- Added a hint to ethereal.email [`53fcff7`](https://git.odit.services/lfk/backend/commit/53fcff77d00fc2b205ada0bcee7bdfe83d94a9f4)
- Fixed missing app_url protocol [`46af786`](https://git.odit.services/lfk/backend/commit/46af7865165bbfb97ed3e6cdfef15dfb72add611)
- Removed the duplicate env copy/create from ci tests [`08e6e59`](https://git.odit.services/lfk/backend/commit/08e6e5965544906f5033f2080166bddc37cc30c7)
- Removed bs console.log [`71c4caa`](https://git.odit.services/lfk/backend/commit/71c4caae8ba67e253d893409b3c5c3a39b08060a)
- Added nodemailer types [`78d2ac3`](https://git.odit.services/lfk/backend/commit/78d2ac3027f7109161ee36e9b3dda628a7039468)
- Added nodemailer dependecy [`908ac4f`](https://git.odit.services/lfk/backend/commit/908ac4f1ce9d78749268353c668e67b57eae6f94)
- Fixed wrong file location [`b4c117b`](https://git.odit.services/lfk/backend/commit/b4c117b7dc326d212598b6e720d0a422134e383d)
- Renamed the template [`fb77f4d`](https://git.odit.services/lfk/backend/commit/fb77f4d798550fdb6fe6b2c8a81198db0328f71e)
- Added a folder for the mail templates [`6b0155f`](https://git.odit.services/lfk/backend/commit/6b0155f01464f5ef73ab679b9e3219743e9db66b)
- Added a folder for the mail templates [`33890b5`](https://git.odit.services/lfk/backend/commit/33890b544b77272ab1c4797e91375d24568eae58)
#### [v0.3.1](https://git.odit.services/lfk/backend/compare/v0.3.0...v0.3.1)
> 27 January 2021
- Merge pull request 'Alpha Release 0.3.1' (#127) from dev into main [`20f960e`](https://git.odit.services/lfk/backend/commit/20f960ed6700fe58c0556895e6485d26c4a4f5e2)
- 🧾New changelog file version [CI SKIP] [skip ci] [`e6fe8fc`](https://git.odit.services/lfk/backend/commit/e6fe8fcd587751392970d3ee412559b4c1d60f21)
- Merge pull request 'new advanced endpoints feature/125-team_runner' (#126) from feature/125-team_runner into dev [`870fd47`](https://git.odit.services/lfk/backend/commit/870fd47c83389345d7b24a15df6a4e61e930d140)
- Added get runners by team test [`69417e9`](https://git.odit.services/lfk/backend/commit/69417e93c081422561db1e211b12f32e539010ce)
- 🧾New changelog file version [CI SKIP] [skip ci] [`71898d5`](https://git.odit.services/lfk/backend/commit/71898d576c2620d2f2e2b4336e62f1d04a443201)
- Created the organizations/runners endpoint [`570c34b`](https://git.odit.services/lfk/backend/commit/570c34bed04e359f389a8f427486bf92891f1dcb)
- Created the runnerTeam/runners endpoint [`7be2971`](https://git.odit.services/lfk/backend/commit/7be2971a9e02bf0e784f7fe5cdd82afbbbf7f854)
- 🧾New changelog file version [CI SKIP] [skip ci] [`aedfcfc`](https://git.odit.services/lfk/backend/commit/aedfcfcc8359afd7dba4fa5e515e8e77fbb3fc6e)
- Added get runners by org test [`f71a22f`](https://git.odit.services/lfk/backend/commit/f71a22f4dd9bf14d39ced91908f6f6a5d8395e56)
- 🚀Bumped version to v0.3.1 [`db08760`](https://git.odit.services/lfk/backend/commit/db0876015bf0599dabb21357f172735888c79aa8)
#### [v0.3.0](https://git.odit.services/lfk/backend/compare/v0.2.1...v0.3.0)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3697783`](https://git.odit.services/lfk/backend/commit/3697783e190e36f1168132d75da1eca7be27b4f6)
- Merge pull request 'OrganiZation rename feature/117-organization' (#121) from feature/117-organization into dev [`161feaf`](https://git.odit.services/lfk/backend/commit/161feaf364195c7b85041e06cdefef72d75b8951)
> 24 January 2021
- Merge pull request 'Alpha Release 0.3.0' (#122) from dev into main [`c964591`](https://git.odit.services/lfk/backend/commit/c9645918398e62a80341d038b6b6b5c60ee4b1c7)
- Renamed files and classed from *Organisation* to *Organization*📝 [`c6c643e`](https://git.odit.services/lfk/backend/commit/c6c643ecf125f5fdf58b105f97efad32e8545dd4)
- Changed organisation* to organization* in descriptions, comments and endoints ✏ [`ef15d0d`](https://git.odit.services/lfk/backend/commit/ef15d0d57619d79e014e0c0331965ca2bc664254)
- Added registration valid company tests [`20e102e`](https://git.odit.services/lfk/backend/commit/20e102ec5c52c6a01144d064c96b8b2bd1ccfd00)
- Added registration invalid citizen tests [`81d2197`](https://git.odit.services/lfk/backend/commit/81d2197a3e978ca43bc79720c32d75f8490f08df)
- Implemented registration key generation [`ad44650`](https://git.odit.services/lfk/backend/commit/ad446500f90f945aadc3510377bcfd2123440da0)
- Implemented a runner selfservice registration creation action [`10af1ba`](https://git.odit.services/lfk/backend/commit/10af1ba34148c992e94fa580e1c0210bfaea5112)
- Created a citizenrunner selfservice create action [`6df195b`](https://git.odit.services/lfk/backend/commit/6df195b6ec5fde27f84cbe54992558715b843b87)
- Added registration invalid company tests [`29aeb04`](https://git.odit.services/lfk/backend/commit/29aeb046de769e37fd7257507e36337237697ea0)
- 🧾New changelog file version [CI SKIP] [skip ci] [`5660aec`](https://git.odit.services/lfk/backend/commit/5660aecb50c0e6dd538850c6375d2f692fb7a1f2)
- Implemented a registration key for organisations [`d490247`](https://git.odit.services/lfk/backend/commit/d490247d1e337a680b385d2115e82f79ba54a601)
- Updates old tests to the new ss-ktokens [`a9843ed`](https://git.odit.services/lfk/backend/commit/a9843ed4598485e6e3d18e78b876b6e000ea6e38)
- Added registration valid citizentests [`72941da`](https://git.odit.services/lfk/backend/commit/72941da1cb1c31fd6bfcba2ee3704f34bdd73ef0)
- Added self-service get invalid tests [`e964a8e`](https://git.odit.services/lfk/backend/commit/e964a8ed44109899516c4d3b0dc35fe4990107a1)
- Implemented runner selfservice token generation [`c39a59e`](https://git.odit.services/lfk/backend/commit/c39a59e54ef0b1cc891684283422805af38969e3)
- 🧾New changelog file version [CI SKIP] [skip ci] [`cc4bf44`](https://git.odit.services/lfk/backend/commit/cc4bf4451cf4eac0e0f1bab02119c2dd5e300f48)
- Renamedpermisssions from *Organisation* to *Organization*📝 [`cd7e9b8`](https://git.odit.services/lfk/backend/commit/cd7e9b86b4b9d3e1ef0312f6fff436fcef4a5c94)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3697783`](https://git.odit.services/lfk/backend/commit/3697783e190e36f1168132d75da1eca7be27b4f6)
- Merge pull request 'OrganiZation rename feature/117-organization' (#121) from feature/117-organization into dev [`161feaf`](https://git.odit.services/lfk/backend/commit/161feaf364195c7b85041e06cdefef72d75b8951)
- Added registration valid company tests [`20e102e`](https://git.odit.services/lfk/backend/commit/20e102ec5c52c6a01144d064c96b8b2bd1ccfd00)
- Added registration invalid company tests [`29aeb04`](https://git.odit.services/lfk/backend/commit/29aeb046de769e37fd7257507e36337237697ea0)
- Added registration valid citizentests [`72941da`](https://git.odit.services/lfk/backend/commit/72941da1cb1c31fd6bfcba2ee3704f34bdd73ef0)
- Citizen runners now have to provide an email address for verification [`dee3639`](https://git.odit.services/lfk/backend/commit/dee36395a69da6c5e1bc4e94bd705b0418a6a3ff)
- Implemented the basics for the runner selfservice registration endpoint [`5288c70`](https://git.odit.services/lfk/backend/commit/5288c701c1ac880f3c8b7ece01457aae4bac87d7)
- Added selfservice get positive test [`0c87906`](https://git.odit.services/lfk/backend/commit/0c87906cc3fa5feaf1d7c186f68d978d4ed7fa8e)
- Implemented the citizen runner self-registration endpoint [`1b5465b`](https://git.odit.services/lfk/backend/commit/1b5465bea810f59cbf8bb1a3e82c062176f79c49)
- Renamedpermisssions from *Organisation* to *Organization*📝 [`cd7e9b8`](https://git.odit.services/lfk/backend/commit/cd7e9b86b4b9d3e1ef0312f6fff436fcef4a5c94)
- Fixed tests testing for a old responseclass [`45c8bb8`](https://git.odit.services/lfk/backend/commit/45c8bb83be0814bed8856a617de435f4694db76c)
- Fixed typo [`46f9503`](https://git.odit.services/lfk/backend/commit/46f9503543ee011d0780caeefa95748e4be45f58)
- 🧾New changelog file version [CI SKIP] [skip ci] [`c5d0646`](https://git.odit.services/lfk/backend/commit/c5d0646c425f83689bffd862edd568ca0c1b5ad8)
@ -32,11 +776,12 @@ All notable changes to this project will be documented in this file. Dates are d
- Marked param as optional (default: false) [`f8d7544`](https://git.odit.services/lfk/backend/commit/f8d754451769e8361ec2367df7bc586f8fffe8eb)
- Bugfix: turned old entity in response to responseclass [`10f98e9`](https://git.odit.services/lfk/backend/commit/10f98e9c992b1fb45334e65082d7c47af214b6ac)
- Resolved missing relation [`3b2ed3f`](https://git.odit.services/lfk/backend/commit/3b2ed3f0f2557dccddf6ba656b201c196cc24bf0)
- Citizen registration now returns tokens [`9dd9304`](https://git.odit.services/lfk/backend/commit/9dd9304a71eae94978710cf9db82dc030ca7a37d)
- Fixed wrong error getting thrown [`6469e3b`](https://git.odit.services/lfk/backend/commit/6469e3bc976c589b42fbac4a95bbee84f67244ee)
- Fixed fluctuating test bahaviour [`1227408`](https://git.odit.services/lfk/backend/commit/1227408407ac66b9689446c1f318b453a9d45069)
- 🚀Bumped version to v0.3.0 [`75e2a44`](https://git.odit.services/lfk/backend/commit/75e2a44c9c7f720d1a5a20611b305e5d56a9155b)
- Merge pull request 'Self service registration feature/112-selfservice_registration' (#120) from feature/112-selfservice_registration into dev [`6a66dd8`](https://git.odit.services/lfk/backend/commit/6a66dd803becb59172e8645c6b55898fb4b4c0b5)
- Fixed fluctuating test bahaviour [`1227408`](https://git.odit.services/lfk/backend/commit/1227408407ac66b9689446c1f318b453a9d45069)
- Implemented the citizen runner self-registration endpoint [`1b5465b`](https://git.odit.services/lfk/backend/commit/1b5465bea810f59cbf8bb1a3e82c062176f79c49)
- Citizen registration now returns tokens [`9dd9304`](https://git.odit.services/lfk/backend/commit/9dd9304a71eae94978710cf9db82dc030ca7a37d)
- Fixed wrong error getting thrown [`6469e3b`](https://git.odit.services/lfk/backend/commit/6469e3bc976c589b42fbac4a95bbee84f67244ee)
- Updated response schema error to a more fitting one [`5a00394`](https://git.odit.services/lfk/backend/commit/5a003945ac7d1a8d446c1bcc8007523b675ab6f5)
- Added check for empty token for runner self-service get [`6434b4d`](https://git.odit.services/lfk/backend/commit/6434b4dfce2da307d66ec5670d570d66ea8af8f8)
- Specified uft-8 format for string [`34c852b`](https://git.odit.services/lfk/backend/commit/34c852b12aaa88e64efa1e0575361c09652e22e1)

View File

@ -1,16 +1,23 @@
# Typescript Build
FROM node:14.15.1-alpine3.12
FROM registry.odit.services/hub/library/node:21.1.0-alpine3.18 as build
ARG NPM_REGISTRY_URL=https://registry.npmjs.org
WORKDIR /app
COPY package.json ./
RUN npm i -g pnpm
RUN pnpm i
RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i
COPY tsconfig.json ormconfig.js ./
COPY src ./src
RUN pnpm run build
RUN pnpm run build \
&& rm -rf /app/node_modules \
&& pnpm i --production --prefer-offline
# final image
FROM node:14.15.1-alpine3.12
COPY package.json ormconfig.js ./
RUN npm i -g pnpm
RUN pnpm i --prod
COPY --from=0 /app/dist dist
ENTRYPOINT ["node", "dist/app.js"]
FROM registry.odit.services/hub/library/node:21.1.0-alpine3.18 as final
WORKDIR /app
COPY --from=build /app/package.json /app/package.json
COPY --from=build /app/ormconfig.js /app/ormconfig.js
COPY --from=build /app/dist /app/dist
COPY --from=build /app/node_modules /app/node_modules
ENTRYPOINT ["node", "/app/dist/app.js"]

View File

@ -15,31 +15,61 @@ Backend Server
1. Rename the .env.example file to .env (you can adjust app port and other settings, if needed)
2. Install Dependencies
```bash
yarn
```
```bash
pnpm i
```
3. Start the server
```bash
yarn dev
```
```bash
pnpm dev
```
### Run Tests
```bash
# Run tests once (server has to run)
yarn test
pnpm test
# Run test in watch mode (reruns on change)
yarn test:watch
pnpm test:watch
# Run test in ci mode (automaticly starts the dev server)
yarn test:ci
pnpm test:ci
```
### Use your own mail templates
> You use your own mail templates by replacing the default ones we provided (either in-code or by mounting them into the /app/static/mail_templates folder).
The mail templates always come in a .html and a .txt variant to provide compatability with legacy mail clients.
Currently the following templates exist:
* pw-reset.(html/txt)
### Generate Docs
```bash
yarn docs
pnpm 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 bs data as test server and ignores the errors).
| Name | Type | Default | Description |
| ---------------------- | ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------- |
| APP_PORT | Number | 4010 | The port the backend server listens on. Is optional. |
| DB_TYPE | String | N/A | The type of the db u want to use. It has to be supported by typeorm. Possible: `sqlite`, `mysql`, `postgresql` |
| DB_HOST | String | N/A | The db's host's ip-address/fqdn or file path for sqlite |
| DB_PORT | String | N/A | The db's port |
| 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. 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 |
| 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
[Visual Studio Code](https://code.visualstudio.com/)
@ -55,10 +85,10 @@ yarn docs
* A new release tag automaticly triggers the release ci pipeline
* main: Protected "release" branch
* The latest tag of the docker image get's build from this
* New releases get created as tags from this
* dev: Current dev branch for merging the different feature branches and bugfixes
* New releases get created as tags from this
* The dev tag of the docker image get's build from this
* Only push minor changes to this branch!
* To merge a feature branch into this please create a pull request
* feature/xyz: Feature branches - nameing scheme: `feature/issueid-title`
* bugfix/xyz: Branches for bugfixes - nameing scheme:`bugfix/issueid-title`
* feature/xyz: Feature branches - naming scheme: `feature/issueid-title`
* bugfix/xyz: Branches for bugfixes - naming scheme:`bugfix/issueid-title`

View File

@ -11,8 +11,12 @@ services:
DB_PORT: bla
DB_USER: bla
DB_PASSWORD: bla
DB_NAME: dev.sqlite
DB_NAME: ./db.sqlite
NODE_ENV: production
POSTALCODE_COUNTRYCODE: DE
SEED_TEST_DATA: "false"
MAILER_URL: https://dev.lauf-fuer-kaya.de/mailer
MAILER_KEY: asdasd
# APP_PORT: 4010
# DB_TYPE: postgres
# DB_HOST: backend_db

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
@ -59,6 +115,35 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# check-password-strength
**Author**: deanilvincent
**Repo**: [object Object]
**License**: MIT
**Description**: A NPM Password strength checker based from Javascript RegExp. Check passphrase if it's "Weak", "Medium" or "Strong"
## License Text
MIT License
Copyright (c) 2020 Mark Deanil Vicente
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.
# class-transformer
**Author**: [object Object]
**Repo**: [object Object]
@ -88,22 +173,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 +190,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,12 +409,60 @@ 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
**License**: MIT
**Description**: A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 100% MIT licensed.
## License Text
Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors
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.
# pg
@ -590,11 +715,80 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# @faker-js/faker
**Author**: undefined
**Repo**: [object Object]
**License**: MIT
**Description**: Generate massive amounts of fake contextual data
## License Text
Faker - Copyright (c) 2022
This software consists of voluntary contributions made by many individuals.
For exact contribution history, see the revision history
available at https://github.com/faker-js/faker
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.
===
From: https://github.com/faker-js/faker/commit/a9f98046c7d5eeaabe12fc587024c06d683800b8
To: https://github.com/faker-js/faker/commit/29234378807c4141588861f69421bf20b5ac635e
Based on faker.js, copyright Marak Squires and contributor, what follows below is the original license.
===
faker.js - Copyright (c) 2020
Marak Squires
http://github.com/marak/faker.js/
faker.js was inspired by and has used data definitions from:
* https://github.com/stympy/faker/ - Copyright (c) 2007-2010 Benjamin Curtis
* http://search.cpan.org/~jasonk/Data-Faker-0.07/ - Copyright 2004-2005 by Jason Kohles
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.
# @odit/license-exporter
**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,13 +1014,15 @@ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SOFTWARE
# axios
**Author**: Matt Zabriskie
# auto-changelog
**Author**: Pete Cook <pete@cookpete.com> (https://github.com/cookpete)
**Repo**: [object Object]
**License**: MIT
**Description**: Promise based HTTP client for the browser and node.js
**Description**: Command line tool for generating a changelog from git tags and commit history
## License Text
Copyright (c) 2014-present Matt Zabriskie
The MIT License
Copyright (c) 2017 Pete Cook https://cookpete.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
@ -934,6 +1130,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

View File

@ -1,103 +1,117 @@
{
"name": "@odit/lfk-backend",
"version": "0.3.0",
"main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend",
"author": {
"name": "ODIT.Services",
"email": "info@odit.services",
"url": "https://odit.services"
},
"contributors": [
{
"name": "Philipp Dormann",
"email": "philipp@philippdormann.de",
"url": "https://philippdormann.de"
},
{
"name": "Nicolai Ort",
"email": "info@nicolai-ort.com",
"url": "https://nicolai-ort.com"
}
],
"license": "CC-BY-NC-SA-4.0",
"dependencies": {
"@odit/class-validator-jsonschema": "2.1.1",
"argon2": "^0.27.1",
"body-parser": "^1.19.0",
"class-transformer": "0.3.1",
"class-validator": "^0.13.1",
"consola": "^2.15.0",
"cookie": "^0.4.1",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"csvtojson": "^2.0.10",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"libphonenumber-js": "^1.9.7",
"mysql": "^2.18.1",
"pg": "^8.5.1",
"reflect-metadata": "^0.1.13",
"routing-controllers": "^0.9.0-alpha.6",
"routing-controllers-openapi": "^2.2.0",
"sqlite3": "5.0.0",
"typeorm": "^0.2.29",
"typeorm-routing-controllers-extensions": "^0.2.0",
"typeorm-seeding": "^1.6.1",
"uuid": "^8.3.2",
"validator": "^13.5.2"
},
"devDependencies": {
"@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/jsonwebtoken": "^8.5.0",
"@types/node": "^14.14.20",
"@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-node": "^9.1.1",
"typedoc": "^0.20.14",
"typescript": "^4.1.3"
},
"scripts": {
"dev": "nodemon src/app.ts",
"build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static",
"docs": "typedoc --out docs src",
"test": "jest",
"test:watch": "jest --watchAll",
"test:ci": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test",
"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",
"release": "release-it --only-version"
},
"release-it": {
"git": {
"commit": true,
"requireCleanWorkingDir": false,
"commitMessage": "🚀Bumped version to v${version}",
"requireBranch": "dev",
"push": false,
"tag": false
},
"npm": {
"publish": false
}
},
"nodemonConfig": {
"ignore": [
"src/tests/*",
"docs/*"
]
}
}
{
"name": "@odit/lfk-backend",
"version": "1.1.3",
"main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend",
"engines": {
"pnpm": "8"
},
"author": {
"name": "ODIT.Services",
"email": "info@odit.services",
"url": "https://odit.services"
},
"contributors": [
{
"name": "Philipp Dormann",
"email": "philipp@philippdormann.de",
"url": "https://philippdormann.de"
},
{
"name": "Nicolai Ort",
"email": "info@nicolai-ort.com",
"url": "https://nicolai-ort.com"
}
],
"license": "CC-BY-NC-SA-4.0",
"dependencies": {
"@odit/class-validator-jsonschema": "2.1.1",
"argon2": "0.27.1",
"axios": "0.21.1",
"body-parser": "1.19.0",
"check-password-strength": "2.0.2",
"class-transformer": "0.3.1",
"class-validator": "0.13.1",
"consola": "2.15.0",
"cookie": "0.4.1",
"cookie-parser": "1.4.5",
"cors": "2.8.5",
"csvtojson": "2.0.10",
"dotenv": "8.2.0",
"express": "4.17.1",
"jsonwebtoken": "8.5.1",
"libphonenumber-js": "1.9.9",
"mysql": "2.18.1",
"pg": "8.5.1",
"reflect-metadata": "0.1.13",
"routing-controllers": "0.9.0-alpha.6",
"routing-controllers-openapi": "2.2.0",
"sqlite3": "5.0.0",
"typeorm": "0.2.30",
"typeorm-routing-controllers-extensions": "0.2.0",
"typeorm-seeding": "1.6.1",
"uuid": "8.3.2",
"validator": "13.5.2"
},
"devDependencies": {
"@faker-js/faker": "7.6.0",
"@odit/license-exporter": "0.0.9",
"@types/cors": "2.8.9",
"@types/csvtojson": "1.1.5",
"@types/express": "4.17.11",
"@types/jest": "26.0.20",
"@types/jsonwebtoken": "8.5.0",
"@types/node": "14.14.22",
"@types/uuid": "8.3.0",
"auto-changelog": "2.4.0",
"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.5.0",
"ts-node": "9.1.1",
"typedoc": "0.20.19",
"typescript": "4.1.3"
},
"scripts": {
"dev": "nodemon src/app.ts",
"build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static",
"docs": "typedoc --out docs src",
"test": "jest",
"test:watch": "jest --watchAll",
"test:ci:generate_env": "ts-node scripts/create_testenv.ts",
"test:ci:run": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test",
"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 --markdown",
"changelog:export": "auto-changelog --commit-limit false -p -u --hide-credit",
"release": "release-it --only-version"
},
"release-it": {
"git": {
"commit": true,
"requireCleanWorkingDir": false,
"commitMessage": "🚀Bumped version to v${version}",
"requireBranch": "dev",
"push": true,
"tag": true,
"tagName": "v${version}",
"tagAnnotation": "v${version}"
},
"npm": {
"publish": false
},
"hooks": {
"after:bump": "npm run changelog:export && npm run licenses:export && git add CHANGELOG.md && git add licenses.md"
}
},
"nodemonConfig": {
"ignore": [
"src/tests/*",
"docs/*"
]
}
}

7803
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

24
scripts/create_testenv.ts Normal file
View File

@ -0,0 +1,24 @@
import consola from "consola";
import fs from "fs";
const env = `
APP_PORT=4010
DB_TYPE=sqlite
DB_HOST=bla
DB_PORT=bla
DB_USER=bla
DB_PASSWORD=bla
DB_NAME=./test.sqlite
NODE_ENV=test
POSTALCODE_COUNTRYCODE=DE
SEED_TEST_DATA=true
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");
}

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

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}`

View File

@ -6,11 +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()
seedTestData: getDataSeeding(),
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") {
@ -19,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);
}

View File

@ -1,103 +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 { 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 { Logout } from '../models/responses/ResponseLogout';
@JsonController('/auth')
export class AuthController {
constructor() {
}
@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(ResponseAuth)
@ResponseSchema(UserNotFoundError)
@ResponseSchema(UsernameOrEmailNeededError)
@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) {
//This really shouldn't just get returned, but sent via mail or sth like that. But for dev only this is fine.
return { "resetToken": await passwordReset.toResetToken() };
}
@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();
}
}

View File

@ -1,6 +1,6 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { DonationIdsNotMatchingError, DonationNotFoundError } from '../errors/DonationErrors';
import { DonorNotFoundError } from '../errors/DonorErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
@ -36,9 +36,16 @@ export class DonationController {
@ResponseSchema(ResponseDonation, { isArray: true })
@ResponseSchema(ResponseDistanceDonation, { isArray: true })
@OpenAPI({ description: 'Lists all donations (fixed or distance based) from all donors. <br> This includes the donations\'s runner\'s distance ran(if distance donation).' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseDonations: ResponseDonation[] = new Array<ResponseDonation>();
const donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] });
let donations: Array<Donation>;
if (page != undefined) {
donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'], skip: page * page_size, take: page_size });
} else {
donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] });
}
donations.forEach(donation => {
responseDonations.push(donation.toResponse());
});
@ -78,7 +85,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 +131,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')

View File

@ -1,6 +1,6 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { DonorHasDonationsError, DonorIdsNotMatchingError, DonorNotFoundError } from '../errors/DonorErrors';
import { CreateDonor } from '../models/actions/create/CreateDonor';
import { UpdateDonor } from '../models/actions/update/UpdateDonor';
@ -25,9 +25,16 @@ export class DonorController {
@Authorized("DONOR:GET")
@ResponseSchema(ResponseDonor, { isArray: true })
@OpenAPI({ description: 'Lists all donor. <br> This includes the donor\'s current donation amount.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseDonors: ResponseDonor[] = new Array<ResponseDonor>();
const donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] });
let donors: Array<Donor>;
if (page != undefined) {
donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'], skip: page * page_size, take: page_size });
} else {
donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] });
}
donors.forEach(donor => {
responseDonors.push(new ResponseDonor(donor));
});

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnection, getConnectionManager, Repository } from 'typeorm';
import { Repository, getConnection, getConnectionManager } from 'typeorm';
import { GroupContactIdsNotMatchingError, GroupContactNotFoundError } from '../errors/GroupContactErrors';
import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors';
import { CreateGroupContact } from '../models/actions/create/CreateGroupContact';
@ -26,9 +26,16 @@ export class GroupContactController {
@Authorized("CONTACT:GET")
@ResponseSchema(ResponseGroupContact, { isArray: true })
@OpenAPI({ description: 'Lists all contacts. <br> This includes the contact\'s associated groups.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseContacts: ResponseGroupContact[] = new Array<ResponseGroupContact>();
const contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'] });
let contacts: Array<GroupContact>;
if (page != undefined) {
contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'], skip: page * page_size, take: page_size });
} else {
contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'] });
}
contacts.forEach(contact => {
responseContacts.push(contact.toResponse());
});

View File

@ -1,7 +1,7 @@
import { Body, CurrentUser, Delete, Get, JsonController, OnUndefined, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { UserDeletionNotConfirmedError, UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } from '../errors/UserErrors';
import { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, UserDeletionNotConfirmedError, UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } from '../errors/UserErrors';
import { UpdateUser } from '../models/actions/update/UpdateUser';
import { User } from '../models/entities/User';
import { ResponseUser } from '../models/responses/ResponseUser';
@ -32,7 +32,7 @@ export class MeController {
return new ResponseUser(user);
}
@Get('/')
@Get('/permissions')
@ResponseSchema(ResponseUserPermissions)
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@OnUndefined(UserNotFoundError)
@ -48,6 +48,10 @@ export class MeController {
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@ResponseSchema(UserIdsNotMatchingError, { statusCode: 406 })
@ResponseSchema(UsernameContainsIllegalCharacterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainUppercaseLetterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainLowercaseLetterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainNumberError, { statusCode: 406 })
@ResponseSchema(PasswordTooShortError, { statusCode: 406 })
@OpenAPI({ description: "Update the yourself. <br> You can't edit your own permissions or group memberships here - Please use the /api/users/:id enpoint instead. <br> Please remember that ids can't be changed." })
async put(@CurrentUser() currentUser: User, @Body({ validate: true }) updateUser: UpdateUser) {
let oldUser = await this.userRepository.findOne({ id: currentUser.id }, { relations: ['groups'] });

View File

@ -1,6 +1,6 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { PermissionIdsNotMatchingError, PermissionNeedsPrincipalError, PermissionNotFoundError } from '../errors/PermissionErrors';
import { PrincipalNotFoundError } from '../errors/PrincipalErrors';
import { CreatePermission } from '../models/actions/create/CreatePermission';
@ -27,9 +27,16 @@ export class PermissionController {
@Authorized("PERMISSION:GET")
@ResponseSchema(ResponsePermission, { isArray: true })
@OpenAPI({ description: 'Lists all permissions for all users and groups.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responsePermissions: ResponsePermission[] = new Array<ResponsePermission>();
const permissions = await this.permissionRepository.find({ relations: ['principal'] });
let permissions: Array<Permission>;
if (page != undefined) {
permissions = await this.permissionRepository.find({ relations: ['principal'], skip: page * page_size, take: page_size });
} else {
permissions = await this.permissionRepository.find({ relations: ['principal'] });
}
permissions.forEach(permission => {
responsePermissions.push(new ResponsePermission(permission));
});

View File

@ -1,106 +1,138 @@
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 { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard';
import { UpdateRunnerCard } from '../models/actions/update/UpdateRunnerCard';
import { RunnerCard } from '../models/entities/RunnerCard';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunnerCard } from '../models/responses/ResponseRunnerCard';
import { ScanController } from './ScanController';
@JsonController('/cards')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class RunnerCardController {
private cardRepository: Repository<RunnerCard>;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.cardRepository = getConnectionManager().get().getRepository(RunnerCard);
}
@Get()
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard, { isArray: true })
@OpenAPI({ description: 'Lists all card.' })
async getAll() {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
const cards = await this.cardRepository.find({ relations: ['runner'] });
cards.forEach(card => {
responseCards.push(new ResponseRunnerCard(card));
});
return responseCards;
}
@Get('/:id')
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@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'] });
if (!card) { throw new RunnerCardNotFoundError(); }
return card.toResponse();
}
@Post()
@Authorized("CARD:CREATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: "Create a new card. <br> You can provide a associated runner by id but you don't have to." })
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();
}
@Put('/:id')
@Authorized("CARD:UPDATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerCardIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: "Update the card whose id you provided. <br> Scans created via this card will still be associated with the old runner. <br> Please remember that ids can't be changed." })
async put(@Param('id') id: number, @Body({ validate: true }) card: UpdateRunnerCard) {
let oldCard = await this.cardRepository.findOne({ id: id });
if (!oldCard) {
throw new RunnerCardNotFoundError();
}
if (oldCard.id != card.id) {
throw new RunnerCardIdsNotMatchingError();
}
await this.cardRepository.save(await card.update(oldCard));
return (await this.cardRepository.findOne({ id: id }, { relations: ['runner'] })).toResponse();
}
@Delete('/:id')
@Authorized("CARD:DELETE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(ResponseEmpty, { statusCode: 204 })
@ResponseSchema(RunnerCardHasScansError, { statusCode: 406 })
@OnUndefined(204)
@OpenAPI({ description: "Delete the card whose id you provided. <br> If no card with this id exists it will just return 204(no content). <br> If the card still has scans associated you have to provide the force=true query param (warning: this deletes all scans associated with by this card - please disable it instead or just remove the runner association)." })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let card = await this.cardRepository.findOne({ id: id });
if (!card) { return null; }
const cardScans = (await this.cardRepository.findOne({ id: id }, { relations: ["scans"] })).scans;
if (cardScans.length != 0 && !force) {
throw new RunnerCardHasScansError();
}
const scanController = new ScanController;
for (let scan of cardScans) {
await scanController.remove(scan.id, force);
}
await this.cardRepository.delete(card);
return card.toResponse();
}
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { Repository, getConnectionManager } from 'typeorm';
import { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard';
import { UpdateRunnerCard } from '../models/actions/update/UpdateRunnerCard';
import { RunnerCard } from '../models/entities/RunnerCard';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunnerCard } from '../models/responses/ResponseRunnerCard';
import { ScanController } from './ScanController';
@JsonController('/cards')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class RunnerCardController {
private cardRepository: Repository<RunnerCard>;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.cardRepository = getConnectionManager().get().getRepository(RunnerCard);
}
@Get()
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard, { isArray: true })
@OpenAPI({ description: 'Lists all card.' })
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
let cards: Array<RunnerCard>;
if (page != undefined) {
cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'], skip: page * page_size, take: page_size });
} else {
cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'] });
}
cards.forEach(card => {
responseCards.push(new ResponseRunnerCard(card));
});
return responseCards;
}
@Get('/:id')
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@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', '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. <br> You can provide the 'returnCards' query param if you want to receive the RESPONSERUNNERCARD objects in the response." })
async postBlancoBulk(@QueryParam("count") count: number, @QueryParam("returnCards") returnCards: boolean = false) {
let createPromises = new Array<any>();
for (let index = 0; index < count; index++) {
createPromises.push(this.cardRepository.save({ runner: null, enabled: true }))
}
const cards = await Promise.all(createPromises);
if (returnCards) {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
for await (let card of cards) {
let dbCard = await this.cardRepository.findOne({ id: card.id });
responseCards.push(new ResponseRunnerCard(dbCard));
}
return responseCards;
}
let response = new ResponseEmpty();
response.response = `Created ${count} new blanco cards.`
return response;
}
@Post()
@Authorized("CARD:CREATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OpenAPI({ description: "Create a new card. <br> You can provide a associated runner by id but you don't have to." })
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', 'runner.group', 'runner.group.parentGroup'] })).toResponse();
}
@Put('/:id')
@Authorized("CARD:UPDATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerCardIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: "Update the card whose id you provided. <br> Scans created via this card will still be associated with the old runner. <br> Please remember that ids can't be changed." })
async put(@Param('id') id: number, @Body({ validate: true }) card: UpdateRunnerCard) {
let oldCard = await this.cardRepository.findOne({ id: id });
if (!oldCard) {
throw new RunnerCardNotFoundError();
}
if (oldCard.id != card.id) {
throw new RunnerCardIdsNotMatchingError();
}
await this.cardRepository.save(await card.update(oldCard));
return (await this.cardRepository.findOne({ id: id }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] })).toResponse();
}
@Delete('/:id')
@Authorized("CARD:DELETE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(ResponseEmpty, { statusCode: 204 })
@ResponseSchema(RunnerCardHasScansError, { statusCode: 406 })
@OnUndefined(204)
@OpenAPI({ description: "Delete the card whose id you provided. <br> If no card with this id exists it will just return 204(no content). <br> If the card still has scans associated you have to provide the force=true query param (warning: this deletes all scans associated with by this card - please disable it instead or just remove the runner association)." })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let card = await this.cardRepository.findOne({ id: id });
if (!card) { return null; }
const cardScans = (await this.cardRepository.findOne({ id: id }, { relations: ["scans"] })).scans;
if (cardScans.length != 0 && !force) {
throw new RunnerCardHasScansError();
}
const scanController = new ScanController;
for (let scan of cardScans) {
await scanController.remove(scan.id, force);
}
await this.cardRepository.delete(card);
return card.toResponse();
}
}

View File

@ -1,6 +1,6 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { RunnerGroupNeededError, RunnerHasDistanceDonationsError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors';
import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors';
import { CreateRunner } from '../models/actions/create/CreateRunner';
@ -30,9 +30,16 @@ export class RunnerController {
@Authorized("RUNNER:GET")
@ResponseSchema(ResponseRunner, { isArray: true })
@OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
const runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'scans.track', 'cards'] });
let runners: Array<Runner>;
if (page != undefined) {
runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'group.parentGroup', 'scans.track'], skip: page * page_size, take: page_size });
} else {
runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'group.parentGroup', 'scans.track'] });
}
runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner));
});
@ -46,7 +53,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 +98,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 +119,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 +132,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();

View File

@ -1,11 +1,13 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { Authorized, BadRequestError, 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 { Repository, getConnectionManager } from 'typeorm';
import { RunnerOrganizationHasRunnersError, RunnerOrganizationHasTeamsError, RunnerOrganizationIdsNotMatchingError, RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors';
import { CreateRunnerOrganization } from '../models/actions/create/CreateRunnerOrganization';
import { UpdateRunnerOrganization } from '../models/actions/update/UpdateRunnerOrganization';
import { Runner } from '../models/entities/Runner';
import { RunnerOrganization } from '../models/entities/RunnerOrganization';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunner } from '../models/responses/ResponseRunner';
import { ResponseRunnerOrganization } from '../models/responses/ResponseRunnerOrganization';
import { RunnerController } from './RunnerController';
import { RunnerTeamController } from './RunnerTeamController';
@ -27,13 +29,20 @@ export class RunnerOrganizationController {
@Authorized("ORGANIZATION:GET")
@ResponseSchema(ResponseRunnerOrganization, { isArray: true })
@OpenAPI({ description: 'Lists all organizations. <br> This includes their address, contact and teams (if existing/associated).' })
async getAll() {
let responseTeams: ResponseRunnerOrganization[] = new Array<ResponseRunnerOrganization>();
const runners = await this.runnerOrganizationRepository.find({ relations: ['contact', 'teams'] });
runners.forEach(runner => {
responseTeams.push(new ResponseRunnerOrganization(runner));
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseOrgs: ResponseRunnerOrganization[] = new Array<ResponseRunnerOrganization>();
let orgs: Array<RunnerOrganization>;
if (page != undefined) {
orgs = await this.runnerOrganizationRepository.find({ relations: ['contact', 'teams'], skip: page * page_size, take: page_size });
} else {
orgs = await this.runnerOrganizationRepository.find({ relations: ['contact', 'teams'] });
}
orgs.forEach(org => {
responseOrgs.push(new ResponseRunnerOrganization(org));
});
return responseTeams;
return responseOrgs;
}
@Get('/:id')
@ -43,11 +52,27 @@ export class RunnerOrganizationController {
@OnUndefined(RunnerOrganizationNotFoundError)
@OpenAPI({ description: 'Lists all information about the organization whose id got provided.' })
async getOne(@Param('id') id: number) {
let runnerOrg = await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['contact', 'teams'] });
let runnerOrg = await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['contact', 'teams', 'teams.runners', 'teams.runners.scans', 'teams.runners.scans.track', 'runners', 'runners.scans', 'runners.scans.track'] });
if (!runnerOrg) { throw new RunnerOrganizationNotFoundError(); }
return new ResponseRunnerOrganization(runnerOrg);
}
@Get('/:id/runners')
@Authorized(["RUNNER:GET", "SCAN:GET"])
@ResponseSchema(ResponseRunner, { isArray: true })
@ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Lists all runners from this org and it\'s teams (if you don\'t provide the ?onlyDirect=true param). <br> This includes the runner\'s group and distance ran.' })
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.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));
});
return responseRunners;
}
@Post()
@Authorized("ORGANIZATION:CREATE")
@ResponseSchema(ResponseRunnerOrganization)
@ -96,6 +121,10 @@ export class RunnerOrganizationController {
@OnUndefined(204)
@OpenAPI({ description: 'Delete the organsisation whose id you provided. <br> If the organization still has runners and/or teams associated this will fail. <br> To delete the organization with all associated runners and teams set the force QueryParam to true (cascading deletion might take a while). <br> This won\'t delete the associated contact. <br> If no organization with this id exists it will just return 204(no content).' })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
if (id == 1) {
throw new BadRequestError("You can't delete the citizen runner org.");
}
let organization = await this.runnerOrganizationRepository.findOne({ id: id });
if (!organization) { return null; }
let runnerOrganization = await this.runnerOrganizationRepository.findOne(organization, { relations: ['contact', 'runners', 'teams'] });

View File

@ -1,98 +1,244 @@
import * as jwt from "jsonwebtoken";
import { Body, Get, JsonController, OnUndefined, Param, Post } 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 { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors';
import { JwtCreator } from '../jwtcreator';
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 { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner';
@JsonController('/runners')
export class RunnerSelfServiceController {
private runnerRepository: Repository<Runner>;
private orgRepository: Repository<RunnerOrganization>;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.runnerRepository = getConnectionManager().get().getRepository(Runner);
this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization);
}
@Get('/me/:jwt')
@ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all information about yourself. <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 get(@Param('jwt') token: string) {
return (new ResponseSelfServiceRunner(await this.getRunner(token)));
}
@Post('/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.' })
async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner) {
let runner = await createRunner.toEntity();
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);
return response;
}
@Post('/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.' })
async registerOrganizationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner) {
const org = await this.getOrgansisation(token);
let runner = await createRunner.toEntity(org);
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);
return response;
}
/**
* Get's a runner by a provided jwt token.
* @param token The runner jwt provided by the runner to identitfy themselves.
*/
private async getRunner(token: string): Promise<Runner> {
if (token == "") { throw new JwtNotProvidedError(); }
let jwtPayload = undefined
try {
jwtPayload = <any>jwt.verify(token, config.jwt_secret);
} catch (error) {
throw new InvalidCredentialsError();
}
const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] });
if (!runner) { throw new RunnerNotFoundError() }
return runner;
}
/**
* Get's a runner org by a provided registration api key.
* @param token The organization's registration api token.
*/
private async getOrgansisation(token: string): Promise<RunnerGroup> {
token = Buffer.from(token, 'base64').toString('utf8');
const organization = await this.orgRepository.findOne({ key: token });
if (!organization) { throw new RunnerOrganizationNotFoundError; }
return organization;
}
import { Request } from "express";
import * as jwt from "jsonwebtoken";
import { BadRequestError, Body, Delete, 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 { MailSendingError } from '../errors/MailErrors';
import { RunnerEmailNeededError, RunnerHasDistanceDonationsError, 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';
import { DonationController } from './DonationController';
import { RunnerCardController } from './RunnerCardController';
import { ScanController } from './ScanController';
@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.
*/
constructor() {
this.runnerRepository = getConnectionManager().get().getRepository(Runner);
this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization);
this.stationRepository = getConnectionManager().get().getRepository(ScanStation);
}
@Get('/runners/me/:jwt')
@ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all information about yourself. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please use the forgot endpoint.' })
async get(@Param('jwt') token: string) {
return (new ResponseSelfServiceRunner(await this.getRunner(token)));
}
@Delete('/runners/me/:jwt')
@ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Deletes all information about yourself. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please use the forgot endpoint.' })
async remove(@Param('jwt') token: string, @QueryParam("force") force: boolean) {
const responseRunner = await this.getRunner(token);
let runner = await this.runnerRepository.findOne({ id: responseRunner.id });
if (!runner) { return null; }
if (!runner) {
throw new RunnerNotFoundError();
}
const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations;
if (runnerDonations.length > 0 && !force) {
throw new RunnerHasDistanceDonationsError();
}
const donationController = new DonationController();
for (let donation of runnerDonations) {
await donationController.remove(donation.id, force);
}
const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards;
const cardController = new RunnerCardController;
for (let card of runnerCards) {
await cardController.remove(card.id, force);
}
const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans;
const scanController = new ScanController;
for (let scan of runnerScans) {
await scanController.remove(scan.id, force);
}
await this.runnerRepository.delete(runner);
return new ResponseSelfServiceRunner(responseRunner);
}
@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/login')
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(ResponseEmpty)
@OpenAPI({ description: 'Use this endpoint to reuqest a new selfservice magic-login-link to be sent to your mail address (rate limited to one mail every 15mins).' })
async requestNewToken(@QueryParam('mail') mail: string, @QueryParam("locale") locale: string = "en") {
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 * 15)) { throw new RunnerSelfserviceTimeoutError(); }
const token = JwtCreator.createSelfService(runner);
try {
await Mailer.sendSelfserviceForgottenMail(runner.email, token, locale)
} 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.' })
async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") {
let runner = await createRunner.toEntity();
if (await this.getRunnerExistsByMail(runner.email)) {
throw new BadRequestError("E-Mail already registered")
}
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, locale)
} catch (error) {
throw new MailSendingError();
}
return response;
}
@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.' })
async registerOrganizationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner, @QueryParam("locale") locale: string = "en") {
const org = await this.getOrgansisation(token);
let runner = await createRunner.toEntity(org);
if (await this.getRunnerExistsByMail(runner.email)) {
throw new BadRequestError("E-Mail already registered")
}
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, locale)
} 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.
*/
private async getRunner(token: string): Promise<Runner> {
if (token == "") { throw new JwtNotProvidedError(); }
let jwtPayload = undefined
try {
jwtPayload = <any>jwt.verify(token, config.jwt_secret);
} catch (error) {
throw new InvalidCredentialsError();
}
const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] });
if (!runner) { throw new RunnerNotFoundError() }
return runner;
}
/**
* Get's a runner org by a provided registration api key.
* @param token The organization's registration api token.
*/
private async getOrgansisation(token: string): Promise<RunnerGroup> {
token = Buffer.from(token, 'base64').toString('utf8');
const organization = await this.orgRepository.findOne({ key: token });
if (!organization) { throw new RunnerOrganizationNotFoundError; }
return organization;
}
/**
* Checks if a runner already exists
* @param email The runner's email address
* @returns Boolean (true if exists, false if not)
*/
private async getRunnerExistsByMail(email: string): Promise<boolean> {
const runner = await this.runnerRepository.findOne({ email });
return runner != undefined
}
}

View File

@ -1,11 +1,12 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors';
import { CreateRunnerTeam } from '../models/actions/create/CreateRunnerTeam';
import { UpdateRunnerTeam } from '../models/actions/update/UpdateRunnerTeam';
import { RunnerTeam } from '../models/entities/RunnerTeam';
import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunner } from '../models/responses/ResponseRunner';
import { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam';
import { RunnerController } from './RunnerController';
@ -26,11 +27,18 @@ export class RunnerTeamController {
@Authorized("TEAM:GET")
@ResponseSchema(ResponseRunnerTeam, { isArray: true })
@OpenAPI({ description: 'Lists all teams. <br> This includes their parent organization and contact (if existing/associated).' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseTeams: ResponseRunnerTeam[] = new Array<ResponseRunnerTeam>();
const runners = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] });
runners.forEach(runner => {
responseTeams.push(new ResponseRunnerTeam(runner));
let teams: Array<RunnerTeam>;
if (page != undefined) {
teams = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'], skip: page * page_size, take: page_size });
} else {
teams = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] });
}
teams.forEach(team => {
responseTeams.push(new ResponseRunnerTeam(team));
});
return responseTeams;
}
@ -42,11 +50,25 @@ export class RunnerTeamController {
@OnUndefined(RunnerTeamNotFoundError)
@OpenAPI({ description: 'Lists all information about the team whose id got provided.' })
async getOne(@Param('id') id: number) {
let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] });
let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact', 'runners', 'runners.scans', 'runners.scans.track'] });
if (!runnerTeam) { throw new RunnerTeamNotFoundError(); }
return new ResponseRunnerTeam(runnerTeam);
}
@Get('/:id/runners')
@Authorized(["RUNNER:GET", "SCAN:GET"])
@ResponseSchema(ResponseRunner, { isArray: true })
@ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 })
@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.group.parentGroup', 'runners.scans', 'runners.scans.track'] })).runners;
runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner));
});
return responseRunners;
}
@Post()
@Authorized("TEAM:CREATE")
@ResponseSchema(ResponseRunnerTeam)

View File

@ -1,6 +1,7 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors';
import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
@ -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 {
@ -34,9 +34,16 @@ export class ScanController {
@ResponseSchema(ResponseScan, { isArray: true })
@ResponseSchema(ResponseTrackScan, { isArray: true })
@OpenAPI({ description: 'Lists all scans (normal or track) from all runners. <br> This includes the scan\'s runner\'s distance ran.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseScans: ResponseScan[] = new Array<ResponseScan>();
const scans = await this.scanRepository.find({ relations: ['runner', 'track', 'runner.scans', 'runner.scans.track', 'card', 'station'] });
let scans: Array<Scan>;
if (page != undefined) {
scans = await this.scanRepository.find({ relations: ['runner', 'track'], skip: page * page_size, take: page_size });
} else {
scans = await this.scanRepository.find({ relations: ['runner', 'track'] });
}
scans.forEach(scan => {
responseScans.push(scan.toResponse());
});
@ -51,7 +58,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.group', 'card', 'station'] })
if (!scan) { throw new ScanNotFoundError(); }
return scan.toResponse();
}
@ -60,22 +67,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 +108,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 +131,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 +143,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();

View File

@ -1,6 +1,6 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { ScanStationHasScansError, ScanStationIdsNotMatchingError, ScanStationNotFoundError } from '../errors/ScanStationErrors';
import { TrackNotFoundError } from '../errors/TrackErrors';
import { CreateScanStation } from '../models/actions/create/CreateScanStation';
@ -26,9 +26,16 @@ export class ScanStationController {
@Authorized("STATION:GET")
@ResponseSchema(ResponseScanStation, { isArray: true })
@OpenAPI({ description: 'Lists all stations. <br> This includes their associated tracks.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseStations: ResponseScanStation[] = new Array<ResponseScanStation>();
const stations = await this.stationRepository.find({ relations: ['track'] });
let stations: Array<ScanStation>;
if (page != undefined) {
stations = await this.stationRepository.find({ relations: ['track'], skip: page * page_size, take: page_size });
} else {
stations = await this.stationRepository.find({ relations: ['track'] });
}
stations.forEach(station => {
responseStations.push(station.toResponse());
});

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { Repository, getConnectionManager } from 'typeorm';
import { StatsClientNotFoundError } from '../errors/StatsClientErrors';
import { TrackNotFoundError } from "../errors/TrackErrors";
import { CreateStatsClient } from '../models/actions/create/CreateStatsClient';
@ -24,9 +24,16 @@ export class StatsClientController {
@Authorized("STATSCLIENT:GET")
@ResponseSchema(ResponseStatsClient, { isArray: true })
@OpenAPI({ description: 'Lists all stats clients. Please remember that the key can only be viewed on creation.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseClients: ResponseStatsClient[] = new Array<ResponseStatsClient>();
const clients = await this.clientRepository.find();
let clients: Array<StatsClient>;
if (page != undefined) {
clients = await this.clientRepository.find({ skip: page * page_size, take: page_size });
} else {
clients = await this.clientRepository.find();
}
clients.forEach(clients => {
responseClients.push(new ResponseStatsClient(clients));
});

View File

@ -1,12 +1,14 @@
import { Get, JsonController, UseBefore } from 'routing-controllers';
import { Get, JsonController, QueryParam, UseBefore } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnection } from 'typeorm';
import StatsAuth from '../middlewares/StatsAuth';
import { Donation } from '../models/entities/Donation';
import { Donor } from '../models/entities/Donor';
import { Runner } from '../models/entities/Runner';
import { RunnerOrganization } from '../models/entities/RunnerOrganization';
import { RunnerTeam } from '../models/entities/RunnerTeam';
import { Scan } from '../models/entities/Scan';
import { TrackScan } from '../models/entities/TrackScan';
import { User } from '../models/entities/User';
import { ResponseStats } from '../models/responses/ResponseStats';
import { ResponseStatsOrgnisation } from '../models/responses/ResponseStatsOrganization';
@ -20,14 +22,26 @@ export class StatsController {
@ResponseSchema(ResponseStats)
@OpenAPI({ description: "A very basic stats endpoint providing basic counters for a dashboard or simmilar" })
async get() {
let connection = getConnection();
let runners = await connection.getRepository(Runner).find({ relations: ['scans', 'scans.track'] });
let teams = await connection.getRepository(RunnerTeam).find();
let orgs = await connection.getRepository(RunnerOrganization).find();
let users = await connection.getRepository(User).find();
let scans = await connection.getRepository(Scan).find();
const connection = getConnection();
const runners = await connection.getRepository(Runner).count();
const teams = await connection.getRepository(RunnerTeam).count();
const orgs = await connection.getRepository(RunnerOrganization).count();
const users = await connection.getRepository(User).count();
const scans = await connection.getRepository(Scan).count({ where: { valid: true } });
const distance_query = await connection.getRepository(Scan).createQueryBuilder('scan')
.leftJoinAndSelect("scan.track", "track").where("scan.valid = TRUE")
.select("SUM(track.distance)", "sum_track").addSelect("SUM(_distance)", "sum_distance")
.getRawOne();
let distace = parseInt(distance_query.sum_track)
if (distance_query.sum_distance) {
distace += parseInt(distance_query.sum_distance)
}
let donations = await connection.getRepository(Donation).find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] });
return new ResponseStats(runners, teams, orgs, users, scans, donations)
const donors = await connection.getRepository(Donor).count();
return new ResponseStats(runners, teams, orgs, users, scans, donations, distace, donors)
}
@Get("/runners/distance")
@ -36,7 +50,10 @@ export class StatsController {
@OpenAPI({ description: "Returns the top ten runners by distance.", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopRunnersByDistance() {
let runners = await getConnection().getRepository(Runner).find({ relations: ['scans', 'group', 'distanceDonations', 'scans.track'] });
let topRunners = runners.sort((runner1, runner2) => runner1.distance - runner2.distance).slice(0, 9);
if (!runners || runners.length == 0) {
return [];
}
let topRunners = runners.sort((runner1, runner2) => runner2.distance - runner1.distance).slice(0, 10);
let responseRunners: ResponseStatsRunner[] = new Array<ResponseStatsRunner>();
topRunners.forEach(runner => {
responseRunners.push(new ResponseStatsRunner(runner));
@ -49,8 +66,11 @@ export class StatsController {
@ResponseSchema(ResponseStatsRunner, { isArray: true })
@OpenAPI({ description: "Returns the top ten runners by donations.", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopRunnersByDonations() {
let runners = await getConnection().getRepository(Runner).find({ relations: ['scans', 'group', 'distanceDonations', 'scans.track'] });
let topRunners = runners.sort((runner1, runner2) => runner1.distanceDonationAmount - runner2.distanceDonationAmount).slice(0, 9);
let runners = await getConnection().getRepository(Runner).find({ relations: ['group', 'distanceDonations', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] });
if (!runners || runners.length == 0) {
return [];
}
let topRunners = runners.sort((runner1, runner2) => runner2.distanceDonationAmount - runner1.distanceDonationAmount).slice(0, 10);
let responseRunners: ResponseStatsRunner[] = new Array<ResponseStatsRunner>();
topRunners.forEach(runner => {
responseRunners.push(new ResponseStatsRunner(runner));
@ -58,6 +78,34 @@ export class StatsController {
return responseRunners;
}
@Get("/runners/laptime")
@UseBefore(StatsAuth)
@ResponseSchema(ResponseStatsRunner, { isArray: true })
@OpenAPI({ description: "Returns the top ten runners by fastest laptime on your selected track (track by id).", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopRunnersByLaptime(@QueryParam("track") track: number) {
let scans = await getConnection().getRepository(TrackScan).find({ relations: ['track', 'runner', 'runner.group', 'runner.scans', 'runner.scans.track', 'runner.distanceDonations'] });
if (!scans || scans.length == 0) {
return [];
}
scans = scans.filter((s) => { return s.track.id == track && s.valid == true && s.lapTime != 0 }).sort((scan1, scan2) => scan1.lapTime - scan2.lapTime);
let topScans = new Array<TrackScan>();
let knownRunners = new Array<number>();
for (let i = 0; i < scans.length && topScans.length < 10; i++) {
const element = scans[i];
if (!knownRunners.includes(element.runner.id)) {
topScans.push(element);
knownRunners.push(element.runner.id);
}
}
let responseRunners: ResponseStatsRunner[] = new Array<ResponseStatsRunner>();
topScans.forEach(scan => {
responseRunners.push(new ResponseStatsRunner(scan.runner, scan.lapTime));
});
return responseRunners;
}
@Get("/scans")
@UseBefore(StatsAuth)
@ResponseSchema(ResponseStatsRunner, { isArray: true })
@ -71,8 +119,11 @@ export class StatsController {
@ResponseSchema(ResponseStatsTeam, { isArray: true })
@OpenAPI({ description: "Returns the top ten teams by distance.", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopTeamsByDistance() {
let teams = await getConnection().getRepository(RunnerTeam).find({ relations: ['runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track'] });
let topTeams = teams.sort((team1, team2) => team1.distance - team2.distance).slice(0, 9);
let teams = await getConnection().getRepository(RunnerTeam).find({ relations: ['parentGroup', 'runners', 'runners.scans', 'runners.scans.track'] });
if (!teams || teams.length == 0) {
return [];
}
let topTeams = teams.sort((team1, team2) => team2.distance - team1.distance).slice(0, 10);
let responseTeams: ResponseStatsTeam[] = new Array<ResponseStatsTeam>();
topTeams.forEach(team => {
responseTeams.push(new ResponseStatsTeam(team));
@ -85,8 +136,11 @@ export class StatsController {
@ResponseSchema(ResponseStatsTeam, { isArray: true })
@OpenAPI({ description: "Returns the top ten teams by donations.", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopTeamsByDonations() {
let teams = await getConnection().getRepository(RunnerTeam).find({ relations: ['runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track'] });
let topTeams = teams.sort((team1, team2) => team1.distanceDonationAmount - team2.distanceDonationAmount).slice(0, 9);
let teams = await getConnection().getRepository(RunnerTeam).find({ relations: ['parentGroup', 'runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track'] });
if (!teams || teams.length == 0) {
return [];
}
let topTeams = teams.sort((team1, team2) => team2.distanceDonationAmount - team1.distanceDonationAmount).slice(0, 10);
let responseTeams: ResponseStatsTeam[] = new Array<ResponseStatsTeam>();
topTeams.forEach(team => {
responseTeams.push(new ResponseStatsTeam(team));
@ -100,7 +154,10 @@ export class StatsController {
@OpenAPI({ description: "Returns the top ten organizations by distance.", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopOrgsByDistance() {
let orgs = await getConnection().getRepository(RunnerOrganization).find({ relations: ['runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.scans', 'teams.runners.distanceDonations', 'teams.runners.scans.track'] });
let topOrgs = orgs.sort((org1, org2) => org1.distance - org2.distance).slice(0, 9);
if (!orgs || orgs.length == 0) {
return [];
}
let topOrgs = orgs.sort((org1, org2) => org2.distance - org1.distance).slice(0, 10);
let responseOrgs: ResponseStatsOrgnisation[] = new Array<ResponseStatsOrgnisation>();
topOrgs.forEach(org => {
responseOrgs.push(new ResponseStatsOrgnisation(org));
@ -113,8 +170,11 @@ export class StatsController {
@ResponseSchema(ResponseStatsOrgnisation, { isArray: true })
@OpenAPI({ description: "Returns the top ten organizations by donations.", security: [{ "StatsApiToken": [] }, { "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
async getTopOrgsByDonations() {
let orgs = await getConnection().getRepository(RunnerOrganization).find({ relations: ['runners', 'runners.scans', 'runners.distanceDonations', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.scans', 'teams.runners.distanceDonations', 'teams.runners.scans.track'] });
let topOrgs = orgs.sort((org1, org2) => org1.distanceDonationAmount - org2.distanceDonationAmount).slice(0, 9);
let orgs = await getConnection().getRepository(RunnerOrganization).find({ relations: ['runners', 'runners.distanceDonations', 'runners.distanceDonations.runner', 'runners.distanceDonations.runner.scans', 'runners.distanceDonations.runner.scans.track', 'teams', 'teams.runners', 'teams.runners.distanceDonations', 'teams.runners.distanceDonations.runner', 'teams.runners.distanceDonations.runner.scans', 'teams.runners.distanceDonations.runner.scans.track'] });
if (!orgs || orgs.length == 0) {
return [];
}
let topOrgs = orgs.sort((org1, org2) => org2.distanceDonationAmount - org1.distanceDonationAmount).slice(0, 10);
let responseOrgs: ResponseStatsOrgnisation[] = new Array<ResponseStatsOrgnisation>();
topOrgs.forEach(org => {
responseOrgs.push(new ResponseStatsOrgnisation(org));

View File

@ -1,6 +1,6 @@
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 { Repository, getConnectionManager } from 'typeorm';
import { TrackHasScanStationsError, TrackIdsNotMatchingError, TrackLapTimeCantBeNegativeError, TrackNotFoundError } from "../errors/TrackErrors";
import { CreateTrack } from '../models/actions/create/CreateTrack';
import { UpdateTrack } from '../models/actions/update/UpdateTrack';
@ -25,9 +25,17 @@ export class TrackController {
@Authorized("TRACK:GET")
@ResponseSchema(ResponseTrack, { isArray: true })
@OpenAPI({ description: 'Lists all tracks.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseTracks: ResponseTrack[] = new Array<ResponseTrack>();
const tracks = await this.trackRepository.find();
let tracks: Array<Track>;
if (page != undefined) {
tracks = await this.trackRepository.find({ skip: page * page_size, take: page_size });
}
else {
tracks = await this.trackRepository.find();
}
tracks.forEach(track => {
responseTracks.push(new ResponseTrack(track));
});

View File

@ -1,7 +1,7 @@
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 { UserDeletionNotConfirmedError, UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } from '../errors/UserErrors';
import { Repository, getConnectionManager } from 'typeorm';
import { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, UserDeletionNotConfirmedError, UserIdsNotMatchingError, UserNotFoundError, UsernameContainsIllegalCharacterError } from '../errors/UserErrors';
import { UserGroupNotFoundError } from '../errors/UserGroupErrors';
import { CreateUser } from '../models/actions/create/CreateUser';
import { UpdateUser } from '../models/actions/update/UpdateUser';
@ -28,9 +28,17 @@ export class UserController {
@Authorized("USER:GET")
@ResponseSchema(ResponseUser, { isArray: true })
@OpenAPI({ description: 'Lists all users. <br> This includes their groups and permissions granted to them.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseUsers: ResponseUser[] = new Array<ResponseUser>();
const users = await this.userRepository.find({ relations: ['permissions', 'groups', 'groups.permissions'] });
let users: Array<User>;
if (page != undefined) {
users = await this.userRepository.find({ relations: ['permissions', 'groups', 'groups.permissions'], skip: page * page_size, take: page_size });
}
else {
users = await this.userRepository.find({ relations: ['permissions', 'groups', 'groups.permissions'] });
}
users.forEach(user => {
responseUsers.push(new ResponseUser(user));
});
@ -66,6 +74,10 @@ export class UserController {
@ResponseSchema(ResponseUser)
@ResponseSchema(UserGroupNotFoundError, { statusCode: 404 })
@ResponseSchema(UsernameContainsIllegalCharacterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainUppercaseLetterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainLowercaseLetterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainNumberError, { statusCode: 406 })
@ResponseSchema(PasswordTooShortError, { statusCode: 406 })
@OpenAPI({ description: 'Create a new user. <br> If you want to grant permissions to the user you have to create them seperately by posting to /api/permissions after creating the user.' })
async post(@Body({ validate: true }) createUser: CreateUser) {
let user;
@ -85,6 +97,10 @@ export class UserController {
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@ResponseSchema(UserIdsNotMatchingError, { statusCode: 406 })
@ResponseSchema(UsernameContainsIllegalCharacterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainUppercaseLetterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainLowercaseLetterError, { statusCode: 406 })
@ResponseSchema(PasswordMustContainNumberError, { statusCode: 406 })
@ResponseSchema(PasswordTooShortError, { statusCode: 406 })
@OpenAPI({ description: "Update the user whose id you provided. <br> To change the permissions directly granted to the user please use /api/permissions instead. <br> Please remember that ids can't be changed." })
async put(@Param('id') id: number, @Body({ validate: true }) updateUser: UpdateUser) {
let oldUser = await this.userRepository.findOne({ id: id });

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 { Repository, getConnectionManager } from 'typeorm';
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,44 @@ 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(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseGroups: ResponseUserGroup[] = new Array<ResponseUserGroup>();
let groups: Array<UserGroup>;
if (page != undefined) {
groups = await this.userGroupsRepository.find({ relations: ['permissions'], skip: page * page_size, take: page_size });
} else {
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 +78,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 +88,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 +100,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 +110,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'] });

17
src/errors/MailErrors.ts Normal file
View File

@ -0,0 +1,17 @@
import { IsString } from 'class-validator';
import { InternalServerError } from 'routing-controllers';
/**
* Error to throw when a permission couldn't be found.
*/
export class MailSendingError extends InternalServerError {
@IsString()
name = "MailSendingError"
@IsString()
message = "We had a problem sending the mail!"
constructor() {
super("We had a problem sending the mail!");
}
}

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.
*/

View File

@ -71,4 +71,33 @@ export class UserDeletionNotConfirmedError extends NotAcceptableError {
@IsString()
message = "You are trying to delete a user! \n If you're sure about doing this: provide the ?force=true query param."
}
export class PasswordMustContainUppercaseLetterError extends NotAcceptableError {
@IsString()
name = "PasswordMustContainUppercaseLetterError"
@IsString()
message = "Passwords must contain at least one uppercase letter."
}
export class PasswordMustContainLowercaseLetterError extends NotAcceptableError {
@IsString()
name = "PasswordMustContainLowercaseLetterError"
@IsString()
message = "Passwords must contain at least one lowercase letter."
}
export class PasswordMustContainNumberError extends NotAcceptableError {
@IsString()
name = "PasswordMustContainNumberError"
@IsString()
message = "Passwords must contain at least one number."
}
export class PasswordTooShortError extends NotAcceptableError {
@IsString()
name = "PasswordTooShortError"
@IsString()
message = "Passwords must be at least ten characters long."
}

64
src/mailer.ts Normal file
View File

@ -0,0 +1,64 @@
import axios from 'axios';
import { config } from './config';
import { MailSendingError } from './errors/MailErrors';
/**
* This class is responsible for all things mail sending.
* This uses axios to communicate with the mailer api (https://git.odit.services/lfk/mailer).
*/
export class Mailer {
public static base: string = config.mailer_url;
public static key: string = config.mailer_key;
public static testing: boolean = config.testing;
/**
* 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 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 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 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();
}
}
/**
* 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 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();
}
}
}

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

View File

@ -33,6 +33,7 @@ export class CreateDistanceDonation extends CreateDonation {
let newDonation = new DistanceDonation;
newDonation.amountPerDistance = this.amountPerDistance;
newDonation.paidAmount = this.paidAmount;
newDonation.donor = await this.getDonor();
newDonation.runner = await this.getRunner();

View File

@ -1,4 +1,4 @@
import { IsInt, IsPositive } from 'class-validator';
import { IsInt, IsOptional, IsPositive } from 'class-validator';
import { getConnection } from 'typeorm';
import { DonorNotFoundError } from '../../../errors/DonorErrors';
import { Donation } from '../../entities/Donation';
@ -16,6 +16,13 @@ export abstract class CreateDonation {
@IsPositive()
donor: number;
/**
* The donation's paid amount in the smalles unit of your currency (default: euro cent).
*/
@IsInt()
@IsOptional()
paidAmount?: number;
/**
* Creates a new Donation entity from this.
*/

View File

@ -21,6 +21,7 @@ export class CreateFixedDonation extends CreateDonation {
let newDonation = new FixedDonation;
newDonation.amount = this.amount;
newDonation.paidAmount = this.paidAmount;
newDonation.donor = await this.getDonor();
return newDonation;

View File

@ -1,39 +1,33 @@
import { IsEmail, IsOptional, IsString } from 'class-validator';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
import { getConnectionManager } from 'typeorm';
import { ResetAlreadyRequestedError, UserDisabledError, UserNotFoundError } from '../../../errors/AuthError';
import { UsernameOrEmailNeededError } from '../../../errors/UserErrors';
import { UserEmailNeededError } from '../../../errors/UserErrors';
import { JwtCreator } from '../../../jwtcreator';
import { User } from '../../entities/User';
/**
* This calss is used to create password reset tokens for users.
* This class is used to create password reset tokens for users.
* These password reset token can be used to set a new password for the user for the next 15mins.
*/
export class CreateResetToken {
/**
* The username of the user that wants to reset their password.
*/
@IsOptional()
@IsString()
username?: string;
/**
* The email address of the user that wants to reset their password.
*/
@IsOptional()
@IsNotEmpty()
@IsEmail()
@IsString()
email?: string;
email: string;
/**
* Create a password reset token based on this.
*/
public async toResetToken(): Promise<any> {
if (this.email === undefined && this.username === undefined) {
throw new UsernameOrEmailNeededError();
public async toResetToken(): Promise<string> {
if (!this.email) {
throw new UserEmailNeededError();
}
let found_user = await getConnectionManager().get().getRepository(User).findOne({ where: [{ username: this.username }, { email: this.email }] });
let found_user = await getConnectionManager().get().getRepository(User).findOne({ where: [{ email: this.email }] });
if (!found_user) { throw new UserNotFoundError(); }
if (found_user.enabled == false) { throw new UserDisabledError(); }
if (found_user.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 15 * 60)) { throw new ResetAlreadyRequestedError(); }
@ -43,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;
}

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,20 +47,32 @@ 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) {
const id = this.card % 200000000000;
const runnerCard = await getConnection().getRepository(RunnerCard).findOne({ id: id }, { relations: ["runner"] });
if (!runnerCard) {
throw new RunnerCardNotFoundError();
}
return track;
return runnerCard;
}
/**
* 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 +80,21 @@ export class CreateTrackScan {
return station;
}
public async validateScan(scan: TrackScan): Promise<boolean> {
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;
/**
* 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 latestScan = await getConnection().getRepository(TrackScan).findOne({ where: { runner: scan.runner, valid: true }, relations: ["track"], order: { id: 'DESC' } });
if (!latestScan) {
scan.lapTime = 0;
scan.valid = true;
}
return false;
else {
scan.lapTime = scan.timestamp - latestScan.timestamp;
scan.valid = (scan.lapTime > scan.track.minimumLapTime);
}
return scan;
}
}

View File

@ -1,9 +1,10 @@
import * as argon2 from "argon2";
import { passwordStrength } from "check-password-strength";
import { IsBoolean, IsEmail, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUrl } from 'class-validator';
import { getConnectionManager } from 'typeorm';
import * as uuid from 'uuid';
import { config } from '../../../config';
import { UserEmailNeededError, UsernameContainsIllegalCharacterError } from '../../../errors/UserErrors';
import { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, UserEmailNeededError, UsernameContainsIllegalCharacterError } from '../../../errors/UserErrors';
import { UserGroupNotFoundError } from '../../../errors/UserGroupErrors';
import { User } from '../../entities/User';
import { UserGroup } from '../../entities/UserGroup';
@ -94,7 +95,13 @@ export class CreateUser {
if (!this.email) {
throw new UserEmailNeededError();
}
if (this.username.includes("@")) { throw new UsernameContainsIllegalCharacterError(); }
if (this.username?.includes("@")) { throw new UsernameContainsIllegalCharacterError(); }
let password_strength = passwordStrength(this.password);
if (!password_strength.contains.includes("uppercase")) { throw new PasswordMustContainUppercaseLetterError(); }
if (!password_strength.contains.includes("lowercase")) { throw new PasswordMustContainLowercaseLetterError(); }
if (!password_strength.contains.includes("number")) { throw new PasswordMustContainNumberError(); }
if (!(password_strength.length > 9)) { throw new PasswordTooShortError(); }
newUser.email = this.email
newUser.username = this.username
@ -107,7 +114,7 @@ export class CreateUser {
newUser.groups = await this.getGroups();
newUser.enabled = this.enabled;
if (!this.profilePic) { newUser.profilePic = `https://dev.lauf-fuer-kaya.de/lfk-logo.png`; }
if (!this.profilePic) { newUser.profilePic = `https://lauf-fuer-kaya.de/lfk-logo.png`; }
else { newUser.profilePic = this.profilePic; }
return newUser;

View File

@ -32,6 +32,7 @@ export class UpdateDistanceDonation extends UpdateDonation {
*/
public async update(donation: DistanceDonation): Promise<DistanceDonation> {
donation.amountPerDistance = this.amountPerDistance;
donation.paidAmount = this.paidAmount;
donation.donor = await this.getDonor();
donation.runner = await this.getRunner();

View File

@ -1,4 +1,4 @@
import { IsInt, IsPositive } from 'class-validator';
import { IsInt, IsOptional, IsPositive } from 'class-validator';
import { getConnection } from 'typeorm';
import { DonorNotFoundError } from '../../../errors/DonorErrors';
import { Donation } from '../../entities/Donation';
@ -23,6 +23,13 @@ export abstract class UpdateDonation {
@IsPositive()
donor: number;
/**
* The donation's paid amount in the smalles unit of your currency (default: euro cent).
*/
@IsInt()
@IsOptional()
paidAmount?: number;
/**
* Creates a new Donation entity from this.
*/

View File

@ -20,6 +20,7 @@ export class UpdateFixedDonation extends UpdateDonation {
*/
public async update(donation: FixedDonation): Promise<FixedDonation> {
donation.amount = this.amount;
donation.paidAmount = this.paidAmount;
donation.donor = await this.getDonor();
return donation;

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

View File

@ -1,12 +1,14 @@
import * as argon2 from "argon2";
import { passwordStrength } from "check-password-strength";
import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUrl } from 'class-validator';
import { getConnectionManager } from 'typeorm';
import { config } from '../../../config';
import { UserEmailNeededError, UsernameContainsIllegalCharacterError } from '../../../errors/UserErrors';
import { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, UserEmailNeededError, UsernameContainsIllegalCharacterError } from '../../../errors/UserErrors';
import { UserGroupNotFoundError } from '../../../errors/UserGroupErrors';
import { User } from '../../entities/User';
import { UserGroup } from '../../entities/UserGroup';
/**
* This class is used to update a User entity (via put request).
*/
@ -104,6 +106,11 @@ export class UpdateUser {
if (this.username.includes("@")) { throw new UsernameContainsIllegalCharacterError(); }
if (this.password) {
let password_strength = passwordStrength(this.password);
if (!password_strength.contains.includes("uppercase")) { throw new PasswordMustContainUppercaseLetterError(); }
if (!password_strength.contains.includes("lowercase")) { throw new PasswordMustContainLowercaseLetterError(); }
if (!password_strength.contains.includes("number")) { throw new PasswordMustContainNumberError(); }
if (!(password_strength.length > 9)) { throw new PasswordTooShortError(); }
user.password = await argon2.hash(this.password + user.uuid);
user.refreshTokenCount = user.refreshTokenCount + 1;
}
@ -117,7 +124,7 @@ export class UpdateUser {
user.phone = this.phone;
user.groups = await this.getGroups();
if (!this.profilePic) { user.profilePic = `https://dev.lauf-fuer-kaya.de/lfk-logo.png`; }
if (!this.profilePic) { user.profilePic = `https://lauf-fuer-kaya.de/lfk-logo.png`; }
else { user.profilePic = this.profilePic; }
return user;

View File

@ -2,7 +2,7 @@ import {
IsInt,
IsNotEmpty
} from "class-validator";
import { Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
import { ResponseDonation } from '../responses/ResponseDonation';
import { Donor } from './Donor';
@ -34,6 +34,13 @@ export abstract class Donation {
*/
public abstract get amount(): number;
/**
* The donation's paid amount in cents (or whatever your currency's smallest unit is.).
* Used to mark donations as paid.
*/
@Column({ nullable: true })
@IsInt()
paidAmount: number;
/**
* Turns this entity into it's response class.

View File

@ -33,6 +33,15 @@ export class Donor extends Participant {
return this.donations.reduce((sum, current) => sum + current.amount, 0);
}
/**
* Returns the total paid donations of a donor based on his linked donations.
*/
@IsInt()
public get paidDonationAmount(): number {
if (!this.donations) { return 0; }
return this.donations.reduce((sum, current) => sum + current.paidAmount, 0);
}
/**
* Turns this entity into it's response class.
*/

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.

View File

@ -52,13 +52,7 @@ export class RunnerCard {
* Generates a ean-13 compliant string for barcode generation.
*/
public get code(): string {
const multiply = [1, 3];
let total = 0;
this.paddedId.split('').forEach((letter, index) => {
total += parseInt(letter, 10) * multiply[index % 2];
});
const checkSum = (Math.ceil(total / 10) * 10) - total;
return this.paddedId + checkSum.toString();
return this.paddedId
}
/**
@ -67,10 +61,11 @@ export class RunnerCard {
private get paddedId(): string {
let id: string = this.id.toString();
if (id.length > 12) {
if (id.length > 11) {
throw new RunnerCardIdOutOfRangeError();
}
while (id.length < 12) { id = '0' + id; }
while (id.length < 11) { id = '0' + id; }
id = '2' + id;
return id;
}

View File

@ -51,6 +51,9 @@ export abstract class RunnerGroup {
*/
@IsInt()
public get distance(): number {
if (!this.runners || this.runners.length == 0) {
return 0;
}
return this.runners.reduce((sum, current) => sum + current.distance, 0);
}

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.
*/

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

View File

@ -0,0 +1,7 @@
/**
* This enum contains all status a donation can inherit regarding it's payment status.
*/
export enum DonationStatus {
OPEN = 'OPEN',
PAID = 'PAID'
}

View File

@ -15,5 +15,6 @@ export enum PermissionTarget {
STATION = 'STATION',
CARD = 'CARD',
DONATION = 'DONATION',
CONTACT = 'CONTACT'
CONTACT = 'CONTACT',
MAIL = 'MAIL'
}

View File

@ -0,0 +1,39 @@
/**
* 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',
SELFSERVICEDONOR = 'SELFSERVICEDONOR'
}

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

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.
*/

View File

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

View File

@ -1,11 +1,27 @@
import { IsInt, IsNotEmpty, IsPositive } from "class-validator";
import { Donation } from '../entities/Donation';
import { DonationStatus } from '../enums/DonationStatus';
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 payment status.
* Provides you with a quick indicator of it's payment status.
*/
status: DonationStatus;
/**
* The donation's id.
*/
@ -25,13 +41,28 @@ export class ResponseDonation {
@IsInt()
amount: number;
/**
* The donation's paid amount in the smalles unit of your currency (default: euro cent).
*/
@IsInt()
paidAmount: number;
/**
* Creates a ResponseDonation object from a scan.
* @param donation The donation the response shall be build for.
*/
public constructor(donation: Donation) {
this.id = donation.id;
this.donor = donation.donor.toResponse();
if (donation.donor) {
this.donor = donation.donor.toResponse();
}
this.amount = donation.amount;
this.paidAmount = donation.paidAmount || 0;
if (this.paidAmount < this.amount) {
this.status = DonationStatus.OPEN;
}
else {
this.status = DonationStatus.PAID;
}
}
}

View File

@ -2,12 +2,20 @@ import {
IsBoolean, IsInt
} from "class-validator";
import { Donor } from '../entities/Donor';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseDonation } from './ResponseDonation';
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?
@ -21,6 +29,14 @@ export class ResponseDonor extends ResponseParticipant {
@IsInt()
donationAmount: number;
/**
* Returns the total paid donations of a donor based on his linked donations.
*/
@IsInt()
paidDonationAmount: number;
donations: Array<ResponseDonation>;
/**
* Creates a ResponseRunner object from a runner.
* @param runner The user the response shall be build for.
@ -29,5 +45,12 @@ export class ResponseDonor extends ResponseParticipant {
super(donor);
this.receiptNeeded = donor.receiptNeeded;
this.donationAmount = donor.donationAmount;
this.paidDonationAmount = donor.paidDonationAmount;
this.donations = new Array<ResponseDonation>();
if (donor.donations?.length > 0) {
for (const donation of donor.donations) {
this.donations.push(donation.toResponse())
}
}
}
}

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"
}

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

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.
*/

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.
*/

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.
*/

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.

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

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.
*/

View File

@ -1,11 +1,19 @@
import { IsInt, IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator";
import { GroupContact } from '../entities/GroupContact';
import { IsInt, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from "class-validator";
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,11 @@ export abstract class ResponseRunnerGroup {
*/
@IsObject()
@IsOptional()
contact?: GroupContact;
contact?: ResponseGroupContact;
@IsOptional()
@IsNumber()
total_distance: number
/**
* Creates a ResponseRunnerGroup object from a runnerGroup.
@ -35,6 +47,7 @@ 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(); };
if (group.runners) { this.total_distance = group.runners.reduce((p, c) => p + c.distance, 0) }
}
}

View File

@ -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,16 @@ 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());
}
for (const team of this.teams) {
this.total_distance += team.total_distance;
}
}
if (!org.key) { this.registrationEnabled = false; }
else { this.registrationKey = Buffer.from(org.key).toString('base64'); }
}

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

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

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

View File

@ -1,16 +1,25 @@
import { IsInt, IsNotEmpty, IsPositive } from 'class-validator';
import { DistanceDonation } from '../entities/DistanceDonation';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseSelfServiceDonor } from './ResponseSelfServiceDonor';
/**
* 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.
*/
@IsNotEmpty()
donor: string;
donor: ResponseSelfServiceDonor;
/**
* The donation's amount in the smalles unit of your currency (default: euro cent).
@ -27,9 +36,7 @@ export class ResponseSelfServiceDonation {
amountPerDistance: number;
public constructor(donation: DistanceDonation) {
if (!donation.donor.middlename) { this.donor = donation.donor.firstname + " " + donation.donor.lastname; }
else { this.donor = donation.donor.firstname + " " + donation.donor.middlename + " " + donation.donor.lastname; }
this.donor = new ResponseSelfServiceDonor(donation.donor);
this.amountPerDistance = donation.amountPerDistance;
this.amount = donation.amount;
}

View File

@ -0,0 +1,51 @@
import { IsInt, IsString } from "class-validator";
import { Donor } from '../entities/Donor';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
/**
* Defines the donor selfservice response.
* Why? B/C runner's are not allowed to view all information available to admin users.
*/
export class ResponseSelfServiceDonor implements IResponse {
/**
* The responseType.
* This contains the type of class/entity this response contains.
*/
responseType: ResponseObjectType = ResponseObjectType.SELFSERVICEDONOR;
/**
* The participant's id.
*/
@IsInt()
id: number;
/**
* The participant's first name.
*/
@IsString()
firstname: string;
/**
* The participant's middle name.
*/
@IsString()
middlename?: string;
/**
* The participant's last name.
*/
@IsString()
lastname: string;
/**
* Creates a ResponseSelfServiceDonor object from a runner.
* @param donor The donor the response shall be build for.
*/
public constructor(donor: Donor) {
this.id = donor.id;
this.firstname = donor.firstname;
this.middlename = donor.middlename;
this.lastname = donor.lastname;
}
}

View File

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

View File

@ -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.
@ -31,10 +38,10 @@ export class ResponseSelfServiceRunner extends ResponseParticipant {
group: string;
/**
* The runner's associated donations.
* The runner's associated distance donations.
*/
@IsString()
donations: ResponseSelfServiceDonation[]
distanceDonations: ResponseSelfServiceDonation[]
/**
* The runner's self-service jwt for auth.
@ -53,7 +60,7 @@ export class ResponseSelfServiceRunner extends ResponseParticipant {
this.distance = runner.distance;
this.donationAmount = runner.distanceDonationAmount;
this.group = this.getTeamString(runner.group);
this.donations = this.getDonations(runner.distanceDonations);
this.distanceDonations = this.getDonations(runner.distanceDonations);
}
/**

View File

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

View File

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

View File

@ -2,17 +2,20 @@ import {
IsInt
} from "class-validator";
import { Donation } from '../entities/Donation';
import { Runner } from '../entities/Runner';
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.
*/
@ -55,12 +58,30 @@ export class ResponseStats {
@IsInt()
total_donation: number;
/**
* The total donation count (cent).
*/
@IsInt()
total_donations: number;
/**
* The total donor count.
*/
@IsInt()
total_donors: number;
/**
* The average distance ran per runner.
*/
@IsInt()
average_distance: number;
/**
* The average donation per distance (cent).
*/
@IsInt()
average_donation: number;
/**
* Creates a new stats response containing some basic statistics for a dashboard or public display.
* @param runners Array containing all runners - the following relations have to be resolved: scans, scans.track
@ -70,14 +91,17 @@ export class ResponseStats {
* @param scans Array containing all scans - no relations have to be resolved.
* @param donations Array containing all donations - the following relations have to be resolved: runner, runner.scans, runner.scans.track
*/
public constructor(runners: Runner[], teams: RunnerTeam[], orgs: RunnerOrganization[], users: User[], scans: Scan[], donations: Donation[]) {
this.total_runners = runners.length;
this.total_teams = teams.length;
this.total_orgs = orgs.length;
this.total_users = users.length;
this.total_scans = scans.filter(scan => { scan.valid === true }).length;
this.total_distance = runners.reduce((sum, current) => sum + current.distance, 0);
public constructor(runners: number, teams: number, orgs: number, users: number, scans: number, donations: Donation[], distance: number, donors: number) {
this.total_runners = runners;
this.total_teams = teams;
this.total_orgs = orgs;
this.total_users = users;
this.total_scans = scans;
this.total_distance = distance;
this.total_donation = donations.reduce((sum, current) => sum + current.amount, 0);
this.total_donations = donations.length;
this.average_donation = this.total_donation / this.total_donations
this.total_donors = donors;
this.average_distance = this.total_distance / this.total_runners;
}
}

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.
*/

View File

@ -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.
*/
@ -41,7 +49,15 @@ export class ResponseStatsOrgnisation {
public constructor(org: RunnerOrganization) {
this.name = org.name;
this.id = org.id;
this.distance = org.distance;
this.donationAmount = org.distanceDonationAmount;
try {
this.distance = org.distance;
} catch {
this.distance = -1;
}
try {
this.donationAmount = org.distanceDonationAmount;
} catch {
this.donationAmount = -1;
}
}
}

View File

@ -1,16 +1,25 @@
import {
IsInt,
IsObject,
IsOptional,
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.
*/
@ -47,23 +56,45 @@ export class ResponseStatsRunner {
@IsInt()
donationAmount: number;
/**
* The runner's fastest laptime in seconds.
*/
@IsInt()
@IsOptional()
minLaptime?: number;
/**
* The runner's group.
*/
@IsObject()
group: RunnerGroup;
group: ResponseRunnerGroup;
/**
* Creates a new runner stats response from a runner
* @param runner The runner whoes response shall be generated - the following relations have to be resolved: scans, group, distanceDonations, scans.track
*/
public constructor(runner: Runner) {
public constructor(runner: Runner, laptime?: number) {
this.id = runner.id;
this.firstname = runner.firstname;
this.middlename = runner.middlename;
if (runner.firstname) {
this.middlename = runner.middlename;
}
this.lastname = runner.lastname;
this.distance = runner.distance;
this.donationAmount = runner.distanceDonationAmount;
this.group = runner.group;
try {
this.distance = runner.distance;
}
catch {
this.distance = -1;
}
try {
this.donationAmount = runner.distanceDonationAmount;
}
catch {
this.donationAmount = -1;
}
if (laptime) {
this.minLaptime = laptime;
}
this.group = runner.group.toResponse();
}
}

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,8 +56,16 @@ export class ResponseStatsTeam {
public constructor(team: RunnerTeam) {
this.name = team.name;
this.id = team.id;
this.parent = team.parentGroup;
this.distance = team.distance;
this.donationAmount = team.distanceDonationAmount;
this.parent = team.parentGroup.toResponse();
try {
this.distance = team.distance;
} catch {
this.distance = -1;
}
try {
this.donationAmount = team.distanceDonationAmount;
} catch {
this.donationAmount = -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.
*/

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

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

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

View File

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

View File

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

@ -1,22 +1,22 @@
import * as argon2 from "argon2";
import { Connection } from 'typeorm';
import { Factory, Seeder } from 'typeorm-seeding';
import * as uuid from 'uuid';
import { CreatePermission } from '../models/actions/create/CreatePermission';
import { CreateUser } from '../models/actions/create/CreateUser';
import { CreateUserGroup } from '../models/actions/create/CreateUserGroup';
import { Permission } from '../models/entities/Permission';
import { User } from '../models/entities/User';
import { UserGroup } from '../models/entities/UserGroup';
import { PermissionAction } from '../models/enums/PermissionAction';
import { PermissionTarget } from '../models/enums/PermissionTargets';
/**
* Seeds a admin group with a demo user into the database for initial setup and auto recovery.
* We know that the nameing isn't perfectly fitting. Feel free to change it.
* We know that the naming isn't perfectly fitting. Feel free to change it.
*/
export default class SeedUsers implements Seeder {
public async run(factory: Factory, connection: Connection): Promise<any> {
let adminGroup: UserGroup = await this.createAdminGroup(connection);
await this.createUser(connection, adminGroup.id);
await this.createUser(connection, adminGroup);
await this.createPermissions(connection, adminGroup.id);
}
@ -27,15 +27,16 @@ export default class SeedUsers implements Seeder {
return await connection.getRepository(UserGroup).save(await adminGroup.toEntity());
}
public async createUser(connection: Connection, group: number) {
let initialUser = new CreateUser();
public async createUser(connection: Connection, group: UserGroup) {
let initialUser = new User();
initialUser.firstname = "demo";
initialUser.lastname = "demo";
initialUser.username = "demo";
initialUser.password = "demo";
initialUser.uuid = uuid.v4();
initialUser.password = await argon2.hash("demo" + initialUser.uuid);
initialUser.email = "demo@dev.lauf-fuer-kaya.de"
initialUser.groups = group;
return await connection.getRepository(User).save(await initialUser.toEntity());
initialUser.groups = [group];
return await connection.getRepository(User).save(initialUser);
}
public async createPermissions(connection: Connection, principal: number) {

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

View File

@ -8,14 +8,15 @@ 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",
"middlename": "demo_logout",
"lastname": "demo_logout",
"username": "demo_logout",
"password": "demo_logout",
"email": "demo_logout@dev.lauf-fuer-kaya.de"
"firstname": "demo_logoutASD123",
"middlename": "demo_logoutASD123",
"lastname": "demo_logoutASD123",
"username": "demo_logoutASD123",
"password": "demo_logoutASD123",
"email": "demo_logoutASD123@dev.lauf-fuer-kaya.de"
}, {
headers: { "authorization": "Bearer " + res_login.data["access_token"] },
validateStatus: undefined
@ -25,7 +26,7 @@ beforeAll(async () => {
describe('POST /api/auth/logout valid', () => {
let refresh_coookie;
it('valid logout with token in cookie should return 200', async () => {
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_logout", password: "demo_logout" });
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_logoutASD123", password: "demo_logoutASD123" });
refresh_coookie = res_login.headers["set-cookie"];
const res = await axios.post(base + '/api/auth/logout', null, {
headers: { "Cookie": refresh_coookie },
@ -34,7 +35,7 @@ describe('POST /api/auth/logout valid', () => {
expect(res.status).toEqual(200);
});
it('valid logout with token in body should return 200', async () => {
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_logout", password: "demo_logout" });
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_logoutASD123", password: "demo_logoutASD123" });
const res = await axios.post(base + '/api/auth/logout', { token: res_login.data["refresh_token"] }, axios_config);
expect(res.status).toEqual(200);
});

View File

@ -8,14 +8,15 @@ 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",
"middlename": "demo_refresh",
"lastname": "demo_refresh",
"username": "demo_refresh",
"password": "demo_refresh",
"email": "demo_refresh@dev.lauf-fuer-kaya.de"
"firstname": "demo_refreshASD312",
"middlename": "demo_refreshASD312",
"lastname": "demo_refreshASD312",
"username": "demo_refreshASD312",
"password": "demo_refreshASD312",
"email": "demo_refreshASD312@dev.lauf-fuer-kaya.de"
}, {
headers: { "authorization": "Bearer " + res_login.data["access_token"] },
validateStatus: undefined
@ -24,7 +25,7 @@ beforeAll(async () => {
describe('POST /api/auth/refresh valid', () => {
it('valid refresh with token in cookie should return 200', async () => {
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_refresh", password: "demo_refresh" });
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_refreshASD312", password: "demo_refreshASD312" });
const res = await axios.post(base + '/api/auth/refresh', null, {
headers: { "Cookie": res_login.headers["set-cookie"] },
validateStatus: undefined
@ -32,7 +33,7 @@ describe('POST /api/auth/refresh valid', () => {
expect(res.status).toEqual(200);
});
it('valid refresh with token in body should return 200', async () => {
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_refresh", password: "demo_refresh" });
const res_login = await axios.post(base + '/api/auth/login', { username: "demo_refreshASD312", password: "demo_refreshASD312" });
const res = await axios.post(base + '/api/auth/refresh', { token: res_login.data["refresh_token"] }, axios_config);
expect(res.status).toEqual(200);
});

View File

@ -8,25 +8,26 @@ 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",
"middlename": "demo_reset",
"lastname": "demo_reset",
"username": "demo_reset",
"password": "demo_reset",
"email": "demo_reset@dev.lauf-fuer-kaya.de"
"firstname": "demo_resetASD312",
"middlename": "demo_resetASD312",
"lastname": "demo_resetASD312",
"username": "demo_resetASD312",
"password": "demo_resetASD312",
"email": "demo_resetASD3121@dev.lauf-fuer-kaya.de"
}, {
headers: { "authorization": "Bearer " + res_login.data["access_token"] },
validateStatus: undefined
});
await axios.post(base + '/api/users', {
"firstname": "demo_reset2",
"middlename": "demo_reset2",
"lastname": "demo_reset2",
"username": "demo_reset2",
"password": "demo_reset2",
"email": "demo_reset1@dev.lauf-fuer-kaya.de"
"firstname": "demo_resetASD3122",
"middlename": "demo_resetASD3122",
"lastname": "demo_resetASD3122",
"username": "demo_resetASD3122",
"password": "demo_resetASD3122",
"email": "demo_resetASD3122@dev.lauf-fuer-kaya.de"
}, {
headers: { "authorization": "Bearer " + res_login.data["access_token"] },
validateStatus: undefined
@ -35,25 +36,17 @@ 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', { username: "demo_reset" });
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_resetASD3121@dev.lauf-fuer-kaya.de" }, axios_config);
reset_token = res1.data.resetToken;
expect(res1.status).toEqual(200);
});
it('valid password reset should return 200', async () => {
const res2 = await axios.post(base + '/api/auth/reset/' + reset_token, { password: "demo" }, axios_config);
expect(res2.status).toEqual(200);
});
it('valid login after reset should return 200', async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo_reset", password: "demo" });
expect(res.status).toEqual(200);
});
});
// ---------------
describe('POST /api/auth/reset invalid requests', () => {
it('request another password reset before the timeout should return 406', async () => {
const res1 = await axios.post(base + '/api/auth/reset', { username: "demo_reset2" }, axios_config);
const res2 = await axios.post(base + '/api/auth/reset', { username: "demo_reset2" }, axios_config);
const res1 = await axios.post(base + '/api/auth/reset', { email: "demo_resetASD3122@dev.lauf-fuer-kaya.de" }, axios_config);
const res2 = await axios.post(base + '/api/auth/reset', { email: "demo_resetASD3122@dev.lauf-fuer-kaya.de" }, axios_config);
expect(res2.status).toEqual(406);
});
});
@ -63,9 +56,9 @@ describe('POST /api/auth/reset invalid token', () => {
const res2 = await axios.post(base + '/api/auth/reset/' + "123123", { password: "demo" }, axios_config);
expect(res2.status).toEqual(401);
});
it('providing no reset token should return 404', async () => {
it('providing no reset token should return 400', async () => {
const res2 = await axios.post(base + '/api/auth/reset/' + "", { password: "demo" }, axios_config);
expect(res2.status).toEqual(404);
expect(res2.status).toEqual(400);
});
});
// ---------------

View File

@ -1,144 +1,186 @@
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 /api/cards illegally', () => {
it('non-existant runner input should return 404', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": 999999999999999999999999
}, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('POST /api/cards successfully (without runner)', () => {
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true
});
});
it('creating a disabled card should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"enabled": false
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": false
});
});
it('creating a enabled card should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"enabled": true
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true
});
});
});
// ---------------
describe('POST /api/cards successfully (with runner)', () => {
let added_org;
let added_runner;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organizations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"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")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
});
});
it('creating a card with runner (no optional params) should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
});
});
it('creating a enabled card with runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id,
"enabled": true
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true
});
});
it('creating a disabled card with runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id,
"enabled": false
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": false
});
});
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
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 = {
headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined
};
});
describe('POST /api/cards illegally', () => {
it('non-existant runner input should return 404', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": 999999999999999999999999
}, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json")
});
});
// ---------------
describe('POST /api/cards successfully (without runner)', () => {
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a disabled card should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"enabled": false
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": false,
"responseType": "RUNNERCARD"
});
});
it('creating a enabled card should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"enabled": true
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": null,
"enabled": true,
"responseType": "RUNNERCARD"
});
});
});
// ---------------
describe('POST /api/cards successfully (with runner)', () => {
let added_org;
let added_runner;
it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organizations', {
"name": "test123"
}, axios_config);
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
});
it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', {
"firstname": "first",
"lastname": "last",
"group": added_org.id
}, axios_config);
added_runner = res2.data;
expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json")
});
it('creating a card with the minimum amount of parameters should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a card with runner (no optional params) should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a enabled card with runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id,
"enabled": true
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"enabled": true,
"responseType": "RUNNERCARD"
});
});
it('creating a disabled card with runner should return 200', async () => {
const res = await axios.post(base + '/api/cards', {
"runner": added_runner.id,
"enabled": false
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.id;
delete res.data.code;
expect(res.data).toEqual({
"runner": added_runner,
"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 a single new bulk card and letting the system return it should return 200', async () => {
const res = await axios.post(base + '/api/cards/bulk?count=1&returnCards=true', {}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
expect(res.data[0].id).toBeDefined();
});
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 50 new bulk cards and letting the system return it should return 200', async () => {
const res = await axios.post(base + '/api/cards/bulk?count=50&returnCards=true', {}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
expect(res.data.length).toEqual(50);
});
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");
});
});

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 = {

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