Template
feat : use dto for serialization
This commit is contained in:
Vendored
+3
-1
@@ -2,5 +2,7 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
}
|
||||
},
|
||||
"javascript.preferences.quoteStyle": "single",
|
||||
"typescript.preferences.quoteStyle": "single"
|
||||
}
|
||||
|
||||
Generated
+7
@@ -18,6 +18,7 @@
|
||||
"@nestjs/swagger": "^7.3.1",
|
||||
"@nestjs/typeorm": "^10.0.2",
|
||||
"@types/ranjs": "^1.22.7",
|
||||
"class-transformer": "^0.5.1",
|
||||
"connect-redis": "^8.0.1",
|
||||
"express-session": "^1.18.0",
|
||||
"pg": "^8.13.1",
|
||||
@@ -4488,6 +4489,12 @@
|
||||
"integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/class-transformer": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
|
||||
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@nestjs/swagger": "^7.3.1",
|
||||
"@nestjs/typeorm": "^10.0.2",
|
||||
"@types/ranjs": "^1.22.7",
|
||||
"class-transformer": "^0.5.1",
|
||||
"connect-redis": "^8.0.1",
|
||||
"express-session": "^1.18.0",
|
||||
"pg": "^8.13.1",
|
||||
|
||||
@@ -17,6 +17,7 @@ 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';
|
||||
|
||||
@ApiTags('lobby')
|
||||
@Controller('games')
|
||||
@@ -52,9 +53,9 @@ export class LobbyController {
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'List all games' })
|
||||
findAll() {
|
||||
const games = this.lobbyService.findAll();
|
||||
return games;
|
||||
async findAll(): Promise<GameResponseDTO[]> {
|
||||
const games = await this.lobbyService.findAll();
|
||||
return games.map((g) => new GameResponseDTO(g));
|
||||
}
|
||||
|
||||
@Sse('/subscribe')
|
||||
@@ -70,9 +71,11 @@ export class LobbyController {
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get game information' })
|
||||
async findOne(@Param('id') id: string, @Param('id', GamePipe) game: Game) {
|
||||
return game;
|
||||
// return new ReadGameDto(game);
|
||||
async findOne(
|
||||
@Param('id') id: string,
|
||||
@Param('id', GamePipe) game: Game,
|
||||
): Promise<GameResponseDTO> {
|
||||
return new GameResponseDTO(game);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Expose, Type } from 'class-transformer';
|
||||
import { ConceptResponseDTO } from './conceptresponse.dto';
|
||||
|
||||
export class ConceptInTurnResponseDTO {
|
||||
constructor(partial: Partial<ConceptInTurnResponseDTO>) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
@Type(() => ConceptResponseDTO)
|
||||
@Expose()
|
||||
concept: ConceptResponseDTO;
|
||||
|
||||
@Expose()
|
||||
order: number;
|
||||
|
||||
@Expose()
|
||||
subconcept: number;
|
||||
|
||||
@Expose()
|
||||
markers: number;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Expose } from 'class-transformer';
|
||||
|
||||
export class ConceptResponseDTO {
|
||||
constructor(partial: Partial<ConceptResponseDTO>) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
|
||||
@Expose()
|
||||
id: number;
|
||||
|
||||
@Expose()
|
||||
value: string;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Expose, Type } from 'class-transformer';
|
||||
import { UserResponseDTO } from 'src/users/dto/userresponse.dto';
|
||||
import { PlayerResponseDTO } from './playerresponse.dto';
|
||||
import { ConceptInTurnResponseDTO } from './conceptinturnresponse.dto';
|
||||
|
||||
export class GameResponseDTO {
|
||||
constructor(partial: Partial<GameResponseDTO>) {
|
||||
Object.assign(this, partial);
|
||||
this.owner = partial.owner;
|
||||
this.players = partial.players;
|
||||
this.currentPlayer = partial.currentPlayer;
|
||||
this.currentConcepts = partial.currentConcepts;
|
||||
}
|
||||
|
||||
@Expose()
|
||||
id: number;
|
||||
|
||||
@Type(() => UserResponseDTO)
|
||||
@Expose()
|
||||
owner: UserResponseDTO;
|
||||
|
||||
@Type(() => PlayerResponseDTO)
|
||||
@Expose()
|
||||
players: PlayerResponseDTO[];
|
||||
|
||||
@Type(() => PlayerResponseDTO)
|
||||
@Expose()
|
||||
currentPlayer: PlayerResponseDTO;
|
||||
|
||||
@Expose()
|
||||
started: boolean;
|
||||
|
||||
@Type(() => ConceptInTurnResponseDTO)
|
||||
@Expose()
|
||||
currentConcepts: ConceptInTurnResponseDTO[];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Expose, Transform, Type } from 'class-transformer';
|
||||
import { UserResponseDTO } from 'src/users/dto/userresponse.dto';
|
||||
|
||||
export class PlayerResponseDTO {
|
||||
constructor(partial: Partial<PlayerResponseDTO>) {
|
||||
Object.assign(this, partial);
|
||||
this.user = partial.user;
|
||||
}
|
||||
|
||||
@Type(() => UserResponseDTO)
|
||||
@Expose()
|
||||
user: UserResponseDTO;
|
||||
|
||||
@Expose()
|
||||
get userId(): string {
|
||||
return this.user.userId;
|
||||
}
|
||||
|
||||
@Expose()
|
||||
score: number;
|
||||
}
|
||||
@@ -19,10 +19,10 @@ export class Game {
|
||||
@Column()
|
||||
seed: string;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@ManyToOne(() => User, { eager: true })
|
||||
owner: User;
|
||||
|
||||
@OneToMany(() => Player, (player) => player.game, { onDelete: 'CASCADE' })
|
||||
@OneToMany(() => Player, (player) => player.game, { eager: true })
|
||||
@JoinTable()
|
||||
players: Player[];
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export class Player {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@ManyToOne(() => User, { eager: true })
|
||||
user: User;
|
||||
|
||||
@ManyToOne(() => Game)
|
||||
@@ -25,4 +25,8 @@ export class Player {
|
||||
|
||||
@Column({ default: 0 })
|
||||
score: number;
|
||||
|
||||
get userId(): string {
|
||||
return this.user.userId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class GamePipe implements PipeTransform {
|
||||
) {}
|
||||
|
||||
async transform(value: any) {
|
||||
const game = await this.gameRepository.findBy({ id: value });
|
||||
const game = await this.gameRepository.findOneBy({ id: value });
|
||||
if (!game) {
|
||||
throw new HttpException('Game not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
+9
-1
@@ -1,7 +1,8 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { NestFactory, Reflector } from '@nestjs/core';
|
||||
import * as session from 'express-session';
|
||||
import { AppModule } from './app.module';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { ClassSerializerInterceptor, INestApplication } from '@nestjs/common';
|
||||
import { RedisStore } from 'connect-redis';
|
||||
import { createClient } from 'redis';
|
||||
|
||||
@@ -30,6 +31,13 @@ async function bootstrap() {
|
||||
}),
|
||||
);
|
||||
|
||||
app.useGlobalInterceptors(
|
||||
new ClassSerializerInterceptor(app.get(Reflector), {
|
||||
// strategy: 'excludeAll', 👈 we'll talk about this later
|
||||
excludeExtraneousValues: true,
|
||||
}),
|
||||
);
|
||||
|
||||
await app.listen(8765);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export class CreateUserDto {
|
||||
method: 'discord' | 'password';
|
||||
username?: string;
|
||||
password: string;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class LoginDto {
|
||||
constructor(userId: string, username: string, avatar: string) {
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.avatar = avatar;
|
||||
}
|
||||
@ApiProperty()
|
||||
userId: string;
|
||||
|
||||
@ApiProperty()
|
||||
username: string;
|
||||
|
||||
@ApiProperty()
|
||||
avatar: string;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Expose } from 'class-transformer';
|
||||
|
||||
export class UserResponseDTO {
|
||||
constructor(partial: Partial<UserResponseDTO>) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
@Expose()
|
||||
userId: string;
|
||||
|
||||
@Expose()
|
||||
username: string;
|
||||
|
||||
@Expose()
|
||||
avatar: string;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Redirect,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import { UserResponseDTO } from './dto/userresponse.dto';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
@@ -39,14 +39,16 @@ export class UsersController {
|
||||
}
|
||||
|
||||
@Get('/whoami')
|
||||
async remove(@Session() session: Record<string, any>): Promise<LoginDto> {
|
||||
async remove(
|
||||
@Session() session: Record<string, any>,
|
||||
): Promise<UserResponseDTO> {
|
||||
if (!session.tokenData) {
|
||||
throw new HttpException(
|
||||
"Session doesn't contains auth information",
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const user = await this.usersService.discordLogin(session);
|
||||
return user;
|
||||
await this.usersService.discordLogin(session);
|
||||
return new UserResponseDTO(session.user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import { UserResponseDTO } from './dto/userresponse.dto';
|
||||
import { URLSearchParams } from 'url';
|
||||
import { User } from './entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
@@ -108,7 +108,7 @@ export class UsersService {
|
||||
async discordLogin(
|
||||
session: Record<string, any>,
|
||||
recursive: boolean = false,
|
||||
): Promise<LoginDto> {
|
||||
): Promise<User> {
|
||||
const req2 = await fetch('https://discord.com/api/users/@me', {
|
||||
headers: {
|
||||
authorization:
|
||||
@@ -135,13 +135,12 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
const data2 = await req2.json();
|
||||
const dto = new LoginDto(data2.id, data2.global_name, data2.avatar);
|
||||
const user = await this.getOrCreateUser(dto.userId, dto);
|
||||
const user = await this.getOrCreateUser(data2.id, data2);
|
||||
session.user = user;
|
||||
return dto;
|
||||
return user;
|
||||
}
|
||||
|
||||
async getOrCreateUser(userId: string, data?: LoginDto) {
|
||||
async getOrCreateUser(userId: string, data?: UserResponseDTO) {
|
||||
let user = await this.usersRepository.findOneBy({ userId });
|
||||
if (!user) {
|
||||
user = this.usersRepository.create({ ...data, userId });
|
||||
|
||||
Reference in New Issue
Block a user