@hiennc24/audit
v2.1.0
Published
Audit trail client SDK for BISO24 services
Readme
@hiennc24/audit
Audit trail client SDK for BISO24 microservices.
Installation
npm install @hiennc24/auditUsage
import { AuditClient, sanitize } from '@hiennc24/audit'
// Initialize client (once per service)
const audit = new AuditClient({
redisUrl: process.env.REDIS_URL,
serviceName: 'svc-inventory',
enabled: process.env.NODE_ENV !== 'test'
})
// Log audit event
await audit.log({
action: 'UPDATE',
entity: 'Product',
entityId: productId,
userId: meta.userId,
orgIds: meta.orgIds,
before: sanitize(originalProduct, ['password']),
after: sanitize(updatedProduct, ['password']),
metadata: { reason: 'Price adjustment' }
})Repository Interceptor (v2.1+)
Zero-code audit integration - Automatically audit all repository operations without manual logging.
Quick Start
import { AuditClient } from '@hiennc24/audit'
// 1. Initialize client
const auditClient = new AuditClient({
redisUrl: process.env.REDIS_URL,
serviceName: 'svc-inventory'
})
// 2. Wrap repository with interceptor (setup once)
const productRepo = auditClient.intercept(baseProductRepo, {
entity: 'Product',
extractContext: (meta) => ({
userId: meta.userId,
orgIds: meta.orgIds,
domain: meta.domain
})
})
// 3. Use normally - audit is automatic!
await productRepo.create(data, meta) // ✅ Auto audited (CREATE)
await productRepo.update(id, updates, meta) // ✅ Auto audited (UPDATE)
await productRepo.delete(id, meta) // ✅ Auto audited (DELETE)
await productRepo.findByIdAndUpdate(id, data, meta) // ✅ Auto audited (UPDATE)
// Non-auditable methods pass through unchanged
const products = await productRepo.findAll() // No audit
const count = await productRepo.count() // No auditSupported Methods
| Method | Action | Before State Fetched |
|--------|--------|---------------------|
| create, insertOne | CREATE | No |
| update, updateById, findByIdAndUpdate, findOneAndUpdate | UPDATE | Yes |
| delete, deleteById, remove, findByIdAndDelete, findByIdAndRemove | DELETE | Yes |
Configuration Options
interface InterceptConfig<TMeta> {
// Required
entity: string // Entity name (e.g., 'Product')
extractContext: (meta: TMeta) => AuditContext // Extract userId, orgIds, domain
// Optional
idExtractor?: (args: any[], methodName: string) => string // Custom ID extraction
sensitiveFields?: string[] // Fields to sanitize (default: SENSITIVE_FIELDS)
findByIdMethod?: string // Method to fetch before state (default: 'findById')
methods?: Record<string, MethodConfig> // Custom method configurations
debug?: boolean // Enable verbose logging
}Advanced Example
// Custom configuration
const orderRepo = auditClient.intercept(baseOrderRepo, {
entity: 'Order',
extractContext: (meta) => ({
userId: meta.user?._id || 'system',
orgIds: meta.orgIds || [],
domain: meta.domain
}),
// Custom ID extraction for non-standard patterns
idExtractor: (args, methodName) => {
if (methodName === 'updateByOrderNumber') {
return args[0].orderNumber
}
return args[0]
},
// Additional sensitive fields
sensitiveFields: ['creditCard', 'cvv', 'apiKey'],
// Use custom findById method
findByIdMethod: 'findOne',
// Debug mode for troubleshooting
debug: process.env.DEBUG === 'true'
})Benefits
- 87% Code Reduction: From 15 lines per operation to 2 lines one-time setup
- Consistency: Impossible to forget audit logging
- Type-Safe: Full TypeScript support with generics
- Performance: <50ms overhead per operation
- Fire-and-Forget: Audit failures don't break business logic
API
AuditClient
constructor(config)- Initialize clientlog(event)- Publish audit event (async, fire-and-forget)intercept(repository, config)- Create intercepted repository with auto-audit (v2.1+)close()- Close Redis connection
sanitize(obj, fields)
Remove sensitive fields from object before logging.
autoSanitize(obj)
Auto-sanitize using common sensitive fields (password, apiKey, creditCard, etc.).
Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| redisUrl | string | required | Redis connection URL |
| serviceName | string | required | Service name (e.g., 'svc-inventory') |
| enabled | boolean | true | Enable/disable auditing |
| streamName | string | 'audit:events' | Redis Stream name |
Features
- Fire-and-forget: Non-blocking, doesn't throw on errors
- Idempotency: Unique eventId for each event
- Sanitizer: Remove sensitive fields before logging
- Lightweight: Zero dependencies on svc-audit service
- Type-safe: Full TypeScript support
Example: Sanitizing Data
import { sanitize, autoSanitize, SENSITIVE_FIELDS } from '@hiennc24/audit'
// Manual sanitization
const sanitized = sanitize(user, ['password', 'ssn'])
// Auto-sanitize common fields
const sanitized = autoSanitize(user)
// Custom sensitive fields
const customSanitized = sanitize(data, [...SENSITIVE_FIELDS, 'customSecret'])Error Handling
The SDK follows a fire-and-forget pattern. Errors are logged to console but never thrown, ensuring audit logging doesn't impact application flow.
License
MIT
