fel kesz game backend

This commit is contained in:
2025-09-15 19:00:35 +02:00
parent 7963f28021
commit 3af8de2797
267 changed files with 15655 additions and 347 deletions
@@ -0,0 +1,419 @@
import { Repository, Not, In } from 'typeorm';
import { AppDataSource } from '../ormconfig';
import { GameAggregate, GameState } from '../../Domain/Game/GameAggregate';
import { IGameRepository } from '../../Domain/IRepository/IGameRepository';
import { logDatabase, logError } from '../../Application/Services/Logger';
export class GameRepository implements IGameRepository {
private repo: Repository<GameAggregate>;
constructor() {
this.repo = AppDataSource.getRepository(GameAggregate);
}
async create(game: Partial<GameAggregate>): Promise<GameAggregate> {
const startTime = performance.now();
try {
const result = await this.repo.save(game);
const endTime = performance.now();
logDatabase('Game created', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${result.id}, gameCode: ${result.gamecode}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game creation failed', `executionTime: ${Math.round(endTime - startTime)}ms`);
logError('GameRepository.create error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to create game in database');
}
}
async findByPage(from: number, to: number): Promise<{ games: GameAggregate[], totalCount: number }> {
const startTime = performance.now();
try {
const limit = to - from + 1;
const offset = from;
// Get total count for pagination
const totalCount = await this.repo.count({
where: { state: Not(GameState.CANCELLED) }
});
// Get paginated results
const games = await this.repo.find({
where: { state: Not(GameState.CANCELLED) },
order: { updatedate: 'DESC' },
take: limit,
skip: offset
});
const endTime = performance.now();
logDatabase('Game page query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}, total: ${totalCount}, from: ${from}, to: ${to}`);
return { games, totalCount };
} catch (error) {
const endTime = performance.now();
logDatabase('Game page query failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`);
logError('GameRepository.findByPage error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to get games page from database');
}
}
async findByPageIncludingDeleted(from: number, to: number): Promise<{ games: GameAggregate[], totalCount: number }> {
const startTime = performance.now();
try {
const limit = to - from + 1;
const offset = from;
// Get total count for pagination (including deleted)
const totalCount = await this.repo.count();
// Get paginated results (including deleted)
const games = await this.repo.find({
order: { updatedate: 'DESC' },
take: limit,
skip: offset
});
const endTime = performance.now();
logDatabase('Game page query (including deleted) completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}, total: ${totalCount}, from: ${from}, to: ${to}`);
return { games, totalCount };
} catch (error) {
const endTime = performance.now();
logDatabase('Game page query (including deleted) failed', `executionTime: ${Math.round(endTime - startTime)}ms, from: ${from}, to: ${to}`);
logError('GameRepository.findByPageIncludingDeleted error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to get games page (including deleted) from database');
}
}
async findById(id: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
const result = await this.repo.findOne({
where: { id, state: Not(GameState.CANCELLED) }
});
const endTime = performance.now();
logDatabase('Game findById completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, found: ${!!result}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game findById failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`);
logError('GameRepository.findById error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find game by id in database');
}
}
async findByIdIncludingDeleted(id: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
const result = await this.repo.findOne({
where: { id }
});
const endTime = performance.now();
logDatabase('Game findByIdIncludingDeleted completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, found: ${!!result}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game findByIdIncludingDeleted failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`);
logError('GameRepository.findByIdIncludingDeleted error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find game by id (including deleted) in database');
}
}
async findByGameCode(gamecode: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
const result = await this.repo.findOne({
where: { gamecode, state: Not(GameState.CANCELLED) }
});
const endTime = performance.now();
logDatabase('Game findByGameCode completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameCode: ${gamecode}, found: ${!!result}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game findByGameCode failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameCode: ${gamecode}`);
logError('GameRepository.findByGameCode error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find game by game code in database');
}
}
async search(query: string, limit?: number, offset?: number): Promise<{ games: GameAggregate[], totalCount: number }> {
const startTime = performance.now();
try {
const queryBuilder = this.repo.createQueryBuilder('game')
.where('game.state != :cancelledState', { cancelledState: GameState.CANCELLED })
.andWhere('(game.gamecode ILIKE :query)', { query: `%${query}%` });
// Get total count
const totalCount = await queryBuilder.getCount();
// Apply pagination if provided
if (limit !== undefined) {
queryBuilder.take(limit);
}
if (offset !== undefined) {
queryBuilder.skip(offset);
}
const games = await queryBuilder.orderBy('game.updatedate', 'DESC').getMany();
const endTime = performance.now();
logDatabase('Game search completed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}, found: ${games.length}, total: ${totalCount}`);
return { games, totalCount };
} catch (error) {
const endTime = performance.now();
logDatabase('Game search failed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}`);
logError('GameRepository.search error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to search games in database');
}
}
async searchIncludingDeleted(query: string, limit?: number, offset?: number): Promise<{ games: GameAggregate[], totalCount: number }> {
const startTime = performance.now();
try {
const queryBuilder = this.repo.createQueryBuilder('game')
.where('(game.gamecode ILIKE :query)', { query: `%${query}%` });
// Get total count
const totalCount = await queryBuilder.getCount();
// Apply pagination if provided
if (limit !== undefined) {
queryBuilder.take(limit);
}
if (offset !== undefined) {
queryBuilder.skip(offset);
}
const games = await queryBuilder.orderBy('game.updatedate', 'DESC').getMany();
const endTime = performance.now();
logDatabase('Game search (including deleted) completed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}, found: ${games.length}, total: ${totalCount}`);
return { games, totalCount };
} catch (error) {
const endTime = performance.now();
logDatabase('Game search (including deleted) failed', `executionTime: ${Math.round(endTime - startTime)}ms, query: ${query}`);
logError('GameRepository.searchIncludingDeleted error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to search games (including deleted) in database');
}
}
async update(id: string, update: Partial<GameAggregate>): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
await this.repo.update(id, update);
const result = await this.findById(id);
const endTime = performance.now();
logDatabase('Game update completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, updated: ${!!result}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game update failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`);
logError('GameRepository.update error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to update game in database');
}
}
async delete(id: string): Promise<any> {
const startTime = performance.now();
try {
const result = await this.repo.delete(id);
const endTime = performance.now();
logDatabase('Game delete completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, affected: ${result.affected}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game delete failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`);
logError('GameRepository.delete error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to delete game from database');
}
}
async softDelete(id: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
await this.repo.update(id, { state: GameState.CANCELLED });
const result = await this.findByIdIncludingDeleted(id);
const endTime = performance.now();
logDatabase('Game soft delete completed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}, updated: ${!!result}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game soft delete failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${id}`);
logError('GameRepository.softDelete error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to soft delete game in database');
}
}
// Game-specific methods
async findActiveGames(): Promise<GameAggregate[]> {
const startTime = performance.now();
try {
const games = await this.repo.find({
where: { state: GameState.ACTIVE },
order: { updatedate: 'DESC' }
});
const endTime = performance.now();
logDatabase('Active games query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}`);
return games;
} catch (error) {
const endTime = performance.now();
logDatabase('Active games query failed', `executionTime: ${Math.round(endTime - startTime)}ms`);
logError('GameRepository.findActiveGames error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find active games in database');
}
}
async findGamesByPlayer(playerId: string): Promise<GameAggregate[]> {
const startTime = performance.now();
try {
const queryBuilder = this.repo.createQueryBuilder('game')
.where('game.state != :cancelledState', { cancelledState: GameState.CANCELLED })
.andWhere('JSON_CONTAINS(game.players, :playerId)', { playerId: `"${playerId}"` })
.orderBy('game.updatedate', 'DESC');
const games = await queryBuilder.getMany();
const endTime = performance.now();
logDatabase('Games by player query completed', `executionTime: ${Math.round(endTime - startTime)}ms, playerId: ${playerId}, found: ${games.length}`);
return games;
} catch (error) {
const endTime = performance.now();
logDatabase('Games by player query failed', `executionTime: ${Math.round(endTime - startTime)}ms, playerId: ${playerId}`);
logError('GameRepository.findGamesByPlayer error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find games by player in database');
}
}
async findWaitingGames(): Promise<GameAggregate[]> {
const startTime = performance.now();
try {
const games = await this.repo.find({
where: { state: GameState.WAITING },
order: { createdate: 'ASC' }
});
const endTime = performance.now();
logDatabase('Waiting games query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}`);
return games;
} catch (error) {
const endTime = performance.now();
logDatabase('Waiting games query failed', `executionTime: ${Math.round(endTime - startTime)}ms`);
logError('GameRepository.findWaitingGames error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find waiting games in database');
}
}
async findFinishedGames(from?: number, to?: number): Promise<{ games: GameAggregate[], totalCount: number }> {
const startTime = performance.now();
try {
const queryBuilder = this.repo.createQueryBuilder('game')
.where('game.state = :finishedState', { finishedState: GameState.FINISHED })
.orderBy('game.enddate', 'DESC');
// Get total count
const totalCount = await queryBuilder.getCount();
// Apply pagination if provided
if (from !== undefined && to !== undefined) {
const limit = to - from + 1;
const offset = from;
queryBuilder.take(limit).skip(offset);
}
const games = await queryBuilder.getMany();
const endTime = performance.now();
logDatabase('Finished games query completed', `executionTime: ${Math.round(endTime - startTime)}ms, found: ${games.length}, total: ${totalCount}`);
return { games, totalCount };
} catch (error) {
const endTime = performance.now();
logDatabase('Finished games query failed', `executionTime: ${Math.round(endTime - startTime)}ms`);
logError('GameRepository.findFinishedGames error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to find finished games in database');
}
}
async addPlayerToGame(gameId: string, playerId: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
const game = await this.findById(gameId);
if (!game) {
return null;
}
// Check if player is already in the game
if (game.players.includes(playerId)) {
return game;
}
// Check if game is full
if (game.players.length >= game.maxplayers) {
throw new Error('Game is full');
}
const updatedPlayers = [...game.players, playerId];
const result = await this.update(gameId, { players: updatedPlayers });
const endTime = performance.now();
logDatabase('Player added to game', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Add player to game failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`);
logError('GameRepository.addPlayerToGame error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to add player to game in database');
}
}
async removePlayerFromGame(gameId: string, playerId: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
const game = await this.findById(gameId);
if (!game) {
return null;
}
const updatedPlayers = game.players.filter(id => id !== playerId);
const result = await this.update(gameId, { players: updatedPlayers });
const endTime = performance.now();
logDatabase('Player removed from game', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Remove player from game failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, playerId: ${playerId}`);
logError('GameRepository.removePlayerFromGame error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to remove player from game in database');
}
}
async updateGameState(gameId: string, started: boolean, finished?: boolean, winner?: string): Promise<GameAggregate | null> {
const startTime = performance.now();
try {
const updateData: Partial<GameAggregate> = { started };
if (started && !finished) {
updateData.state = GameState.ACTIVE;
updateData.startdate = new Date();
}
if (finished) {
updateData.finished = true;
updateData.state = GameState.FINISHED;
updateData.enddate = new Date();
if (winner) {
updateData.winner = winner;
}
}
const result = await this.update(gameId, updateData);
const endTime = performance.now();
logDatabase('Game state updated', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}, started: ${started}, finished: ${finished}, winner: ${winner}`);
return result;
} catch (error) {
const endTime = performance.now();
logDatabase('Game state update failed', `executionTime: ${Math.round(endTime - startTime)}ms, gameId: ${gameId}`);
logError('GameRepository.updateGameState error', error instanceof Error ? error : new Error(String(error)));
throw new Error('Failed to update game state in database');
}
}
}