From cd6ac4f42afa726c058767750dfb4a2e26c919f4 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 10 Nov 2023 20:03:59 +0100 Subject: [PATCH] feat: big update --- packages/api/.env.example | 3 + packages/api/package.json | 3 + packages/api/prisma/schema.prisma | 30 ++++- .../connections/connections.controller.ts | 14 +-- .../@core/connections/connections.module.ts | 4 +- .../services/connections.service.ts | 40 +++--- .../{index.ts => crm-connection.service.ts} | 118 ++++++++---------- .../src/@core/sentry/sentry.interceptor.ts | 22 ++++ .../api/src/@core/sentry/sentry.module.ts | 32 +++++ .../api/src/@core/tasks/tasks.service.spec.ts | 18 +++ packages/api/src/@core/tasks/tasks.service.ts | 45 +++++++ packages/api/src/@core/utils/config.ts | 7 +- packages/api/src/app.module.ts | 15 ++- pnpm-lock.yaml | 91 +++++++++++++- 14 files changed, 334 insertions(+), 108 deletions(-) rename packages/api/src/@core/connections/services/crm/{index.ts => crm-connection.service.ts} (83%) create mode 100644 packages/api/src/@core/sentry/sentry.interceptor.ts create mode 100644 packages/api/src/@core/sentry/sentry.module.ts create mode 100644 packages/api/src/@core/tasks/tasks.service.spec.ts create mode 100644 packages/api/src/@core/tasks/tasks.service.ts diff --git a/packages/api/.env.example b/packages/api/.env.example index 74ccd6524..48540c9b6 100644 --- a/packages/api/.env.example +++ b/packages/api/.env.example @@ -1,8 +1,11 @@ ENV=dev +PROD_DISTRIBUTION=managed # could be self-host if you want to run it locally DATABASE_URL= JWT_SECRET="SECRET" POSTGRES_HOST= +# Sentry for logging errors +SENTRY_DSN=your_sentry_dsn_here # INTEGRATIONS PROVIDER CREDENTIALS FOR PANORA # CRM diff --git a/packages/api/package.json b/packages/api/package.json index 5fc295e45..b75d51990 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -28,8 +28,11 @@ "@nestjs/mapped-types": "*", "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", + "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.14", "@prisma/client": "^5.4.2", + "@sentry/node": "^7.80.0", + "@sentry/tracing": "^7.80.0", "axios": "^1.5.1", "bcrypt": "^5.1.1", "crypto": "^1.0.1", diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index e0e4c581f..8c434c3b5 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -81,12 +81,13 @@ model organizations { } model projects { - id_project BigInt @id(map: "pk_projects") @default(autoincrement()) + id_project BigInt @id(map: "pk_projects") @default(autoincrement()) name String id_organization BigInt api_keys api_keys[] connections connections[] - organizations organizations @relation(fields: [id_organization], references: [id_organization], onDelete: NoAction, onUpdate: NoAction, map: "fk_6") + linked_users linked_users[] + organizations organizations @relation(fields: [id_organization], references: [id_organization], onDelete: NoAction, onUpdate: NoAction, map: "fk_6") @@index([id_organization], map: "fk_1_projects") } @@ -109,16 +110,33 @@ model users { /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model connections { - id_connection BigInt @id(map: "pk_connections") @default(autoincrement()) + id_connection BigInt @id(map: "pk_connections") @default(autoincrement()) provider_slug String + account_url String? token_type String access_token String? refresh_token String? - expiration_timestamp DateTime? @db.Timestamp(6) - created_at DateTime @db.Timestamp(6) + expiration_timestamp DateTime? @db.Timestamp(6) + created_at DateTime @db.Timestamp(6) id_project BigInt - projects projects @relation(fields: [id_project], references: [id_project], onDelete: NoAction, onUpdate: NoAction, map: "fk_9") + id_linked_user BigInt + linked_users linked_users @relation(fields: [id_linked_user], references: [id_linked_user], onDelete: NoAction, onUpdate: NoAction, map: "fk_11") + projects projects @relation(fields: [id_project], references: [id_project], onDelete: NoAction, onUpdate: NoAction, map: "fk_9") @@unique([access_token, refresh_token], map: "index_3") @@index([id_project], map: "fk_1") + @@index([id_linked_user], map: "fk_connections_to_linkedusersid") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model linked_users { + id_linked_user BigInt @id(map: "key_id_linked_users") @default(autoincrement()) + linked_user_origin_id String + alias String + status String + id_project BigInt + connections connections[] + projects projects @relation(fields: [id_project], references: [id_project], onDelete: NoAction, onUpdate: NoAction, map: "fk_10") + + @@index([id_project], map: "fk_proectid_linked_users") } diff --git a/packages/api/src/@core/connections/connections.controller.ts b/packages/api/src/@core/connections/connections.controller.ts index ddbdf4db1..9b2a920e6 100644 --- a/packages/api/src/@core/connections/connections.controller.ts +++ b/packages/api/src/@core/connections/connections.controller.ts @@ -10,7 +10,7 @@ export class ConnectionsController { handleCRMCallback( @Res() res: Response, @Query('projectId') projectId: string, - @Query('customerId') customerId: string, + @Query('linkedUserId') linkedUserId: string, @Query('providerName') providerName: string, @Query('returnUrl') returnUrl: string, @Query('code') code: string, @@ -20,21 +20,11 @@ export class ConnectionsController { this.connectionsService.handleCRMCallBack( projectId, - customerId, + linkedUserId, providerName, code, zohoAccountURL, ); res.redirect(returnUrl); } - - @Get('oauth/crm/refresh') - handleCRMTokensRefresh( - @Query('customerId') customerId: string, - @Query('providerName') providerName: string, - ) { - //TODO; ADD VERIFICATION OF PARAMS - - this.connectionsService.handleCRMTokensRefresh(customerId, providerName); - } } diff --git a/packages/api/src/@core/connections/connections.module.ts b/packages/api/src/@core/connections/connections.module.ts index e4cdbc1b8..3ce9d156b 100644 --- a/packages/api/src/@core/connections/connections.module.ts +++ b/packages/api/src/@core/connections/connections.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; import { ConnectionsService } from './services/connections.service'; import { ConnectionsController } from './connections.controller'; +import { CrmConnectionsService } from './services/crm/crm-connection.service'; +import { PrismaService } from '../prisma/prisma.service'; @Module({ controllers: [ConnectionsController], - providers: [ConnectionsService], + providers: [ConnectionsService, CrmConnectionsService, PrismaService], }) export class ConnectionsModule {} diff --git a/packages/api/src/@core/connections/services/connections.service.ts b/packages/api/src/@core/connections/services/connections.service.ts index 498e66825..7262297a5 100644 --- a/packages/api/src/@core/connections/services/connections.service.ts +++ b/packages/api/src/@core/connections/services/connections.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { CrmConnectionsService } from './crm'; +import { CrmConnectionsService } from './crm/crm-connection.service'; import { NotFoundError } from 'src/@core/utils/errors'; -import { PrismaService } from 'src/@core/prisma/prisma.service'; @Injectable() export class ConnectionsService { @@ -19,14 +18,11 @@ export class ConnectionsService { // we catch the tmp token and swap it against oauth2 server for access/refresh tokens // to perform actions on his behalf // this call pass 1. integrationID 2. CustomerId 3. Panora Api Key - constructor( - private crmConnectionService: CrmConnectionsService, - private prismaService: PrismaService, - ) {} + constructor(private crmConnectionService: CrmConnectionsService) {} async handleCRMCallBack( projectId: string, - customerId: string, + linkedUserId: string, providerName: string, code: string, zohoAccountURL?: string, @@ -38,7 +34,7 @@ export class ConnectionsService { throw new NotFoundError('no hubspot code found'); } return this.crmConnectionService.handleHubspotCallback( - customerId, + linkedUserId, projectId, code, ); @@ -47,7 +43,7 @@ export class ConnectionsService { throw new NotFoundError('no zoho code/ zoho AccountURL found'); } return this.crmConnectionService.handleZohoCallback( - customerId, + linkedUserId, projectId, code, zohoAccountURL, @@ -57,7 +53,7 @@ export class ConnectionsService { throw new NotFoundError('no pipedrive code found'); } return this.crmConnectionService.handlePipedriveCallback( - customerId, + linkedUserId, projectId, code, ); @@ -69,7 +65,7 @@ export class ConnectionsService { throw new NotFoundError('no zendesk code found'); } return this.crmConnectionService.handleZendeskCallback( - customerId, + linkedUserId, projectId, code, ); @@ -84,25 +80,37 @@ export class ConnectionsService { } } - async handleCRMTokensRefresh(customerId: string, providerId: string) { + async handleCRMTokensRefresh( + connectionId: bigint, + providerId: string, + refresh_token: string, + account_url?: string, + ) { try { switch (providerId) { case 'hubspot': return this.crmConnectionService.handleHubspotTokenRefresh( - customerId, + connectionId, + refresh_token, ); case 'zoho': - return this.crmConnectionService.handleZohoTokenRefresh(customerId); + return this.crmConnectionService.handleZohoTokenRefresh( + connectionId, + refresh_token, + account_url, + ); case 'pipedrive': return this.crmConnectionService.handlePipedriveTokenRefresh( - customerId, + connectionId, + refresh_token, ); case 'freshsales': //todo: LATER break; case 'zendesk': return this.crmConnectionService.handleZendeskTokenRefresh( - customerId, + connectionId, + refresh_token, ); default: return; diff --git a/packages/api/src/@core/connections/services/crm/index.ts b/packages/api/src/@core/connections/services/crm/crm-connection.service.ts similarity index 83% rename from packages/api/src/@core/connections/services/crm/index.ts rename to packages/api/src/@core/connections/services/crm/crm-connection.service.ts index 5c3ea3938..cee6644b1 100644 --- a/packages/api/src/@core/connections/services/crm/index.ts +++ b/packages/api/src/@core/connections/services/crm/crm-connection.service.ts @@ -15,11 +15,12 @@ export class CrmConnectionsService { constructor(private prisma: PrismaService) {} async handleHubspotCallback( - customerId: string, + linkedUserId: string, projectId: string, code: string, ) { try { + //first create a linked_user //reconstruct the redirect URI that was passed in the frontend it must be the same const REDIRECT_URI = `${config.OAUTH_REDIRECT_BASE}/oauth/crm/callback`; //tocheck @@ -47,8 +48,13 @@ export class CrmConnectionsService { expiration_timestamp: new Date( new Date().getTime() + data.expires_in * 1000, ), - id_project: Number(projectId), - id_end_customer: customerId, + created_at: new Date(), + projects: { + connect: { id_project: BigInt(projectId) }, + }, + linked_users: { + connect: { id_linked_user: BigInt(linkedUserId) }, + }, //id of the end-customer defined in the company application, this is how requests could be made on behlaf of the user // without it, we cant retrieve the right row in our db }, @@ -66,7 +72,7 @@ export class CrmConnectionsService { } async handleZohoCallback( - customerId: string, + linkedUserId: string, projectId: string, code: string, accountURL: string, @@ -86,12 +92,9 @@ export class CrmConnectionsService { `https://${accountURL}/oauth/v2/token`, formData, ); - //TODO: handle if res throws an error const data: ZohoOAuthResponse = res.data; console.log('OAuth credentials : zoho ', data); - // save tokens for this customer inside our db //TODO: encrypt the access token and refresh tokens - //TODO: add account_url for ZOHO const db_res = await this.prisma.connections.create({ data: { provider_slug: 'zoho', @@ -101,8 +104,13 @@ export class CrmConnectionsService { expiration_timestamp: new Date( new Date().getTime() + data.expires_in * 1000, ), - id_project: Number(projectId), - id_end_customer: customerId, + created_at: new Date(), + projects: { + connect: { id_project: BigInt(projectId) }, + }, + linked_users: { + connect: { id_linked_user: BigInt(linkedUserId) }, + }, account_url: accountURL, }, }); @@ -119,7 +127,7 @@ export class CrmConnectionsService { } async handlePipedriveCallback( - customerId: string, + linkedUserId: string, projectId: string, code: string, ) { @@ -158,8 +166,13 @@ export class CrmConnectionsService { expiration_timestamp: new Date( new Date().getTime() + data.expires_in * 1000, ), - id_project: Number(projectId), - id_end_customer: customerId, + created_at: new Date(), + projects: { + connect: { id_project: BigInt(projectId) }, + }, + linked_users: { + connect: { id_linked_user: BigInt(linkedUserId) }, + }, }, }); } catch (error) { @@ -180,7 +193,7 @@ export class CrmConnectionsService { } async handleZendeskCallback( - customerId: string, + linkedUserId: string, projectId: string, code: string, ) { @@ -219,8 +232,13 @@ export class CrmConnectionsService { expiration_timestamp: new Date( new Date().getTime() + data.expires_in * 1000, ), - id_project: Number(projectId), - id_end_customer: customerId, + created_at: new Date(), + projects: { + connect: { id_project: BigInt(projectId) }, + }, + linked_users: { + connect: { id_linked_user: BigInt(linkedUserId) }, + }, }, }); } catch (error) { @@ -235,15 +253,8 @@ export class CrmConnectionsService { } } - async handleHubspotTokenRefresh(customerId: string) { + async handleHubspotTokenRefresh(connectionId: bigint, refresh_token: string) { try { - //get the refresh token for the expired one - const connection = await this.prisma.connections.findFirst({ - where: { - id_end_customer: customerId, - provider_slug: 'hubspot', - }, - }); const REDIRECT_URI = `${config.OAUTH_REDIRECT_BASE}/oauth/crm/callback`; const formData = { @@ -251,7 +262,7 @@ export class CrmConnectionsService { client_id: config.HUBSPOT_CLIENT_ID, client_secret: config.HUBSPOT_CLIENT_SECRET, redirect_uri: REDIRECT_URI, - refresh_token: connection.refresh_token, + refresh_token: refresh_token, }; const res = await axios.post( 'https://api.hubapi.com/oauth/v1/token', @@ -260,8 +271,7 @@ export class CrmConnectionsService { const data: HubspotOAuthResponse = res.data; await this.prisma.connections.update({ where: { - id_end_customer: customerId, - provider_slug: 'hubspot', + id_connection: connectionId, }, data: { access_token: data.access_token, @@ -284,21 +294,17 @@ export class CrmConnectionsService { } } - async handlePipedriveTokenRefresh(customerId: string) { + async handlePipedriveTokenRefresh( + connectionId: bigint, + refresh_token: string, + ) { try { - //get the refresh token for the expired one - const connection = await this.prisma.connections.findFirst({ - where: { - id_end_customer: customerId, - provider_slug: 'pipedrive', - }, - }); const REDIRECT_URI = `${config.OAUTH_REDIRECT_BASE}/oauth/crm/callback`; const formData = { grant_type: 'refresh_token', redirect_uri: REDIRECT_URI, - refresh_token: connection.refresh_token, + refresh_token: refresh_token, }; const res = await axios.post( 'https://oauth.pipedrive.com/oauth/token', @@ -315,8 +321,7 @@ export class CrmConnectionsService { const data: HubspotOAuthResponse = res.data; await this.prisma.connections.update({ where: { - id_end_customer: customerId, - provider_slug: 'pipedrive', + id_connection: connectionId, }, data: { access_token: data.access_token, @@ -339,15 +344,12 @@ export class CrmConnectionsService { } } - async handleZohoTokenRefresh(customerId: string) { + async handleZohoTokenRefresh( + connectionId: bigint, + refresh_token: string, + account_url: string, + ) { try { - //get the refresh token for the expired one - const connection = await this.prisma.connections.findFirst({ - where: { - id_end_customer: customerId, - provider_slug: 'hubspot', - }, - }); const REDIRECT_URI = `${config.OAUTH_REDIRECT_BASE}/oauth/crm/callback`; const formData = { @@ -355,17 +357,13 @@ export class CrmConnectionsService { client_id: config.HUBSPOT_CLIENT_ID, client_secret: config.HUBSPOT_CLIENT_SECRET, redirect_uri: REDIRECT_URI, - refresh_token: connection.refresh_token, + refresh_token: refresh_token, }; - const res = await axios.post( - `${connection.account_url}/oauth/v2/token`, - formData, - ); + const res = await axios.post(`${account_url}/oauth/v2/token`, formData); const data: ZohoOAuthResponse = res.data; await this.prisma.connections.update({ where: { - id_end_customer: customerId, - provider_slug: 'zoho', + id_connection: connectionId, }, data: { access_token: data.access_token, @@ -388,20 +386,11 @@ export class CrmConnectionsService { } } - async handleZendeskTokenRefresh(customerId: string) { + async handleZendeskTokenRefresh(connectionId: bigint, refresh_token: string) { try { - //get the refresh token for the expired one - const connection = await this.prisma.connections.findFirst({ - where: { - id_end_customer: customerId, - provider_slug: 'zendesk', - }, - }); - //const REDIRECT_URI = `${config.OAUTH_REDIRECT_BASE}/oauth/crm/callback`; - const formData = { grant_type: 'refresh_token', - refresh_token: connection.refresh_token, + refresh_token: refresh_token, }; const res = await axios.post( 'https://api.getbase.com/oauth2/token', @@ -418,8 +407,7 @@ export class CrmConnectionsService { const data: ZendeskOAuthResponse = res.data; await this.prisma.connections.update({ where: { - id_end_customer: customerId, - provider_slug: 'zendesk', + id_connection: connectionId, }, data: { access_token: data.access_token, diff --git a/packages/api/src/@core/sentry/sentry.interceptor.ts b/packages/api/src/@core/sentry/sentry.interceptor.ts new file mode 100644 index 000000000..5e9980288 --- /dev/null +++ b/packages/api/src/@core/sentry/sentry.interceptor.ts @@ -0,0 +1,22 @@ +// sentry.interceptor.ts +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import * as Sentry from '@sentry/node'; +import { catchError } from 'rxjs/operators'; + +@Injectable() +export class SentryInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + catchError((error) => { + Sentry.captureException(error); + throw error; + }), + ); + } +} diff --git a/packages/api/src/@core/sentry/sentry.module.ts b/packages/api/src/@core/sentry/sentry.module.ts new file mode 100644 index 000000000..89c08392a --- /dev/null +++ b/packages/api/src/@core/sentry/sentry.module.ts @@ -0,0 +1,32 @@ +// sentry.module.ts +import { Module, DynamicModule, Global } from '@nestjs/common'; +import * as Sentry from '@sentry/node'; +import { SentryInterceptor } from './sentry.interceptor'; +import config from '../utils/config'; + +@Global() +@Module({}) +export class SentryModule { + static forRoot(): DynamicModule { + const isProduction = config.NODE_ENV === 'production'; + const sentry_dsn = config.SENTRY_DSN; + const distribution = config.PROD_DISTRIBUTION; + + //enable sentry only if we are in production environment and if the product is managed by Panora + if (isProduction && sentry_dsn && distribution == 'managed') { + Sentry.init({ + dsn: sentry_dsn, + }); + } + + return { + module: SentryModule, + providers: isProduction + ? [{ provide: 'APP_INTERCEPTOR', useClass: SentryInterceptor }] + : [], + exports: isProduction + ? [{ provide: 'APP_INTERCEPTOR', useClass: SentryInterceptor }] + : [], + }; + } +} diff --git a/packages/api/src/@core/tasks/tasks.service.spec.ts b/packages/api/src/@core/tasks/tasks.service.spec.ts new file mode 100644 index 000000000..cb482301f --- /dev/null +++ b/packages/api/src/@core/tasks/tasks.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { TasksService } from './tasks.service'; + +describe('TasksService', () => { + let service: TasksService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [TasksService], + }).compile(); + + service = module.get(TasksService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/api/src/@core/tasks/tasks.service.ts b/packages/api/src/@core/tasks/tasks.service.ts new file mode 100644 index 000000000..ef3c6b71b --- /dev/null +++ b/packages/api/src/@core/tasks/tasks.service.ts @@ -0,0 +1,45 @@ +// tasks.service.ts +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { PrismaService } from '../prisma/prisma.service'; +import { ConnectionsService } from '../connections/services/connections.service'; + +@Injectable() +export class TasksService implements OnModuleInit { + constructor( + private prisma: PrismaService, + private connectionsService: ConnectionsService, + ) {} + + onModuleInit() { + this.handleCron(); + } + + @Cron(CronExpression.EVERY_HOUR) + async handleCron() { + // refresh all tokens that expire in 3 days + const threeDaysFromNow = new Date(); + threeDaysFromNow.setDate(threeDaysFromNow.getDate() + 3); + + const connectionsToRefresh = await this.prisma.connections.findMany({ + where: { + expiration_timestamp: { + lte: threeDaysFromNow, + }, + }, + }); + + for (const connection of connectionsToRefresh) { + if (connection.refresh_token) { + const account_url = + connection.provider_slug == 'zoho' ? connection.account_url : ''; + await this.connectionsService.handleCRMTokensRefresh( + connection.id_connection, + connection.provider_slug, + connection.refresh_token, + account_url, + ); + } + } + } +} diff --git a/packages/api/src/@core/utils/config.ts b/packages/api/src/@core/utils/config.ts index 82cd78398..61e6f8fda 100644 --- a/packages/api/src/@core/utils/config.ts +++ b/packages/api/src/@core/utils/config.ts @@ -1,7 +1,3 @@ -import dotenv from 'dotenv'; - -dotenv.config(); - const config = { HUBSPOT_CLIENT_ID: process.env.HUBSPOT_CLIENT_ID, HUBSPOT_CLIENT_SECRET: process.env.HUBSPOT_CLIENT_SECRET, @@ -14,6 +10,9 @@ const config = { ZENDESK_CLIENT_ID: process.env.ZENDESK_CLIENT_ID, ZENDESK_CLIENT_SECRET: process.env.ZENDESK_CLIENT_SECRET, OAUTH_REDIRECT_BASE: process.env.OAUTH_REDIRECT_BASE, + SENTRY_DSN: process.env.SENTRY_DSN, + NODE_ENV: process.env.ENV, + PROD_DISTRIBUTION: process.env.PROD_DISTRIBUTION, }; export default config; diff --git a/packages/api/src/app.module.ts b/packages/api/src/app.module.ts index 7ed16e56c..aeb9563b8 100644 --- a/packages/api/src/app.module.ts +++ b/packages/api/src/app.module.ts @@ -6,6 +6,11 @@ import { AuthModule } from './@core/auth/auth.module'; import { AuthService } from './@core/auth/auth.service'; import { ConfigModule } from '@nestjs/config'; import { ConnectionsModule } from './@core/connections/connections.module'; +import { ScheduleModule } from '@nestjs/schedule'; +import { TasksService } from './@core/tasks/tasks.service'; +import { SentryModule } from './@core/sentry/sentry.module'; +import { ConnectionsService } from './@core/connections/services/connections.service'; +import { CrmConnectionsService } from './@core/connections/services/crm/crm-connection.service'; @Module({ imports: [ @@ -13,8 +18,16 @@ import { ConnectionsModule } from './@core/connections/connections.module'; AuthModule, ConfigModule.forRoot({ isGlobal: true }), ConnectionsModule, + ScheduleModule.forRoot(), + SentryModule.forRoot(), ], controllers: [AppController], - providers: [AppService, AuthService], + providers: [ + AppService, + AuthService, + TasksService, + ConnectionsService, + CrmConnectionsService, + ], }) export class AppModule {} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf92c29a9..25b869b8d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,12 +41,21 @@ importers: '@nestjs/platform-express': specifier: ^10.0.0 version: 10.0.0(@nestjs/common@10.0.0)(@nestjs/core@10.0.0) + '@nestjs/schedule': + specifier: ^4.0.0 + version: 4.0.0(@nestjs/common@10.0.0)(@nestjs/core@10.0.0)(reflect-metadata@0.1.13) '@nestjs/swagger': specifier: ^7.1.14 version: 7.1.14(@nestjs/common@10.0.0)(@nestjs/core@10.0.0)(class-transformer@0.2.3)(class-validator@0.11.1)(reflect-metadata@0.1.13) '@prisma/client': specifier: ^5.4.2 version: 5.4.2(prisma@5.4.2) + '@sentry/node': + specifier: ^7.80.0 + version: 7.80.0 + '@sentry/tracing': + specifier: ^7.80.0 + version: 7.80.0 axios: specifier: ^1.5.1 version: 1.5.1 @@ -56,12 +65,9 @@ importers: crypto: specifier: ^1.0.1 version: 1.0.1 -<<<<<<< HEAD -======= dotenv: specifier: ^16.3.1 version: 16.3.1 ->>>>>>> feat/fix-auth passport: specifier: ^0.6.0 version: 0.6.0 @@ -3541,6 +3547,20 @@ packages: transitivePeerDependencies: - supports-color + /@nestjs/schedule@4.0.0(@nestjs/common@10.0.0)(@nestjs/core@10.0.0)(reflect-metadata@0.1.13): + resolution: {integrity: sha512-zz4h54m/F/1qyQKvMJCRphmuwGqJltDAkFxUXCVqJBXEs5kbPt93Pza3heCQOcMH22MZNhGlc9DmDMLXVHmgVQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.12 + dependencies: + '@nestjs/common': 10.0.0(class-transformer@0.2.3)(class-validator@0.11.1)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.0.0(@nestjs/common@10.0.0)(@nestjs/platform-express@10.0.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + cron: 3.1.3 + reflect-metadata: 0.1.13 + uuid: 9.0.1 + dev: false + /@nestjs/schematics@10.0.0(chokidar@3.5.3)(typescript@5.1.3): resolution: {integrity: sha512-gfUy/N1m1paN33BXq4d7HoCM+zM4rFxYjqAb8jkrBfBHiwyEhHHozfX/aRy/kOnAcy/VP8v4Zs4HKKrbRRlHnw==} peerDependencies: @@ -3659,6 +3679,55 @@ packages: resolution: {integrity: sha512-fqeucJ3LH0e1eyFdT0zRx+oETLancu5+n4lhiYECyEz6H2RDskPJHJYHkVc0LhkU4Uv7fuEnppKU3nVKNzMh8g==} requiresBuild: true + /@sentry-internal/tracing@7.80.0: + resolution: {integrity: sha512-P1Ab9gamHLsbH9D82i1HY8xfq9dP8runvc4g50AAd6OXRKaJ45f2KGRZUmnMEVqBQ7YoPYp2LFMkrhNYbcZEoQ==} + engines: {node: '>=8'} + dependencies: + '@sentry/core': 7.80.0 + '@sentry/types': 7.80.0 + '@sentry/utils': 7.80.0 + dev: false + + /@sentry/core@7.80.0: + resolution: {integrity: sha512-nJiiymdTSEyI035/rdD3VOq6FlOZ2wWLR5bit9LK8a3rzHU3UXkwScvEo6zYgs0Xp1sC0yu1S9+0BEiYkmi29A==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.80.0 + '@sentry/utils': 7.80.0 + dev: false + + /@sentry/node@7.80.0: + resolution: {integrity: sha512-J35fqe8J5ac/17ZXT0ML3opYGTOclqYNE9Sybs1y9n6BqacHyzH8By72YrdI03F7JJDHwrcGw+/H8hGpkCwi0Q==} + engines: {node: '>=8'} + dependencies: + '@sentry-internal/tracing': 7.80.0 + '@sentry/core': 7.80.0 + '@sentry/types': 7.80.0 + '@sentry/utils': 7.80.0 + https-proxy-agent: 5.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@sentry/tracing@7.80.0: + resolution: {integrity: sha512-y9zBVMpCgY5Y6dBZrnKKHf6K9YWjGo3S35tPwDV1mQLml64bi6bNr6Fc6OBzXyrl9OTJAO71A1Z7DlAu6BQY9w==} + engines: {node: '>=8'} + dependencies: + '@sentry-internal/tracing': 7.80.0 + dev: false + + /@sentry/types@7.80.0: + resolution: {integrity: sha512-4bpMO+2jWiWLDa8zbTASWWNLWe6yhjfPsa7/6VH5y9x1NGtL8oRbqUsTgsvjF3nmeHEMkHQsC8NHPaQ/ibFmZQ==} + engines: {node: '>=8'} + dev: false + + /@sentry/utils@7.80.0: + resolution: {integrity: sha512-XbBCEl6uLvE50ftKwrEo6XWdDaZXHXu+kkHXTPWQEcnbvfZKLuG9V0Hxtxxq3xQgyWmuF05OH1GcqYqiO+v5Yg==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.80.0 + dev: false + /@sideway/address@4.1.4: resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==} dependencies: @@ -4040,6 +4109,10 @@ packages: '@types/node': 20.3.1 dev: false + /@types/luxon@3.3.4: + resolution: {integrity: sha512-H9OXxv4EzJwE75aTPKpiGXJq+y4LFxjpsdgKwSmr503P5DkWc3AG7VAFYrFNVvqemT5DfgZJV9itYhqBHSGujA==} + dev: false + /@types/mdast@3.0.14: resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} dependencies: @@ -6034,6 +6107,13 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /cron@3.1.3: + resolution: {integrity: sha512-KVxeKTKYj2eNzN4ElnT6nRSbjbfhyxR92O/Jdp6SH3pc05CDJws59jBrZWEMQlxevCiE6QUTrXy+Im3vC3oD3A==} + dependencies: + '@types/luxon': 3.3.4 + luxon: 3.4.3 + dev: false + /cross-fetch@3.1.8: resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} dependencies: @@ -9445,6 +9525,11 @@ packages: dependencies: yallist: 4.0.0 + /luxon@3.4.3: + resolution: {integrity: sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==} + engines: {node: '>=12'} + dev: false + /macos-release@2.5.1: resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} engines: {node: '>=6'}