Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions infra/dev/compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "3.9"
version: '3.9'

name: task-tracker-api

Expand All @@ -10,7 +10,7 @@ services:
env_file:
- .env
ports:
- "3000:3000"
- '3000:3000'
depends_on:
database:
condition: service_healthy
Expand All @@ -21,10 +21,10 @@ services:
deploy:
resources:
limits:
cpus: "2.0"
cpus: '2.0'
memory: 1024M
reservations:
cpus: "0.5"
cpus: '0.5'
memory: 256M

database:
Expand All @@ -39,41 +39,40 @@ services:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_DATABASE}
ports:
- "6000:5432"
- '6000:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\" -q
|| exit 1",
test: [
'CMD-SHELL',
'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -q
|| exit 1',
]
interval: 5s
timeout: 5s
retries: 5
profiles: [ "infra" ]
profiles: ['infra']

redis:
hostname: redis
container_name: redis
image: redis:7-alpine
restart: always
ports:
- "${REDIS_PORT:-6999}:6379"
- '${REDIS_PORT:-6999}:6379'
command: redis-server --save 60 1 --loglevel notice
volumes:
- redis_data:/data
networks:
- backend
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
timeout: 3s
retries: 5
profiles: [ "infra" ]
profiles: ['infra']

minio:
hostname: minio
Expand All @@ -84,14 +83,14 @@ services:
MINIO_ROOT_USER: ${S3_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY}
ports:
- "9000:9000" # API
- "9001:9001" # Console (UI)
- '9000:9000' # API
- '9001:9001' # Console (UI)
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
networks:
- backend
profiles: [ "infra" ]
profiles: ['infra']

minio-init:
image: minio/mc:latest
Expand All @@ -102,7 +101,7 @@ services:
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY}
networks:
- backend
profiles: [ "infra" ]
profiles: ['infra']
entrypoint: >
/bin/sh -c " sleep 5; mc alias set myminio http://minio:9000
${S3_ACCESS_KEY} ${S3_SECRET_KEY}; mc mb myminio/${S3_BUCKET_NAME}
Expand Down
25 changes: 19 additions & 6 deletions libs/database/src/database.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
DatabaseModuleOptions,
DatabaseModuleOptionsFactory,
} from './interfaces';
import { MigrationService } from './migration.service';

@Module({
providers: [],
Expand All @@ -28,7 +29,11 @@ export class DatabaseModule implements OnApplicationShutdown {
return {
module: DatabaseModule,
global: config.global ?? false,
providers: [this.createOptionsProvider(config), this.createDatabaseProvider()],
providers: [
this.createOptionsProvider(config),
this.createDatabaseProvider(),
MigrationService,
],
exports: [DATABASE_SERVICE],
};
}
Expand All @@ -38,7 +43,11 @@ export class DatabaseModule implements OnApplicationShutdown {
module: DatabaseModule,
global: config.global ?? false,
imports: config.imports ?? [],
providers: [...this.createAsyncProviders(config), this.createDatabaseProvider()],
providers: [
...this.createAsyncProviders(config),
this.createDatabaseProvider(),
MigrationService,
],
exports: [DATABASE_SERVICE],
};
}
Expand All @@ -61,13 +70,17 @@ export class DatabaseModule implements OnApplicationShutdown {
const pool = new Pool({
connectionString: url.toString(),
max: 20,
min: 5,
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 30000,
maxUses: 7500,
min: 2,
connectionTimeoutMillis: 2000,
idleTimeoutMillis: 10000,
maxUses: 5000,
keepAlive: true,
});

pool.on('error', (err) => {
DatabaseModule.logger.error('Database pool connection lost or reset', err);
});

this.pool = pool;

return drizzle(pool, {
Expand Down
5 changes: 5 additions & 0 deletions libs/database/src/interfaces/database-module.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export interface DatabaseModuleOptions {
schema: Record<string, unknown>;
logging?: boolean;
global?: boolean;
/**
* Запускать миграции автоматически при инициализации модуля
* @default true
*/
runMigrations?: boolean;
}

export interface DatabaseModuleOptionsFactory {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Inject, Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { DATABASE_SERVICE, type DatabaseService } from '@libs/database';

import { DATABASE_OPTIONS, DATABASE_SERVICE } from './database.constants';
import type { DatabaseService, DatabaseModuleOptions } from './interfaces';
import * as path from 'path';

@Injectable()
Expand All @@ -10,9 +12,15 @@ export class MigrationService implements OnModuleInit {
constructor(
@Inject(DATABASE_SERVICE)
private readonly db: DatabaseService<Record<string, unknown>>,
@Inject(DATABASE_OPTIONS)
private readonly options: DatabaseModuleOptions,
) {}

async onModuleInit() {
if (this.options.runMigrations === false) {
return;
}

this.logger.debug('Checking for database migrations...');
try {
await migrate(this.db, {
Expand Down
21 changes: 7 additions & 14 deletions src/modules/app/app.module.ts → src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@ import { Module } from '@nestjs/common';
import { ConfigModule } from '@libs/config';
import { DatabaseModule } from '@libs/database';
import { ConfigService } from '@nestjs/config';
import * as schema from '../../shared/entities';
import * as schema from './shared/entities';
import { APP_FILTER, APP_PIPE } from '@nestjs/core';
import { ZodValidationPipe } from 'nestjs-zod';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
import { HealthModule } from '@libs/health';
import { UserModule } from '../user';
import { UserModule } from './user';
import { GlobalExceptionFilter } from '@shared/error';
import { AuthModule } from '../auth';
import { AuthModule } from './auth/auth.module';
import { BullBoardModule } from '@bull-board/nestjs';
import { FastifyAdapter } from '@bull-board/fastify';
import { MailProcessor } from '@shared/workers';
import { BullModule } from '@nestjs/bullmq';
import { MailAdapter } from '@shared/adapters/mail';
import { MigrationService } from '@shared/migration';
import { TeamsModule } from '../teams';
import { ProjectsModule } from '../projects';
import { MailModule } from '@shared/adapters/mail';
import { TeamsModule } from './teams';
import { ProjectsModule } from './projects';

@Module({
imports: [
Expand Down Expand Up @@ -62,12 +60,7 @@ import { ProjectsModule } from '../projects';
HealthModule.register('gateway'),
],
providers: [
MigrationService,
{
provide: 'IMailPort',
useClass: MailAdapter,
},
MailProcessor,
MailModule,
{
provide: APP_PIPE,
useClass: ZodValidationPipe,
Expand Down
66 changes: 66 additions & 0 deletions src/auth/application/auth.facade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Injectable } from '@nestjs/common';
import {
SignInUseCase,
SignUpUseCase,
SignOutUseCase,
SignUpVerifyUseCase,
RefreshTokensUseCase,
ResetPasswordUseCase,
VerifyResetPasswordUseCase,
ConfirmResetPasswordUseCase,
} from './use-cases';
import {
PasswordResetConfirmDto,
ResetPasswordDto,
SignInDto,
SignUpDto,
VerifyDto,
VerifyResetCodeDto,
} from './dtos';
import type { DeviceMetadata } from '../infrastructure/utils/get-device-meta';

@Injectable()
export class AuthFacade {
constructor(
private readonly signInUseCase: SignInUseCase,
private readonly signUpUseCase: SignUpUseCase,
private readonly signOutUseCase: SignOutUseCase,
private readonly signUpVerifyUseCase: SignUpVerifyUseCase,
private readonly refreshTokensUseCase: RefreshTokensUseCase,
private readonly resetPasswordUseCase: ResetPasswordUseCase,
private readonly verifyResetPasswordUseCase: VerifyResetPasswordUseCase,
private readonly confirmResetPasswordUseCase: ConfirmResetPasswordUseCase,
) {}

async signIn(dto: SignInDto, device: DeviceMetadata) {
return this.signInUseCase.execute(dto, device);
}

async signUp(dto: SignUpDto) {
return this.signUpUseCase.execute(dto);
}

async verifySignUp(dto: VerifyDto, device: DeviceMetadata) {
return this.signUpVerifyUseCase.execute(dto, device);
}

async signOut(userId: string) {
return this.signOutUseCase.execute(userId);
}

async refreshTokens(token: string, device: DeviceMetadata) {
return this.refreshTokensUseCase.execute(token, device);
}

async sendResetCode(dto: ResetPasswordDto) {
return this.resetPasswordUseCase.execute(dto);
}

async verifyResetCode(dto: VerifyResetCodeDto) {
return this.verifyResetPasswordUseCase.execute(dto);
}

async confirmNewPassword(dto: PasswordResetConfirmDto) {
return this.confirmResetPasswordUseCase.execute(dto);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { Body, HttpCode, HttpStatus, Post, Req, Res, UseGuards } from '@nestjs/common';
import { AuthService } from '../services';
import {
PostLoginSwagger,
PostLogoutSwagger,
PostRefreshSwagger,
PostRegisterSwagger,
PostSignUpConfirmSwagger,
} from './auth.swagger';
import { SignInDto, SignUpDto, VerifyDto } from '../dtos';
} from './swagger';
import { SignInDto, SignUpDto, VerifyDto } from '../../dtos';
import type { FastifyReply, FastifyRequest } from 'fastify';
import { getDeviceMeta } from '../helpers';
import { BearerAuthGuard, CookieAuthGuard } from '@shared/guards';
import { AuthFacade } from '../../auth.facade';
import { getDeviceMeta } from '@core/auth/infrastructure/utils/get-device-meta';
import { ApiBaseController } from '@shared/decorators';

@ApiBaseController('auth', 'Auth')
export class AuthController {
constructor(private readonly facade: AuthService) {}
constructor(private readonly facade: AuthFacade) {}

@Post('sign-up')
@PostRegisterSwagger()
Expand All @@ -27,13 +27,13 @@ export class AuthController {
@Post('sign-up/confirm')
@PostSignUpConfirmSwagger()
@HttpCode(201)
async verify(
async verifySignUp(
@Res({ passthrough: true }) res: FastifyReply,
@Req() req: FastifyRequest,
@Body() dto: VerifyDto,
) {
const meta = getDeviceMeta(req);
const { tokens, ...response } = await this.facade.verify(dto, meta);
const { tokens, ...response } = await this.facade.verifySignUp(dto, meta);

res.setCookie('refresh', tokens.refresh, {
httpOnly: true,
Expand Down Expand Up @@ -85,7 +85,7 @@ export class AuthController {
async refresh(@Res({ passthrough: true }) res: FastifyReply, @Req() req: FastifyRequest) {
const meta = getDeviceMeta(req);
const session = req.cookies?.['refresh'];
const { tokens, ...response } = await this.facade.refresh(session, meta);
const { tokens, ...response } = await this.facade.refreshTokens(session, meta);

res.setCookie('refresh', tokens.refresh, {
httpOnly: true,
Expand Down
Loading
Loading