feat: implement encryption helper and transaction setting handler

- Added EncryptionHelper for encrypting and decrypting transaction settings.
- Introduced TransactionSettingHandler to manage changes in transaction settings.
- Updated SalesPriceFormulaModel to use decrypted values.
- Enhanced SalesPriceFormulaDataService with methods to get and update transaction settings.
- Modified SalesPriceFormulaReadService to return decrypted values in the response.
pull/172/head
shancheas 2025-07-08 12:01:39 +07:00
parent bd14dc0b4b
commit 0f46f5ff39
7 changed files with 130 additions and 3 deletions

View File

@ -6,6 +6,7 @@ import {
import { Column, Entity } from 'typeorm'; import { Column, Entity } from 'typeorm';
import { BaseModel } from 'src/core/modules/data/model/base.model'; import { BaseModel } from 'src/core/modules/data/model/base.model';
import { FormulaType } from '../../constants'; import { FormulaType } from '../../constants';
import { EncryptionHelper } from '../../domain/helpers/encryption.helper';
@Entity(TABLE_NAME.PRICE_FORMULA) @Entity(TABLE_NAME.PRICE_FORMULA)
export class SalesPriceFormulaModel export class SalesPriceFormulaModel
@ -48,6 +49,7 @@ export class TransactionSettingModel
//TODO: add logic to get value from key //TODO: add logic to get value from key
percentValue(): number { percentValue(): number {
return this.value ?? 100; const value = EncryptionHelper.decrypt(this.key);
return Number(value) ?? 100;
} }
} }

View File

@ -158,4 +158,15 @@ export class TransactionSettingDataService extends BaseDataService<TransactionSe
) { ) {
super(repo); super(repo);
} }
// create function get one transaction setting
async getTransactionSetting() {
return this.repo.findOne({
where: {},
});
}
async updateTransactionSetting(payload: any) {
return this.repo.update(payload.id, payload);
}
} }

View File

@ -30,7 +30,12 @@ export class SalesPriceFormulaReadService extends BaseReadService<SalesPriceForm
}, },
}); });
return data; const value = data.percentValue();
return {
...data,
value: value,
};
} catch (error) { } catch (error) {
throw error; throw error;
} }

View File

@ -0,0 +1,72 @@
import * as crypto from 'crypto';
export class EncryptionHelper {
private static readonly algorithm = 'aes-256-cbc';
private static readonly secretKey = crypto.scryptSync(
'sales-price-formula-secret',
'salt',
32,
);
/**
* Encrypts a string using AES-256-CBC algorithm
* @param text - The string to encrypt
* @returns Encrypted string in format: iv:encryptedData
*/
static encrypt(text: string): string {
if (!text) {
return '';
}
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(this.algorithm, this.secretKey, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return `${iv.toString('hex')}:${encrypted}`;
}
/**
* Decrypts a string that was encrypted using the encrypt method
* @param encryptedText - The encrypted string in format: iv:encryptedData
* @returns Decrypted original string
*/
static decrypt(encryptedText: string): string {
if (!encryptedText) {
return '';
}
try {
const [ivHex, encrypted] = encryptedText.split(':');
if (!ivHex || !encrypted) {
throw new Error('Invalid encrypted text format');
}
const iv = Buffer.from(ivHex, 'hex');
const decipher = crypto.createDecipheriv(
this.algorithm,
this.secretKey,
iv,
);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (error) {
throw new Error('Failed to decrypt data: Invalid encrypted text format');
}
}
/**
* Generates a hash of the input string using SHA-256
* @param text - The string to hash
* @returns SHA-256 hash of the input string
*/
static hash(text: string): string {
if (!text) {
return '';
}
return crypto.createHash('sha256').update(text).digest('hex');
}
}

View File

@ -0,0 +1,34 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { ChangeDocEvent } from 'src/modules/configuration/couch/domain/events/change-doc.event';
import { TransactionSettingDataService } from '../../../data/services/sales-price-formula-data.service';
import { EncryptionHelper } from '../../helpers/encryption.helper';
@EventsHandler(ChangeDocEvent)
export class TransactionSettingHandler
implements IEventHandler<ChangeDocEvent>
{
constructor(
private transactionSettingService: TransactionSettingDataService, // private orchestrator: SalesPriceFormulaDataOrchestrator,
) {}
async handle(event: ChangeDocEvent) {
const data = event.data.data;
const database = event.data.database;
if (database !== 'api_configuration') return;
const value = data.value;
const currentTransactionSetting =
await this.transactionSettingService.getTransactionSetting();
if (value === currentTransactionSetting.value) return;
const key = EncryptionHelper.encrypt(`${value}`);
const payload = {
id: currentTransactionSetting.id,
value: 0,
key,
};
await this.transactionSettingService.updateTransactionSetting(payload);
}
}

View File

@ -24,6 +24,7 @@ import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'
import { UpdateTransactionSettingManager } from './domain/usecases/managers/update-transaction-setting.manager'; import { UpdateTransactionSettingManager } from './domain/usecases/managers/update-transaction-setting.manager';
import { TransactionModel } from '../transaction/data/models/transaction.model'; import { TransactionModel } from '../transaction/data/models/transaction.model';
import { CouchModule } from 'src/modules/configuration/couch/couch.module'; import { CouchModule } from 'src/modules/configuration/couch/couch.module';
import { TransactionSettingHandler } from './domain/usecases/handlers/transaction-setting.handler';
@Global() @Global()
@Module({ @Module({
@ -58,6 +59,7 @@ import { CouchModule } from 'src/modules/configuration/couch/couch.module';
SalesPriceFormulaDataOrchestrator, SalesPriceFormulaDataOrchestrator,
SalesPriceFormulaReadOrchestrator, SalesPriceFormulaReadOrchestrator,
TransactionSettingHandler,
], ],
exports: [SalesPriceFormulaDataService, SalesPriceFormulaReadService], exports: [SalesPriceFormulaDataService, SalesPriceFormulaReadService],
}) })

View File

@ -97,7 +97,8 @@ export class WhatsappService {
{ {
parameter_name: 'queue_time', parameter_name: 'queue_time',
type: 'text', type: 'text',
text: toTime(data.time), // replace with queue_time variable // text: toTime(data.time), // replace with queue_time variable
text: '--:--',
}, },
], ],
}, },