diff --git a/src/notifications/migrations/AddNotificationCompositeIndex.ts b/src/notifications/migrations/AddNotificationCompositeIndex.ts new file mode 100644 index 00000000..c3469cbe --- /dev/null +++ b/src/notifications/migrations/AddNotificationCompositeIndex.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner, TableIndex } from 'typeorm'; + +export class AddNotificationCompositeIndex1700000000000 implements MigrationInterface { + name = 'AddNotificationCompositeIndex1700000000000'; + + async up(queryRunner: QueryRunner): Promise { + await queryRunner.createIndex( + 'notifications', + new TableIndex({ + name: 'IDX_notifications_user_type_status_created', + columnNames: ['userId', 'type', 'status', 'createdAt'], + }), + ); + } + + async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropIndex('notifications', 'IDX_notifications_user_type_status_created'); + } +} diff --git a/src/security/encryption/provider-token-encryption.service.ts b/src/security/encryption/provider-token-encryption.service.ts new file mode 100644 index 00000000..519d9cbc --- /dev/null +++ b/src/security/encryption/provider-token-encryption.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from 'crypto'; + +@Injectable() +export class ProviderTokenEncryptionService { + private readonly algorithm = 'aes-256-gcm'; + private readonly key: Buffer; + private readonly salt: string; + + constructor() { + this.salt = process.env.PROVIDER_TOKEN_ENCRYPTION_SALT || 'default-salt'; + const masterSecret = process.env.TOKEN_ENCRYPTION_SECRET || 'fallback-secret'; + this.key = pbkdf2Sync(masterSecret, this.salt, 600000, 32, 'sha256'); + } + + encrypt(plaintext: string): string { + const iv = randomBytes(16); + const cipher = createCipheriv(this.algorithm, this.key, iv); + let encrypted = cipher.update(plaintext, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + const authTag = cipher.getAuthTag().toString('hex'); + return ${iv.toString('hex')}::; + } + + decrypt(ciphertext: string): string { + const [ivHex, authTagHex, encrypted] = ciphertext.split(':'); + const decipher = createDecipheriv(this.algorithm, this.key, Buffer.from(ivHex, 'hex')); + decipher.setAuthTag(Buffer.from(authTagHex, 'hex')); + let decrypted = decipher.update(encrypted, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; + } +} \ No newline at end of file