feat: add some security
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"local": {
|
||||
"baseUrl": "http://localhost:3000"
|
||||
"baseUrl": "http://localhost:3000/v1"
|
||||
},
|
||||
"dev": {
|
||||
"baseUrl": "http://localhost:3000"
|
||||
|
||||
@@ -2,4 +2,11 @@ import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('app', () => ({
|
||||
environment: process.env.NODE_ENV || 'production',
|
||||
corsOrigins: process.env.CORS_ORIGINS?.split(',').map((origin) =>
|
||||
origin.trim(),
|
||||
),
|
||||
corsSubdomain: process.env.CORS_SUBDOMAIN,
|
||||
swaggerServers: process.env.SWAGGER_SERVERS?.split(',').map((origin) =>
|
||||
origin.trim(),
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -18,4 +18,7 @@ export default Joi.object({
|
||||
JWT_AUDIENCE: Joi.string().required(),
|
||||
REDIS_HOST: Joi.string().required(),
|
||||
REDIS_PORT: Joi.number().port().default(6379),
|
||||
CORS_ORIGINS: Joi.string().required(),
|
||||
CORS_SUBDOMAIN: Joi.string().required(),
|
||||
SWAGGER_SERVERS: Joi.string().required(),
|
||||
});
|
||||
|
||||
+44
-8
@@ -4,10 +4,13 @@ import { I18nValidationExceptionFilter, I18nValidationPipe } from 'nestjs-i18n';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
||||
import helmet from 'helmet';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
app.setGlobalPrefix('v1');
|
||||
|
||||
app.useGlobalPipes(
|
||||
new I18nValidationPipe({
|
||||
whitelist: true,
|
||||
@@ -32,24 +35,57 @@ async function bootstrap() {
|
||||
|
||||
app.useGlobalInterceptors(new ResponseInterceptor());
|
||||
|
||||
const configService = app.get(ConfigService);
|
||||
|
||||
const origins = configService.get<string[]>('app.corsOrigins');
|
||||
const subdomain = configService.get<string>('app.corsSubdomain');
|
||||
|
||||
console.log(subdomain, origins);
|
||||
|
||||
app.enableCors({
|
||||
origin: ['http://localhost:5173'],
|
||||
origin: (origin: string, callback: any) => {
|
||||
if (!origin) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
if (origins?.includes(origin)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
try {
|
||||
const hostname = new URL(origin).hostname;
|
||||
|
||||
if (hostname === subdomain || hostname.endsWith(`.${subdomain}`)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
return callback(null, true);
|
||||
}
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
callback(new Error('Not allowed by CORS'));
|
||||
},
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
app.use(helmet());
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Borna API')
|
||||
.setDescription('Borna API Documentation')
|
||||
.setTitle('Heala API')
|
||||
.setDescription('Heala API Documentation')
|
||||
.setVersion('1.0')
|
||||
.setLicense('MIT', 'https://opensource.org/license/mit')
|
||||
.addServer('http://localhost:3000')
|
||||
.build();
|
||||
.setLicense('MIT', 'https://opensource.org/license/mit');
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
const swaggerServers = configService.get<string[]>('app.swaggerServers');
|
||||
|
||||
SwaggerModule.setup('api/v1', app, document);
|
||||
swaggerServers?.forEach((server) => config.addServer(server));
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config.build());
|
||||
|
||||
SwaggerModule.setup('v1/docs', app, document);
|
||||
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { AdminAuthService } from './providers/admin-auth.service';
|
||||
import { AdminLoginDTO } from './dtos/admin-login.dto';
|
||||
import { Public } from '@/modules/auth/decorators/public.decorator';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
|
||||
@Controller('admin/auth')
|
||||
@Throttle({ login: {} })
|
||||
export class AdminAuthController {
|
||||
constructor(
|
||||
/**
|
||||
|
||||
@@ -17,9 +17,11 @@ import {
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
|
||||
@Controller('admin/choices')
|
||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||
@Throttle({ admin: {} })
|
||||
export class AdminChoicesController {
|
||||
constructor(
|
||||
/**
|
||||
|
||||
@@ -21,9 +21,11 @@ import {
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
|
||||
@Controller('admin/questions')
|
||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||
@Throttle({ admin: {} })
|
||||
export class AdminQuestionsController {
|
||||
constructor(
|
||||
/**
|
||||
|
||||
@@ -15,9 +15,11 @@ import {
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
|
||||
@Controller('admin/scoring')
|
||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||
@Throttle({ admin: {} })
|
||||
export class AdminScoringController {
|
||||
constructor(
|
||||
/**
|
||||
|
||||
@@ -17,9 +17,11 @@ import {
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
|
||||
@Controller('admin/tests')
|
||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||
@Throttle({ admin: {} })
|
||||
export class AdminTestsController {
|
||||
constructor(
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AuthService } from './providers/auth.service';
|
||||
import { VerifyOtpDTO } from './dtos/verify-otp.dto';
|
||||
import { Public } from './decorators/public.decorator';
|
||||
import { RefreshTokenDTO } from './dtos/refresh-token.dto';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
@@ -17,6 +18,7 @@ export class AuthController {
|
||||
@Public()
|
||||
@Post('login')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Throttle({ login: {} })
|
||||
public async logIn(@Body() loginDto: LoginDTO) {
|
||||
return await this.authService.logIn(loginDto);
|
||||
}
|
||||
@@ -24,6 +26,7 @@ export class AuthController {
|
||||
@Public()
|
||||
@Post('verify-otp')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Throttle({ otp: {} })
|
||||
public async verifyOTP(@Body() verifyOtpDto: VerifyOtpDTO) {
|
||||
return await this.authService.verifyOTP(verifyOtpDto);
|
||||
}
|
||||
@@ -31,6 +34,7 @@ export class AuthController {
|
||||
@Public()
|
||||
@Post('refresh')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Throttle({ login: {} })
|
||||
public async refreshToken(@Body() refreshTokenDto: RefreshTokenDTO) {
|
||||
return await this.authService.refreshToken(refreshTokenDto);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user