Template
working on words
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Expose, Type } from 'class-transformer';
|
||||
import { UserResponseDTO } from 'src/users/dto/userresponse.dto';
|
||||
import { UserResponseDTO } from './userresponse.dto';
|
||||
import { PlayerResponseDTO } from './playerresponse.dto';
|
||||
import { ConceptInTurnResponseDTO } from './conceptinturnresponse.dto';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Expose, Type } from 'class-transformer';
|
||||
import { UserResponseDTO } from 'src/users/dto/userresponse.dto';
|
||||
import { UserResponseDTO } from './userresponse.dto';
|
||||
|
||||
export class PlayerResponseDTO {
|
||||
constructor(partial: Partial<PlayerResponseDTO>) {
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Expose, Type } from 'class-transformer';
|
||||
import { UserResponseDTO } from 'src/users/dto/userresponse.dto';
|
||||
import { Expose } from 'class-transformer';
|
||||
|
||||
export class WordResponseDTO {
|
||||
constructor(partial: Partial<WordResponseDTO>) {
|
||||
@@ -13,4 +12,7 @@ export class WordResponseDTO {
|
||||
|
||||
@Expose()
|
||||
ownerId: string;
|
||||
|
||||
@Expose()
|
||||
enabled: boolean;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { GameResponseDTO } from 'src/games/dto/gameresponse.dto';
|
||||
import { PlayerResponseDTO } from 'src/games/dto/playerresponse.dto';
|
||||
import { Game } from 'src/games/entities/game.entity';
|
||||
import { GameResponseDTO } from './dto/gameresponse.dto';
|
||||
import { PlayerResponseDTO } from './dto/playerresponse.dto';
|
||||
|
||||
export class GameCreateEvent extends GameResponseDTO {
|
||||
type: 'create';
|
||||
@@ -15,7 +14,7 @@ export class GameJoinEvent {
|
||||
gameId: number;
|
||||
constructor(
|
||||
public player: PlayerResponseDTO,
|
||||
game?: Game,
|
||||
game?: Partial<GameResponseDTO>,
|
||||
) {
|
||||
this.gameId = game.id;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { WordResponseDTO } from './dto/wordresponse.dto';
|
||||
|
||||
export class WordCreateEvent {
|
||||
type: 'create';
|
||||
constructor(public word: WordResponseDTO) {}
|
||||
}
|
||||
|
||||
export class WordEditEvent {
|
||||
type: 'edit';
|
||||
constructor(public word: WordResponseDTO) {}
|
||||
}
|
||||
|
||||
export class WordDeleteEvent {
|
||||
type: 'delete';
|
||||
constructor(public id: number) {}
|
||||
}
|
||||
|
||||
export class WordEnableEvent {
|
||||
type: 'enable';
|
||||
constructor(public id: number) {}
|
||||
}
|
||||
|
||||
export class WordDisableEvent {
|
||||
type: 'disable';
|
||||
constructor(public id: number) {}
|
||||
}
|
||||
@@ -17,8 +17,9 @@ import { GamePipe } from '../pipe/game.pipe';
|
||||
import { Game } from '../entities/game.entity';
|
||||
import { Observable, fromEvent, map } from 'rxjs';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { GameResponseDTO } from '../dto/gameresponse.dto';
|
||||
import { SessionData } from 'express-session';
|
||||
import { GameResponseDTO } from '../../../events/dto/gameresponse.dto';
|
||||
import { GetUser } from 'src/users/user.pipe';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
|
||||
@ApiTags('lobby')
|
||||
@Controller('games')
|
||||
@@ -41,15 +42,15 @@ export class LobbyController {
|
||||
})
|
||||
@ApiResponse({ status: 403, description: 'Forbidden.' })
|
||||
@UseGuards(AuthGuard)
|
||||
async create(@Session() session: SessionData) {
|
||||
const games = await this.lobbyService.findByOwner(session.user);
|
||||
async create(@GetUser() user: User) {
|
||||
const games = await this.lobbyService.findByOwner(user);
|
||||
if (games) {
|
||||
throw new HttpException(
|
||||
'You can only create one game at a time',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
const createdGame = await this.lobbyService.create(session.user);
|
||||
const createdGame = await this.lobbyService.create(user);
|
||||
return new GameResponseDTO(createdGame);
|
||||
}
|
||||
|
||||
@@ -85,10 +86,10 @@ export class LobbyController {
|
||||
@UseGuards(AuthGuard)
|
||||
async remove(
|
||||
@Param('id') id: string,
|
||||
@Session() session: SessionData,
|
||||
@Param('id', GamePipe) game: Game,
|
||||
@GetUser() user: User,
|
||||
) {
|
||||
if (game.owner.id != session.user.id) {
|
||||
if (game.owner.id != user.id) {
|
||||
throw new HttpException(
|
||||
'You can only delete games you own',
|
||||
HttpStatus.FORBIDDEN,
|
||||
@@ -102,20 +103,20 @@ export class LobbyController {
|
||||
@UseGuards(AuthGuard)
|
||||
async join(
|
||||
@Param('id') id: string,
|
||||
@Session() session: SessionData,
|
||||
@Param('id', GamePipe) game: Game,
|
||||
@GetUser() user: User,
|
||||
) {
|
||||
if (game.started) {
|
||||
throw new HttpException('Game already started', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (game.players.some((p) => p.user.id == session.user.id)) {
|
||||
if (game.players.some((p) => p.user.id == user.id)) {
|
||||
throw new HttpException(
|
||||
'You are already in this game',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
return await this.lobbyService.joinGame(game, session.user);
|
||||
return await this.lobbyService.joinGame(game, user);
|
||||
}
|
||||
|
||||
@Post(':id/leave')
|
||||
@@ -123,12 +124,12 @@ export class LobbyController {
|
||||
@UseGuards(AuthGuard)
|
||||
leave(
|
||||
@Param('id') id: string,
|
||||
@Session() session: SessionData,
|
||||
@Param('id', GamePipe) game: Game,
|
||||
@GetUser() user: User,
|
||||
) {
|
||||
if (game.started) {
|
||||
throw new HttpException('Game already started', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return this.lobbyService.leaveGame(game, session.user);
|
||||
return this.lobbyService.leaveGame(game, user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
GameCreateEvent,
|
||||
GameDeleteEvent,
|
||||
GameJoinEvent,
|
||||
} from 'src/games/events/lobby';
|
||||
} from 'events/lobby.events';
|
||||
|
||||
@Injectable()
|
||||
export class LobbyService {
|
||||
|
||||
@@ -6,18 +6,25 @@ import {
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SessionData } from 'express-session';
|
||||
import { UsersService } from '../users.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate {
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
constructor(private userService: UsersService) {}
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const http = context.switchToHttp();
|
||||
const request = http.getRequest<Request>();
|
||||
if (!(request.session as Record<string, any>).user) {
|
||||
if (!(request.session as SessionData).user) {
|
||||
throw new HttpException('Please log in', HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
const user = await this.userService.findUser(request.session.user.id);
|
||||
if (!user) {
|
||||
throw new HttpException(
|
||||
'Session expired. Please log in',
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
createParamDecorator,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
|
||||
export const GetUser = createParamDecorator(
|
||||
(data: unknown, req: Record<string, string>) => {
|
||||
if (!req.user) {
|
||||
throw new HttpException(
|
||||
'You are not authentified',
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
return req.user;
|
||||
},
|
||||
);
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
ParseIntPipe,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UserResponseDTO } from './dto/userresponse.dto';
|
||||
import { UserResponseDTO } from '../../events/dto/userresponse.dto';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { UserResponseDTO } from './dto/userresponse.dto';
|
||||
import { UserResponseDTO } from '../../events/dto/userresponse.dto';
|
||||
import { URLSearchParams } from 'url';
|
||||
import { User } from './entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
@@ -6,8 +6,9 @@ import {
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
Session,
|
||||
Put,
|
||||
Sse,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
@@ -15,19 +16,34 @@ import {
|
||||
import { WordService } from '../services/word.service';
|
||||
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { AuthGuard } from 'src/users/guards/auth.guard';
|
||||
import { SessionData } from 'express-session';
|
||||
import { UsersService } from 'src/users/users.service';
|
||||
import { WordResponseDTO } from '../dto/wordresponse.dto';
|
||||
import { WordResponseDTO } from '../../../events/dto/wordresponse.dto';
|
||||
import { WordRecieveDTO } from '../dto/wordrecieve.dto';
|
||||
import { WordPipe } from '../pipe/word.pipe';
|
||||
import { Word } from '../entities/word.entity';
|
||||
import { GetUser } from 'src/users/user.pipe';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
import { fromEvent, map, Observable } from 'rxjs';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
@ApiTags('words')
|
||||
@Controller('words')
|
||||
export class WordController {
|
||||
constructor(
|
||||
private readonly wordService: WordService,
|
||||
private readonly userService: UsersService,
|
||||
private eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
@Sse('/subscribe')
|
||||
subscribe(): Observable<{ data: string }> {
|
||||
return fromEvent(this.eventEmitter, this.wordService.sse_prefix).pipe(
|
||||
map((payload) => {
|
||||
return {
|
||||
data: JSON.stringify(payload),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@Delete()
|
||||
@ApiOperation({ summary: 'Delete a word' })
|
||||
@UseGuards(AuthGuard)
|
||||
@@ -43,21 +59,38 @@ export class WordController {
|
||||
@ApiOperation({ summary: 'Submit a word' })
|
||||
@ApiBody({ type: WordRecieveDTO })
|
||||
@UseGuards(AuthGuard)
|
||||
async create(@Body('value') value: string, @Session() session: SessionData) {
|
||||
const user = await this.userService.findUser(session.user.id);
|
||||
if (!user) {
|
||||
throw new HttpException(
|
||||
'You are not authentified',
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
async create(@Body('value') value: string, @GetUser() user: User) {
|
||||
const word = await this.wordService.create(value, user);
|
||||
return new WordResponseDTO(word);
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
@ApiOperation({ summary: 'List all words' })
|
||||
@ApiOperation({ summary: 'List enabled words' })
|
||||
async read() {
|
||||
return (await this.wordService.listActive()).map(
|
||||
(w) => new WordResponseDTO(w),
|
||||
);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
@ApiOperation({ summary: 'List all words' })
|
||||
async readAll() {
|
||||
return (await this.wordService.list()).map((w) => new WordResponseDTO(w));
|
||||
}
|
||||
|
||||
@Put('/enable/:id')
|
||||
@ApiOperation({ summary: 'Enable a word' })
|
||||
@UseGuards(AuthGuard)
|
||||
async enable(@Param('id', ParseIntPipe, WordPipe) word: Word) {
|
||||
await this.wordService.enable(word);
|
||||
return new WordResponseDTO(word);
|
||||
}
|
||||
|
||||
@Put('/disable/:id')
|
||||
@ApiOperation({ summary: 'Disable a word' })
|
||||
@UseGuards(AuthGuard)
|
||||
async disable(@Param('id', ParseIntPipe, WordPipe) word: Word) {
|
||||
await this.wordService.enable(word);
|
||||
return new WordResponseDTO(word);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class Word {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@Column({ unique: true })
|
||||
value: string;
|
||||
|
||||
@JoinColumn({ name: 'ownerId' })
|
||||
@@ -25,6 +25,9 @@ export class Word {
|
||||
@Column({ name: 'ownerId' })
|
||||
ownerId: string;
|
||||
|
||||
@Column({ default: false })
|
||||
enabled: boolean;
|
||||
|
||||
@ManyToMany(() => Topic, (topic) => topic.words)
|
||||
@JoinTable()
|
||||
topics: Topic[];
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
PipeTransform,
|
||||
} from '@nestjs/common';
|
||||
import { WordService } from '../services/word.service';
|
||||
|
||||
@Injectable()
|
||||
export class WordPipe implements PipeTransform {
|
||||
constructor(private readonly wordService: WordService) {}
|
||||
|
||||
async transform(id: number) {
|
||||
const word = await this.wordService.getById(id);
|
||||
if (!word) {
|
||||
throw new HttpException('Word not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return word;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,20 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Word } from '../entities/word.entity';
|
||||
import { User } from 'src/users/entities/user.entity';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import {
|
||||
WordCreateEvent,
|
||||
WordDeleteEvent,
|
||||
WordDisableEvent,
|
||||
WordEnableEvent,
|
||||
} from '../../../events/word.events';
|
||||
|
||||
@Injectable()
|
||||
export class WordService {
|
||||
sse_prefix = 'sse.word';
|
||||
constructor(
|
||||
@InjectRepository(Word) private wordRepository: Repository<Word>,
|
||||
private eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
async getById(id: number) {
|
||||
@@ -15,7 +24,8 @@ export class WordService {
|
||||
}
|
||||
|
||||
async delete(word: Word) {
|
||||
return await this.wordRepository.remove(word);
|
||||
await this.wordRepository.remove(word);
|
||||
this.eventEmitter.emit(this.sse_prefix, new WordDeleteEvent(word.id));
|
||||
}
|
||||
|
||||
async create(value: string, owner: User) {
|
||||
@@ -23,11 +33,29 @@ export class WordService {
|
||||
word.owner = owner;
|
||||
word.value = value;
|
||||
await this.wordRepository.save(word);
|
||||
this.eventEmitter.emit(this.sse_prefix, new WordCreateEvent(word));
|
||||
return word;
|
||||
}
|
||||
|
||||
async list() {
|
||||
const words = await this.wordRepository.find({});
|
||||
async listActive() {
|
||||
const words = await this.wordRepository.findBy({ enabled: true });
|
||||
return words;
|
||||
}
|
||||
|
||||
async list() {
|
||||
const words = await this.wordRepository.find();
|
||||
return words;
|
||||
}
|
||||
|
||||
async enable(word: Word) {
|
||||
word.enabled = true;
|
||||
await this.wordRepository.save(word);
|
||||
this.eventEmitter.emit(this.sse_prefix, new WordEnableEvent(word.id));
|
||||
}
|
||||
|
||||
async disable(word: Word) {
|
||||
word.enabled = false;
|
||||
await this.wordRepository.save(word);
|
||||
this.eventEmitter.emit(this.sse_prefix, new WordDisableEvent(word.id));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user