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 { Message } from './games/entities/message.entity';
|
||||||
import { LobbyModule } from './lobby/lobby.module';
|
import { LobbyModule } from './lobby/lobby.module';
|
||||||
import { Concept } from './concept/entities/concept.entity';
|
import { Concept } from './concept/entities/concept.entity';
|
||||||
|
import { ConceptModule } from './concept/concept.module';
|
||||||
|
|
||||||
const configService = new ConfigService();
|
const configService = new ConfigService();
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ const configService = new ConfigService();
|
|||||||
GamesModule,
|
GamesModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
WordModule,
|
WordModule,
|
||||||
|
ConceptModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
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 { UsersModule } from 'src/users/users.module';
|
||||||
import { Concept } from './entities/concept.entity';
|
import { Concept } from './entities/concept.entity';
|
||||||
|
import { ConceptService } from './concept.service';
|
||||||
|
import { ConceptController } from './concept.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UsersModule, TypeOrmModule.forFeature([Concept])],
|
imports: [UsersModule, TypeOrmModule.forFeature([Concept])],
|
||||||
|
providers: [ConceptService],
|
||||||
|
controllers: [ConceptController],
|
||||||
})
|
})
|
||||||
export class ConceptModule {}
|
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';
|
} from 'events/concept.events';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WordService {
|
export class ConceptService {
|
||||||
sse_prefix = 'sse.concept';
|
sse_prefix = 'sse.concept';
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Concept) private conceptRepository: Repository<Concept>,
|
@InjectRepository(Concept) private conceptRepository: Repository<Concept>,
|
||||||
@@ -42,13 +42,13 @@ export class WordService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async listActive() {
|
async listActive() {
|
||||||
const words = await this.conceptRepository.findBy({ enabled: true });
|
const concepts = await this.conceptRepository.findBy({ enabled: true });
|
||||||
return words;
|
return concepts;
|
||||||
}
|
}
|
||||||
|
|
||||||
async list() {
|
async list() {
|
||||||
const words = await this.conceptRepository.find();
|
const concepts = await this.conceptRepository.find();
|
||||||
return words;
|
return concepts;
|
||||||
}
|
}
|
||||||
|
|
||||||
async enable(concept: Concept) {
|
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,
|
HttpStatus,
|
||||||
Param,
|
Param,
|
||||||
Post,
|
Post,
|
||||||
Session,
|
|
||||||
Sse,
|
Sse,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
@@ -19,11 +18,12 @@ import { Observable, fromEvent, map } from 'rxjs';
|
|||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { GameService } from '../services/games.service';
|
import { GameService } from '../services/games.service';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { SessionData } from 'express-session';
|
|
||||||
// import { WordResponseDTO } from 'events/dto/wordresponse.dto';
|
// import { WordResponseDTO } from 'events/dto/wordresponse.dto';
|
||||||
import { WordRecieveDTO } from 'src/word/dto/wordrecieve.dto';
|
import { WordRecieveDTO } from 'src/word/dto/wordrecieve.dto';
|
||||||
import { LobbyService } from 'src/lobby/lobby.service';
|
import { LobbyService } from 'src/lobby/lobby.service';
|
||||||
import { WordResponseDTO } from 'events/dto/wordresponse.dto';
|
import { WordResponseDTO } from 'events/dto/wordresponse.dto';
|
||||||
|
import { GetUser } from 'src/users/user.pipe';
|
||||||
|
import { User } from 'src/users/entities/user.entity';
|
||||||
|
|
||||||
@ApiTags('game')
|
@ApiTags('game')
|
||||||
@Controller('games/:id')
|
@Controller('games/:id')
|
||||||
@@ -52,7 +52,7 @@ export class GamesController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
async start(
|
async start(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Session() session: SessionData,
|
@GetUser() user: User,
|
||||||
@Param('id', GamePipe) game: Game,
|
@Param('id', GamePipe) game: Game,
|
||||||
) {
|
) {
|
||||||
if (game.started) {
|
if (game.started) {
|
||||||
@@ -64,7 +64,7 @@ export class GamesController {
|
|||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (game.ownerId != session.user?.id) {
|
if (game.ownerId != user.id) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'You can only start games you own',
|
'You can only start games you own',
|
||||||
HttpStatus.FORBIDDEN,
|
HttpStatus.FORBIDDEN,
|
||||||
@@ -79,42 +79,39 @@ export class GamesController {
|
|||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
surrender(
|
surrender(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Session() session: SessionData,
|
@GetUser() user: User,
|
||||||
@Param('id', GamePipe) game: Game,
|
@Param('id', GameStartedPipe) game: Game,
|
||||||
) {
|
) {
|
||||||
if (!game.started) {
|
return this.lobbyService.leaveGame(game, user);
|
||||||
throw new HttpException('Game has not started', HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
return this.lobbyService.leaveGame(game, session.user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('kick/:playerId')
|
@Post('kick/:playerId')
|
||||||
@ApiOperation({ summary: 'Kick a player' })
|
@ApiOperation({ summary: 'Kick a player' })
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
kick(
|
kick(
|
||||||
@Session() session: SessionData,
|
@GetUser() user: User,
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Param('id', GamePipe) game: Game,
|
@Param('id', GamePipe) game: Game,
|
||||||
@Param('playerId') playerId: string,
|
@Param('playerId') playerId: string,
|
||||||
) {
|
) {
|
||||||
if (session.user.id != game.ownerId) {
|
if (user.id != game.ownerId) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'You do do not own this game',
|
'You do do not own this game',
|
||||||
HttpStatus.FORBIDDEN,
|
HttpStatus.FORBIDDEN,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return this.lobbyService.kickPlayer(game, session.user, playerId);
|
return this.lobbyService.kickPlayer(game, user, playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('currentWord')
|
@Get('currentWord')
|
||||||
@ApiOperation({ summary: 'Read Current Word' })
|
@ApiOperation({ summary: 'Read Current Word' })
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
getCurrentWord(
|
getCurrentWord(
|
||||||
@Session() session: SessionData,
|
@GetUser() user: User,
|
||||||
@Param('id') id: string,
|
@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(
|
throw new HttpException(
|
||||||
'It is not your turn to play',
|
'It is not your turn to play',
|
||||||
HttpStatus.FORBIDDEN,
|
HttpStatus.FORBIDDEN,
|
||||||
@@ -128,15 +125,12 @@ export class GamesController {
|
|||||||
@ApiBody({ type: WordRecieveDTO })
|
@ApiBody({ type: WordRecieveDTO })
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
async postMessage(
|
async postMessage(
|
||||||
@Session() session: SessionData,
|
@GetUser() user: User,
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Param('id', GamePipe) game: Game,
|
@Param('id', GameStartedPipe) game: Game,
|
||||||
@Body('value') value: string,
|
@Body('value') value: string,
|
||||||
) {
|
) {
|
||||||
const player = game.players.find((p) => p.userId == session.user.id);
|
const player = game.players.find((p) => p.userId == user.id);
|
||||||
if (!game.started) {
|
|
||||||
throw new HttpException('Game not started', HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
if (!player) {
|
if (!player) {
|
||||||
throw new HttpException('You are not in this game', HttpStatus.FORBIDDEN);
|
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>,
|
@InjectRepository(Game) private gameRepository: Repository<Game>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async transform(value: any) {
|
async transform(value: number) {
|
||||||
const game = await this.gameRepository.findOneBy({ id: value });
|
const game = await this.gameRepository.findOneBy({ id: value });
|
||||||
if (!game) {
|
if (!game) {
|
||||||
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
||||||
@@ -30,8 +30,8 @@ export class GameStartedPipe implements PipeTransform {
|
|||||||
@InjectRepository(Game) private gameRepository: Repository<Game>,
|
@InjectRepository(Game) private gameRepository: Repository<Game>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async transform(value: any) {
|
async transform(value: number) {
|
||||||
const game = await this.gameRepository.findOne(value);
|
const game = await this.gameRepository.findOneBy({ id: value });
|
||||||
if (!game) {
|
if (!game) {
|
||||||
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,4 @@
|
|||||||
import { User } from 'src/users/entities/user.entity';
|
import { Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
Entity,
|
|
||||||
JoinTable,
|
|
||||||
ManyToMany,
|
|
||||||
ManyToOne,
|
|
||||||
OneToMany,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Unique,
|
|
||||||
} from 'typeorm';
|
|
||||||
import { Word } from './word.entity';
|
import { Word } from './word.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
|||||||
Reference in New Issue
Block a user