Template
feat : add concept module
This commit is contained in:
@@ -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 {}
|
||||
|
||||
@@ -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,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 {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export class ConceptRecieveDTO {
|
||||
constructor(partial: Partial<ConceptRecieveDTO>) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
|
||||
value: string;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
Reference in New Issue
Block a user