import { OnModuleInit, Inject, Injectable, HttpException, HttpStatus, InternalServerErrorException, ForbiddenException, } from '@nestjs/common'; import { firstValueFrom, lastValueFrom } from 'rxjs'; import { DucClient } from '../duc/client.config'; import { ClientGrpc } from '@nestjs/microservices'; import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc'; import { CustomerSetLinksRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; import { CustomersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; import { CustomerLinksConfig } from './dtos/customers'; import ErrorCodes from 'src/utils/errorCodes'; import jwt from 'jsonwebtoken'; import { GetSecretValueCommand, SecretsManagerClient, } from '@aws-sdk/client-secrets-manager'; import getEnv from 'src/utils/getEnv'; import { logger } from 'elastic-apm-node'; import { ReadService, ProtoServices as PipelineProtoServices, } from '@dadosfera/protospack-v2/dist/lib/PipelineV2'; import { Metadata } from '@grpc/grpc-js'; import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client'; import DadosferaLogger from '@dadosfera/dadosfera-logs'; // This function will accept any string, which may result in a bug. @Injectable() export class CustomersService implements OnModuleInit { private customerService: CustomersProtoService; private logger: DadosferaLogger; private pipelineReadService: ReadService.PipelineV2ReadService; constructor( @Inject(DucClient.name) private readonly grpcClient: ClientGrpc, @Inject(PipelinesClientConfiguration.name) private readonly pipelinesGrpcClient: ClientGrpc, @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, ) { this.logger = dadosferaLogger.logger; } onModuleInit() { this.customerService = this.grpcClient.getService( ProtoServices.CustomersProtoService, ); this.pipelineReadService = this.pipelinesGrpcClient.getService( PipelineProtoServices.PipelineV2ReadService, ); } async getCustomer(customerId: string) { return await lastValueFrom( this.customerService.CustomerFindOneById({ id: customerId }) ) } async getLinks(customerId: string): Promise { try { const result = await lastValueFrom( this.customerService.CustomerGetLinks({ customerId }), ); return (result.links as CustomerLinksConfig) || null; } catch (err) { if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) throw new HttpException(err.details, HttpStatus.NOT_FOUND); throw err; } } async setLinks(customerId: string, links: CustomerLinksConfig) { if (!customerId || !links) { throw new HttpException(null, HttpStatus.BAD_REQUEST); } try { return await firstValueFrom( this.customerService.CustomerSetLinks({ customerId, links: links as CustomerSetLinksRequest['links'], }), ); } catch (err) { if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) throw new HttpException(err.details, HttpStatus.NOT_FOUND); else throw err; } } async generateToken( expiresIn = '30m', data: { customerId: string; userId: string; customerName: string }, ) { const { customerId, userId: generatedById, customerName } = data; const secretsManagerClient = new SecretsManagerClient({}); const getSecretComand = new GetSecretValueCommand({ SecretId: `${getEnv()}/${customerName}/jwt-signing-key`, }); let private_key_pem; await secretsManagerClient .send(getSecretComand) .then((res) => { const secret = JSON.parse(res.SecretString); private_key_pem = secret.private_key_pem; }) .catch((err) => { logger.error(err.stack); throw new InternalServerErrorException( 'Error finding keys for customer', ); }); let jwt_token; try { jwt_token = jwt.sign( { customerId, generatedById, }, private_key_pem, { algorithm: 'RS256', expiresIn, issuer: 'maestro', }, ); } catch (err) { logger.error(err.stack); throw new InternalServerErrorException('Error generating token'); } // const verify = jwt.verify(jwt_token, public_key_pem, { // algorithms: ['RS256'], // issuer: 'maestro', // }); // const decoded = jwt.decode(jwt_token, { complete: true }); return jwt_token; } async getMonitoringDashboardUrl(metadata: Metadata) { logger.info('CustomersService - getMonitoringDashboardUrl'); const res = await lastValueFrom( this.pipelineReadService.PipelineV2GetDashboardUrl( { dashboard_id: '98', exp: '15m', metabase_customer_name: 'dadosferatech', }, metadata, ), ); logger.info('Done'); return res; } async getLogsDashboardUrl(metadata: Metadata) { logger.info('CustomersService - getMixPanelLogsDashboardUrl'); const res = await lastValueFrom( this.pipelineReadService.PipelineV2GetDashboardUrl( { dashboard_id: '103', exp: '15m', metabase_customer_name: 'dadosferatech', }, metadata, ), ); logger.info('Done'); return res; } async getAccessDashboardUrl(customerName: string, metadata: Metadata) { /* * TODO(Refactor): dar um jeito de exibir o dash da sbm diferente dos outros customer * pois o signicado de department para sbm significa as instituições do usuários */ if (customerName !== 'sbmoffshorecom') { throw new ForbiddenException(); } logger.info('CustomersService - getAccessDashboardUrl'); const res = await this.getDashboardUrl('105', metadata); logger.info('Done'); return res; } private async getDashboardUrl(dashboardId: string, metadata: Metadata) { return await lastValueFrom( this.pipelineReadService.PipelineV2GetDashboardUrl( { dashboard_id: dashboardId, exp: '15m', metabase_customer_name: 'dadosferatech', }, metadata ) ); } async enableEnforceMfa(id: string, enabled: boolean) { return await lastValueFrom( this.customerService.CustomerUpdateEnforceMfa({ customerId: id, enforceMfa: enabled }) ) } async updateOrganizationInfo( customerId: string, data: { companyName: string; companySite: string; domain: string; cnpj: string; description: string; }, ) { try { const result = await lastValueFrom( this.customerService.OrganizationUpdate({ customerId, companyName: data.companyName || '', companySite: data.companySite || '', domain: data.domain || '', cnpj: data.cnpj || '', description: data.description || '', }), ); return result; } catch (err) { if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) throw new HttpException(err.details, HttpStatus.NOT_FOUND); else throw err; } } async getOrganizationInfo(customerId: string) { try { const customerResponse = await lastValueFrom( this.customerService.CustomerFindOneById({ id: customerId }) ); const customer = customerResponse.customer; return { companyName: customer.companyName || '', companySite: customer.companySite || '', domain: customer.domain || '', cnpj: customer.cnpj || '', description: customer.description || '' }; } catch (err) { if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) throw new HttpException(err.details, HttpStatus.NOT_FOUND); else throw err; } } }