Compare commits

..

7 Commits
1.3.5 ... 1.3.8

Author SHA1 Message Date
30b61db2c1 chore(release): 1.3.8
All checks were successful
Build release images / build-container (push) Successful in 1m20s
2025-04-09 10:23:48 +02:00
8237d5f210 feat(RunnerCardController): putByCode 2025-04-09 10:23:01 +02:00
03e0a29096 chore(release): 1.3.7
All checks were successful
Build release images / build-container (push) Successful in 1m16s
2025-04-08 21:15:59 +02:00
a6afba93e2 feat(stats): Publish runners by kiosk stat 2025-04-08 21:15:41 +02:00
a41758cd9c chore(release): 1.3.6
All checks were successful
Build release images / build-container (push) Successful in 1m32s
2025-04-08 21:06:01 +02:00
d6755ed134 feat(runners): Allow created via being set via api 2025-04-08 21:04:38 +02:00
599c75fc00 fix(participant): Switch to correct type 2025-04-08 21:03:23 +02:00
9 changed files with 117 additions and 4 deletions

View File

@@ -2,9 +2,31 @@
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.
#### [1.3.8](https://git.odit.services/lfk/backend/compare/1.3.7...1.3.8)
- feat(RunnerCardController): putByCode [`8237d5f`](https://git.odit.services/lfk/backend/commit/8237d5f21067c0872a7eff7c8d1506edf44ec10c)
#### [1.3.7](https://git.odit.services/lfk/backend/compare/1.3.6...1.3.7)
> 8 April 2025
- feat(stats): Publish runners by kiosk stat [`a6afba9`](https://git.odit.services/lfk/backend/commit/a6afba93e243ca419c282a16cad023d06d864e0e)
- chore(release): 1.3.7 [`03e0a29`](https://git.odit.services/lfk/backend/commit/03e0a290965648579956ac1f8e8542c97a667ed8)
#### [1.3.6](https://git.odit.services/lfk/backend/compare/1.3.5...1.3.6)
> 8 April 2025
- chore(release): 1.3.6 [`a41758c`](https://git.odit.services/lfk/backend/commit/a41758cd9c83105c3a4b407744bafe2f0f6fb48a)
- feat(runners): Allow created via being set via api [`d6755ed`](https://git.odit.services/lfk/backend/commit/d6755ed134071df635bc9d5821ceb2396c0f1d22)
- fix(participant): Switch to correct type [`599c75f`](https://git.odit.services/lfk/backend/commit/599c75fc00217eaec3cc87c0de50d059bdde685f)
#### [1.3.5](https://git.odit.services/lfk/backend/compare/1.3.4...1.3.5) #### [1.3.5](https://git.odit.services/lfk/backend/compare/1.3.4...1.3.5)
> 8 April 2025
- feat(runners): Generate selfservice urls on runner if requested or create/update/get single [`5415cd3`](https://git.odit.services/lfk/backend/commit/5415cd38a727e76632a01a4d2634a1777df5542c) - feat(runners): Generate selfservice urls on runner if requested or create/update/get single [`5415cd3`](https://git.odit.services/lfk/backend/commit/5415cd38a727e76632a01a4d2634a1777df5542c)
- chore(release): 1.3.5 [`bb213f0`](https://git.odit.services/lfk/backend/commit/bb213f001eff2157abf8741128f624f9cc991afe)
#### [1.3.4](https://git.odit.services/lfk/backend/compare/1.3.3...1.3.4) #### [1.3.4](https://git.odit.services/lfk/backend/compare/1.3.3...1.3.4)

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-backend", "name": "@odit/lfk-backend",
"version": "1.3.5", "version": "1.3.8",
"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

@@ -5,6 +5,7 @@ import { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFo
import { RunnerNotFoundError } from '../errors/RunnerErrors'; import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard'; import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard';
import { UpdateRunnerCard } from '../models/actions/update/UpdateRunnerCard'; import { UpdateRunnerCard } from '../models/actions/update/UpdateRunnerCard';
import { UpdateRunnerCardByCode } from '../models/actions/update/UpdateRunnerCardByCode';
import { RunnerCard } from '../models/entities/RunnerCard'; import { RunnerCard } from '../models/entities/RunnerCard';
import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseEmpty } from '../models/responses/ResponseEmpty';
import { ResponseRunnerCard } from '../models/responses/ResponseRunnerCard'; import { ResponseRunnerCard } from '../models/responses/ResponseRunnerCard';
@@ -112,6 +113,28 @@ export class RunnerCardController {
return (await this.cardRepository.findOne({ id: id }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] })).toResponse(); return (await this.cardRepository.findOne({ id: id }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] })).toResponse();
} }
@Put('/:code')
@Authorized("CARD:UPDATE")
@ResponseSchema(ResponseRunnerCard)
@ResponseSchema(RunnerCardNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerNotFoundError, { statusCode: 404 })
@ResponseSchema(RunnerCardIdsNotMatchingError, { statusCode: 406 })
@OpenAPI({ description: "Update the card whose code you provided." })
async putByCode(@Param('code') code: string, @Body({ validate: true }) card: UpdateRunnerCardByCode) {
let oldCard = await this.cardRepository.findOne({ code: code });
if (!oldCard) {
throw new RunnerCardNotFoundError();
}
if (oldCard.code != card.code) {
throw new RunnerCardIdsNotMatchingError();
}
await this.cardRepository.save(await card.update(oldCard));
return (await this.cardRepository.findOne({ code: code }, { relations: ['runner', 'runner.group', 'runner.group.parentGroup'] })).toResponse();
}
@Delete('/:id') @Delete('/:id')
@Authorized("CARD:DELETE") @Authorized("CARD:DELETE")
@ResponseSchema(ResponseRunnerCard) @ResponseSchema(ResponseRunnerCard)

View File

@@ -24,6 +24,7 @@ export class StatsController {
async get() { async get() {
const connection = getConnection(); const connection = getConnection();
const runnersViaSelfservice = await connection.getRepository(Runner).count({ where: { created_via: "selfservice" } }); const runnersViaSelfservice = await connection.getRepository(Runner).count({ where: { created_via: "selfservice" } });
const runnersViaKiosk = await connection.getRepository(Runner).count({ where: { created_via: "kiosk" } });
const runners = await connection.getRepository(Runner).count(); const runners = await connection.getRepository(Runner).count();
const teams = await connection.getRepository(RunnerTeam).count(); const teams = await connection.getRepository(RunnerTeam).count();
const orgs = await connection.getRepository(RunnerOrganization).count(); const orgs = await connection.getRepository(RunnerOrganization).count();
@@ -42,7 +43,7 @@ export class StatsController {
let donations = await connection.getRepository(Donation).find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] }); let donations = await connection.getRepository(Donation).find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] });
const donors = await connection.getRepository(Donor).count(); const donors = await connection.getRepository(Donor).count();
return new ResponseStats(runnersViaSelfservice, runners, teams, orgs, users, scans, donations, distace, donors) return new ResponseStats(runnersViaSelfservice, runners, teams, orgs, users, scans, donations, distace, donors, runnersViaKiosk)
} }
@Get("/runners/distance") @Get("/runners/distance")

View File

@@ -50,4 +50,11 @@ export abstract class CreateParticipant {
@IsOptional() @IsOptional()
@IsObject() @IsObject()
address?: Address; address?: Address;
/**
* how the participant got into the system
*/
@IsOptional()
@IsString()
created_via?: string;
} }

View File

@@ -32,6 +32,9 @@ export class CreateRunner extends CreateParticipant {
newRunner.email = this.email; newRunner.email = this.email;
newRunner.group = await this.getGroup(); newRunner.group = await this.getGroup();
newRunner.address = this.address; newRunner.address = this.address;
if (this.created_via) {
newRunner.created_via = this.created_via;
}
Address.validate(newRunner.address); Address.validate(newRunner.address);
return newRunner; return newRunner;

View File

@@ -0,0 +1,50 @@
import { IsBoolean, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerNotFoundError } from '../../../errors/RunnerErrors';
import { Runner } from '../../entities/Runner';
import { RunnerCard } from '../../entities/RunnerCard';
/**
* This class is used to update a RunnerCard entity (via put request).
*/
export class UpdateRunnerCardByCode {
/**
* The card's code.
*/
@IsString()
@IsNotEmpty()
code?: string;
/**
* The runner's id.
*/
@IsInt()
@IsOptional()
runner?: number;
/**
* Is the updated card enabled (for fraud reasons)?
* Default: true
*/
@IsBoolean()
enabled: boolean = true;
/**
* Creates a new RunnerCard entity from this.
*/
public async update(card: RunnerCard): Promise<RunnerCard> {
card.enabled = this.enabled;
card.runner = await this.getRunner();
return card;
}
public async getRunner(): Promise<Runner> {
if (!this.runner) { return null; }
const runner = await getConnection().getRepository(Runner).findOne({ id: this.runner });
if (!runner) {
throw new RunnerNotFoundError();
}
return runner;
}
}

View File

@@ -80,7 +80,7 @@ export abstract class Participant {
*/ */
@Column({ nullable: true, default: "backend" }) @Column({ nullable: true, default: "backend" })
@IsOptional() @IsOptional()
@IsEmail() @IsString()
created_via?: string; created_via?: string;
/** /**

View File

@@ -22,6 +22,12 @@ export class ResponseStats implements IResponse {
@IsInt() @IsInt()
runnersViaSelfservice: number; runnersViaSelfservice: number;
/**
* The amount of runners registered via kiosk.
*/
@IsInt()
runnersViaKiosk: number;
/** /**
* The amount of runners registered in the system. * The amount of runners registered in the system.
*/ */
@@ -98,7 +104,7 @@ export class ResponseStats implements IResponse {
* @param scans number of scans - no relations have to be resolved. * @param scans number of scans - no relations have to be resolved.
* @param donations Array containing all donations - the following relations have to be resolved: runner, runner.scans, runner.scans.track * @param donations Array containing all donations - the following relations have to be resolved: runner, runner.scans, runner.scans.track
*/ */
public constructor(runnersViaSelfservice: number, runners: number, teams: number, orgs: number, users: number, scans: number, donations: Donation[], distance: number, donors: number) { public constructor(runnersViaSelfservice: number, runners: number, teams: number, orgs: number, users: number, scans: number, donations: Donation[], distance: number, donors: number, runnersViaKiosk: number) {
this.runnersViaSelfservice = runnersViaSelfservice; this.runnersViaSelfservice = runnersViaSelfservice;
this.total_runners = runners; this.total_runners = runners;
this.total_teams = teams; this.total_teams = teams;
@@ -111,5 +117,6 @@ export class ResponseStats implements IResponse {
this.average_donation = this.total_donation / this.total_donations this.average_donation = this.total_donation / this.total_donations
this.total_donors = donors; this.total_donors = donors;
this.average_distance = this.total_distance / this.total_runners; this.average_distance = this.total_distance / this.total_runners;
this.runnersViaKiosk = runnersViaKiosk;
} }
} }