Compare commits

...

30 Commits

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
41 changed files with 8659 additions and 317 deletions

View File

@ -26,6 +26,13 @@ get:
path: odit-ci-bot
name: apikey
---
kind: secret
name: npm_url
get:
path: odit-npm-cache
name: url
---
kind: pipeline
type: kubernetes
@ -41,8 +48,12 @@ steps:
- name: run tests
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
@ -61,6 +72,7 @@ steps:
- git clone $DRONE_REMOTE_URL .
- git checkout dev
- name: build dev
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
@ -68,49 +80,13 @@ steps:
password:
from_secret: docker_password
build_args:
- NPM_REGISTRY_DOMAIN:
from_secret: npmjs_domain
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
- 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: registry.odit.services/hub/library/node:19.5.0-alpine3.16
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: git_ssh
- name: run full license export
depends_on: ["clone"]
image: registry.odit.services/hub/library/node:19.5.0-alpine3.16
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: git_ssh
trigger:
branch:
@ -134,6 +110,7 @@ steps:
- git merge main
- git checkout main
- name: build latest
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
@ -141,10 +118,8 @@ steps:
password:
from_secret: docker_password
build_args:
- NPM_REGISTRY_DOMAIN:
from_secret: npmjs_domain
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/backend
tags:
- latest
@ -173,6 +148,7 @@ name: build:tags
steps:
- name: build $DRONE_TAG
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
@ -180,21 +156,13 @@ steps:
password:
from_secret: docker_password
build_args:
- NPM_REGISTRY_DOMAIN:
from_secret: npmjs_domain
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/backend
tags:
- "${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: ci_token
- name: trigger js lib build
image: idcooldi/drone-webhook
settings:

1
.gitignore vendored
View File

@ -136,4 +136,3 @@ build
lib
/oss-attribution
*.tmp
pnpm-lock.yaml

View File

@ -2,8 +2,235 @@
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)

View File

@ -1,15 +1,23 @@
# Typescript Build
FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16
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 npx pnpm@7.26.3 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 npm run build
RUN pnpm run build \
&& rm -rf /app/node_modules \
&& pnpm i --production --prefer-offline
# final image
FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16
COPY package.json ormconfig.js ./
RUN npx pnpm@7.26.3 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,24 +15,24 @@ 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
@ -44,30 +44,30 @@ Currently the following templates exist:
### 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)
| 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
@ -85,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

@ -444,6 +444,25 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**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
@ -454,7 +473,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## License Text
MIT License
Copyright (c) 2010 - 2021 Brian Carlson
Copyright (c) 2010 - 2020 Brian Carlson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -696,6 +715,75 @@ 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]
@ -926,6 +1014,35 @@ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SOFTWARE
# auto-changelog
**Author**: Pete Cook <pete@cookpete.com> (https://github.com/cookpete)
**Repo**: [object Object]
**License**: MIT
**Description**: Command line tool for generating a changelog from git tags and commit history
## License Text
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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# cp-cli
**Author**: undefined
**Repo**: [object Object]

View File

@ -1,108 +1,117 @@
{
"name": "@odit/lfk-backend",
"version": "0.12.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",
"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": {
"@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",
"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",
"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
}
},
"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

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

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,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,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 { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard';
@ -26,9 +26,16 @@ export class RunnerCardController {
@Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard, { isArray: true })
@OpenAPI({ description: 'Lists all card.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
const cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'] });
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));
});

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', 'group.parentGroup', '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));
});

View File

@ -1,6 +1,6 @@
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';
@ -29,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')
@ -45,7 +52,7 @@ 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);
}
@ -114,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,6 +1,6 @@
import { Request } from "express";
import * as jwt from "jsonwebtoken";
import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers';
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';
@ -116,10 +116,10 @@ export class RunnerSelfServiceController {
return scan.toResponse();
}
@Post('/runners/forgot')
@Post('/runners/login')
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(ResponseEmpty)
@OpenAPI({ description: 'Use this endpoint to reuqest a new selfservice token/link to be sent to your mail address (rate limited to one mail every 24hrs).' })
@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();
@ -127,7 +127,7 @@ export class RunnerSelfServiceController {
const runner = await this.runnerRepository.findOne({ email: mail });
if (!runner) { throw new RunnerNotFoundError(); }
if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 60 * 24)) { throw new RunnerSelfserviceTimeoutError(); }
if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 15)) { throw new RunnerSelfserviceTimeoutError(); }
const token = JwtCreator.createSelfService(runner);
try {
@ -148,8 +148,11 @@ export class RunnerSelfServiceController {
@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);
@ -170,6 +173,9 @@ export class RunnerSelfServiceController {
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'] }));
@ -225,4 +231,14 @@ export class RunnerSelfServiceController {
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,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 { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors';
import { CreateRunnerTeam } from '../models/actions/create/CreateRunnerTeam';
import { UpdateRunnerTeam } from '../models/actions/update/UpdateRunnerTeam';
@ -27,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;
}
@ -43,7 +50,7 @@ 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);
}

View File

@ -1,7 +1,7 @@
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';
@ -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.group', '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.group', '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();
}

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

@ -3,6 +3,7 @@ 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';
@ -21,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")

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 { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, 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));
});

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 { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors';
import { CreateUserGroup } from '../models/actions/create/CreateUserGroup';
import { UpdateUserGroup } from '../models/actions/update/UpdateUserGroup';
@ -27,9 +27,16 @@ export class UserGroupController {
@Authorized("USERGROUP:GET")
@ResponseSchema(ResponseUserGroup, { isArray: true })
@OpenAPI({ description: 'Lists all groups. <br> The information provided might change while the project continues to evolve.' })
async getAll() {
async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseGroups: ResponseUserGroup[] = new Array<ResponseUserGroup>();
const groups = await this.userGroupsRepository.find({ relations: ['permissions'] });
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());
});

View File

@ -58,11 +58,11 @@ export class CreateTrackScan {
*/
public async getCard(): Promise<RunnerCard> {
const id = this.card % 200000000000;
const track = await getConnection().getRepository(RunnerCard).findOne({ id: id }, { relations: ["runner"] });
if (!track) {
const runnerCard = await getConnection().getRepository(RunnerCard).findOne({ id: id }, { relations: ["runner"] });
if (!runnerCard) {
throw new RunnerCardNotFoundError();
}
return track;
return runnerCard;
}
/**
@ -86,14 +86,13 @@ export class CreateTrackScan {
* @returns The validated scan with it's laptime set.
*/
public async validateScan(scan: TrackScan): Promise<TrackScan> {
const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner, valid: true }, relations: ["track"] });
if (scans.length == 0) {
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;
}
else {
const newestScan = scans[scans.length - 1];
scan.lapTime = scan.timestamp - newestScan.timestamp;
scan.lapTime = scan.timestamp - latestScan.timestamp;
scan.valid = (scan.lapTime > scan.track.minimumLapTime);
}
return scan;

View File

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

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

@ -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
}
/**

View File

@ -53,7 +53,9 @@ export class ResponseDonation implements IResponse {
*/
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) {

View File

@ -4,6 +4,7 @@ import {
import { Donor } from '../entities/Donor';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
import { ResponseDonation } from './ResponseDonation';
import { ResponseParticipant } from './ResponseParticipant';
/**
@ -34,6 +35,8 @@ export class ResponseDonor extends ResponseParticipant implements IResponse {
@IsInt()
paidDonationAmount: number;
donations: Array<ResponseDonation>;
/**
* Creates a ResponseRunner object from a runner.
* @param runner The user the response shall be build for.
@ -43,5 +46,11 @@ export class ResponseDonor extends ResponseParticipant implements IResponse {
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,4 +1,4 @@
import { IsInt, IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator";
import { IsInt, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from "class-validator";
import { RunnerGroup } from '../entities/RunnerGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse';
@ -36,6 +36,10 @@ export abstract class ResponseRunnerGroup implements IResponse {
@IsOptional()
contact?: ResponseGroupContact;
@IsOptional()
@IsNumber()
total_distance: number
/**
* Creates a ResponseRunnerGroup object from a runnerGroup.
* @param group The runnerGroup the response shall be build for.
@ -44,5 +48,6 @@ export abstract class ResponseRunnerGroup implements IResponse {
this.id = group.id;
this.name = group.name;
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

@ -67,6 +67,9 @@ export class ResponseRunnerOrganization extends ResponseRunnerGroup implements I
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; }

View File

@ -2,11 +2,6 @@ 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';
@ -63,12 +58,30 @@ export class ResponseStats implements IResponse {
@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
@ -78,14 +91,17 @@ export class ResponseStats implements IResponse {
* @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

@ -11,7 +11,7 @@ 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> {

View File

@ -0,0 +1,19 @@
import axios from 'axios';
async function main() {
console.time("batches")
for (let i = 0; i < 100; i++) {
const batch = [];
for (let i = 0; i < 6; i++) {
batch.push(axios.post('http://localhost:4010/api/scans/trackscans', { card: 200000000001, station: 2 }, {
headers: {
Authorization: 'Bearer 10F2E64.BB4F6CC5-2148-4CCF-88B5-0AA85D0508A9'
}
}))
}
await Promise.all(batch)
console.timeLog("batches", `Finished batch ${i}`)
}
console.timeEnd("batches")
}
main();

View File

@ -22,6 +22,12 @@ describe('deletion (non-existant)', () => {
expect(res2.status).toEqual(204);
});
});
describe('deletion of citizen sould fail', () => {
it('delete', async () => {
const res3 = await axios.delete(base + '/api/organizations/1', axios_config);
expect(res3.status).toEqual(400);
});
});
// ---------------
describe('adding + deletion (successfull)', () => {
let added_org_id

View File

@ -66,6 +66,8 @@ describe('adding + getting scans', () => {
const res = await axios.get(base + '/api/scans/' + added_scan.id, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
delete res.data.runner.distance;
delete added_scan.runner.distance;
expect(res.data).toEqual(added_scan);
});
it('check if scans was added via the runner/scans endpoint.', async () => {

View File

@ -1,5 +1,7 @@
import { faker } from '@faker-js/faker';
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
let access_token;
@ -21,7 +23,7 @@ describe('delete selfservice runner invalid', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
added_runner = res.data;
expect(res.status).toEqual(200);
@ -50,7 +52,7 @@ describe('delete selfservice runner valid', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
added_runner = res.data;
expect(res.status).toEqual(200);

View File

@ -15,20 +15,20 @@ beforeAll(async () => {
};
});
describe('POST /api/runners/me/forgot invalid syntax/mail should fail', () => {
describe('POST /api/runners/me/login invalid syntax/mail should fail', () => {
it('get without mail return 404', async () => {
const res = await axios.post(base + '/api/runners/forgot', null, axios_config);
const res = await axios.post(base + '/api/runners/login', null, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json");
});
it('get without bs mail return 404', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=asdasdasdasdasd@tester.test.dev.lauf-fuer-kaya.de', null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=asdasdasdasdasd@tester.test.dev.lauf-fuer-kaya.de', null, axios_config);
expect(res.status).toEqual(404);
expect(res.headers['content-type']).toContain("application/json");
});
});
// ---------------
describe('POST /api/runners/me/forgot 2 times within timeout should fail', () => {
describe('POST /api/runners/me/login 2 times within timeout should fail', () => {
let added_runner;
it('registering as citizen should return 200', async () => {
const res = await axios.post(base + '/api/runners/register', {
@ -42,19 +42,19 @@ describe('POST /api/runners/me/forgot 2 times within timeout should fail', () =>
added_runner = res.data;
});
it('post with valid mail should return 200', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=' + added_runner.email, null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=' + added_runner.email, null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
});
it('2nd post with valid mail should return 406', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=' + added_runner.email, null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=' + added_runner.email, null, axios_config);
expect(res.status).toEqual(406);
expect(res.headers['content-type']).toContain("application/json");
});
});
// ---------------
describe('POST /api/runners/me/forgot valid should return 200', () => {
describe('POST /api/runners/me/login valid should return 200', () => {
let added_runner;
let new_token;
it('registering as citizen should return 200', async () => {
@ -69,7 +69,7 @@ describe('POST /api/runners/me/forgot valid should return 200', () => {
added_runner = res.data;
});
it('post with valid mail should return 200', async () => {
const res = await axios.post(base + '/api/runners/forgot?mail=' + added_runner.email, null, axios_config);
const res = await axios.post(base + '/api/runners/login?mail=' + added_runner.email, null, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
new_token = res.data.token;

View File

@ -1,3 +1,4 @@
import { faker } from '@faker-js/faker';
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
@ -30,7 +31,7 @@ describe('register + get should return 200', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");

View File

@ -1,3 +1,4 @@
import { faker } from '@faker-js/faker';
import axios from 'axios';
import { config } from '../../config';
const base = "http://localhost:" + config.internal_port
@ -39,7 +40,7 @@ describe('register invalid citizen', () => {
const res = await axios.post(base + '/api/runners/register', {
"middlename": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
@ -48,7 +49,7 @@ describe('register invalid citizen', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"middlename": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
@ -59,7 +60,26 @@ describe('register invalid citizen', () => {
"middlename": "string",
"lastname": "string",
"phone": "peter",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
});
it('registering as citizen with duplicate mail should return 400', async () => {
const mail = faker.internet.exampleEmail();
await axios.post(base + '/api/runners/register', {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"phone": "peter",
"email": mail,
}, axios_config);
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"phone": "peter",
"email": mail,
}, axios_config);
expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json");
@ -71,7 +91,7 @@ describe('register citizen valid', () => {
const res = await axios.post(base + '/api/runners/register', {
"firstname": "string",
"lastname": "string",
"email": "user@example.com"
"email": faker.internet.exampleEmail(),
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
@ -81,7 +101,7 @@ describe('register citizen valid', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",
@ -187,7 +207,7 @@ describe('register valid company', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",
@ -214,7 +234,7 @@ describe('register valid company', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",
@ -232,7 +252,7 @@ describe('register valid company', () => {
"firstname": "string",
"middlename": "string",
"lastname": "string",
"email": "user@example.com",
"email": faker.internet.exampleEmail(),
"phone": "+4909132123456",
"address": {
address1: "Teststreet 1",