190 lines
5.8 KiB
TypeScript
190 lines
5.8 KiB
TypeScript
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');
|
|
}
|
|
}
|