Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f86c4d4f2a | |||
| ffb2a1d64a | |||
| 8e74802de7 | |||
| ce1d1396e0 | |||
| e5076603fd |
Generated
-11
@@ -25,7 +25,6 @@
|
|||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"csvtojson": "^2.0.10",
|
"csvtojson": "^2.0.10",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"hcaptcha": "^0.1.1",
|
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
"nodemailer": "^6.7.8",
|
"nodemailer": "^6.7.8",
|
||||||
@@ -5049,11 +5048,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
||||||
},
|
},
|
||||||
"node_modules/hcaptcha": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hcaptcha/-/hcaptcha-0.1.1.tgz",
|
|
||||||
"integrity": "sha512-iMrDmH2VpIEKOrcKWidVjI89FdDKTEdZ7PfPWkP27sTazIIkob8YfdY2ezaufAnWBiUUcvzsn0qF+dyXtBH2Vw=="
|
|
||||||
},
|
|
||||||
"node_modules/hexoid": {
|
"node_modules/hexoid": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
|
||||||
@@ -13436,11 +13430,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
||||||
},
|
},
|
||||||
"hcaptcha": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hcaptcha/-/hcaptcha-0.1.1.tgz",
|
|
||||||
"integrity": "sha512-iMrDmH2VpIEKOrcKWidVjI89FdDKTEdZ7PfPWkP27sTazIIkob8YfdY2ezaufAnWBiUUcvzsn0qF+dyXtBH2Vw=="
|
|
||||||
},
|
|
||||||
"hexoid": {
|
"hexoid": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"csvtojson": "^2.0.10",
|
"csvtojson": "^2.0.10",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"hcaptcha": "^0.1.1",
|
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
"nodemailer": "^6.7.8",
|
"nodemailer": "^6.7.8",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
|
|||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { Serealize } from '../interceptors/serialize.interceptor';
|
import { Serealize } from '../interceptors/serialize.interceptor';
|
||||||
import { HcaptchaGuard } from '../guards/hcaptcha.guard';
|
import { TurnstileGuard } from '../guards/turnstile.guard';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { LoginAdminDto } from './dto/input/login-admin.dto';
|
import { LoginAdminDto } from './dto/input/login-admin.dto';
|
||||||
import { LoginOperadorDto } from './dto/input/login-operador.dto';
|
import { LoginOperadorDto } from './dto/input/login-operador.dto';
|
||||||
@@ -15,7 +15,7 @@ export class AuthController {
|
|||||||
constructor(private authService: AuthService) {}
|
constructor(private authService: AuthService) {}
|
||||||
|
|
||||||
@Serealize(AuthTokenOutputDto)
|
@Serealize(AuthTokenOutputDto)
|
||||||
@UseGuards(HcaptchaGuard)
|
@UseGuards(TurnstileGuard)
|
||||||
@Post('login-admin')
|
@Post('login-admin')
|
||||||
@ApiOperation({ description: 'Login del admin.' })
|
@ApiOperation({ description: 'Login del admin.' })
|
||||||
@ApiBody({
|
@ApiBody({
|
||||||
@@ -27,7 +27,7 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serealize(AuthTokenOutputDto)
|
@Serealize(AuthTokenOutputDto)
|
||||||
@UseGuards(HcaptchaGuard)
|
@UseGuards(TurnstileGuard)
|
||||||
@Post('login-operador')
|
@Post('login-operador')
|
||||||
@ApiOperation({ description: 'Login del operador.' })
|
@ApiOperation({ description: 'Login del operador.' })
|
||||||
@ApiBody({
|
@ApiBody({
|
||||||
@@ -45,7 +45,7 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serealize(AuthTokenOutputDto)
|
@Serealize(AuthTokenOutputDto)
|
||||||
@UseGuards(HcaptchaGuard)
|
@UseGuards(TurnstileGuard)
|
||||||
@Post('login-usuario')
|
@Post('login-usuario')
|
||||||
@ApiOperation({ description: 'Login del usuario.' })
|
@ApiOperation({ description: 'Login del usuario.' })
|
||||||
@ApiBody({
|
@ApiBody({
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
const { verify } = require('hcaptcha');
|
|
||||||
import {
|
|
||||||
BadRequestException,
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
ForbiddenException,
|
|
||||||
Injectable,
|
|
||||||
InternalServerErrorException,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class HcaptchaGuard implements CanActivate {
|
|
||||||
constructor(private configService: ConfigService) {}
|
|
||||||
|
|
||||||
canActivate(
|
|
||||||
context: ExecutionContext,
|
|
||||||
): boolean | Promise<boolean> | Observable<boolean> {
|
|
||||||
const secret = this.configService.get<string>('HCAPTCHA_KEY');
|
|
||||||
const token = context.switchToHttp().getRequest().headers.hcaptcha;
|
|
||||||
|
|
||||||
if (this.configService.get<string>('STATE') !== 'produccion') return true;
|
|
||||||
if (!token)
|
|
||||||
throw new BadRequestException('No se mando un token de hcaptcha.');
|
|
||||||
return verify(secret, token)
|
|
||||||
.then((data) => {
|
|
||||||
if (data.success === true) return true;
|
|
||||||
else throw new ForbiddenException('El token de hcaptcha no es válido.');
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
throw new InternalServerErrorException(
|
|
||||||
'Ocurrió un error con el hcaptcha.',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
ForbiddenException,
|
||||||
|
Injectable,
|
||||||
|
InternalServerErrorException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import * as https from 'https';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
function verifyTurnstile(
|
||||||
|
secret: string,
|
||||||
|
token: string,
|
||||||
|
): Promise<{ success: boolean }> {
|
||||||
|
const body = new URLSearchParams({ secret, response: token }).toString();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = https.request(
|
||||||
|
{
|
||||||
|
hostname: 'challenges.cloudflare.com',
|
||||||
|
path: '/turnstile/v0/siteverify',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Content-Length': Buffer.byteLength(body),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(data));
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
req.on('error', reject);
|
||||||
|
req.write(body);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TurnstileGuard implements CanActivate {
|
||||||
|
constructor(private configService: ConfigService) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
if (this.configService.get<string>('STATE') === 'desarrollo') return true;
|
||||||
|
|
||||||
|
const token = context.switchToHttp().getRequest().headers.turnstile;
|
||||||
|
if (!token)
|
||||||
|
throw new BadRequestException('No se envió un token de turnstile.');
|
||||||
|
|
||||||
|
const secret = this.configService.get<string>('TURNSTILE_SECRET_KEY');
|
||||||
|
return verifyTurnstile(secret, token)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.success === true) return true;
|
||||||
|
throw new ForbiddenException('El token de turnstile no es válido.');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (
|
||||||
|
err instanceof ForbiddenException ||
|
||||||
|
err instanceof BadRequestException
|
||||||
|
)
|
||||||
|
throw err;
|
||||||
|
throw new InternalServerErrorException(
|
||||||
|
'Ocurrió un error con turnstile.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -450,15 +450,35 @@ export class PrestamoService {
|
|||||||
if (filtros.pagina) {
|
if (filtros.pagina) {
|
||||||
options.take = 25;
|
options.take = 25;
|
||||||
options.skip = (parseInt(filtros.pagina) - 1) * 25;
|
options.skip = (parseInt(filtros.pagina) - 1) * 25;
|
||||||
return this.fullInformacionPrestamoView
|
|
||||||
.findAndCount(options)
|
|
||||||
.then((infoPrestamos) => {
|
|
||||||
const prestamos: Prestamo[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < infoPrestamos[0].length; i++)
|
const activoTrue =
|
||||||
prestamos.push(this.fullViewToPrestamo(infoPrestamos[0][i]));
|
(typeof filtros.activo === 'boolean' && filtros.activo) ||
|
||||||
return [prestamos, infoPrestamos[1]];
|
(typeof filtros.activo === 'string' && filtros.activo === 'true');
|
||||||
});
|
const canUseLightCount =
|
||||||
|
activoTrue && !filtros.usuario && !filtros.id_tipo_usuario;
|
||||||
|
|
||||||
|
if (canUseLightCount) {
|
||||||
|
const { usuario: _u, id_tipo_usuario: _tu, ...countWhere } =
|
||||||
|
busqueda;
|
||||||
|
const [rows, total] = await Promise.all([
|
||||||
|
this.fullInformacionPrestamoView.find(options),
|
||||||
|
this.informacionPrestamoView.count({
|
||||||
|
where: countWhere as FindOptionsWhere<InformacionPrestamoView>,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
const prestamos = rows.map((row) => this.fullViewToPrestamo(row));
|
||||||
|
|
||||||
|
return [prestamos, total];
|
||||||
|
}
|
||||||
|
|
||||||
|
const infoPrestamos = await this.fullInformacionPrestamoView.findAndCount(
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
const prestamos: Prestamo[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < infoPrestamos[0].length; i++)
|
||||||
|
prestamos.push(this.fullViewToPrestamo(infoPrestamos[0][i]));
|
||||||
|
return [prestamos, infoPrestamos[1]];
|
||||||
}
|
}
|
||||||
options.order = { id_institucion: 'ASC', id_prestamo: 'ASC' };
|
options.order = { id_institucion: 'ASC', id_prestamo: 'ASC' };
|
||||||
return this.fullInformacionPrestamoView.find(options);
|
return this.fullInformacionPrestamoView.find(options);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
ApiTags,
|
ApiTags,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import { Serealize } from '../interceptors/serialize.interceptor';
|
import { Serealize } from '../interceptors/serialize.interceptor';
|
||||||
import { HcaptchaGuard } from '../guards/hcaptcha.guard';
|
import { TurnstileGuard } from '../guards/turnstile.guard';
|
||||||
import { UsuarioService } from './usuario.service';
|
import { UsuarioService } from './usuario.service';
|
||||||
import { ValidarUsuarioService } from '../validar-usuario/validar-usuario.service';
|
import { ValidarUsuarioService } from '../validar-usuario/validar-usuario.service';
|
||||||
import { Operador } from '../operador/entity/operador.entity';
|
import { Operador } from '../operador/entity/operador.entity';
|
||||||
@@ -39,7 +39,7 @@ export class UsuarioController {
|
|||||||
|
|
||||||
@Serealize(MessageOutputDto)
|
@Serealize(MessageOutputDto)
|
||||||
@Post('registrar')
|
@Post('registrar')
|
||||||
@UseGuards(HcaptchaGuard)
|
@UseGuards(TurnstileGuard)
|
||||||
@ApiOperation({ description: 'Registro de usuario.' })
|
@ApiOperation({ description: 'Registro de usuario.' })
|
||||||
@ApiBody({
|
@ApiBody({
|
||||||
description: 'Variables que necesita el endpoint.',
|
description: 'Variables que necesita el endpoint.',
|
||||||
|
|||||||
Reference in New Issue
Block a user