Compare commits

..

No commits in common. "19a290c3a931ead0d9ae9ebb0985bfbaac54df59" and "6fd246f43cb3f4d0ccb6e017ee699889ba17daac" have entirely different histories.

5 changed files with 122 additions and 162 deletions

View File

@ -1,109 +1,108 @@
{ {
"name": "@odit/lfk-backend", "name": "@odit/lfk-backend",
"version": "0.12.0", "version": "0.12.0",
"main": "src/app.ts", "main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend", "repository": "https://git.odit.services/lfk/backend",
"author": { "author": {
"name": "ODIT.Services", "name": "ODIT.Services",
"email": "info@odit.services", "email": "info@odit.services",
"url": "https://odit.services" "url": "https://odit.services"
}, },
"contributors": [ "contributors": [
{ {
"name": "Philipp Dormann", "name": "Philipp Dormann",
"email": "philipp@philippdormann.de", "email": "philipp@philippdormann.de",
"url": "https://philippdormann.de" "url": "https://philippdormann.de"
}, },
{ {
"name": "Nicolai Ort", "name": "Nicolai Ort",
"email": "info@nicolai-ort.com", "email": "info@nicolai-ort.com",
"url": "https://nicolai-ort.com" "url": "https://nicolai-ort.com"
} }
], ],
"license": "CC-BY-NC-SA-4.0", "license": "CC-BY-NC-SA-4.0",
"dependencies": { "dependencies": {
"@odit/class-validator-jsonschema": "2.1.1", "@odit/class-validator-jsonschema": "2.1.1",
"argon2": "0.27.1", "argon2": "0.27.1",
"axios": "0.21.1", "axios": "0.21.1",
"body-parser": "1.19.0", "body-parser": "1.19.0",
"check-password-strength": "2.0.2", "check-password-strength": "2.0.2",
"class-transformer": "0.3.1", "class-transformer": "0.3.1",
"class-validator": "0.13.1", "class-validator": "0.13.1",
"consola": "2.15.0", "consola": "2.15.0",
"cookie": "0.4.1", "cookie": "0.4.1",
"cookie-parser": "1.4.5", "cookie-parser": "1.4.5",
"cors": "2.8.5", "cors": "2.8.5",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"dotenv": "8.2.0", "dotenv": "8.2.0",
"express": "4.17.1", "express": "4.17.1",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
"libphonenumber-js": "1.9.9", "libphonenumber-js": "1.9.9",
"mysql": "2.18.1", "mysql": "2.18.1",
"pg": "8.5.1", "pg": "8.5.1",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"routing-controllers": "0.9.0-alpha.6", "routing-controllers": "0.9.0-alpha.6",
"routing-controllers-openapi": "2.2.0", "routing-controllers-openapi": "2.2.0",
"sqlite3": "5.0.0", "sqlite3": "5.0.0",
"typeorm": "0.2.30", "typeorm": "0.2.30",
"typeorm-routing-controllers-extensions": "0.2.0", "typeorm-routing-controllers-extensions": "0.2.0",
"typeorm-seeding": "1.6.1", "typeorm-seeding": "1.6.1",
"uuid": "8.3.2", "uuid": "8.3.2",
"validator": "13.5.2" "validator": "13.5.2"
}, },
"devDependencies": { "devDependencies": {
"@faker-js/faker": "^7.6.0", "@odit/license-exporter": "0.0.9",
"@odit/license-exporter": "0.0.9", "@types/cors": "2.8.9",
"@types/cors": "2.8.9", "@types/csvtojson": "1.1.5",
"@types/csvtojson": "1.1.5", "@types/express": "4.17.11",
"@types/express": "4.17.11", "@types/jest": "26.0.20",
"@types/jest": "26.0.20", "@types/jsonwebtoken": "8.5.0",
"@types/jsonwebtoken": "8.5.0", "@types/node": "14.14.22",
"@types/node": "14.14.22", "@types/uuid": "8.3.0",
"@types/uuid": "8.3.0", "cp-cli": "2.0.0",
"cp-cli": "2.0.0", "jest": "26.6.3",
"jest": "26.6.3", "nodemon": "2.0.7",
"nodemon": "2.0.7", "release-it": "14.2.2",
"release-it": "14.2.2", "rimraf": "3.0.2",
"rimraf": "3.0.2", "start-server-and-test": "1.11.7",
"start-server-and-test": "1.11.7", "ts-jest": "26.5.0",
"ts-jest": "26.5.0", "ts-node": "9.1.1",
"ts-node": "9.1.1", "typedoc": "0.20.19",
"typedoc": "0.20.19", "typescript": "4.1.3"
"typescript": "4.1.3" },
}, "scripts": {
"scripts": { "dev": "nodemon src/app.ts",
"dev": "nodemon src/app.ts", "build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static",
"build": "rimraf ./dist && tsc && cp-cli ./src/static ./dist/static", "docs": "typedoc --out docs src",
"docs": "typedoc --out docs src", "test": "jest",
"test": "jest", "test:watch": "jest --watchAll",
"test:watch": "jest --watchAll", "test:ci:generate_env": "ts-node scripts/create_testenv.ts",
"test:ci:generate_env": "ts-node scripts/create_testenv.ts", "test:ci:run": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test",
"test:ci:run": "start-server-and-test dev http://localhost:4010/api/docs/openapi.json test", "test:ci": "npm run test:ci:generate_env && npm run test:ci:run",
"test:ci": "npm run test:ci:generate_env && npm run test:ci:run", "seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed",
"seed": "ts-node ./node_modules/typeorm/cli.js schema:sync && ts-node ./node_modules/typeorm-seeding/dist/cli.js seed", "openapi:export": "ts-node scripts/openapi_export.ts",
"openapi:export": "ts-node scripts/openapi_export.ts", "licenses:export": "license-exporter --markdown",
"licenses:export": "license-exporter --markdown", "release": "release-it --only-version"
"release": "release-it --only-version" },
}, "release-it": {
"release-it": { "git": {
"git": { "commit": true,
"commit": true, "requireCleanWorkingDir": false,
"requireCleanWorkingDir": false, "commitMessage": "🚀Bumped version to v${version}",
"commitMessage": "🚀Bumped version to v${version}", "requireBranch": "dev",
"requireBranch": "dev", "push": true,
"push": true, "tag": true,
"tag": true, "tagName": "v${version}",
"tagName": "v${version}", "tagAnnotation": "v${version}"
"tagAnnotation": "v${version}" },
}, "npm": {
"npm": { "publish": false
"publish": false }
} },
}, "nodemonConfig": {
"nodemonConfig": { "ignore": [
"ignore": [ "src/tests/*",
"src/tests/*", "docs/*"
"docs/*" ]
] }
} }
}

View File

@ -1,6 +1,6 @@
import { Request } from "express"; import { Request } from "express";
import * as jwt from "jsonwebtoken"; import * as jwt from "jsonwebtoken";
import { BadRequestError, 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';
@ -116,7 +116,7 @@ export class RunnerSelfServiceController {
return scan.toResponse(); return scan.toResponse();
} }
@Post('/runners/login') @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).' })
@ -148,11 +148,8 @@ export class RunnerSelfServiceController {
@OpenAPI({ description: 'Create a new selfservice runner in the citizen org. <br> This endpoint shoud be used to allow "everyday citizen" to register themselves. <br> You have to provide a mail address, b/c the future we\'ll implement email verification.' }) @OpenAPI({ description: 'Create a new selfservice runner in the citizen org. <br> This endpoint shoud be used to allow "everyday citizen" to register themselves. <br> You have to provide a mail address, b/c the future we\'ll implement email verification.' })
async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") { async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner, @QueryParam("locale") locale: string = "en") {
let runner = await createRunner.toEntity(); let runner = await createRunner.toEntity();
if (await this.getRunnerExistsByMail(runner.email)) {
throw new BadRequestError("E-Mail already registered")
}
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);
@ -173,9 +170,6 @@ export class RunnerSelfServiceController {
const org = await this.getOrgansisation(token); const org = await this.getOrgansisation(token);
let runner = await createRunner.toEntity(org); let runner = await createRunner.toEntity(org);
if (await this.getRunnerExistsByMail(runner.email)) {
throw new BadRequestError("E-Mail already registered")
}
runner = await this.runnerRepository.save(runner); 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'] }));
@ -231,14 +225,4 @@ export class RunnerSelfServiceController {
return organization; return organization;
} }
/**
* Checks if a runner already exists
* @param email The runner's email address
* @returns Boolean (true if exists, false if not)
*/
private async getRunnerExistsByMail(email: string): Promise<boolean> {
const runner = await this.runnerRepository.findOne({ email });
return runner != undefined
}
} }

View File

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

View File

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

View File

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