diff --git a/.drone.yml b/.drone.yml index 8b7504e..c226a56 100644 --- a/.drone.yml +++ b/.drone.yml @@ -29,7 +29,7 @@ get: --- kind: pipeline type: kubernetes -name: tests:node_14.15.1-alpine3.12 +name: tests:node clone: disable: true steps: @@ -39,7 +39,7 @@ steps: - git clone $DRONE_REMOTE_URL . - git checkout $DRONE_SOURCE_BRANCH - name: run tests - image: node:14.15.1-alpine3.12 + image: registry.odit.services/hub/library/node:19.5.0-alpine3.16 commands: - yarn - yarn test:ci @@ -61,53 +61,23 @@ steps: - git clone $DRONE_REMOTE_URL . - git checkout dev - name: build dev - image: plugins/docker - depends_on: [clone] + depends_on: ["clone"] + image: registry.odit.services/library/drone-kaniko settings: username: from_secret: docker_username password: from_secret: docker_password - repo: registry.odit.services/lfk/backend + build_args: + - NPM_REGISTRY_DOMAIN: + from_secret: npmjs_domain + - NPM_REGISTRY_TOKEN: + from_secret: npmjs_token + repo: lfk/backend tags: - dev + cache: true registry: registry.odit.services - mtu: 1000 - - name: run changelog export - depends_on: ["clone"] - image: node:latest - commands: - - npx auto-changelog --commit-limit false -p -u --hide-credit - - name: push new changelog to repo - depends_on: ["run changelog export"] - image: appleboy/drone-git-push - settings: - branch: dev - commit: true - commit_message: 🧾New changelog file version [CI SKIP] [skip ci] - author_email: bot@odit.services - remote: git@git.odit.services:lfk/backend.git - ssh_key: - from_secret: git_ssh - - name: run full license export - depends_on: ["clone"] - image: node:14.15.1-alpine3.12 - commands: - - yarn - - yarn licenses:export - - name: push new licenses file to repo - depends_on: ["run full license export"] - image: appleboy/drone-git-push - settings: - branch: dev - commit: true - commit_message: 📖New license file version [CI SKIP] [skip ci] - author_email: bot@odit.services - remote: git@git.odit.services:lfk/backend.git - skip_verify: true - ssh_key: - from_secret: git_ssh - trigger: branch: @@ -132,17 +102,22 @@ steps: - git checkout main - name: build latest depends_on: ["clone"] - image: plugins/docker + image: registry.odit.services/library/drone-kaniko settings: username: from_secret: docker_username password: from_secret: docker_password - repo: registry.odit.services/lfk/backend + build_args: + - NPM_REGISTRY_DOMAIN: + from_secret: npmjs_domain + - NPM_REGISTRY_TOKEN: + from_secret: npmjs_token + repo: lfk/backend tags: - latest + cache: true registry: registry.odit.services - mtu: 1000 - name: push merge to repo depends_on: ["clone"] image: appleboy/drone-git-push @@ -166,18 +141,23 @@ name: build:tags steps: - name: build $DRONE_TAG - image: plugins/docker - depends_on: [clone] + depends_on: ["clone"] + image: registry.odit.services/library/drone-kaniko settings: username: from_secret: docker_username password: from_secret: docker_password - repo: registry.odit.services/lfk/backend + build_args: + - NPM_REGISTRY_DOMAIN: + from_secret: npmjs_domain + - NPM_REGISTRY_TOKEN: + from_secret: npmjs_token + repo: lfk/backend tags: - - '${DRONE_TAG}' + - "${DRONE_TAG}" + cache: true registry: registry.odit.services - mtu: 1000 - name: trigger node lib build image: idcooldi/drone-webhook settings: @@ -192,4 +172,4 @@ steps: from_secret: ci_token trigger: event: - - tag \ No newline at end of file + - tag diff --git a/.gitignore b/.gitignore index 448b818..dd3a803 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,5 @@ build /docs lib /oss-attribution -*.tmp \ No newline at end of file +*.tmp +pnpm-lock.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index f669de7..7bbbe15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,56 @@ All notable changes to this project will be documented in this file. Dates are displayed in UTC. +#### [v0.13.1](https://git.odit.services/lfk/backend/compare/v0.13.0...v0.13.1) + +- 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 -> Kaniko based builds [`0e78951`](https://git.odit.services/lfk/backend/commit/0e789513008085d0db94fc3b2dd9e74a5e583049) +- Drone images to odit registry [`6ad56b3`](https://git.odit.services/lfk/backend/commit/6ad56b31269bf19a740c1b6b1a303a8a9d7d59d0) +- Bumped container base images [`d95c6d3`](https://git.odit.services/lfk/backend/commit/d95c6d33657f6aa977a8ebfefad7e199bb1cc9c3) +- Enabled tag via release script [`9217421`](https://git.odit.services/lfk/backend/commit/92174212213f874e41c9472a927bcf87b963ac94) +- Pinned pnpm for builds [`4570845`](https://git.odit.services/lfk/backend/commit/4570845b3e1bd00c228fe1b09b658c24e20aba7f) +- 🚀Bumped version to v0.12.0 [`4c10e20`](https://git.odit.services/lfk/backend/commit/4c10e20b91a8101ee37b230373ceb3e024582b41) +- Ignore pnpm lock [`1f2c8ab`](https://git.odit.services/lfk/backend/commit/1f2c8abb22f3ff1e61b7350b517bd699c3e315f6) +- 🧾New changelog file version [CI SKIP] [skip ci] [`31b258b`](https://git.odit.services/lfk/backend/commit/31b258b4ce82213144160a4233b7fd127e456776) + #### [v0.11.1](https://git.odit.services/lfk/backend/compare/v0.11.0...v0.11.1) +> 22 April 2021 + +- Merge pull request 'Release 0.11.1' (#196) from dev into main [`f19f280`](https://git.odit.services/lfk/backend/commit/f19f2808d88414f1877c01f10996dac68b6f9617) - 🧾New changelog file version [CI SKIP] [skip ci] [`2229cdf`](https://git.odit.services/lfk/backend/commit/2229cdf20db1a98f9f76a99fa9d3f463cdf6d804) - 🧾New changelog file version [CI SKIP] [skip ci] [`348fe52`](https://git.odit.services/lfk/backend/commit/348fe52c42cfa32239b703041820f725e147154e) - Now prefixing runnercards with 2 [`8a82e05`](https://git.odit.services/lfk/backend/commit/8a82e059b74ceabf43c9cbfe9c9b89ef6ce15a28) +- 🧾New changelog file version [CI SKIP] [skip ci] [`3b9cd2e`](https://git.odit.services/lfk/backend/commit/3b9cd2e1bbbe8e69c3883233a98f286d768c2b79) - Added fix for the appended 2 [`eb526fb`](https://git.odit.services/lfk/backend/commit/eb526fb57faf631fd6e84af99af738ab1b3481c7) - 🚀Bumped version to v0.11.1 [`95320ca`](https://git.odit.services/lfk/backend/commit/95320ca1bccc2886553accea6a428aadffda0a27) - 🧾New changelog file version [CI SKIP] [skip ci] [`f2d127f`](https://git.odit.services/lfk/backend/commit/f2d127fc98d75ce658424624abd382c087737ca0) diff --git a/Dockerfile b/Dockerfile index fe4793a..0a949b9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,15 @@ # Typescript Build -FROM node:14.15.1-alpine3.12 +FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16 WORKDIR /app COPY package.json ./ -RUN npm i -g pnpm -RUN pnpm i +RUN npx pnpm@7.26.3 i COPY tsconfig.json ormconfig.js ./ COPY src ./src -RUN pnpm run build +RUN npm run build + # final image -FROM node:14.15.1-alpine3.12 +FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16 COPY package.json ormconfig.js ./ -RUN npm i -g pnpm -RUN pnpm i --prod +RUN npx pnpm@7.26.3 i --prod COPY --from=0 /app/dist dist ENTRYPOINT ["node", "dist/app.js"] \ No newline at end of file diff --git a/README.md b/README.md index 06a44b1..dcb7734 100644 --- a/README.md +++ b/README.md @@ -51,23 +51,23 @@ yarn docs > 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,8 +85,8 @@ 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 diff --git a/licenses.md b/licenses.md index d37ec17..0a3e72e 100644 --- a/licenses.md +++ b/licenses.md @@ -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 (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] diff --git a/package.json b/package.json index c0c57d6..684cddb 100644 --- a/package.json +++ b/package.json @@ -1,106 +1,114 @@ -{ - "name": "@odit/lfk-backend", - "version": "0.11.1", - "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": false, - "tag": false - }, - "npm": { - "publish": false - } - }, - "nodemonConfig": { - "ignore": [ - "src/tests/*", - "docs/*" - ] - } -} +{ + "name": "@odit/lfk-backend", + "version": "0.13.1", + "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": { + "@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/*" + ] + } +} diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index 9563975..822fa1b 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -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 24hrs).' }) async requestNewToken(@QueryParam('mail') mail: string, @QueryParam("locale") locale: string = "en") { if (!mail) { throw new RunnerNotFoundError(); @@ -148,8 +148,11 @@ export class RunnerSelfServiceController { @OpenAPI({ description: 'Create a new selfservice runner in the citizen org.
This endpoint shoud be used to allow "everyday citizen" to register themselves.
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 { + const runner = await this.runnerRepository.findOne({ email }); + return runner != undefined + } } \ No newline at end of file diff --git a/src/models/actions/create/CreateUser.ts b/src/models/actions/create/CreateUser.ts index a29fb62..d85a8ba 100644 --- a/src/models/actions/create/CreateUser.ts +++ b/src/models/actions/create/CreateUser.ts @@ -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; diff --git a/src/models/actions/update/UpdateUser.ts b/src/models/actions/update/UpdateUser.ts index e5685eb..88f36c6 100644 --- a/src/models/actions/update/UpdateUser.ts +++ b/src/models/actions/update/UpdateUser.ts @@ -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; diff --git a/src/tests/selfservice/selfservice_delete.spec.ts b/src/tests/selfservice/selfservice_delete.spec.ts index 9add148..423d93e 100644 --- a/src/tests/selfservice/selfservice_delete.spec.ts +++ b/src/tests/selfservice/selfservice_delete.spec.ts @@ -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); diff --git a/src/tests/selfservice/selfservice_forgotten.spec.ts b/src/tests/selfservice/selfservice_forgotten.spec.ts index c67ef7e..9eb865e 100644 --- a/src/tests/selfservice/selfservice_forgotten.spec.ts +++ b/src/tests/selfservice/selfservice_forgotten.spec.ts @@ -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; diff --git a/src/tests/selfservice/selfservice_get.spec.ts b/src/tests/selfservice/selfservice_get.spec.ts index 7867127..201cc08 100644 --- a/src/tests/selfservice/selfservice_get.spec.ts +++ b/src/tests/selfservice/selfservice_get.spec.ts @@ -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"); diff --git a/src/tests/selfservice/selfservice_register.spec.ts b/src/tests/selfservice/selfservice_register.spec.ts index 36dc7fa..724857a 100644 --- a/src/tests/selfservice/selfservice_register.spec.ts +++ b/src/tests/selfservice/selfservice_register.spec.ts @@ -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",