Skip to content
This repository has been archived by the owner on Feb 16, 2024. It is now read-only.

Commit

Permalink
feat: add centrifugo to handle real time messages
Browse files Browse the repository at this point in the history
  • Loading branch information
relby committed Jan 31, 2024
1 parent 0a5c83b commit ff08f1f
Show file tree
Hide file tree
Showing 17 changed files with 326 additions and 13 deletions.
26 changes: 25 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,26 @@ services:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_DB: ${POSTGRES_DB}

centrifugo:
container_name: poketrade-centrifugo
image: centrifugo/centrifugo:v5.2.0
environment:
CENTRIFUGO_PORT: ${CENTRIFUGO_PORT}
CENTRIFUGO_ALLOWED_ORIGINS: ${CENTRIFUGO_ALLOWED_ORIGINS}
CENTRIFUGO_API_KEY: ${CENTRIFUGO_API_KEY}
CENTRIFUGO_TOKEN_HMAC_SECRET_KEY: ${CENTRIFUGO_TOKEN_HMAC_SECRET_KEY}
CENTRIFUGO_NAMESPACES: "[{\"name\": \"personal\"}]"

CENTRIFUGO_ADMIN: "true"
CENTRIFUGO_ADMIN_SECRET: ${CENTRIFUGO_ADMIN_SECRET}
CENTRIFUGO_ADMIN_PASSWORD: ${CENTRIFUGO_ADMIN_PASSWORD}
ulimits:
nofile:
soft: 65535
hard: 65535
ports:
- ${POSTGRES_PORT}:${POSTGRES_PORT}
- ${CENTRIFUGO_PORT}:${CENTRIFUGO_PORT}

app:
build: ./
Expand All @@ -26,9 +44,15 @@ services:

JWT_SECRET: ${JWT_SECRET}
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN}

CENTRIFUGO_API_URL: http://centrifugo:${CENTRIFUGO_PORT}/api
CENTRIFUGO_API_KEY: ${CENTRIFUGO_API_KEY}
CENTRIFUGO_TOKEN_HMAC_SECRET_KEY: ${CENTRIFUGO_TOKEN_HMAC_SECRET_KEY}
depends_on:
- postgres
- centrifugo
links:
- postgres
- centrifugo
ports:
- ${PORT}:${PORT}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
"@nestjs/common": "10.3.1",
"@nestjs/config": "3.1.1",
"@nestjs/core": "10.3.1",
"@nestjs/event-emitter": "2.0.3",
"@nestjs/jwt": "10.2.0",
"@nestjs/passport": "10.0.3",
"@nestjs/platform-express": "10.3.1",
"@nestjs/swagger": "7.2.0",
"bcrypt": "5.1.1",
"cent.js": "4.0.0",
"class-transformer": "0.5.1",
"class-validator": "0.14.1",
"dotenv": "16.4.1",
Expand Down
41 changes: 41 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions public/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,58 @@
}
]
}
},
"/api/centrifugo/token/connection": {
"post": {
"operationId": "CentrifugoController_generateCentrifugoConnectionToken",
"parameters": [],
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenerateCentrifugoConnectionTokenOutputDTO"
}
}
}
}
},
"tags": [
"Centrifugo"
],
"security": [
{
"AccessToken": []
}
]
}
},
"/api/centrifugo/token/subscription": {
"post": {
"operationId": "CentrifugoController_generateCentrifugoSubscriptionToken",
"parameters": [],
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GenerateCentrifugoSubscriptionTokenOutputDTO"
}
}
}
}
},
"tags": [
"Centrifugo"
],
"security": [
{
"AccessToken": []
}
]
}
}
},
"info": {
Expand Down Expand Up @@ -1031,6 +1083,28 @@
"receiver",
"status"
]
},
"GenerateCentrifugoConnectionTokenOutputDTO": {
"type": "object",
"properties": {
"connectionToken": {
"type": "string"
}
},
"required": [
"connectionToken"
]
},
"GenerateCentrifugoSubscriptionTokenOutputDTO": {
"type": "object",
"properties": {
"subscriptionToken": {
"type": "string"
}
},
"required": [
"subscriptionToken"
]
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/api/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import { TradesController } from './controllers/trades.controller';
import { TradesModule } from 'src/infra/ioc/trades.module';
import { TradeProfile } from './profiles/trade.profile';
import { pojos } from '@automapper/pojos';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { CentrifugoModule } from 'src/infra/centrifugo/centrifugo.module';
import { CentrifugoController } from './controllers/centrifugo.controller';

@Module({
imports: [
Expand All @@ -38,6 +41,9 @@ import { pojos } from '@automapper/pojos';
}),
PassportModule,

EventEmitterModule.forRoot(),
CentrifugoModule,

AuthModule,
PokemonsModule,
UsersModule,
Expand All @@ -62,6 +68,7 @@ import { pojos } from '@automapper/pojos';
UsersController,
PacksController,
TradesController,
CentrifugoController,
],
})
export class ApiModule {}
42 changes: 42 additions & 0 deletions src/api/controllers/centrifugo.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Controller, HttpCode, HttpStatus, Post, UseGuards } from '@nestjs/common';
import { CentrifugoService } from 'src/infra/centrifugo/centrifugo.service';
import { User } from '../decorators/user.decorator';
import { UserEntity } from 'src/infra/postgres/tables';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { ApiCreatedResponse, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { GenerateCentrifugoConnectionTokenOutputDTO } from '../dtos/centrifugo/generate-centrifugo-connection-token.output.dto';
import { GenerateCentrifugoSubscriptionTokenOutputDTO } from '../dtos/centrifugo/generate-centrifugo-subscription-token.output.dto';

@ApiTags('Centrifugo')
@Controller('centrifugo')
export class CentrifugoController {
public constructor(
private readonly centrifugoService: CentrifugoService,
) {}

@ApiCreatedResponse({ type: GenerateCentrifugoConnectionTokenOutputDTO })
@ApiSecurity('AccessToken')
@Post('token/connection')
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard)
public async generateCentrifugoConnectionToken(
@User() user: UserEntity,
): Promise<GenerateCentrifugoConnectionTokenOutputDTO> {
const connectionToken = await this.centrifugoService.generateCentrifugoConnectionToken(user);

return { connectionToken };
}

@ApiCreatedResponse({ type: GenerateCentrifugoSubscriptionTokenOutputDTO })
@ApiSecurity('AccessToken')
@Post('token/subscription')
@HttpCode(HttpStatus.CREATED)
@UseGuards(JwtAuthGuard)
public async generateCentrifugoSubscriptionToken(
@User() user: UserEntity,
): Promise<GenerateCentrifugoSubscriptionTokenOutputDTO> {
const subscriptionToken = await this.centrifugoService.generateCentrifugoSubscriptionToken(user);

return { subscriptionToken };
}
}
2 changes: 1 addition & 1 deletion src/api/controllers/trades.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class TradesController {
return this.mapper.map<PendingTradeEntity, PendingTradeOutputDTO>(
pendingTrade,
'PendingTradeEntity',
'CreatePendingTradeOutputDTO',
'PendingTradeOutputDTO',
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { JWT } from 'src/common/types';

export class GenerateCentrifugoConnectionTokenOutputDTO {
@ApiProperty()
connectionToken: JWT;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { JWT } from 'src/common/types';

export class GenerateCentrifugoSubscriptionTokenOutputDTO {
@ApiProperty()
subscriptionToken: JWT;
}
2 changes: 1 addition & 1 deletion src/api/strategies/jwt-auth.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy) {
}

public async validate(tokenPayload: UserTokenPayload): Promise<UserEntity> {
return this.usersService.getUser({ id: tokenPayload.id }, {
return this.usersService.getUser({ id: tokenPayload.sub }, {
errorMessage: 'Unauthorized',
errorStatus: HttpStatus.UNAUTHORIZED,
});
Expand Down
2 changes: 2 additions & 0 deletions src/common/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const PENDING_TRADE_CREATED_EVENT = 'pending-trade.created';
export const PENDING_TRADE_ACCEPTED_EVENT = 'pending-trade.accepted';
2 changes: 1 addition & 1 deletion src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type RemovePropertiesWith<T extends Record<string, unknown>, U> = {
export type RemovePropertiesWithNever<T extends Record<string, unknown>> = RemovePropertiesWith<T, never>;

export type UserTokenPayload = {
id: UUIDv4;
sub: UUIDv4;
};

export type PaginatedArray<T> = {
Expand Down
8 changes: 2 additions & 6 deletions src/core/use-cases/auth.use-case.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { RegisterUserInputDTO } from 'src/api/dtos/auth/register-user.input.dto';
import { JWT, UserTokenPayload } from 'src/common/types';
import { JWT } from 'src/common/types';
import { UsersUseCase } from './users.use-case';
import * as bcrypt from 'bcrypt';
import { UserEntity } from 'src/infra/postgres/tables';
Expand All @@ -14,11 +14,7 @@ export class AuthUseCase {
) {}

private async generateAccessToken(user: UserEntity): Promise<JWT> {
const userTokenPayload: UserTokenPayload = {
id: user.id,
};

return this.jwtService.signAsync(userTokenPayload) as Promise<JWT>;
return this.jwtService.signAsync({}, { subject: user.id }) as Promise<JWT>;
}

public async loginUser(user: UserEntity): Promise<{ accessToken: JWT }> {
Expand Down
Loading

0 comments on commit ff08f1f

Please sign in to comment.