feat : add concept module

This commit is contained in:
2024-12-31 15:47:36 +01:00
parent 0500415252
commit 6bb83a3d5c
10 changed files with 162 additions and 41 deletions
+2
View File
@@ -15,6 +15,7 @@ import { WordModule } from './word/words.module';
import { Message } from './games/entities/message.entity';
import { LobbyModule } from './lobby/lobby.module';
import { Concept } from './concept/entities/concept.entity';
import { ConceptModule } from './concept/concept.module';
const configService = new ConfigService();
@@ -48,6 +49,7 @@ const configService = new ConfigService();
GamesModule,
UsersModule,
WordModule,
ConceptModule,
],
})
export class AppModule {}
+104
View File
@@ -0,0 +1,104 @@
import {
Body,
Controller,
Delete,
Get,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Post,
Put,
Sse,
UseGuards,
} from '@nestjs/common';
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
import { AuthGuard } from 'src/users/guards/auth.guard';
import { fromEvent, map, Observable } from 'rxjs';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ConceptService } from './concept.service';
import { Concept } from './entities/concept.entity';
import { ConceptPipe } from './concept.pipe';
import { ConceptRecieveDTO } from './dto/conceptrecieve.dto';
import { ConceptResponseDTO } from 'events/dto/conceptresponse.dto';
@ApiTags('concepts')
@Controller('concepts')
export class ConceptController {
constructor(
private readonly conceptService: ConceptService,
private eventEmitter: EventEmitter2,
) {}
@Sse('/subscribe')
subscribe(): Observable<{ data: string }> {
return fromEvent(this.eventEmitter, this.conceptService.sse_prefix).pipe(
map((payload) => {
return {
data: JSON.stringify(payload),
};
}),
);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete a concept' })
@UseGuards(AuthGuard)
async delete(@Param('id') id: number) {
const concept = await this.conceptService.getById(id);
if (!concept) {
throw new HttpException('Concept not found', HttpStatus.NOT_FOUND);
}
await this.conceptService.delete(concept);
}
@Post('/')
@ApiOperation({ summary: 'Submit a concept' })
@ApiBody({ type: ConceptRecieveDTO })
@UseGuards(AuthGuard)
async create(@Body('value') value: string) {
const concept = await this.conceptService.create(value, 'placeholder.jpg');
return new ConceptResponseDTO(concept);
}
@Get('/')
@ApiOperation({ summary: 'List enabled concept' })
async read() {
return (await this.conceptService.listActive()).map(
(w) => new ConceptResponseDTO(w),
);
}
@Get('/all')
@ApiOperation({ summary: 'List all concepts' })
async readAll() {
return (await this.conceptService.list()).map(
(w) => new ConceptResponseDTO(w),
);
}
@Put(':id/enable')
@ApiOperation({ summary: 'Enable a concept' })
@UseGuards(AuthGuard)
async enable(
@Param('id', ParseIntPipe) _id: string,
@Param('id', ConceptPipe) concept: Concept,
) {
await this.conceptService.enable(concept);
return new ConceptResponseDTO(concept);
}
@Put(':id/disable')
@ApiOperation({ summary: 'Disable a word' })
@UseGuards(AuthGuard)
async disable(
@Param('id', ParseIntPipe) _id: string,
@Param('id', ConceptPipe) concept: Concept,
) {
await this.conceptService.disable(concept);
return new ConceptResponseDTO(concept);
}
}
+4
View File
@@ -4,8 +4,12 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from 'src/users/users.module';
import { Concept } from './entities/concept.entity';
import { ConceptService } from './concept.service';
import { ConceptController } from './concept.controller';
@Module({
imports: [UsersModule, TypeOrmModule.forFeature([Concept])],
providers: [ConceptService],
controllers: [ConceptController],
})
export class ConceptModule {}
+20
View File
@@ -0,0 +1,20 @@
import {
HttpException,
HttpStatus,
Injectable,
PipeTransform,
} from '@nestjs/common';
import { ConceptService } from './concept.service';
@Injectable()
export class ConceptPipe implements PipeTransform {
constructor(private readonly conceptService: ConceptService) {}
async transform(id: number) {
const word = await this.conceptService.getById(id);
if (!word) {
throw new HttpException('Word not found', HttpStatus.NOT_FOUND);
}
return word;
}
}
+5 -5
View File
@@ -12,7 +12,7 @@ import {
} from 'events/concept.events';
@Injectable()
export class WordService {
export class ConceptService {
sse_prefix = 'sse.concept';
constructor(
@InjectRepository(Concept) private conceptRepository: Repository<Concept>,
@@ -42,13 +42,13 @@ export class WordService {
}
async listActive() {
const words = await this.conceptRepository.findBy({ enabled: true });
return words;
const concepts = await this.conceptRepository.findBy({ enabled: true });
return concepts;
}
async list() {
const words = await this.conceptRepository.find();
return words;
const concepts = await this.conceptRepository.find();
return concepts;
}
async enable(concept: Concept) {
+7
View File
@@ -0,0 +1,7 @@
export class ConceptRecieveDTO {
constructor(partial: Partial<ConceptRecieveDTO>) {
Object.assign(this, partial);
}
value: string;
}
+16 -22
View File
@@ -6,7 +6,6 @@ import {
HttpStatus,
Param,
Post,
Session,
Sse,
UseGuards,
} from '@nestjs/common';
@@ -19,11 +18,12 @@ import { Observable, fromEvent, map } from 'rxjs';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { GameService } from '../services/games.service';
import { ConfigService } from '@nestjs/config';
import { SessionData } from 'express-session';
// import { WordResponseDTO } from 'events/dto/wordresponse.dto';
import { WordRecieveDTO } from 'src/word/dto/wordrecieve.dto';
import { LobbyService } from 'src/lobby/lobby.service';
import { WordResponseDTO } from 'events/dto/wordresponse.dto';
import { GetUser } from 'src/users/user.pipe';
import { User } from 'src/users/entities/user.entity';
@ApiTags('game')
@Controller('games/:id')
@@ -52,7 +52,7 @@ export class GamesController {
@UseGuards(AuthGuard)
async start(
@Param('id') id: string,
@Session() session: SessionData,
@GetUser() user: User,
@Param('id', GamePipe) game: Game,
) {
if (game.started) {
@@ -64,7 +64,7 @@ export class GamesController {
HttpStatus.BAD_REQUEST,
);
}
if (game.ownerId != session.user?.id) {
if (game.ownerId != user.id) {
throw new HttpException(
'You can only start games you own',
HttpStatus.FORBIDDEN,
@@ -79,42 +79,39 @@ export class GamesController {
@UseGuards(AuthGuard)
surrender(
@Param('id') id: string,
@Session() session: SessionData,
@Param('id', GamePipe) game: Game,
@GetUser() user: User,
@Param('id', GameStartedPipe) game: Game,
) {
if (!game.started) {
throw new HttpException('Game has not started', HttpStatus.BAD_REQUEST);
}
return this.lobbyService.leaveGame(game, session.user);
return this.lobbyService.leaveGame(game, user);
}
@Post('kick/:playerId')
@ApiOperation({ summary: 'Kick a player' })
@UseGuards(AuthGuard)
kick(
@Session() session: SessionData,
@GetUser() user: User,
@Param('id') id: string,
@Param('id', GamePipe) game: Game,
@Param('playerId') playerId: string,
) {
if (session.user.id != game.ownerId) {
if (user.id != game.ownerId) {
throw new HttpException(
'You do do not own this game',
HttpStatus.FORBIDDEN,
);
}
return this.lobbyService.kickPlayer(game, session.user, playerId);
return this.lobbyService.kickPlayer(game, user, playerId);
}
@Get('currentWord')
@ApiOperation({ summary: 'Read Current Word' })
@UseGuards(AuthGuard)
getCurrentWord(
@Session() session: SessionData,
@GetUser() user: User,
@Param('id') id: string,
@Param('id', GamePipe) game: Game,
@Param('id', GameStartedPipe) game: Game,
) {
if (session.user.id != game.currentPlayer.userId) {
if (user.id != game.currentPlayer.userId) {
throw new HttpException(
'It is not your turn to play',
HttpStatus.FORBIDDEN,
@@ -128,15 +125,12 @@ export class GamesController {
@ApiBody({ type: WordRecieveDTO })
@UseGuards(AuthGuard)
async postMessage(
@Session() session: SessionData,
@GetUser() user: User,
@Param('id') id: string,
@Param('id', GamePipe) game: Game,
@Param('id', GameStartedPipe) game: Game,
@Body('value') value: string,
) {
const player = game.players.find((p) => p.userId == session.user.id);
if (!game.started) {
throw new HttpException('Game not started', HttpStatus.BAD_REQUEST);
}
const player = game.players.find((p) => p.userId == user.id);
if (!player) {
throw new HttpException('You are not in this game', HttpStatus.FORBIDDEN);
}
+3 -3
View File
@@ -15,7 +15,7 @@ export class GamePipe implements PipeTransform {
@InjectRepository(Game) private gameRepository: Repository<Game>,
) {}
async transform(value: any) {
async transform(value: number) {
const game = await this.gameRepository.findOneBy({ id: value });
if (!game) {
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
@@ -30,8 +30,8 @@ export class GameStartedPipe implements PipeTransform {
@InjectRepository(Game) private gameRepository: Repository<Game>,
) {}
async transform(value: any) {
const game = await this.gameRepository.findOne(value);
async transform(value: number) {
const game = await this.gameRepository.findOneBy({ id: value });
if (!game) {
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
}
+1 -11
View File
@@ -1,14 +1,4 @@
import { User } from 'src/users/entities/user.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
import { Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Word } from './word.entity';
@Entity()