first commit

This commit is contained in:
Bastian Wagner
2026-07-31 21:02:47 +02:00
commit 6bea4f766a
512 changed files with 64459 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { randomBytes } from 'crypto';
import { PenaltyEntity } from '../penalty/entities/penalty.entity';
import { Player } from '../players/entities/player.entity';
import { Transaction } from '../transactions/entitites/transaction.entity';
import { Repository } from 'typeorm';
import {
PublicAccessStatusDto,
PublicPlayerDto,
PublicPlayerHistoryDto,
PublicTeamDto,
PublicTransactionDto,
} from './dto/public-access.dto';
import { Team } from './entities/team.entity';
import { TeamAccessService } from './team-access.service';
@Injectable()
export class PublicTeamAccessService {
constructor(
@InjectRepository(Team)
private readonly teamRepository: Repository<Team>,
@InjectRepository(Player)
private readonly playerRepository: Repository<Player>,
@InjectRepository(Transaction)
private readonly transactionRepository: Repository<Transaction>,
@InjectRepository(PenaltyEntity)
private readonly penaltyRepository: Repository<PenaltyEntity>,
private readonly access: TeamAccessService,
) {}
async getStatus(
userId: number,
teamId: number,
): Promise<PublicAccessStatusDto> {
await this.access.assertMember(userId, teamId);
return this.toStatus(await this.loadManagedTeam(teamId));
}
async setEnabled(
userId: number,
teamId: number,
enabled: boolean,
): Promise<PublicAccessStatusDto> {
await this.access.assertManager(userId, teamId);
const team = await this.loadManagedTeam(teamId);
if (enabled && !team.publicAccessToken) {
team.publicAccessToken = this.createToken();
}
team.publicAccessEnabled = enabled;
await this.teamRepository.save(team);
return this.toStatus(team);
}
async rotate(userId: number, teamId: number): Promise<PublicAccessStatusDto> {
await this.access.assertManager(userId, teamId);
const team = await this.loadManagedTeam(teamId);
team.publicAccessToken = this.createToken();
await this.teamRepository.save(team);
return this.toStatus(team);
}
async getPublicTeam(token: string): Promise<PublicTeamDto> {
const team = await this.loadPublicTeam(token);
const [players, penalties] = await Promise.all([
this.playerRepository.find({
where: { team: { id: team.id }, active: true },
order: { lastName: 'ASC', firstName: 'ASC' },
}),
this.penaltyRepository.find({
where: { team: { id: team.id } },
order: { description: 'ASC' },
}),
]);
const publicPlayers = players.map((player) => this.toPublicPlayer(player));
return {
name: team.name,
balance: Number(team.balance),
outstanding: publicPlayers.reduce(
(total, player) => total - Number(player.balance),
0,
),
players: publicPlayers,
penalties: penalties.map((penalty) => ({
id: penalty.id,
description: penalty.description,
amount: Number(penalty.amount),
})),
};
}
async getPublicPlayerHistory(
token: string,
playerId: number,
): Promise<PublicPlayerHistoryDto> {
const team = await this.loadPublicTeam(token);
const player = await this.playerRepository.findOne({
where: { id: playerId, team: { id: team.id }, active: true },
});
if (!player) throw new NotFoundException('Freigabe nicht gefunden.');
return {
player: this.toPublicPlayer(player),
transactions: await this.loadTransactions(player.id),
};
}
async getMemberPlayerTransactions(
userId: number,
teamId: number,
playerId: number,
): Promise<PublicTransactionDto[]> {
await this.access.assertMember(userId, teamId);
const player = await this.playerRepository.findOne({
where: { id: playerId, team: { id: teamId } },
});
if (!player) throw new NotFoundException('Spieler nicht gefunden.');
return this.loadTransactions(player.id);
}
private async loadManagedTeam(teamId: number): Promise<Team> {
const team = await this.teamRepository
.createQueryBuilder('team')
.addSelect('team.publicAccessToken')
.where('team.id = :teamId', { teamId })
.getOne();
if (!team) throw new NotFoundException('Team nicht gefunden.');
return team;
}
private async loadPublicTeam(token: string): Promise<Team> {
if (!/^[a-f0-9]{64}$/.test(token)) {
throw new NotFoundException('Freigabe nicht gefunden.');
}
const team = await this.teamRepository.findOne({
where: { publicAccessToken: token, publicAccessEnabled: true },
select: {
id: true,
name: true,
balance: true,
publicAccessEnabled: true,
publicAccessToken: true,
},
});
if (!team) throw new NotFoundException('Freigabe nicht gefunden.');
return team;
}
private async loadTransactions(
playerId: number,
): Promise<PublicTransactionDto[]> {
const transactions = await this.transactionRepository.find({
where: { player: { id: playerId } },
order: { date: 'DESC' },
take: 20,
});
return transactions.map((transaction) => ({
id: transaction.id,
note: transaction.note,
date: transaction.date,
amount: Number(transaction.amount),
type: transaction.type
? { id: transaction.type.id, name: transaction.type.name }
: null,
}));
}
private toPublicPlayer(player: Player): PublicPlayerDto {
return {
id: player.id,
firstName: player.firstName,
lastName: player.lastName,
balance: Number(player.balance),
active: player.active,
};
}
private toStatus(team: Team): PublicAccessStatusDto {
return {
enabled: team.publicAccessEnabled,
token: team.publicAccessToken ?? null,
};
}
private createToken(): string {
return randomBytes(32).toString('hex');
}
}