Alpha release 0.1.0 #9
							
								
								
									
										20
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								.drone.yml
									
									
									
									
									
								
							@@ -1,3 +1,23 @@
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
name: tests:node_latest
 | 
			
		||||
clone:
 | 
			
		||||
  disable: true
 | 
			
		||||
steps:
 | 
			
		||||
  - name: checkout pr
 | 
			
		||||
    image: alpine/git
 | 
			
		||||
    commands:
 | 
			
		||||
      - git clone $DRONE_REMOTE_URL .
 | 
			
		||||
      - git checkout $DRONE_SOURCE_BRANCH
 | 
			
		||||
  - name: run tests
 | 
			
		||||
    image: node:latest
 | 
			
		||||
    commands:
 | 
			
		||||
      - yarn
 | 
			
		||||
      - yarn test:ci
 | 
			
		||||
trigger:
 | 
			
		||||
  event:
 | 
			
		||||
    - pull_request
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
type: docker
 | 
			
		||||
 
 | 
			
		||||
@@ -2,4 +2,4 @@
 | 
			
		||||
 | 
			
		||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
 | 
			
		||||
 | 
			
		||||
#### 0.0.1
 | 
			
		||||
#### 0.1.0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										75
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								README.md
									
									
									
									
									
								
							@@ -1,3 +1,74 @@
 | 
			
		||||
# mailer
 | 
			
		||||
# @lfk/mailer
 | 
			
		||||
[](https://ci.odit.services/lfk/mailer)
 | 
			
		||||
 | 
			
		||||
Handles mail generation and sending (pw reset, welcome mail, etc)
 | 
			
		||||
Handles mail generation and sending (pw reset, welcome mail, etc)
 | 
			
		||||
 | 
			
		||||
## Dev Setup 🛠
 | 
			
		||||
> Local dev setup
 | 
			
		||||
 | 
			
		||||
1. Rename the .env.example file to .env (you can adjust app port and other settings, if needed) or generate a example env with `yarn && yarn test:generate_env`.
 | 
			
		||||
2. Install Dependencies
 | 
			
		||||
   ```bash
 | 
			
		||||
   yarn
 | 
			
		||||
   ```
 | 
			
		||||
3. Start the server
 | 
			
		||||
   ```bash
 | 
			
		||||
   yarn dev
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
## Templates
 | 
			
		||||
> The mailer uses html and plaintext templates to generate various mails.
 | 
			
		||||
> The templates are stored in src/templates by default.
 | 
			
		||||
 | 
			
		||||
We provide a set of default templates that we use for the ["Lauf für Kaya!" charity run](https://lauf-fuer-kaya.de).
 | 
			
		||||
We use handlebars for templateing utilizing i18next for translation - the i18n string format in the templates is : `{{__ "string"}}`
 | 
			
		||||
You can provide your own templates by replacing the ones we provided before compiling the project or by simply mounting your custom templates into the docker container.
 | 
			
		||||
 | 
			
		||||
The server currently needs the following templates to work:
 | 
			
		||||
* pw-reset.html
 | 
			
		||||
* pw-reset.txt
 | 
			
		||||
* test.html
 | 
			
		||||
* test.txt
 | 
			
		||||
* welcome_runner.html
 | 
			
		||||
* welcome_runner.txt
 | 
			
		||||
 | 
			
		||||
| Name | Type | Default | Description
 | 
			
		||||
| - | - | - | -
 | 
			
		||||
| APP_PORT | Number | 4010 | The port the backend server listens on. Is optional.
 | 
			
		||||
| NODE_ENV | String | dev | The apps env - influences debug info.
 | 
			
		||||
| API_KEY | String(min length: 64) | Random generated string | The api key you want to use for auth (query-param `key`), has to be at least 64 chars long.
 | 
			
		||||
| API_URL | String(url) | "http://localhost:8080" | The URL ponting to the base (root) of the lfk runner system.
 | 
			
		||||
| MAIL_SERVER | String(FQDN) | None | The mailserver (smtp) used to send mails via nodemailer.
 | 
			
		||||
| MAIL_PORT | Number | 25 | The mailserver's port (smtp).
 | 
			
		||||
| MAIL_USER | String | None | The username used to authenticate against the mailserver.
 | 
			
		||||
| MAIL_PASSWORD | String | None | The password used to authenticate against the mailserver.
 | 
			
		||||
| MAIL_FROM | String | None | The mail address that mails get sent from.
 | 
			
		||||
| PRIVACY_URL | String | "/privacy" | The url path that get's attached to the app url to link to the privacy page.
 | 
			
		||||
| IMPRINT_URL | String | "/imprint" | The url path that get's attached to the app url to link to the imprint page.
 | 
			
		||||
| COPYRIGHT_OWNER  | String | "LfK!" | Text that gets inserted as the "copyright by" owner in the mails.
 | 
			
		||||
| EVENT_NAME | String | "Testing 4 Kaya" | The event's name - used to generate the mail text.
 | 
			
		||||
| CONTACT_MAIL  | String(email) | MAIL_FROM | Contact mail address listed at the bottom of some mail templates.
 | 
			
		||||
 | 
			
		||||
## Recommended Editor
 | 
			
		||||
 | 
			
		||||
[Visual Studio Code](https://code.visualstudio.com/)
 | 
			
		||||
 | 
			
		||||
### Recommended Extensions
 | 
			
		||||
 | 
			
		||||
* will be automatically recommended via ./vscode/extensions.json
 | 
			
		||||
* we also provide a config for i18n-ally in the .vscode folder
 | 
			
		||||
 | 
			
		||||
## Staging
 | 
			
		||||
### Branches & Tags
 | 
			
		||||
* vX.Y.Z: Release tags created from the main branch
 | 
			
		||||
   * The version numbers follow the semver standard
 | 
			
		||||
   * 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
 | 
			
		||||
   * 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`
 | 
			
		||||
							
								
								
									
										4
									
								
								jest.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								jest.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
  preset: 'ts-jest',
 | 
			
		||||
  testEnvironment: 'node',
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										114
									
								
								licenses.md
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								licenses.md
									
									
									
									
									
								
							@@ -428,6 +428,35 @@ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
    SOFTWARE
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# @types/jest
 | 
			
		||||
**Author**: undefined
 | 
			
		||||
**Repo**: https://github.com/DefinitelyTyped/DefinitelyTyped.git
 | 
			
		||||
**License**: MIT
 | 
			
		||||
**Description**: TypeScript definitions for Jest
 | 
			
		||||
## License Text
 | 
			
		||||
    MIT License
 | 
			
		||||
 | 
			
		||||
    Copyright (c) Microsoft Corporation.
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# @types/node
 | 
			
		||||
**Author**: undefined
 | 
			
		||||
**Repo**: https://github.com/DefinitelyTyped/DefinitelyTyped.git
 | 
			
		||||
@@ -486,6 +515,33 @@ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
    SOFTWARE
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# axios
 | 
			
		||||
**Author**: Matt Zabriskie
 | 
			
		||||
**Repo**: https://github.com/axios/axios.git
 | 
			
		||||
**License**: MIT
 | 
			
		||||
**Description**: Promise based HTTP client for the browser and node.js
 | 
			
		||||
## License Text
 | 
			
		||||
Copyright (c) 2014-present Matt Zabriskie
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# cp-cli
 | 
			
		||||
**Author**: undefined
 | 
			
		||||
**Repo**: git+https://github.com/screendriver/cp-cli.git
 | 
			
		||||
@@ -515,6 +571,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# jest
 | 
			
		||||
**Author**: undefined
 | 
			
		||||
**Repo**: https://github.com/facebook/jest
 | 
			
		||||
**License**: MIT
 | 
			
		||||
**Description**: Delightful JavaScript Testing.
 | 
			
		||||
## License Text
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) Facebook, Inc. and its affiliates.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# nodemon
 | 
			
		||||
**Author**: [object Object]
 | 
			
		||||
**Repo**: https://github.com/remy/nodemon.git
 | 
			
		||||
@@ -604,6 +689,35 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
## License Text
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# ts-jest
 | 
			
		||||
**Author**: Kulshekhar Kabra <kulshekhar@users.noreply.github.com> (https://github.com/kulshekhar)
 | 
			
		||||
**Repo**: git+https://github.com/kulshekhar/ts-jest.git
 | 
			
		||||
**License**: MIT
 | 
			
		||||
**Description**: A preprocessor with source maps support to help use TypeScript with Jest
 | 
			
		||||
## License Text
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2016-2018
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
# ts-node
 | 
			
		||||
**Author**: [object Object]
 | 
			
		||||
**Repo**: git://github.com/TypeStrong/ts-node.git
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										176
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								package.json
									
									
									
									
									
								
							@@ -1,84 +1,92 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@odit/lfk-mailer",
 | 
			
		||||
  "version": "0.0.1",
 | 
			
		||||
  "description": "The document mailer for the LfK! runner system. This generates and sends mails (password reset, welcome, ...)",
 | 
			
		||||
  "main": "src/app.ts",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "nodemon src/app.ts",
 | 
			
		||||
    "build": "rimraf ./dist && tsc && cp-cli ./src/templates ./dist/templates && cp-cli ./src/locales ./dist/locales",
 | 
			
		||||
    "licenses:export": "license-exporter --markdown",
 | 
			
		||||
    "release": "release-it --only-version",
 | 
			
		||||
    "translations:sort": "node ./scripts/sort_translations.js",
 | 
			
		||||
    "test:generate_env": "ts-node ./scripts/create_testenv.ts"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git@git.odit.services:lfk/mailer.git"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "odit",
 | 
			
		||||
    "lfk",
 | 
			
		||||
    "mail",
 | 
			
		||||
    "node"
 | 
			
		||||
  ],
 | 
			
		||||
  "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",
 | 
			
		||||
    "class-transformer": "0.3.1",
 | 
			
		||||
    "class-validator": "^0.13.1",
 | 
			
		||||
    "consola": "^2.15.3",
 | 
			
		||||
    "cors": "^2.8.5",
 | 
			
		||||
    "dotenv": "^8.2.0",
 | 
			
		||||
    "express": "^4.17.1",
 | 
			
		||||
    "handlebars": "^4.7.6",
 | 
			
		||||
    "i18next": "^19.8.7",
 | 
			
		||||
    "i18next-fs-backend": "^1.0.8",
 | 
			
		||||
    "nodemailer": "^6.5.0",
 | 
			
		||||
    "reflect-metadata": "^0.1.13",
 | 
			
		||||
    "routing-controllers": "0.9.0-alpha.6",
 | 
			
		||||
    "routing-controllers-openapi": "2.2.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@odit/license-exporter": "^0.0.10",
 | 
			
		||||
    "@types/express": "^4.17.11",
 | 
			
		||||
    "@types/node": "^14.14.22",
 | 
			
		||||
    "@types/nodemailer": "^6.4.0",
 | 
			
		||||
    "cp-cli": "^2.0.0",
 | 
			
		||||
    "nodemon": "^2.0.7",
 | 
			
		||||
    "release-it": "^14.2.2",
 | 
			
		||||
    "rimraf": "^3.0.2",
 | 
			
		||||
    "start-server-and-test": "^1.12.0",
 | 
			
		||||
    "ts-node": "^9.1.1",
 | 
			
		||||
    "typescript": "^4.1.3"
 | 
			
		||||
  },
 | 
			
		||||
  "release-it": {
 | 
			
		||||
    "git": {
 | 
			
		||||
      "commit": true,
 | 
			
		||||
      "requireCleanWorkingDir": false,
 | 
			
		||||
      "commitMessage": "🚀Bumped version to v${version}",
 | 
			
		||||
      "requireBranch": "dev",
 | 
			
		||||
      "push": false,
 | 
			
		||||
      "tag": false
 | 
			
		||||
    },
 | 
			
		||||
    "npm": {
 | 
			
		||||
      "publish": false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
{
 | 
			
		||||
  "name": "@odit/lfk-mailer",
 | 
			
		||||
  "version": "0.1.0",
 | 
			
		||||
  "description": "The document mailer for the LfK! runner system. This generates and sends mails (password reset, welcome, ...)",
 | 
			
		||||
  "main": "src/app.ts",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "nodemon src/app.ts",
 | 
			
		||||
    "build": "rimraf ./dist && tsc && cp-cli ./src/templates ./dist/templates && cp-cli ./src/locales ./dist/locales",
 | 
			
		||||
    "licenses:export": "license-exporter --markdown",
 | 
			
		||||
    "release": "release-it --only-version",
 | 
			
		||||
    "translations:sort": "node ./scripts/sort_translations.js",
 | 
			
		||||
    "test": "jest",
 | 
			
		||||
    "test:watch": "jest --watchAll",
 | 
			
		||||
    "test:generate_env": "ts-node ./scripts/create_testenv.ts",
 | 
			
		||||
    "test:ci": "npm run test:generate_env && npm run test:ci:run",
 | 
			
		||||
    "test:ci:run": "start-server-and-test dev http://localhost:4010/docs/openapi.json test"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "git@git.odit.services:lfk/mailer.git"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "odit",
 | 
			
		||||
    "lfk",
 | 
			
		||||
    "mail",
 | 
			
		||||
    "node"
 | 
			
		||||
  ],
 | 
			
		||||
  "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",
 | 
			
		||||
    "class-transformer": "0.3.1",
 | 
			
		||||
    "class-validator": "^0.13.1",
 | 
			
		||||
    "consola": "^2.15.3",
 | 
			
		||||
    "cors": "^2.8.5",
 | 
			
		||||
    "dotenv": "^8.2.0",
 | 
			
		||||
    "express": "^4.17.1",
 | 
			
		||||
    "handlebars": "^4.7.6",
 | 
			
		||||
    "i18next": "^19.8.7",
 | 
			
		||||
    "i18next-fs-backend": "^1.0.8",
 | 
			
		||||
    "nodemailer": "^6.5.0",
 | 
			
		||||
    "reflect-metadata": "^0.1.13",
 | 
			
		||||
    "routing-controllers": "0.9.0-alpha.6",
 | 
			
		||||
    "routing-controllers-openapi": "2.2.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@odit/license-exporter": "^0.0.10",
 | 
			
		||||
    "@types/express": "^4.17.11",
 | 
			
		||||
    "@types/jest": "^26.0.20",
 | 
			
		||||
    "@types/node": "^14.14.22",
 | 
			
		||||
    "@types/nodemailer": "^6.4.0",
 | 
			
		||||
    "axios": "^0.21.1",
 | 
			
		||||
    "cp-cli": "^2.0.0",
 | 
			
		||||
    "jest": "^26.6.3",
 | 
			
		||||
    "nodemon": "^2.0.7",
 | 
			
		||||
    "release-it": "^14.2.2",
 | 
			
		||||
    "rimraf": "^3.0.2",
 | 
			
		||||
    "start-server-and-test": "^1.12.0",
 | 
			
		||||
    "ts-jest": "^26.5.2",
 | 
			
		||||
    "ts-node": "^9.1.1",
 | 
			
		||||
    "typescript": "^4.1.3"
 | 
			
		||||
  },
 | 
			
		||||
  "release-it": {
 | 
			
		||||
    "git": {
 | 
			
		||||
      "commit": true,
 | 
			
		||||
      "requireCleanWorkingDir": false,
 | 
			
		||||
      "commitMessage": "🚀Bumped version to v${version}",
 | 
			
		||||
      "requireBranch": "dev",
 | 
			
		||||
      "push": false,
 | 
			
		||||
      "tag": false
 | 
			
		||||
    },
 | 
			
		||||
    "npm": {
 | 
			
		||||
      "publish": false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ import { MailServerConfigError } from './errors/MailErrors';
 | 
			
		||||
 */
 | 
			
		||||
export class Mailer {
 | 
			
		||||
    private transport: Mail;
 | 
			
		||||
    private static interpolations = { copyright_owner: config.copyright_owner }
 | 
			
		||||
    private static interpolations = { copyright_owner: config.copyright_owner, event_name: config.event_name, contact_mail: config.contact_mail }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Main constructor.
 | 
			
		||||
@@ -69,11 +69,18 @@ export class Mailer {
 | 
			
		||||
    public async sendResetMail(to_address: string, token: string, locale: string = "en") {
 | 
			
		||||
        await i18next.changeLanguage(locale);
 | 
			
		||||
 | 
			
		||||
        const reset_link = `${config.app_url}/reset/${(Buffer.from(token)).toString("base64")}`
 | 
			
		||||
        const replacements = {
 | 
			
		||||
            recipient_mail: to_address,
 | 
			
		||||
            copyright_owner: config.copyright_owner,
 | 
			
		||||
            link_imprint: `${config.app_url}/imprint`,
 | 
			
		||||
            link_privacy: `${config.app_url}/privacy`,
 | 
			
		||||
            reset_link: `${config.app_url}/reset/${(Buffer.from(token)).toString("base64")}`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const template_html = Handlebars.compile(fs.readFileSync(__dirname + '/templates/pw-reset.html', { encoding: 'utf8' }));
 | 
			
		||||
        const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/pw-reset.txt', { encoding: 'utf8' }));
 | 
			
		||||
        const body_html = template_html({ recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy`, reset_link });
 | 
			
		||||
        const body_txt = template_txt({ recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy`, reset_link });
 | 
			
		||||
        const body_html = template_html(replacements);
 | 
			
		||||
        const body_txt = template_txt(replacements);
 | 
			
		||||
 | 
			
		||||
        const mail: MailOptions = {
 | 
			
		||||
            to: to_address,
 | 
			
		||||
@@ -91,13 +98,18 @@ export class Mailer {
 | 
			
		||||
    public async sendTestMail(locale: string = "en") {
 | 
			
		||||
        await i18next.changeLanguage(locale);
 | 
			
		||||
        const to_address: string = config.mail_from;
 | 
			
		||||
        const replacements = {
 | 
			
		||||
            recipient_mail: to_address,
 | 
			
		||||
            copyright_owner: config.copyright_owner,
 | 
			
		||||
            link_imprint: `${config.app_url}/imprint`,
 | 
			
		||||
            link_privacy: `${config.app_url}/privacy`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const template_html = Handlebars.compile(fs.readFileSync(__dirname + '/templates/test.html', { encoding: 'utf8' }));
 | 
			
		||||
        const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/test.txt', { encoding: 'utf8' }));
 | 
			
		||||
        const body_html = template_html({ recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy` });
 | 
			
		||||
        const body_txt = template_txt({ recipient_mail: to_address, copyright_owner: config.copyright_owner, link_imprint: `${config.app_url}/imprint`, link_privacy: `${config.app_url}/privacy` });
 | 
			
		||||
        const body_html = template_html(replacements);
 | 
			
		||||
        const body_txt = template_txt(replacements);
 | 
			
		||||
 | 
			
		||||
        fs.writeFileSync("./test.tmp", body_txt);
 | 
			
		||||
        const mail: MailOptions = {
 | 
			
		||||
            to: to_address,
 | 
			
		||||
            subject: i18next.t("test-mail", Mailer.interpolations).toString(),
 | 
			
		||||
@@ -107,6 +119,39 @@ export class Mailer {
 | 
			
		||||
        await this.sendMail(mail);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function for sending a reset mail from the reset mail template.
 | 
			
		||||
     * @param to_address The address the mail will be sent to. Should always get pulled from a user object.
 | 
			
		||||
     * @param token The requested password reset token - will be combined with the app_url to generate a password reset link.
 | 
			
		||||
     */
 | 
			
		||||
    public async sendWelcomeMail(to_address: string, token: string, locale: string = "en") {
 | 
			
		||||
        await i18next.changeLanguage(locale);
 | 
			
		||||
 | 
			
		||||
        const replacements = {
 | 
			
		||||
            recipient_mail: to_address,
 | 
			
		||||
            copyright_owner: config.copyright_owner,
 | 
			
		||||
            link_imprint: `${config.app_url}/imprint`,
 | 
			
		||||
            link_privacy: `${config.app_url}/privacy`,
 | 
			
		||||
            selfservice_link: `${config.app_url}/selfservice/profile/${token}`,
 | 
			
		||||
            forgot_link: `${config.app_url}/selfservice`,
 | 
			
		||||
            contact_mail: config.contact_mail,
 | 
			
		||||
            event_name: config.event_name
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const template_html = Handlebars.compile(fs.readFileSync(__dirname + '/templates/welcome_runner.html', { encoding: 'utf8' }));
 | 
			
		||||
        const template_txt = Handlebars.compile(fs.readFileSync(__dirname + '/templates/welcome_runner.txt', { encoding: 'utf8' }));
 | 
			
		||||
        const body_html = template_html(replacements);
 | 
			
		||||
        const body_txt = template_txt(replacements);
 | 
			
		||||
 | 
			
		||||
        const mail: MailOptions = {
 | 
			
		||||
            to: to_address,
 | 
			
		||||
            subject: i18next.t("event_name-registration", Mailer.interpolations).toString(),
 | 
			
		||||
            text: body_txt,
 | 
			
		||||
            html: body_html
 | 
			
		||||
        };
 | 
			
		||||
        await this.sendMail(mail);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Wrapper function for sending a mail via this object's transporter.
 | 
			
		||||
     * @param mail MailOptions object containing the 
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,9 @@ export const config = {
 | 
			
		||||
    mail_from: process.env.MAIL_FROM,
 | 
			
		||||
    privacy_url: process.env.PRIVACY_URL || "/privacy",
 | 
			
		||||
    imprint_url: process.env.IMPRINT_URL || "/imprint",
 | 
			
		||||
    copyright_owner: process.env.COPYRIGHT_OWNER || "LfK!"
 | 
			
		||||
    copyright_owner: process.env.COPYRIGHT_OWNER || "LfK!",
 | 
			
		||||
    event_name: process.env.EVENT_NAME || "Testing 4 Kaya!",
 | 
			
		||||
    contact_mail: process.env.CONTACT_MAIL || process.env.MAIL_FROM,
 | 
			
		||||
}
 | 
			
		||||
let errors = 0
 | 
			
		||||
if (typeof config.internal_port !== "number") {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import { Mailer } from '../Mailer';
 | 
			
		||||
import { locales } from '../models/LocaleEnum';
 | 
			
		||||
import { ResetMail } from '../models/ResetMail';
 | 
			
		||||
import { SuccessResponse } from '../models/SuccessResponse';
 | 
			
		||||
import { WelcomeMail } from '../models/WelcomeMail';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The mail controller handels all endpoints concerning Mail sending.
 | 
			
		||||
@@ -45,4 +46,20 @@ export class MailController {
 | 
			
		||||
        }
 | 
			
		||||
        return new SuccessResponse(locale);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Post('/registration')
 | 
			
		||||
    @OpenAPI({ description: "Sends registration welcome mails", parameters: [{ in: "query", name: "locale", schema: { type: "string", enum: ["de", "en"] } }] })
 | 
			
		||||
    async sendRegistrationWelcome(@Body({ validate: true }) mailOptions: WelcomeMail, @QueryParam("locale") locale: locales) {
 | 
			
		||||
        if (!this.initialized) {
 | 
			
		||||
            await this.mailer.init();
 | 
			
		||||
            this.initialized = true;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            this.mailer.sendWelcomeMail(mailOptions.address, mailOptions.selfserviceToken, locale?.toString())
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.log(error)
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
        return new SuccessResponse(locale);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
    "a-password-reset-for-your-account-got-requested": "Ein Passwort Reset wurde für dein Konto beantragt.",
 | 
			
		||||
    "all-rights-reserved": "Alle Rechte vorbehalten",
 | 
			
		||||
    "event_name-registration": "{{event_name}} Registrierung",
 | 
			
		||||
    "if-you-didnt-register-yourself-you-should-contact-us-to-get-your-data-removed-from-our-systems": "Solltest du dich nicht selbst registriert haben schick uns bitte eine Mail und wir entfernen deine Daten aus unserem System: ",
 | 
			
		||||
    "if-you-didnt-request-the-reset-please-ignore-this-mail": "Solltest du den Reset nicht beantragt haben kannst du diese Mail einfach ignorieren.",
 | 
			
		||||
    "if-you-ever-loose-the-link-you-can-request-a-new-one-by-visiting-our-website": "Solltest du den Link verlieren kannst du auf unserer Website einen neuen beantragen:",
 | 
			
		||||
    "imprint": "Impressum",
 | 
			
		||||
    "lfk-mail-test": "{{copyright_owner}} - Mail test",
 | 
			
		||||
    "lfk-password-reset": "{{copyright_owner}} - Passwort zurücksetzen",
 | 
			
		||||
@@ -9,8 +12,15 @@
 | 
			
		||||
    "privacy": "Datenschutz",
 | 
			
		||||
    "reset-password": "Passwort zurücksetzen",
 | 
			
		||||
    "test-mail": "Test mail",
 | 
			
		||||
    "this-is-a-test-mail-triggered-by-an-admin-in-the-lfk-backend": "Das ist eine Testmail, die von einem Admin im LfK! backend erzeugt wurde.",
 | 
			
		||||
    "thanks-for-registering-and-welcome-to-the-event_name": "Vielen Dank für die Registrierung und willkommen beim {{event_name}}!",
 | 
			
		||||
    "the-only-thing-you-have-to-do-now-is-to-bring-your-registration-code-with-you": "Du must nichts weiter machen, außer deinen Registrierungscode zum Lauf mitzubringen.",
 | 
			
		||||
    "this-is-a-test-mail-triggered-by-an-admin-in-the-lfk-backend": "Das ist eine Testmail, die von einem Admin im LfK! Backend erzeugt wurde.",
 | 
			
		||||
    "this-mail-was-sent-to-recipient_mail-because-someone-request-a-mail-test-for-this-mail-address": "Du bekommst diese Mail, weil jemand eine Testmail für deine Mail-Adresse angefragt hat.",
 | 
			
		||||
    "this-mail-was-sent-to-you-because-someone-request-a-password-reset-for-a-account-linked-to-the-mail-address": "Du bekommst diese E-Mail, weil jemand einen Passwort-Reset für deinen Account beantragt hat.",
 | 
			
		||||
    "this-mail-was-sent-to-you-because-someone-used-your-mail-address-to-register-themselfes-for-the-event_name": "Du bekommst diese Mail, weil jemand deine E-Mail-Adresse verwendet hat, um sich beim {{event_name}} zu registrieren.",
 | 
			
		||||
    "view-my-data": "Meine Daten",
 | 
			
		||||
    "we-successfully-processed-your-registration": "Wir haben deine Registrierung erfolgreich verarbeitet.",
 | 
			
		||||
    "welcome": "Willkommen",
 | 
			
		||||
    "you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice": "Du kannst deinen Registrierungscode, deine Rundenzeiten unv vieles mehr im Selfservice einsehen:",
 | 
			
		||||
    "your-password-wont-be-changed-until-you-click-the-reset-link-below-and-set-a-new-one": "Dein Passwort wird erst zurückgesetzt, wenn du den Reset-Link öffnest und ein neues Passwort setzt."
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
    "a-password-reset-for-your-account-got-requested": "A password reset for your account got requested.",
 | 
			
		||||
    "all-rights-reserved": "All rights reserved.",
 | 
			
		||||
    "event_name-registration": "{{event_name}} Registration",
 | 
			
		||||
    "if-you-didnt-register-yourself-you-should-contact-us-to-get-your-data-removed-from-our-systems": "If you didn't register yourself you should contact us to get your data removed from our systems:",
 | 
			
		||||
    "if-you-didnt-request-the-reset-please-ignore-this-mail": "If you didn't request the reset please ignore this mail.",
 | 
			
		||||
    "if-you-ever-loose-the-link-you-can-request-a-new-one-by-visiting-our-website": "If you ever loose the link you can request a new one by visiting our website:",
 | 
			
		||||
    "imprint": "Imprint",
 | 
			
		||||
    "lfk-mail-test": "{{copyright_owner}} - Mail test",
 | 
			
		||||
    "lfk-password-reset": "{{copyright_owner}} - Password reset",
 | 
			
		||||
@@ -9,8 +12,15 @@
 | 
			
		||||
    "privacy": "Privacy",
 | 
			
		||||
    "reset-password": "Reset password",
 | 
			
		||||
    "test-mail": "Test mail",
 | 
			
		||||
    "thanks-for-registering-and-welcome-to-the-event_name": "Thanks for registering and welcome to the {{event_name}}!",
 | 
			
		||||
    "the-only-thing-you-have-to-do-now-is-to-bring-your-registration-code-with-you": "The only thing you have to do now is to bring your registration code with you.",
 | 
			
		||||
    "this-is-a-test-mail-triggered-by-an-admin-in-the-lfk-backend": "This is a test mail triggered by an admin in the LfK! backend.",
 | 
			
		||||
    "this-mail-was-sent-to-recipient_mail-because-someone-request-a-mail-test-for-this-mail-address": "This mail was sent to you because someone request a mail test for this mail address.",
 | 
			
		||||
    "this-mail-was-sent-to-you-because-someone-request-a-password-reset-for-a-account-linked-to-the-mail-address": "This mail was sent to you because someone request a password reset for a account linked to the mail address.",
 | 
			
		||||
    "this-mail-was-sent-to-you-because-someone-used-your-mail-address-to-register-themselfes-for-the-event_name": "This mail was sent to you, because someone used your mail address to register themselfes for the {{event_name}}",
 | 
			
		||||
    "view-my-data": "View my data",
 | 
			
		||||
    "we-successfully-processed-your-registration": "We successfully processed your registration.",
 | 
			
		||||
    "welcome": "Welcome",
 | 
			
		||||
    "you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice": "You can view your registration code, lap times and much more by visiting our selfservice:",
 | 
			
		||||
    "your-password-wont-be-changed-until-you-click-the-reset-link-below-and-set-a-new-one": "Your password won't be changed until you click the reset link below and set a new one."
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/models/WelcomeMail.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/models/WelcomeMail.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Simple welcome mail request class for validation and easier handling.
 | 
			
		||||
 */
 | 
			
		||||
export class WelcomeMail {
 | 
			
		||||
 | 
			
		||||
    @IsString()
 | 
			
		||||
    @IsEmail()
 | 
			
		||||
    @IsNotEmpty()
 | 
			
		||||
    address: string;
 | 
			
		||||
 | 
			
		||||
    @IsString()
 | 
			
		||||
    @IsNotEmpty()
 | 
			
		||||
    selfserviceToken: string;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										397
									
								
								src/templates/welcome_runner.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								src/templates/welcome_runner.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,397 @@
 | 
			
		||||
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:v="urn:schemas-microsoft-com:vml">
 | 
			
		||||
<head>
 | 
			
		||||
    <title>{{__ "event_name-registration"}}</title> <!-- The title tag shows in email notifications, like Android 4.4. -->
 | 
			
		||||
    <meta charset="utf-8"> <!-- utf-8 works for most cases -->
 | 
			
		||||
    <meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
 | 
			
		||||
    <meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
 | 
			
		||||
    <meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no"> <!-- Tell iOS not to automatically link certain text strings. -->
 | 
			
		||||
 | 
			
		||||
    <!-- CSS Reset : BEGIN -->
 | 
			
		||||
    <style>
 | 
			
		||||
        /* What it does: Remove spaces around the email design added by some email clients. */
 | 
			
		||||
        /* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
 | 
			
		||||
        html,
 | 
			
		||||
        body {
 | 
			
		||||
            margin: 0 auto !important;
 | 
			
		||||
            padding: 0 !important;
 | 
			
		||||
            height: 100% !important;
 | 
			
		||||
            width: 100% !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Stops email clients resizing small text. */
 | 
			
		||||
        * {
 | 
			
		||||
            -ms-text-size-adjust: 100%;
 | 
			
		||||
            -webkit-text-size-adjust: 100%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Centers email on Android 4.4 */
 | 
			
		||||
        div[style*="margin: 16px 0"] {
 | 
			
		||||
            margin:0 !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Stops Outlook from adding extra spacing to tables. */
 | 
			
		||||
        table,
 | 
			
		||||
        td {
 | 
			
		||||
            mso-table-lspace: 0pt !important;
 | 
			
		||||
            mso-table-rspace: 0pt !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Fixes webkit padding issue. */
 | 
			
		||||
        table {
 | 
			
		||||
            border: 0;
 | 
			
		||||
            border-spacing: 0;
 | 
			
		||||
            border-collapse: collapse
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Forces Samsung Android mail clients to use the entire viewport. */
 | 
			
		||||
        #MessageViewBody,
 | 
			
		||||
        #MessageWebViewDiv{
 | 
			
		||||
            width: 100% !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Uses a better rendering method when resizing images in IE. */
 | 
			
		||||
        img {
 | 
			
		||||
            -ms-interpolation-mode:bicubic;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Prevents Windows 10 Mail from underlining links despite inline CSS. Styles for underlined links should be inline. */
 | 
			
		||||
        a {
 | 
			
		||||
            text-decoration: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: A work-around for email clients automatically linking certain text strings. */
 | 
			
		||||
        /* iOS */
 | 
			
		||||
        a[x-apple-data-detectors],
 | 
			
		||||
        .unstyle-auto-detected-links a,
 | 
			
		||||
        .aBn {
 | 
			
		||||
            border-bottom: 0 !important;
 | 
			
		||||
            cursor: default !important;
 | 
			
		||||
            color: inherit !important;
 | 
			
		||||
            text-decoration: none !important;
 | 
			
		||||
            font-size: inherit !important;
 | 
			
		||||
            font-family: inherit !important;
 | 
			
		||||
            font-weight: inherit !important;
 | 
			
		||||
            line-height: inherit !important;
 | 
			
		||||
        }
 | 
			
		||||
        u + #body a,        /* Gmail */
 | 
			
		||||
        #MessageViewBody a  /* Samsung Mail */
 | 
			
		||||
        {
 | 
			
		||||
           color: inherit;
 | 
			
		||||
           text-decoration: none;
 | 
			
		||||
           font-size: inherit;
 | 
			
		||||
           font-family: inherit;
 | 
			
		||||
           font-weight: inherit;
 | 
			
		||||
           line-height: inherit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Prevents Gmail from changing the text color in conversation threads. */
 | 
			
		||||
        .im {
 | 
			
		||||
            color: inherit !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Prevents Gmail from displaying an download button on large, non-linked images. */
 | 
			
		||||
        .a6S {
 | 
			
		||||
            display: none !important;
 | 
			
		||||
            opacity: 0.01 !important;
 | 
			
		||||
        }
 | 
			
		||||
        /* If the above doesn't work, add a .g-img class to any image in question. */
 | 
			
		||||
        img.g-img + div {
 | 
			
		||||
            display:none !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89  */
 | 
			
		||||
        /* Create one of these media queries for each additional viewport size you'd like to fix */
 | 
			
		||||
 | 
			
		||||
        /* iPhone 4, 4S, 5, 5S, 5C, and 5SE */
 | 
			
		||||
        @media only screen and (min-device-width: 320px) and (max-device-width: 374px) {
 | 
			
		||||
            u ~ div .email-container {
 | 
			
		||||
                min-width: 320px !important;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* iPhone 6, 6S, 7, 8, and X */
 | 
			
		||||
        @media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
 | 
			
		||||
            u ~ div .email-container {
 | 
			
		||||
                min-width: 375px !important;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* iPhone 6+, 7+, and 8+ */
 | 
			
		||||
        @media only screen and (min-device-width: 414px) {
 | 
			
		||||
            u ~ div .email-container {
 | 
			
		||||
                min-width: 414px !important;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
    <!-- What it does: Helps DPI scaling in Outlook 2007-2013 -->
 | 
			
		||||
    <!--[if gte mso 9]>
 | 
			
		||||
    <xml>
 | 
			
		||||
        <o:OfficeDocumentSettings>
 | 
			
		||||
            <o:AllowPNG/>
 | 
			
		||||
            <o:PixelsPerInch>96</o:PixelsPerInch>
 | 
			
		||||
        </o:OfficeDocumentSettings>
 | 
			
		||||
        </xml>
 | 
			
		||||
    <![endif]-->
 | 
			
		||||
 | 
			
		||||
    <!-- CSS Reset : END -->
 | 
			
		||||
 | 
			
		||||
    <!-- Progressive Enhancements : BEGIN -->
 | 
			
		||||
    <style>
 | 
			
		||||
        /* What it does: Hover styles for buttons and tags */
 | 
			
		||||
        .s-btn__primary:hover {
 | 
			
		||||
            background: #0077CC !important;
 | 
			
		||||
            border-color: #0077CC !important;
 | 
			
		||||
        }
 | 
			
		||||
        .s-btn__white:hover {
 | 
			
		||||
            background: #EFF0F1 !important;
 | 
			
		||||
            border-color: #EFF0F1 !important;
 | 
			
		||||
        }
 | 
			
		||||
        .s-btn__outlined:hover {
 | 
			
		||||
            background: rgba(0,119,204,.05) !important;
 | 
			
		||||
            color: #005999 !important;
 | 
			
		||||
        }
 | 
			
		||||
        .s-tag:hover,
 | 
			
		||||
        .post-tag:hover {
 | 
			
		||||
            border-color: #cee0ed !important;
 | 
			
		||||
            background: #cee0ed !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Styles markdown links that we can't write inline CSS for. */
 | 
			
		||||
        .has-markdown a,
 | 
			
		||||
        .has-markdown a:visited {
 | 
			
		||||
            color: #0077CC !important;
 | 
			
		||||
            text-decoration: none !important;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Styles markdown code blocks that we can't write inline CSS for. */
 | 
			
		||||
        code {
 | 
			
		||||
            padding: 1px 5px;
 | 
			
		||||
            background-color: #EFF0F1;
 | 
			
		||||
            color: #242729;
 | 
			
		||||
            font-size: 13px;
 | 
			
		||||
            line-height: inherit;
 | 
			
		||||
            font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, sans-serif;
 | 
			
		||||
        }
 | 
			
		||||
        pre {
 | 
			
		||||
            margin: 0 0 15px;
 | 
			
		||||
            line-height: 17px;
 | 
			
		||||
            background-color: #EFF0F1;
 | 
			
		||||
            padding: 4px 8px;
 | 
			
		||||
            border-radius: 3px;
 | 
			
		||||
            overflow-x: auto;
 | 
			
		||||
        }
 | 
			
		||||
        pre code {
 | 
			
		||||
            margin: 0 0 15px;
 | 
			
		||||
            padding: 0;
 | 
			
		||||
            line-height: 17px;
 | 
			
		||||
            background-color: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Styles markdown blockquotes that we can't write inline CSS for. */
 | 
			
		||||
        blockquote {
 | 
			
		||||
            margin: 0 0 15px;
 | 
			
		||||
            padding: 4px 10px;
 | 
			
		||||
            background-color: #FFF8DC;
 | 
			
		||||
            border-left: 2px solid #ffeb8e;
 | 
			
		||||
        }
 | 
			
		||||
        blockquote p {
 | 
			
		||||
            padding: 4px 0;
 | 
			
		||||
            margin: 0;
 | 
			
		||||
            overflow-wrap: break-word;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* What it does: Rounds corners in email clients that support it */
 | 
			
		||||
        .bar {
 | 
			
		||||
            border-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
        .btr {
 | 
			
		||||
            border-top-left-radius: 5px;
 | 
			
		||||
            border-top-right-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
        .bbr {
 | 
			
		||||
            border-bottom-left-radius: 5px;
 | 
			
		||||
            border-bottom-right-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @media screen and (max-width: 680px) {
 | 
			
		||||
            /* What it does: Forces table cells into full-width rows. */
 | 
			
		||||
            .stack-column,
 | 
			
		||||
            .stack-column-center {
 | 
			
		||||
                display: block !important;
 | 
			
		||||
                width: 100% !important;
 | 
			
		||||
                max-width: 100% !important;
 | 
			
		||||
                direction: ltr !important;
 | 
			
		||||
            }
 | 
			
		||||
            /* And center justify these ones. */
 | 
			
		||||
            .stack-column-center {
 | 
			
		||||
                text-align: center !important;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Hides things in small viewports. */
 | 
			
		||||
            .hide-on-mobile {
 | 
			
		||||
                display: none !important;
 | 
			
		||||
                max-height: 0 !important;
 | 
			
		||||
                overflow: hidden !important;
 | 
			
		||||
                visibility: hidden !important;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* What it does: Utility classes to reduce spacing for smaller viewports. */
 | 
			
		||||
            .sm-p-none  {padding: 0 !important;}
 | 
			
		||||
            .sm-pt-none {padding-top: 0 !important;}
 | 
			
		||||
            .sm-pb-none {padding-bottom: 0 !important;}
 | 
			
		||||
            .sm-pr-none {padding-right: 0 !important;}
 | 
			
		||||
            .sm-pl-none {padding-left: 0 !important;}
 | 
			
		||||
            .sm-px-none {padding-left: 0 !important; padding-right: 0 !important;}
 | 
			
		||||
            .sm-py-none {padding-top: 0 !important; padding-bottom: 0 !important;}
 | 
			
		||||
 | 
			
		||||
            .sm-p   {padding: 20px !important;}
 | 
			
		||||
            .sm-pt  {padding-top: 20px !important;}
 | 
			
		||||
            .sm-pb  {padding-bottom: 20px !important;}
 | 
			
		||||
            .sm-pr  {padding-right: 20px !important;}
 | 
			
		||||
            .sm-pl  {padding-left: 20px !important;}
 | 
			
		||||
            .sm-px  {padding-left: 20px !important; padding-right: 20px !important;}
 | 
			
		||||
            .sm-py  {padding-top: 20px !important; padding-bottom: 20px !important;}
 | 
			
		||||
            .sm-mb  {margin-bottom: 20px !important;}
 | 
			
		||||
 | 
			
		||||
            /* What it does: Utility classes to kill border radius for smaller viewports. Used mainly on the email's main container(s). */
 | 
			
		||||
            .bar,
 | 
			
		||||
            .btr,
 | 
			
		||||
            .bbr {
 | 
			
		||||
                border-top-left-radius: 0;
 | 
			
		||||
                border-top-right-radius: 0;
 | 
			
		||||
                border-bottom-left-radius: 0;
 | 
			
		||||
                border-bottom-right-radius: 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
    <!-- Progressive Enhancements : END -->
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
    The email background color is defined in three places, just below. If you change one, remember to change the others.
 | 
			
		||||
    1. body tag: for most email clients
 | 
			
		||||
    2. center tag: for Gmail and Inbox mobile apps and web versions of Gmail, GSuite, Inbox, Yahoo, AOL, Libero, Comcast, freenet, Mail.ru, Orange.fr
 | 
			
		||||
    3. mso conditional: For Windows 10 Mail
 | 
			
		||||
-->
 | 
			
		||||
<body width="100%" style="margin: 0; padding: 0 !important; background: #f3f3f5; mso-line-height-rule: exactly;">
 | 
			
		||||
    <center style="width: 100%; background: #f3f3f5;">
 | 
			
		||||
    <!--[if mso | IE]>
 | 
			
		||||
    <table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f3f5;">
 | 
			
		||||
    <tr>
 | 
			
		||||
    <td>
 | 
			
		||||
    <![endif]-->
 | 
			
		||||
 | 
			
		||||
        <!-- Visually Hidden Preview Text : BEGIN -->
 | 
			
		||||
        <div style="display: none; font-size: 1px; line-height: 1px; max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
 | 
			
		||||
            {{__ "event_name-registration"}}
 | 
			
		||||
        </div>
 | 
			
		||||
        <!-- Visually Hidden Preview Text : END -->
 | 
			
		||||
 | 
			
		||||
        <div class="email-container" style="max-width: 680px; margin: 0 auto;">
 | 
			
		||||
            <!--[if mso]>
 | 
			
		||||
            <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
 | 
			
		||||
            <tr>
 | 
			
		||||
            <td>
 | 
			
		||||
            <![endif]-->
 | 
			
		||||
            <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="max-width: 680px; width:100%">
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td style="padding: 30px; background-color: #ffffff;" class="sm-p bar">
 | 
			
		||||
                        <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
 | 
			
		||||
                                    <h1 style="font-weight: bold; font-size: 27px; line-height: 27px; color: #0C0D0E; margin: 0 0 15px 0;">{{event_name}}</h1>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
 | 
			
		||||
                                    <h1 style="font-weight: bold; font-size: 21px; line-height: 21px; color: #0C0D0E; margin: 0 0 15px 0;">{{__ "welcome"}}</h1>
 | 
			
		||||
                                    <p style="margin: 0 0 15px;" class="has-markdown">
 | 
			
		||||
                                        {{__ "thanks-for-registering-and-welcome-to-the-event_name"}} <br>
 | 
			
		||||
                                        {{__ "we-successfully-processed-your-registration"}}<br><br>
 | 
			
		||||
                                        {{__ "the-only-thing-you-have-to-do-now-is-to-bring-your-registration-code-with-you"}}<br>
 | 
			
		||||
                                        {{__ "you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice"}}
 | 
			
		||||
                                    </p>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <!-- Button Row : BEGIN -->
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td>
 | 
			
		||||
                                    <!-- Button : BEGIN -->
 | 
			
		||||
                                    <table align="left" border="0" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
                                        <tr>
 | 
			
		||||
                                            <td class="s-btn s-btn__primary" style="border-radius: 4px; background: #0095ff;">
 | 
			
		||||
                                                <a class="s-btn s-btn__primary" href="{{selfservice_link}}" target="_parent" style="background: #0095FF; border: 1px solid #0077cc; box-shadow: inset 0 1px 0 0 rgba(102,191,255,.75); font-family: arial, sans-serif; font-size: 17px; line-height: 17px; color: #ffffff; text-align: center; text-decoration: none; padding: 13px 17px; display: block; border-radius: 4px; white-space: nowrap;">{{__ "view-my-data"}}</a>
 | 
			
		||||
                                            </td>
 | 
			
		||||
                                        </tr>
 | 
			
		||||
                                    </table>
 | 
			
		||||
                                    <!-- Button : END -->
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="padding-bottom: 15px; font-family: arial, sans-serif; font-size: 15px; line-height: 21px; color: #3C3F44; text-align: left;">
 | 
			
		||||
                                    <p>Link: <a href="{{selfservice_link}}">{{selfservice_link}}</a><br>
 | 
			
		||||
                                    {{__ "if-you-ever-loose-the-link-you-can-request-a-new-one-by-visiting-our-website"}} <a href="{{forgot_link}}">{{forgot_link}}</a></p></td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <!-- Button Row : END -->
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
 | 
			
		||||
                <!-----------------------------
 | 
			
		||||
 | 
			
		||||
                    EMAIL BODY : END
 | 
			
		||||
 | 
			
		||||
                ------------------------------>
 | 
			
		||||
 | 
			
		||||
                <!-- Footer : BEGIN -->
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td style="padding: 30px;" class="sm-p">
 | 
			
		||||
                        <table align="left" border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
 | 
			
		||||
                            <!-- Subscription Info : BEGIN -->
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="padding-bottom: 10px; font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
 | 
			
		||||
                                    Copyright © {{copyright_owner}}. {{__ "all-rights-reserved"}}
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
 | 
			
		||||
                                    <a href="{{link_imprint}}"
 | 
			
		||||
                                    style="color: #9199A1; text-decoration: underline;">{{__ "imprint"}}</a>    
 | 
			
		||||
                                    <a href="{{link_privacy}}" style="color: #9199A1; text-decoration: underline;">{{__ "privacy"}}</a>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <!-- Subscription Info : BEGIN -->
 | 
			
		||||
                            <!-- HR line : BEGIN -->
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="padding: 30px 0;" width="100%" class="sm-py">
 | 
			
		||||
                                    <table aria-hidden="true" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%">
 | 
			
		||||
                                        <tr>
 | 
			
		||||
                                            <td height="1" width="100%" style="font-size: 0; line-height: 0; border-top: 1px solid #D6D8DB;"> </td>
 | 
			
		||||
                                        </tr>
 | 
			
		||||
                                    </table>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <!-- HR line : END -->
 | 
			
		||||
                            <tr>
 | 
			
		||||
                                <td style="padding-bottom: 5px; font-size: 12px; line-height: 15px; font-family: arial, sans-serif; color: #9199A1; text-align: left;">
 | 
			
		||||
                                    {{__ "this-mail-was-sent-to-you-because-someone-used-your-mail-address-to-register-themselfes-for-the-event_name"}}.<br>
 | 
			
		||||
                                    {{__ "if-you-didnt-register-yourself-you-should-contact-us-to-get-your-data-removed-from-our-systems"}} <a href="mailto:{{contact_mail}}">{{contact_mail}}</a>
 | 
			
		||||
                                </td>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                            <!-- Sender Info : END -->
 | 
			
		||||
                        </table>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <!-- Footer : END -->
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
    <!--[if mso | IE]>
 | 
			
		||||
    </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <![endif]-->
 | 
			
		||||
    </center>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										17
									
								
								src/templates/welcome_runner.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/templates/welcome_runner.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
{{__ "event_name-registration"}}
 | 
			
		||||
 | 
			
		||||
{{__ "thanks-for-registering-and-welcome-to-the-event_name"}}
 | 
			
		||||
{{__ "we-successfully-processed-your-registration"}}
 | 
			
		||||
 | 
			
		||||
{{__ "the-only-thing-you-have-to-do-now-is-to-bring-your-registration-code-with-you"}}
 | 
			
		||||
{{__ "you-can-view-your-registration-code-lap-times-and-much-more-by-visiting-our-selfservice"}}
 | 
			
		||||
 | 
			
		||||
{{selfservice_link}}
 | 
			
		||||
 | 
			
		||||
{{__ "if-you-ever-loose-the-link-you-can-request-a-new-one-by-visiting-our-website"}} {{forgot_link}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Copyright © {{copyright_owner}}. {{__ "all-rights-reserved"}}.
 | 
			
		||||
{{__ "imprint"}}: {{link_imprint}} | {{__ "privacy"}}: {{link_privacy}}
 | 
			
		||||
{{__ "this-mail-was-sent-to-you-because-someone-used-your-mail-address-to-register-themselfes-for-the-event_name"}}
 | 
			
		||||
{{__ "if-you-didnt-register-yourself-you-should-contact-us-to-get-your-data-removed-from-our-systems"}} {{contact_mail}} 
 | 
			
		||||
							
								
								
									
										34
									
								
								src/tests/api_docs.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/tests/api_docs.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import { config } from '../config';
 | 
			
		||||
const base = "http://localhost:" + config.internal_port
 | 
			
		||||
 | 
			
		||||
describe('GET /docs/openapi.json', () => {
 | 
			
		||||
	it('OpenAPI Spec is availdable 200', async () => {
 | 
			
		||||
		const res = await axios.get(base + '/docs/openapi.json');
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
describe('GET /docs/swagger.json', () => {
 | 
			
		||||
	it('OpenAPI Spec is availdable 200', async () => {
 | 
			
		||||
		const res = await axios.get(base + '/docs/swagger.json');
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
describe('GET /docs/swaggerui', () => {
 | 
			
		||||
	it('swaggerui is availdable 200', async () => {
 | 
			
		||||
		const res = await axios.get(base + '/docs/swaggerui');
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
describe('GET /docs/redoc', () => {
 | 
			
		||||
	it('redoc is availdable 200', async () => {
 | 
			
		||||
		const res = await axios.get(base + '/docs/redoc');
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
describe('GET /docs/rapidoc', () => {
 | 
			
		||||
	it('rapidoc is availdable 200', async () => {
 | 
			
		||||
		const res = await axios.get(base + '/docs/rapidoc');
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										72
									
								
								src/tests/pw_reset_mail.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/tests/pw_reset_mail.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import { config } from '../config';
 | 
			
		||||
const base = "http://localhost:" + config.internal_port
 | 
			
		||||
 | 
			
		||||
const axios_config = {
 | 
			
		||||
	validateStatus: undefined
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('POST /reset without auth', () => {
 | 
			
		||||
	it('Post without auth should return 401', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset', null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(401);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('POST /reset with auth but wrong body', () => {
 | 
			
		||||
	it('Post with auth but no body should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?key=' + config.api_key, null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth but no mail should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?key=' + config.api_key, { resetKey: "test" }, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth but no reset key should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?key=' + config.api_key, { address: "test@dev.lauf-fuer-kaya.de" }, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth but invalid mail should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?key=' + config.api_key, { resetKey: "test", address: "testdev.l.de" }, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('POST /reset with auth and vaild body', () => {
 | 
			
		||||
	it('Post with auth, body and no locale should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?key=' + config.api_key, {
 | 
			
		||||
			resetKey: "test",
 | 
			
		||||
			address: "test@dev.lauf-fuer-kaya.de"
 | 
			
		||||
		}, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "en"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth, body and locale=en should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?locale=en&key=' + config.api_key, {
 | 
			
		||||
			resetKey: "test",
 | 
			
		||||
			address: "test@dev.lauf-fuer-kaya.de"
 | 
			
		||||
		}, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "en"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth, body and locale=de should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/reset?locale=de&key=' + config.api_key, {
 | 
			
		||||
			resetKey: "test",
 | 
			
		||||
			address: "test@dev.lauf-fuer-kaya.de"
 | 
			
		||||
		}, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "de"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										72
									
								
								src/tests/selfservice_welcome_mail.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/tests/selfservice_welcome_mail.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import { config } from '../config';
 | 
			
		||||
const base = "http://localhost:" + config.internal_port
 | 
			
		||||
 | 
			
		||||
const axios_config = {
 | 
			
		||||
	validateStatus: undefined
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('POST /registration without auth', () => {
 | 
			
		||||
	it('Post without auth should return 401', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration', null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(401);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('POST /registration with auth but wrong body', () => {
 | 
			
		||||
	it('Post with auth but no body should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?key=' + config.api_key, null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth but no mail should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?key=' + config.api_key, { selfserviceToken: "test" }, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth but no reset key should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?key=' + config.api_key, { address: "test@dev.lauf-fuer-kaya.de" }, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth but invalid mail should return 400', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?key=' + config.api_key, { selfserviceToken: "test", address: "testdev.l.de" }, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(400);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('POST /reset with auth and vaild body', () => {
 | 
			
		||||
	it('Post with auth, body and no locale should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?key=' + config.api_key, {
 | 
			
		||||
			selfserviceToken: "test",
 | 
			
		||||
			address: "test@dev.lauf-fuer-kaya.de"
 | 
			
		||||
		}, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "en"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth, body and locale=en should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?locale=en&key=' + config.api_key, {
 | 
			
		||||
			selfserviceToken: "test",
 | 
			
		||||
			address: "test@dev.lauf-fuer-kaya.de"
 | 
			
		||||
		}, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "en"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth, body and locale=de should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/registration?locale=de&key=' + config.api_key, {
 | 
			
		||||
			selfserviceToken: "test",
 | 
			
		||||
			address: "test@dev.lauf-fuer-kaya.de"
 | 
			
		||||
		}, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "de"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										44
									
								
								src/tests/test_mail.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/tests/test_mail.spec.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import { config } from '../config';
 | 
			
		||||
const base = "http://localhost:" + config.internal_port
 | 
			
		||||
 | 
			
		||||
const axios_config = {
 | 
			
		||||
	validateStatus: undefined
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('POST /test without auth', () => {
 | 
			
		||||
	it('Post without auth should return 401', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/test', null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(401);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe('POST /test with auth', () => {
 | 
			
		||||
	it('Post with auth and no locale should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/test?key=' + config.api_key, null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "en"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth and locale=en should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/test?locale=en&key=' + config.api_key, null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "en"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
	it('Post with auth and locale=de should return 200', async () => {
 | 
			
		||||
		const res = await axios.post(base + '/test?locale=de&key=' + config.api_key, null, axios_config);
 | 
			
		||||
		expect(res.status).toEqual(200);
 | 
			
		||||
		expect(res.data).toEqual({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Sent!",
 | 
			
		||||
			locale: "de"
 | 
			
		||||
		})
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
		Reference in New Issue
	
	Block a user