Compare commits

...

23 Commits

Author SHA1 Message Date
e89e07d0fc Merge pull request 'Release 0.10.1' (#189) from dev into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: #189
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-03 16:24:25 +00:00
c28843c405 🧾New changelog file version [CI SKIP] [skip ci]
All checks were successful
continuous-integration/drone/pr Build is passing
2021-04-03 16:17:51 +00:00
4834a6698b Removed duplicate openapi statement
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-03 18:16:56 +02:00
69afd4d587 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-03 16:15:38 +00:00
24d152fdc8 🚀Bumped version to v0.10.1
Some checks reported errors
continuous-integration/drone/push Build was killed
2021-04-03 18:14:47 +02:00
4279e43743 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-03 16:14:17 +00:00
d837654617 Merge pull request 'Selfservice donations reformatting feature/187-selfservice_donation' (#188) from feature/187-selfservice_donation into dev
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: #188
2021-04-03 16:13:34 +00:00
0767943721 Switched selfservice donation.donor from string to object
All checks were successful
continuous-integration/drone/pr Build is passing
ref #187
2021-04-03 17:07:44 +02:00
ca87774767 Adjusted runner property names
ref #187
2021-04-03 17:06:54 +02:00
f693f2cde9 Added new responsetype for new class
ref #187
2021-04-03 17:05:58 +02:00
d70c5b1bbc New class: ResponseSelfServiceDonor
ref #187
2021-04-03 17:05:10 +02:00
71e3d0efe2 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-01 16:39:21 +00:00
b517dff8a8 Merge pull request 'Release 0.10.0' (#186) from dev into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #186
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-04-01 16:38:30 +00:00
114c246ace 🧾New changelog file version [CI SKIP] [skip ci]
All checks were successful
continuous-integration/drone/pr Build is passing
2021-04-01 16:31:25 +00:00
d7703c9e07 Merge branch 'dev' of git.odit.services:lfk/backend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-01 18:30:38 +02:00
dc3071f7d2 🚀Bumped version to v0.10.0 2021-04-01 18:30:30 +02:00
5fb355f450 🧾New changelog file version [CI SKIP] [skip ci] 2021-04-01 16:30:20 +00:00
33c13de32c Merge pull request 'Mail locales feature/184-mail_locales' (#185) from feature/184-mail_locales into dev
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: #185
2021-04-01 16:29:39 +00:00
1be073a4fa Added locale to mail related user endpoints
All checks were successful
continuous-integration/drone/pr Build is passing
ref #184
2021-04-01 18:25:09 +02:00
b0d8249452 Merge branch 'feature/184-mail_locales' of git.odit.services:lfk/backend into feature/184-mail_locales 2021-04-01 18:23:21 +02:00
7af883f271 Added locale to mail related runner endpoints
ref #184
2021-04-01 18:23:19 +02:00
f5433076b0 Added locale to mail related runner endpoints
ref #84
2021-04-01 18:23:15 +02:00
6aafe4a6ae 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 16:43:52 +00:00
7 changed files with 317 additions and 235 deletions

View File

@@ -2,8 +2,39 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v0.10.1](https://git.odit.services/lfk/backend/compare/v0.10.0...v0.10.1)
- 🧾New changelog file version [CI SKIP] [skip ci] [`69afd4d`](https://git.odit.services/lfk/backend/commit/69afd4d5877401eb46df430f43a7feb273abda1e)
- 🚀Bumped version to v0.10.1 [`24d152f`](https://git.odit.services/lfk/backend/commit/24d152fdc8fe17fffa2f2a718d7145ba8a91d79c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`4279e43`](https://git.odit.services/lfk/backend/commit/4279e4374304887e8db40eab77763b20bbce91a1)
- Removed duplicate openapi statement [`4834a66`](https://git.odit.services/lfk/backend/commit/4834a6698b0958602421c1478a95fec7edda910b)
- Merge pull request 'Selfservice donations reformatting feature/187-selfservice_donation' (#188) from feature/187-selfservice_donation into dev [`d837654`](https://git.odit.services/lfk/backend/commit/d837654617f7de5d055ffb06c65e2cd52f65c604)
- New class: ResponseSelfServiceDonor [`d70c5b1`](https://git.odit.services/lfk/backend/commit/d70c5b1bbc9f02782f8755b6929e2d3458e10221)
- Switched selfservice donation.donor from string to object [`0767943`](https://git.odit.services/lfk/backend/commit/0767943721b6964d542f580c541e744f86444ac6)
- Adjusted runner property names [`ca87774`](https://git.odit.services/lfk/backend/commit/ca87774767807a2c4bc869b0de95cc73832a8405)
- 🧾New changelog file version [CI SKIP] [skip ci] [`71e3d0e`](https://git.odit.services/lfk/backend/commit/71e3d0efe2cbde47aea0f26cb5a8b5cd3312707d)
- Added new responsetype for new class [`f693f2c`](https://git.odit.services/lfk/backend/commit/f693f2cde9a04147155aea4de5d52e1d19d722ca)
#### [v0.10.0](https://git.odit.services/lfk/backend/compare/v0.9.2...v0.10.0)
> 1 April 2021
- Merge pull request 'Release 0.10.0' (#186) from dev into main [`b517dff`](https://git.odit.services/lfk/backend/commit/b517dff8a82c960836d9f0be90fd89f3ba2fae7d)
- 🚀Bumped version to v0.10.0 [`dc3071f`](https://git.odit.services/lfk/backend/commit/dc3071f7d2be298f0bb02d86ec67ed1125cd3b49)
- Added locale to mail related runner endpoints [`7af883f`](https://git.odit.services/lfk/backend/commit/7af883f27198206af542bcaff4686221d3788e87)
- Added locale to mail related runner endpoints [`f543307`](https://git.odit.services/lfk/backend/commit/f5433076b01c743ed9af085fccadb8f1edc26419)
- 🧾New changelog file version [CI SKIP] [skip ci] [`5fb355f`](https://git.odit.services/lfk/backend/commit/5fb355f450f19e96d3671b1a46e94d564495942b)
- 🧾New changelog file version [CI SKIP] [skip ci] [`114c246`](https://git.odit.services/lfk/backend/commit/114c246aceba566cc0dd6daab51a77b951b031cc)
- Merge pull request 'Mail locales feature/184-mail_locales' (#185) from feature/184-mail_locales into dev [`33c13de`](https://git.odit.services/lfk/backend/commit/33c13de32c68a3d9e87e4fd9ad12a815ed8c9fde)
- Added locale to mail related user endpoints [`1be073a`](https://git.odit.services/lfk/backend/commit/1be073a4fa39f0332a46f567ee6af10a9137844c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`6aafe4a`](https://git.odit.services/lfk/backend/commit/6aafe4a6ae7d253ab39220e551c52ae067cc481a)
#### [v0.9.2](https://git.odit.services/lfk/backend/compare/v0.9.1...v0.9.2) #### [v0.9.2](https://git.odit.services/lfk/backend/compare/v0.9.1...v0.9.2)
> 29 March 2021
- Merge pull request 'Release 0.9.2' (#183) from dev into main [`bdeeb03`](https://git.odit.services/lfk/backend/commit/bdeeb036459c2a2131e843d8a5a6b338e0ba46ea)
- 🧾New changelog file version [CI SKIP] [skip ci] [`675c876`](https://git.odit.services/lfk/backend/commit/675c8762e8e4cf28d2f334d5ab2e1cb6b594e33c)
- Fixed bug in return creation [`6c9b91d`](https://git.odit.services/lfk/backend/commit/6c9b91d75a0d08fc4ab0e72c7a09bd0133566368) - Fixed bug in return creation [`6c9b91d`](https://git.odit.services/lfk/backend/commit/6c9b91d75a0d08fc4ab0e72c7a09bd0133566368)
- 🧾New changelog file version [CI SKIP] [skip ci] [`8c00aef`](https://git.odit.services/lfk/backend/commit/8c00aefd6ce3723d9f83d1c94e6491d5d597391f) - 🧾New changelog file version [CI SKIP] [skip ci] [`8c00aef`](https://git.odit.services/lfk/backend/commit/8c00aefd6ce3723d9f83d1c94e6491d5d597391f)
- 🚀Bumped version to v0.9.2 [`89e3924`](https://git.odit.services/lfk/backend/commit/89e392473c52a3f328545699a0f4df89be33ba89) - 🚀Bumped version to v0.9.2 [`89e3924`](https://git.odit.services/lfk/backend/commit/89e392473c52a3f328545699a0f4df89be33ba89)

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-backend", "name": "@odit/lfk-backend",
"version": "0.9.2", "version": "0.10.1",
"main": "src/app.ts", "main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend", "repository": "https://git.odit.services/lfk/backend",
"author": { "author": {

View File

@@ -1,228 +1,228 @@
import { Request } from "express"; import { Request } from "express";
import * as jwt from "jsonwebtoken"; import * as jwt from "jsonwebtoken";
import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { getConnectionManager, Repository } from 'typeorm';
import { config } from '../config'; import { config } from '../config';
import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError'; import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError';
import { MailSendingError } from '../errors/MailErrors'; import { MailSendingError } from '../errors/MailErrors';
import { RunnerEmailNeededError, RunnerHasDistanceDonationsError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors'; import { RunnerEmailNeededError, RunnerHasDistanceDonationsError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors';
import { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors'; import { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors';
import { ScanStationNotFoundError } from '../errors/ScanStationErrors'; import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
import { JwtCreator } from '../jwtcreator'; import { JwtCreator } from '../jwtcreator';
import { Mailer } from '../mailer'; import { Mailer } from '../mailer';
import ScanAuth from '../middlewares/ScanAuth'; import ScanAuth from '../middlewares/ScanAuth';
import { CreateSelfServiceCitizenRunner } from '../models/actions/create/CreateSelfServiceCitizenRunner'; import { CreateSelfServiceCitizenRunner } from '../models/actions/create/CreateSelfServiceCitizenRunner';
import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServiceRunner'; import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServiceRunner';
import { Runner } from '../models/entities/Runner'; import { Runner } from '../models/entities/Runner';
import { RunnerGroup } from '../models/entities/RunnerGroup'; import { RunnerGroup } from '../models/entities/RunnerGroup';
import { RunnerOrganization } from '../models/entities/RunnerOrganization'; import { RunnerOrganization } from '../models/entities/RunnerOrganization';
import { ScanStation } from '../models/entities/ScanStation'; import { ScanStation } from '../models/entities/ScanStation';
import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseScanStation } from '../models/responses/ResponseScanStation'; import { ResponseScanStation } from '../models/responses/ResponseScanStation';
import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation'; import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation';
import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner';
import { ResponseSelfServiceScan } from '../models/responses/ResponseSelfServiceScan'; import { ResponseSelfServiceScan } from '../models/responses/ResponseSelfServiceScan';
import { DonationController } from './DonationController'; import { DonationController } from './DonationController';
import { RunnerCardController } from './RunnerCardController'; import { RunnerCardController } from './RunnerCardController';
import { ScanController } from './ScanController'; import { ScanController } from './ScanController';
@JsonController() @JsonController()
export class RunnerSelfServiceController { export class RunnerSelfServiceController {
private runnerRepository: Repository<Runner>; private runnerRepository: Repository<Runner>;
private orgRepository: Repository<RunnerOrganization>; private orgRepository: Repository<RunnerOrganization>;
private stationRepository: Repository<ScanStation>; private stationRepository: Repository<ScanStation>;
/** /**
* Gets the repository of this controller's model/entity. * Gets the repository of this controller's model/entity.
*/ */
constructor() { constructor() {
this.runnerRepository = getConnectionManager().get().getRepository(Runner); this.runnerRepository = getConnectionManager().get().getRepository(Runner);
this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization); this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization);
this.stationRepository = getConnectionManager().get().getRepository(ScanStation); this.stationRepository = getConnectionManager().get().getRepository(ScanStation);
} }
@Get('/runners/me/:jwt') @Get('/runners/me/:jwt')
@ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError) @OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all information about yourself. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please use the forgot endpoint.' }) @OpenAPI({ description: 'Lists all information about yourself. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please use the forgot endpoint.' })
async get(@Param('jwt') token: string) { async get(@Param('jwt') token: string) {
return (new ResponseSelfServiceRunner(await this.getRunner(token))); return (new ResponseSelfServiceRunner(await this.getRunner(token)));
} }
@Delete('/runners/me/:jwt') @Delete('/runners/me/:jwt')
@ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError) @OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Deletes all information about yourself. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please use the forgot endpoint.' }) @OpenAPI({ description: 'Deletes all information about yourself. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please use the forgot endpoint.' })
async remove(@Param('jwt') token: string, @QueryParam("force") force: boolean) { async remove(@Param('jwt') token: string, @QueryParam("force") force: boolean) {
const responseRunner = await this.getRunner(token); const responseRunner = await this.getRunner(token);
let runner = await this.runnerRepository.findOne({ id: responseRunner.id }); let runner = await this.runnerRepository.findOne({ id: responseRunner.id });
if (!runner) { return null; } if (!runner) { return null; }
if (!runner) { if (!runner) {
throw new RunnerNotFoundError(); throw new RunnerNotFoundError();
} }
const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations; const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations;
if (runnerDonations.length > 0 && !force) { if (runnerDonations.length > 0 && !force) {
throw new RunnerHasDistanceDonationsError(); throw new RunnerHasDistanceDonationsError();
} }
const donationController = new DonationController(); const donationController = new DonationController();
for (let donation of runnerDonations) { for (let donation of runnerDonations) {
await donationController.remove(donation.id, force); await donationController.remove(donation.id, force);
} }
const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards; const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards;
const cardController = new RunnerCardController; const cardController = new RunnerCardController;
for (let card of runnerCards) { for (let card of runnerCards) {
await cardController.remove(card.id, force); await cardController.remove(card.id, force);
} }
const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans; const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans;
const scanController = new ScanController; const scanController = new ScanController;
for (let scan of runnerScans) { for (let scan of runnerScans) {
await scanController.remove(scan.id, force); await scanController.remove(scan.id, force);
} }
await this.runnerRepository.delete(runner); await this.runnerRepository.delete(runner);
return new ResponseSelfServiceRunner(responseRunner); return new ResponseSelfServiceRunner(responseRunner);
} }
@Get('/runners/me/:jwt/scans') @Get('/runners/me/:jwt/scans')
@ResponseSchema(ResponseSelfServiceScan, { isArray: true }) @ResponseSchema(ResponseSelfServiceScan, { isArray: true })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(RunnerNotFoundError) @OnUndefined(RunnerNotFoundError)
@OpenAPI({ description: 'Lists all your (runner) scans. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please contact support.' }) @OpenAPI({ description: 'Lists all your (runner) scans. <br> Please provide your runner jwt(that code we gave you during registration) for auth. <br> If you lost your jwt/personalized link please contact support.' })
async getScans(@Param('jwt') token: string) { async getScans(@Param('jwt') token: string) {
const scans = (await this.getRunner(token)).scans; const scans = (await this.getRunner(token)).scans;
let responseScans = new Array<ResponseSelfServiceScan>() let responseScans = new Array<ResponseSelfServiceScan>()
for (let scan of scans) { for (let scan of scans) {
responseScans.push(new ResponseSelfServiceScan(scan)); responseScans.push(new ResponseSelfServiceScan(scan));
} }
return responseScans; return responseScans;
} }
@Get('/stations/me') @Get('/stations/me')
@UseBefore(ScanAuth) @UseBefore(ScanAuth)
@ResponseSchema(ResponseScanStation) @ResponseSchema(ResponseScanStation)
@ResponseSchema(ScanStationNotFoundError, { statusCode: 404 }) @ResponseSchema(ScanStationNotFoundError, { statusCode: 404 })
@OnUndefined(ScanStationNotFoundError) @OnUndefined(ScanStationNotFoundError)
@OpenAPI({ description: 'Lists basic information about the station whose token got provided. <br> This includes it\'s associated track.', security: [{ "StationApiToken": [] }] }) @OpenAPI({ description: 'Lists basic information about the station whose token got provided. <br> This includes it\'s associated track.', security: [{ "StationApiToken": [] }] })
async getStationMe(@Req() req: Request) { async getStationMe(@Req() req: Request) {
let scan = await this.stationRepository.findOne({ id: parseInt(req.headers["station_id"].toString()) }, { relations: ['track'] }) let scan = await this.stationRepository.findOne({ id: parseInt(req.headers["station_id"].toString()) }, { relations: ['track'] })
if (!scan) { throw new ScanStationNotFoundError(); } if (!scan) { throw new ScanStationNotFoundError(); }
return scan.toResponse(); return scan.toResponse();
} }
@Post('/runners/forgot') @Post('/runners/forgot')
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@OnUndefined(ResponseEmpty) @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 token/link to be sent to your mail address (rate limited to one mail every 24hrs).' })
async requestNewToken(@QueryParam('mail') mail: string) { async requestNewToken(@QueryParam('mail') mail: string, @QueryParam("locale") locale: string = "en") {
if (!mail) { if (!mail) {
throw new RunnerNotFoundError(); throw new RunnerNotFoundError();
} }
const runner = await this.runnerRepository.findOne({ email: mail }); const runner = await this.runnerRepository.findOne({ email: mail });
if (!runner) { throw new RunnerNotFoundError(); } if (!runner) { throw new RunnerNotFoundError(); }
if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 60 * 24)) { throw new RunnerSelfserviceTimeoutError(); } if (runner.resetRequestedTimestamp > (Math.floor(Date.now() / 1000) - 60 * 60 * 24)) { throw new RunnerSelfserviceTimeoutError(); }
const token = JwtCreator.createSelfService(runner); const token = JwtCreator.createSelfService(runner);
try { try {
await Mailer.sendSelfserviceForgottenMail(runner.email, token, "en") await Mailer.sendSelfserviceForgottenMail(runner.email, token, locale)
} catch (error) { } catch (error) {
throw new MailSendingError(); throw new MailSendingError();
} }
runner.resetRequestedTimestamp = Math.floor(Date.now() / 1000); runner.resetRequestedTimestamp = Math.floor(Date.now() / 1000);
await this.runnerRepository.save(runner); await this.runnerRepository.save(runner);
return { token }; return { token };
} }
@Post('/runners/register') @Post('/runners/register')
@ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerEmailNeededError, { statusCode: 406 }) @ResponseSchema(RunnerEmailNeededError, { statusCode: 406 })
@OpenAPI({ description: 'Create a new selfservice runner in the citizen org. <br> This endpoint shoud be used to allow "everyday citizen" to register themselves. <br> You have to provide a mail address, b/c the future we\'ll implement email verification.' }) @OpenAPI({ description: 'Create a new selfservice runner in the citizen org. <br> This endpoint shoud be used to allow "everyday citizen" to register themselves. <br> You have to provide a mail address, b/c the future we\'ll implement email verification.' })
async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner) { async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") {
let runner = await createRunner.toEntity(); let runner = await createRunner.toEntity();
runner = await this.runnerRepository.save(runner); 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'] })); 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); response.token = JwtCreator.createSelfService(runner);
try { try {
await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, "en") await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, locale)
} catch (error) { } catch (error) {
throw new MailSendingError(); throw new MailSendingError();
} }
return response; return response;
} }
@Post('/runners/register/:token') @Post('/runners/register/:token')
@ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(ResponseSelfServiceRunner)
@ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Create a new selfservice runner in a provided org. <br> The orgs get provided and authorized via api tokens that can be optained via the /organizations endpoint.' }) @OpenAPI({ description: 'Create a new selfservice runner in a provided org. <br> The orgs get provided and authorized via api tokens that can be optained via the /organizations endpoint.' })
async registerOrganizationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner) { async registerOrganizationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner, @QueryParam("locale") locale: string = "en") {
const org = await this.getOrgansisation(token); const org = await this.getOrgansisation(token);
let runner = await createRunner.toEntity(org); let runner = await createRunner.toEntity(org);
runner = await this.runnerRepository.save(runner); 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'] })); 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); response.token = JwtCreator.createSelfService(runner);
try { try {
await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, "en") await Mailer.sendSelfserviceWelcomeMail(runner.email, response.token, locale)
} catch (error) { } catch (error) {
throw new MailSendingError(); throw new MailSendingError();
} }
return response; return response;
} }
@Get('/organizations/selfservice/:token') @Get('/organizations/selfservice/:token')
@ResponseSchema(ResponseSelfServiceOrganisation, { isArray: false }) @ResponseSchema(ResponseSelfServiceOrganisation, { isArray: false })
@ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 })
@OpenAPI({ description: 'Get the basic info and teams for a org.' }) @OpenAPI({ description: 'Get the basic info and teams for a org.' })
async getSelfserviceOrg(@Param('token') token: string) { async getSelfserviceOrg(@Param('token') token: string) {
const orgid = (await this.getOrgansisation(token)).id; const orgid = (await this.getOrgansisation(token)).id;
const org = await this.orgRepository.findOne({ id: orgid }, { relations: ['teams'] }) const org = await this.orgRepository.findOne({ id: orgid }, { relations: ['teams'] })
return new ResponseSelfServiceOrganisation(<RunnerOrganization>org); return new ResponseSelfServiceOrganisation(<RunnerOrganization>org);
} }
/** /**
* Get's a runner by a provided jwt token. * Get's a runner by a provided jwt token.
* @param token The runner jwt provided by the runner to identitfy themselves. * @param token The runner jwt provided by the runner to identitfy themselves.
*/ */
private async getRunner(token: string): Promise<Runner> { private async getRunner(token: string): Promise<Runner> {
if (token == "") { throw new JwtNotProvidedError(); } if (token == "") { throw new JwtNotProvidedError(); }
let jwtPayload = undefined let jwtPayload = undefined
try { try {
jwtPayload = <any>jwt.verify(token, config.jwt_secret); jwtPayload = <any>jwt.verify(token, config.jwt_secret);
} catch (error) { } catch (error) {
throw new InvalidCredentialsError(); throw new InvalidCredentialsError();
} }
const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }); const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] });
if (!runner) { throw new RunnerNotFoundError() } if (!runner) { throw new RunnerNotFoundError() }
return runner; return runner;
} }
/** /**
* Get's a runner org by a provided registration api key. * Get's a runner org by a provided registration api key.
* @param token The organization's registration api token. * @param token The organization's registration api token.
*/ */
private async getOrgansisation(token: string): Promise<RunnerGroup> { private async getOrgansisation(token: string): Promise<RunnerGroup> {
token = Buffer.from(token, 'base64').toString('utf8'); token = Buffer.from(token, 'base64').toString('utf8');
const organization = await this.orgRepository.findOne({ key: token }); const organization = await this.orgRepository.findOne({ key: token });
if (!organization) { throw new RunnerOrganizationNotFoundError; } if (!organization) { throw new RunnerOrganizationNotFoundError; }
return organization; return organization;
} }
} }

View File

@@ -35,4 +35,5 @@ export enum ResponseObjectType {
USER = 'USER', USER = 'USER',
USERGROUP = 'USERGROUP', USERGROUP = 'USERGROUP',
USERPERMISSIONS = 'USERPERMISSIONS', USERPERMISSIONS = 'USERPERMISSIONS',
SELFSERVICEDONOR = 'SELFSERVICEDONOR'
} }

View File

@@ -2,6 +2,7 @@ import { IsInt, IsNotEmpty, IsPositive } from 'class-validator';
import { DistanceDonation } from '../entities/DistanceDonation'; import { DistanceDonation } from '../entities/DistanceDonation';
import { ResponseObjectType } from '../enums/ResponseObjectType'; import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse'; import { IResponse } from './IResponse';
import { ResponseSelfServiceDonor } from './ResponseSelfServiceDonor';
/** /**
* Defines the runner selfservice donation response. * Defines the runner selfservice donation response.
@@ -18,7 +19,7 @@ export class ResponseSelfServiceDonation implements IResponse {
* The donation's donor. * The donation's donor.
*/ */
@IsNotEmpty() @IsNotEmpty()
donor: string; donor: ResponseSelfServiceDonor;
/** /**
* The donation's amount in the smalles unit of your currency (default: euro cent). * The donation's amount in the smalles unit of your currency (default: euro cent).
@@ -35,9 +36,7 @@ export class ResponseSelfServiceDonation implements IResponse {
amountPerDistance: number; amountPerDistance: number;
public constructor(donation: DistanceDonation) { public constructor(donation: DistanceDonation) {
if (!donation.donor.middlename) { this.donor = donation.donor.firstname + " " + donation.donor.lastname; } this.donor = new ResponseSelfServiceDonor(donation.donor);
else { this.donor = donation.donor.firstname + " " + donation.donor.middlename + " " + donation.donor.lastname; }
this.amountPerDistance = donation.amountPerDistance; this.amountPerDistance = donation.amountPerDistance;
this.amount = donation.amount; this.amount = donation.amount;
} }

View File

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

View File

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