@solidxai/core 0.1.6-beta.22 → 0.1.6-beta.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/helpers/bootstrap.helper.js +1 -1
  2. package/dist/helpers/bootstrap.helper.js.map +1 -1
  3. package/dist/helpers/solid-registry.d.ts +3 -0
  4. package/dist/helpers/solid-registry.d.ts.map +1 -1
  5. package/dist/helpers/solid-registry.js +7 -0
  6. package/dist/helpers/solid-registry.js.map +1 -1
  7. package/dist/interfaces.d.ts +1 -0
  8. package/dist/interfaces.d.ts.map +1 -1
  9. package/dist/interfaces.js.map +1 -1
  10. package/dist/jobs/computed-field-evaluation-queue-options.d.ts +1 -0
  11. package/dist/jobs/computed-field-evaluation-queue-options.d.ts.map +1 -1
  12. package/dist/jobs/computed-field-evaluation-queue-options.js +1 -0
  13. package/dist/jobs/computed-field-evaluation-queue-options.js.map +1 -1
  14. package/dist/services/queues/rabbitmq-publisher.service.d.ts +1 -0
  15. package/dist/services/queues/rabbitmq-publisher.service.d.ts.map +1 -1
  16. package/dist/services/queues/rabbitmq-publisher.service.js +6 -1
  17. package/dist/services/queues/rabbitmq-publisher.service.js.map +1 -1
  18. package/dist/services/queues/rabbitmq-subscriber.service.d.ts +1 -0
  19. package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -1
  20. package/dist/services/queues/rabbitmq-subscriber.service.js +15 -4
  21. package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
  22. package/dist/services/request-context.service.d.ts +2 -1
  23. package/dist/services/request-context.service.d.ts.map +1 -1
  24. package/dist/services/request-context.service.js.map +1 -1
  25. package/dist/services/solid-introspect.service.d.ts +6 -1
  26. package/dist/services/solid-introspect.service.d.ts.map +1 -1
  27. package/dist/services/solid-introspect.service.js +27 -2
  28. package/dist/services/solid-introspect.service.js.map +1 -1
  29. package/dist/subscribers/audit.subscriber.d.ts +3 -5
  30. package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
  31. package/dist/subscribers/audit.subscriber.js +9 -38
  32. package/dist/subscribers/audit.subscriber.js.map +1 -1
  33. package/dist/subscribers/created-by-updated-by.subscriber.d.ts +0 -1
  34. package/dist/subscribers/created-by-updated-by.subscriber.d.ts.map +1 -1
  35. package/dist/subscribers/created-by-updated-by.subscriber.js +3 -13
  36. package/dist/subscribers/created-by-updated-by.subscriber.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/helpers/bootstrap.helper.ts +1 -1
  39. package/src/helpers/solid-registry.ts +9 -0
  40. package/src/interfaces.ts +1 -0
  41. package/src/jobs/computed-field-evaluation-queue-options.ts +1 -0
  42. package/src/services/queues/rabbitmq-publisher.service.ts +8 -2
  43. package/src/services/queues/rabbitmq-subscriber.service.ts +16 -5
  44. package/src/services/request-context.service.ts +2 -1
  45. package/src/services/solid-introspect.service.ts +28 -0
  46. package/src/subscribers/audit.subscriber.ts +9 -52
  47. package/src/subscribers/created-by-updated-by.subscriber.ts +22 -16
@@ -1 +1 @@
1
- {"version":3,"file":"audit.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/audit.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAuE;AACvE,4FAAuF;AACvF,4DAAuD;AACvD,uFAAmF;AAEnF,iFAA4E;AAUrE,IAAM,eAAe,GAArB,MAAM,eAAe;IAExB,YAGqB,qBAA4C,EAI7D,iBAA2D,EAC1C,0BAAsD;QALtD,0BAAqB,GAArB,qBAAqB,CAAuB;QAI5C,sBAAiB,GAAjB,iBAAiB,CAAyB;QAC1C,+BAA0B,GAA1B,0BAA0B,CAA4B;QAWnE,WAAM,GAAG,IAAI,OAAO,EAAuB,CAAC;IARpD,CAAC;IAED,gBAAgB,CAAC,UAAsB;QACnC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAKO,OAAO,CAAC,KAA2B,EAAE,IAAkB;QAC3D,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QACtC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,MAAW,EAAE,QAAwB;QAChE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;YAC/C,KAAK,EAAE;gBACH,YAAY,EAAE,IAAA,0BAAU,EAAC,QAAQ,CAAC,IAAI,CAAC;aAC1C;YACD,SAAS,EAAE;gBACP,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,IAAI;aACf;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAEhG,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC3C,KAAK,CAAC,mBAAmB;YACzB,CAAC,CAAC,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;YAC1E,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,YAAY,KAAK,aAAa,CAAC,CACvE,CAAC;QAEF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACjB,CAAC;QAOD,OAAO,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACtC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACtC,OAAO,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,CAAC;QAC3D,CAAC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAuB;QACrC,IAAI,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAE5D,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAChB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAkE;aACxG,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAuB;QACrC,IAAI,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAE5D,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAChB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE;oBACF,KAAK,CAAC,MAAM;oBACZ,KAAK,CAAC,QAAQ;oBACd,KAAK,CAAC,cAAc;oBACpB,KAAK,CAAC,cAAc,IAAI,EAAE;iBACoC;aACrE,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAuB;QACrC,IAAI,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAE5D,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAChB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE;oBACF,KAAK,CAAC,MAAM;oBACZ,KAAK,CAAC,QAAQ;oBACd,KAAK,CAAC,cAAc;iBAC0C;aACrE,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAGD,KAAK,CAAC,sBAAsB,CAAC,KAA2B;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAGtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC;gBACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;oBAChB,KAAK,QAAQ;wBAAE,MAAM,IAAI,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;wBAAC,MAAM;oBAC9F,KAAK,QAAQ;wBAAE,MAAM,IAAI,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;wBAAC,MAAM;oBAC9F,KAAK,QAAQ;wBAAE,MAAM,IAAI,CAAC,qBAAqB,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;wBAAC,MAAM;gBAClG,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;YAGb,CAAC;QACL,CAAC;IACL,CAAC;IAED,wBAAwB,CAAC,KAA2B;QAEhD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC;CACJ,CAAA;AAnIY,0CAAe;0BAAf,eAAe;IAF3B,IAAA,mBAAU,EAAC,EAAC,KAAK,EAAE,cAAK,CAAC,SAAS,EAAC,CAAC;IAU5B,WAAA,IAAA,eAAM,EAAC,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,mDAAuB,CAAC,CAAC,CAAA;qCAHV,+CAAqB;QAIzB,mDAAuB;QACd,0DAA0B;GAVlE,eAAe,CAmI3B","sourcesContent":["import { forwardRef, Inject, Injectable, Scope } from '@nestjs/common';\nimport { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';\nimport { lowerFirst } from 'src/helpers/string.helper';\nimport { ModelMetadataRepository } from 'src/repository/model-metadata.repository';\nimport { DataSource, EntityMetadata, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';\nimport { ChatterMessageService } from '../services/chatter-message.service';\n\n\ntype DeferredCall =\n | { kind: 'insert'; args: Parameters<ChatterMessageService['postAuditMessageOnInsert']> }\n | { kind: 'update'; args: Parameters<ChatterMessageService['postAuditMessageOnUpdate']> }\n | { kind: 'delete'; args: Parameters<ChatterMessageService['postAuditMessageOnDelete']> };\n\n@Injectable({scope: Scope.TRANSIENT})\n// @EventSubscriber()\nexport class AuditSubscriber implements EntitySubscriberInterface {\n private dataSource: DataSource;\n constructor(\n // @InjectDataSource()\n // private readonly dataSource: DataSource,\n private readonly chatterMessageService: ChatterMessageService,\n // @InjectRepository(ModelMetadata)\n // private readonly modelMetadataRepo: Repository<ModelMetadata>,\n @Inject(forwardRef(() => ModelMetadataRepository))\n private readonly modelMetadataRepo: ModelMetadataRepository,\n private readonly modelMetadataHelperService: ModelMetadataHelperService,\n ) {\n // this.dataSource.subscribers.push(this);\n }\n\n bindToDataSource(dataSource: DataSource) {\n this.dataSource = dataSource;\n this.dataSource.subscribers.push(this);\n }\n\n // Per-transaction buffer (auto-GC when queryRunner is gone)\n private perTxn = new WeakMap<any, DeferredCall[]>();\n\n private enqueue(event: { queryRunner: any }, call: DeferredCall) {\n const qr = event.queryRunner;\n const arr = this.perTxn.get(qr) ?? [];\n arr.push(call);\n this.perTxn.set(qr, arr);\n }\n\n private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<boolean> {\n const model = await this.modelMetadataRepo.findOne({\n where: {\n singularName: lowerFirst(metadata.name)\n },\n relations: {\n fields: true,\n module: true\n }\n });\n\n if (!model || !model.enableAuditTracking) {\n return false;\n }\n\n const modelFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName)\n\n const auditFields = modelFields.filter(field =>\n field.enableAuditTracking &&\n !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&\n !(field.type === 'relation' && field.relationType === 'one-to-many')\n );\n\n if (auditFields.length === 0) {\n return false;\n }\n\n // if (!entity) {\n // console.warn(`[AuditSubscriber] Skipping audit for ${metadata.name} – entity is undefined or null`);\n // return false;\n // }\n\n return entity && auditFields.some(field => {\n const fieldValue = entity[field.name];\n return fieldValue !== undefined && fieldValue !== null;\n });\n }\n\n async afterInsert(event: InsertEvent<any>) {\n if (await this.shouldTrackAudit(event.entity, event.metadata)) {\n // await this.chatterMessageService.postAuditMessageOnInsert(event.entity, event.metadata);\n this.enqueue(event, {\n kind: 'insert',\n args: [event.entity, event.metadata] as Parameters<ChatterMessageService['postAuditMessageOnInsert']>,\n });\n }\n }\n\n async afterUpdate(event: UpdateEvent<any>) {\n if (await this.shouldTrackAudit(event.entity, event.metadata)) {\n // await this.chatterMessageService.postAuditMessageOnUpdate(event.entity, event.metadata, event.databaseEntity, event.updatedColumns || []);\n this.enqueue(event, {\n kind: 'update',\n args: [\n event.entity, // entity (after)\n event.metadata,\n event.databaseEntity, // entity (before)\n event.updatedColumns ?? [],\n ] as Parameters<ChatterMessageService['postAuditMessageOnUpdate']>,\n });\n }\n }\n\n async afterRemove(event: RemoveEvent<any>) {\n if (await this.shouldTrackAudit(event.entity, event.metadata)) {\n // await this.chatterMessageService.postAuditMessageOnDelete(event.entity, event.metadata, event.databaseEntity);\n this.enqueue(event, {\n kind: 'delete',\n args: [\n event.entity,\n event.metadata,\n event.databaseEntity,\n ] as Parameters<ChatterMessageService['postAuditMessageOnDelete']>,\n });\n }\n }\n\n // --------- transaction lifecycle ----------\n async afterTransactionCommit(event: { queryRunner: any }) {\n const batch = this.perTxn.get(event.queryRunner) ?? [];\n this.perTxn.delete(event.queryRunner);\n\n // Now we’re OUTSIDE the DB transaction — safe to do I/O/DB writes inside chatter service.\n for (const item of batch) {\n try {\n switch (item.kind) {\n case 'insert': await this.chatterMessageService.postAuditMessageOnInsert(...item.args); break;\n case 'update': await this.chatterMessageService.postAuditMessageOnUpdate(...item.args); break;\n case 'delete': await this.chatterMessageService.postAuditMessageOnDelete(...item.args); break;\n }\n } catch (e) {\n // Best effort: log and continue; your core txn was already committed\n // Optionally: send to a generic error logger/metric here\n }\n }\n }\n\n afterTransactionRollback(event: { queryRunner: any }) {\n // Drop buffered calls; the write never happened\n this.perTxn.delete(event.queryRunner);\n }\n}\n\n// import { DataSource, EntityMetadata, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';\n// import { Injectable } from '@nestjs/common';\n// import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';\n// import { Repository } from 'typeorm';\n// import { ModelMetadata } from '../entities/model-metadata.entity';\n// import { lowerFirst } from 'src/helpers/string.helper';\n// import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';\n// import { ChatterMessagePayload } from 'src/jobs/chatter-queue-publisher.service';\n// import { RequestContextService } from 'src/services/request-context.service';\n// import { PublisherFactory } from 'src/services/queues/publisher-factory.service';\n\n// @EventSubscriber()\n// @Injectable()\n// export class AuditSubscriber implements EntitySubscriberInterface {\n// private perTxn = new WeakMap<any, ChatterMessagePayload[]>();\n\n// constructor(\n// @InjectDataSource() private readonly dataSource: DataSource,\n// @InjectRepository(ModelMetadata) private readonly modelMetadataRepo: Repository<ModelMetadata>,\n// private readonly modelMetadataHelperService: ModelMetadataHelperService,\n// private readonly requestContext: RequestContextService,\n// private readonly publisherFactory: PublisherFactory<any>\n// ) {\n// this.dataSource.subscribers.push(this);\n// }\n\n// // --- small cache to avoid metadata queries on every row ---\n// private modelCache = new Map<string, { enable: boolean; fields: Array<{ name: string; enableAuditTracking: boolean; type: string; relationType?: string }>; ts: number }>();\n// private cacheTTLms = 60_000;\n\n// private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<{ enable: boolean; auditFields?: string[] }> {\n// const key = metadata.name;\n// const now = Date.now();\n// const cached = this.modelCache.get(key);\n// if (cached && (now - cached.ts) < this.cacheTTLms) {\n// if (!cached.enable) return { enable: false };\n// const fields = cached.fields.filter(f =>\n// f.enableAuditTracking &&\n// !['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(f.type) &&\n// !(f.type === 'relation' && f.relationType === 'one-to-many')\n// );\n// const present = fields.map(f => f.name).filter(n => entity?.[n] !== undefined);\n// return { enable: present.length > 0, auditFields: present };\n// }\n\n// const model = await this.modelMetadataRepo.findOne({\n// where: { singularName: lowerFirst(metadata.name) },\n// relations: { fields: true, module: true },\n// });\n// const enable = !!model?.enableAuditTracking;\n// const fields = model?.fields ?? [];\n// this.modelCache.set(key, { enable, fields, ts: now });\n\n// if (!enable) return { enable: false };\n// const filtered = fields.filter(f =>\n// f.enableAuditTracking &&\n// !['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(f.type) &&\n// !(f.type === 'relation' && f.relationType === 'one-to-many')\n// );\n// const present = filtered.map(f => f.name).filter(n => entity?.[n] !== undefined);\n// return { enable: present.length > 0, auditFields: present };\n// }\n\n// private push(event: { queryRunner: any }, msg: ChatterMessagePayload) {\n// const arr = this.perTxn.get(event.queryRunner) ?? [];\n// arr.push(msg);\n// this.perTxn.set(event.queryRunner, arr);\n// }\n\n// async afterInsert(event: InsertEvent<any>) {\n// if (!event.entity) return;\n// const enable = await this.shouldTrackAudit(event.entity, event.metadata);\n// if (!enable) return;\n\n// const payload: ChatterMessagePayload = {\n// eventType: 'insert',\n// model: event.metadata.name,\n// entityId: String(event.entity.id ?? event.entity.uuid ?? ''),\n// occurredAt: new Date().toISOString(),\n// after: this.safeCopy(event.entity),\n// userId: this.getUserId(),\n// };\n// this.push(event, payload);\n// }\n\n// async afterUpdate(event: UpdateEvent<any>) {\n// // Updated entity may be null if you used raw query; fall back to databaseEntity\n// const current = event.entity ?? {};\n// const before = event.databaseEntity ?? {};\n// const { enable, auditFields } = await this.shouldTrackAudit(current, event.metadata);\n// if (!enable) return;\n\n// const changedCols = (event.updatedColumns || []).map(c => c.propertyName);\n// const payload: ChatterMessagePayload = {\n// eventType: 'update',\n// model: event.metadata.name,\n// entityId: String((current as any).id ?? (before as any).id ?? ''),\n// occurredAt: new Date().toISOString(),\n// before: this.pick(before, auditFields || changedCols),\n// after: this.pick(current, auditFields || changedCols),\n// diff: changedCols,\n// userId: this.getUserId(),\n// };\n// this.push(event, payload);\n// }\n\n// async afterRemove(event: RemoveEvent<any>) {\n// const base = event.entity ?? event.databaseEntity;\n// if (!base) return;\n\n// const { enable } = await this.shouldTrackAudit(base, event.metadata);\n// if (!enable) return;\n\n// const payload: ChatterMessagePayload = {\n// eventType: 'delete',\n// model: event.metadata.name,\n// entityId: String((base as any).id ?? ''),\n// occurredAt: new Date().toISOString(),\n// before: this.safeCopy(base),\n// userId: this.getUserId(),\n// };\n// this.push(event, payload);\n// }\n\n// // Publish AFTER the transaction commits -> no idle-in-transaction\n// async afterTransactionCommit(event: { queryRunner: any }) {\n// const batch = this.perTxn.get(event.queryRunner) ?? [];\n// this.perTxn.delete(event.queryRunner);\n// for (const msg of batch) {\n// try {\n// await this.publisherFactory.publish({ payload: msg, parentEntity: msg.model, parentEntityId: msg.entityId }, 'ChatterQueuePublisher');\n// } catch (err) {\n// // log + optionally send to a DLQ or retry queue\n// // do NOT throw; commit already happened\n// // your RabbitMqPublisher likely tracks failures in MqMessage tables anyway\n// }\n// }\n// }\n\n// afterTransactionRollback(event: { queryRunner: any }) {\n// this.perTxn.delete(event.queryRunner);\n// }\n\n// // --- small helpers to keep payloads JSON-safe and small ---\n// private safeCopy(obj: any) {\n// try {\n// return JSON.parse(JSON.stringify(obj));\n// } catch {\n// return {}; // strip circular refs\n// }\n// }\n\n// private pick(obj: any, keys: string[]) {\n// const out: any = {};\n// for (const k of keys) out[k] = obj?.[k];\n// return this.safeCopy(out);\n// }\n\n// private getUserId(): string | null {\n\n// const activeUser = this.requestContext.getActiveUser();\n// if (activeUser?.sub)\n// return String(activeUser.sub);\n// }\n\n\n// }"]}
1
+ {"version":3,"file":"audit.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/audit.subscriber.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, Scope } from '@nestjs/common';\nimport { lowerFirst } from 'src/helpers/string.helper';\nimport { SolidRegistry } from 'src/helpers/solid-registry';\nimport { DataSource, EntityMetadata, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';\nimport { ChatterMessageService } from '../services/chatter-message.service';\n\n\ntype DeferredCall =\n | { kind: 'insert'; args: Parameters<ChatterMessageService['postAuditMessageOnInsert']> }\n | { kind: 'update'; args: Parameters<ChatterMessageService['postAuditMessageOnUpdate']> }\n | { kind: 'delete'; args: Parameters<ChatterMessageService['postAuditMessageOnDelete']> };\n\n@Injectable({scope: Scope.TRANSIENT})\n// @EventSubscriber()\nexport class AuditSubscriber implements EntitySubscriberInterface {\n private dataSource: DataSource;\n constructor(\n private readonly chatterMessageService: ChatterMessageService,\n private readonly solidRegistry: SolidRegistry,\n ) { }\n\n bindToDataSource(dataSource: DataSource) {\n this.dataSource = dataSource;\n this.dataSource.subscribers.push(this);\n }\n\n // Per-transaction buffer (auto-GC when queryRunner is gone)\n private perTxn = new WeakMap<any, DeferredCall[]>();\n\n private enqueue(event: { queryRunner: any }, call: DeferredCall) {\n const qr = event.queryRunner;\n const arr = this.perTxn.get(qr) ?? [];\n arr.push(call);\n this.perTxn.set(qr, arr);\n }\n\n private shouldTrackAudit(metadata: EntityMetadata): boolean {\n return this.solidRegistry.isAuditableModel(lowerFirst(metadata.name));\n }\n\n async afterInsert(event: InsertEvent<any>) {\n if (this.shouldTrackAudit(event.metadata)) {\n // await this.chatterMessageService.postAuditMessageOnInsert(event.entity, event.metadata);\n this.enqueue(event, {\n kind: 'insert',\n args: [event.entity, event.metadata] as Parameters<ChatterMessageService['postAuditMessageOnInsert']>,\n });\n }\n }\n\n async afterUpdate(event: UpdateEvent<any>) {\n if (this.shouldTrackAudit(event.metadata)) {\n // await this.chatterMessageService.postAuditMessageOnUpdate(event.entity, event.metadata, event.databaseEntity, event.updatedColumns || []);\n this.enqueue(event, {\n kind: 'update',\n args: [\n event.entity, // entity (after)\n event.metadata,\n event.databaseEntity, // entity (before)\n event.updatedColumns ?? [],\n ] as Parameters<ChatterMessageService['postAuditMessageOnUpdate']>,\n });\n }\n }\n\n async afterRemove(event: RemoveEvent<any>) {\n if (this.shouldTrackAudit(event.metadata)) {\n // await this.chatterMessageService.postAuditMessageOnDelete(event.entity, event.metadata, event.databaseEntity);\n this.enqueue(event, {\n kind: 'delete',\n args: [\n event.entity,\n event.metadata,\n event.databaseEntity,\n ] as Parameters<ChatterMessageService['postAuditMessageOnDelete']>,\n });\n }\n }\n\n // --------- transaction lifecycle ----------\n async afterTransactionCommit(event: { queryRunner: any }) {\n const batch = this.perTxn.get(event.queryRunner) ?? [];\n this.perTxn.delete(event.queryRunner);\n\n // Now we’re OUTSIDE the DB transaction — safe to do I/O/DB writes inside chatter service.\n for (const item of batch) {\n try {\n switch (item.kind) {\n case 'insert': await this.chatterMessageService.postAuditMessageOnInsert(...item.args); break;\n case 'update': await this.chatterMessageService.postAuditMessageOnUpdate(...item.args); break;\n case 'delete': await this.chatterMessageService.postAuditMessageOnDelete(...item.args); break;\n }\n } catch (e) {\n // Best effort: log and continue; your core txn was already committed\n // Optionally: send to a generic error logger/metric here\n }\n }\n }\n\n afterTransactionRollback(event: { queryRunner: any }) {\n // Drop buffered calls; the write never happened\n this.perTxn.delete(event.queryRunner);\n }\n}\n\n// import { DataSource, EntityMetadata, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';\n// import { Injectable } from '@nestjs/common';\n// import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';\n// import { Repository } from 'typeorm';\n// import { ModelMetadata } from '../entities/model-metadata.entity';\n// import { lowerFirst } from 'src/helpers/string.helper';\n// import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';\n// import { ChatterMessagePayload } from 'src/jobs/chatter-queue-publisher.service';\n// import { RequestContextService } from 'src/services/request-context.service';\n// import { PublisherFactory } from 'src/services/queues/publisher-factory.service';\n\n// @EventSubscriber()\n// @Injectable()\n// export class AuditSubscriber implements EntitySubscriberInterface {\n// private perTxn = new WeakMap<any, ChatterMessagePayload[]>();\n\n// constructor(\n// @InjectDataSource() private readonly dataSource: DataSource,\n// @InjectRepository(ModelMetadata) private readonly modelMetadataRepo: Repository<ModelMetadata>,\n// private readonly modelMetadataHelperService: ModelMetadataHelperService,\n// private readonly requestContext: RequestContextService,\n// private readonly publisherFactory: PublisherFactory<any>\n// ) {\n// this.dataSource.subscribers.push(this);\n// }\n\n// // --- small cache to avoid metadata queries on every row ---\n// private modelCache = new Map<string, { enable: boolean; fields: Array<{ name: string; enableAuditTracking: boolean; type: string; relationType?: string }>; ts: number }>();\n// private cacheTTLms = 60_000;\n\n// private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<{ enable: boolean; auditFields?: string[] }> {\n// const key = metadata.name;\n// const now = Date.now();\n// const cached = this.modelCache.get(key);\n// if (cached && (now - cached.ts) < this.cacheTTLms) {\n// if (!cached.enable) return { enable: false };\n// const fields = cached.fields.filter(f =>\n// f.enableAuditTracking &&\n// !['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(f.type) &&\n// !(f.type === 'relation' && f.relationType === 'one-to-many')\n// );\n// const present = fields.map(f => f.name).filter(n => entity?.[n] !== undefined);\n// return { enable: present.length > 0, auditFields: present };\n// }\n\n// const model = await this.modelMetadataRepo.findOne({\n// where: { singularName: lowerFirst(metadata.name) },\n// relations: { fields: true, module: true },\n// });\n// const enable = !!model?.enableAuditTracking;\n// const fields = model?.fields ?? [];\n// this.modelCache.set(key, { enable, fields, ts: now });\n\n// if (!enable) return { enable: false };\n// const filtered = fields.filter(f =>\n// f.enableAuditTracking &&\n// !['mediaSingle', 'mediaMultiple', 'computed', 'richText', 'json'].includes(f.type) &&\n// !(f.type === 'relation' && f.relationType === 'one-to-many')\n// );\n// const present = filtered.map(f => f.name).filter(n => entity?.[n] !== undefined);\n// return { enable: present.length > 0, auditFields: present };\n// }\n\n// private push(event: { queryRunner: any }, msg: ChatterMessagePayload) {\n// const arr = this.perTxn.get(event.queryRunner) ?? [];\n// arr.push(msg);\n// this.perTxn.set(event.queryRunner, arr);\n// }\n\n// async afterInsert(event: InsertEvent<any>) {\n// if (!event.entity) return;\n// const enable = await this.shouldTrackAudit(event.entity, event.metadata);\n// if (!enable) return;\n\n// const payload: ChatterMessagePayload = {\n// eventType: 'insert',\n// model: event.metadata.name,\n// entityId: String(event.entity.id ?? event.entity.uuid ?? ''),\n// occurredAt: new Date().toISOString(),\n// after: this.safeCopy(event.entity),\n// userId: this.getUserId(),\n// };\n// this.push(event, payload);\n// }\n\n// async afterUpdate(event: UpdateEvent<any>) {\n// // Updated entity may be null if you used raw query; fall back to databaseEntity\n// const current = event.entity ?? {};\n// const before = event.databaseEntity ?? {};\n// const { enable, auditFields } = await this.shouldTrackAudit(current, event.metadata);\n// if (!enable) return;\n\n// const changedCols = (event.updatedColumns || []).map(c => c.propertyName);\n// const payload: ChatterMessagePayload = {\n// eventType: 'update',\n// model: event.metadata.name,\n// entityId: String((current as any).id ?? (before as any).id ?? ''),\n// occurredAt: new Date().toISOString(),\n// before: this.pick(before, auditFields || changedCols),\n// after: this.pick(current, auditFields || changedCols),\n// diff: changedCols,\n// userId: this.getUserId(),\n// };\n// this.push(event, payload);\n// }\n\n// async afterRemove(event: RemoveEvent<any>) {\n// const base = event.entity ?? event.databaseEntity;\n// if (!base) return;\n\n// const { enable } = await this.shouldTrackAudit(base, event.metadata);\n// if (!enable) return;\n\n// const payload: ChatterMessagePayload = {\n// eventType: 'delete',\n// model: event.metadata.name,\n// entityId: String((base as any).id ?? ''),\n// occurredAt: new Date().toISOString(),\n// before: this.safeCopy(base),\n// userId: this.getUserId(),\n// };\n// this.push(event, payload);\n// }\n\n// // Publish AFTER the transaction commits -> no idle-in-transaction\n// async afterTransactionCommit(event: { queryRunner: any }) {\n// const batch = this.perTxn.get(event.queryRunner) ?? [];\n// this.perTxn.delete(event.queryRunner);\n// for (const msg of batch) {\n// try {\n// await this.publisherFactory.publish({ payload: msg, parentEntity: msg.model, parentEntityId: msg.entityId }, 'ChatterQueuePublisher');\n// } catch (err) {\n// // log + optionally send to a DLQ or retry queue\n// // do NOT throw; commit already happened\n// // your RabbitMqPublisher likely tracks failures in MqMessage tables anyway\n// }\n// }\n// }\n\n// afterTransactionRollback(event: { queryRunner: any }) {\n// this.perTxn.delete(event.queryRunner);\n// }\n\n// // --- small helpers to keep payloads JSON-safe and small ---\n// private safeCopy(obj: any) {\n// try {\n// return JSON.parse(JSON.stringify(obj));\n// } catch {\n// return {}; // strip circular refs\n// }\n// }\n\n// private pick(obj: any, keys: string[]) {\n// const out: any = {};\n// for (const k of keys) out[k] = obj?.[k];\n// return this.safeCopy(out);\n// }\n\n// private getUserId(): string | null {\n\n// const activeUser = this.requestContext.getActiveUser();\n// if (activeUser?.sub)\n// return String(activeUser.sub);\n// }\n\n\n// }"]}
@@ -9,6 +9,5 @@ export declare class CreatedByUpdatedBySubscriber implements EntitySubscriberInt
9
9
  beforeInsert(event: InsertEvent<any>): Promise<void>;
10
10
  beforeUpdate(event: UpdateEvent<any>): Promise<void>;
11
11
  private stampUserField;
12
- private loadUser;
13
12
  }
14
13
  //# sourceMappingURL=created-by-updated-by.subscriber.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"created-by-updated-by.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAmB,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3G,qBAEa,4BAA6B,YAAW,yBAAyB;IAItE,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAJ1C,OAAO,CAAC,UAAU,CAAa;gBAGV,iBAAiB,EAAE,UAAU,EAC7B,qBAAqB,EAAE,qBAAqB;IAKjE,gBAAgB,CAAC,UAAU,EAAE,UAAU;IAKjC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;IAIpC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;YAI5B,cAAc;YAoBd,QAAQ;CAOzB"}
1
+ {"version":3,"file":"created-by-updated-by.subscriber.d.ts","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,yBAAyB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1F,qBAEa,4BAA6B,YAAW,yBAAyB;IAItE,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAJ1C,OAAO,CAAC,UAAU,CAAa;gBAGV,iBAAiB,EAAE,UAAU,EAC7B,qBAAqB,EAAE,qBAAqB;IAKjE,gBAAgB,CAAC,UAAU,EAAE,UAAU;IAKjC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;IAIpC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC;YAI5B,cAAc;CAmC/B"}
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.CreatedByUpdatedBySubscriber = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const typeorm_1 = require("@nestjs/typeorm");
18
- const user_entity_1 = require("../entities/user.entity");
19
18
  const request_context_service_1 = require("../services/request-context.service");
20
19
  const typeorm_2 = require("typeorm");
21
20
  let CreatedByUpdatedBySubscriber = class CreatedByUpdatedBySubscriber {
@@ -41,23 +40,14 @@ let CreatedByUpdatedBySubscriber = class CreatedByUpdatedBySubscriber {
41
40
  if (!activeUserOrUndefined) {
42
41
  return;
43
42
  }
44
- const loadedUser = await this.loadUser(activeUserOrUndefined);
45
43
  if (isInsert) {
46
- event.entity.createdBy = loadedUser?.id;
47
- event.entity.updatedBy = loadedUser?.id;
44
+ event.entity.createdBy = activeUserOrUndefined?.sub;
45
+ event.entity.updatedBy = activeUserOrUndefined?.sub;
48
46
  }
49
47
  else {
50
- event.entity.updatedBy = loadedUser?.id;
48
+ event.entity.updatedBy = activeUserOrUndefined?.sub;
51
49
  }
52
50
  }
53
- async loadUser(activeUser) {
54
- const userRepo = this.defaultDataSource.getRepository(user_entity_1.User);
55
- const loadedUser = await userRepo.findOne({
56
- where: { id: activeUser.sub },
57
- });
58
- return loadedUser;
59
- ;
60
- }
61
51
  };
62
52
  exports.CreatedByUpdatedBySubscriber = CreatedByUpdatedBySubscriber;
63
53
  exports.CreatedByUpdatedBySubscriber = CreatedByUpdatedBySubscriber = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"created-by-updated-by.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmD;AACnD,6CAAmD;AACnD,yDAAgD;AAEhD,iFAA6E;AAC7E,qCAA2G;AAIpG,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IAErC,YAEqB,iBAA6B,EAC7B,qBAA4C;QAD5C,sBAAiB,GAAjB,iBAAiB,CAAY;QAC7B,0BAAqB,GAArB,qBAAqB,CAAuB;IAGjE,CAAC;IAED,gBAAgB,CAAC,UAAsB;QACnC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAA0C,EAAE,QAAiB;QACtF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACX,CAAC;QAED,MAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,CAAC;QACzE,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAkD,CAAC,CAAC;QAC3F,IAAI,QAAQ,EAAE,CAAC;YACX,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,UAAU,EAAE,EAAE,CAAC;YACxC,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,UAAU,EAAE,EAAE,CAAC;QAC5C,CAAC;aACI,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,UAAU,EAAE,EAAE,CAAC;QAC5C,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,UAA0B;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,kBAAI,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;YACtC,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE;SAChC,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;QAAA,CAAC;IACvB,CAAC;CACJ,CAAA;AAlDY,oEAA4B;uCAA5B,4BAA4B;IAFxC,IAAA,mBAAU,EAAC,EAAC,KAAK,EAAE,cAAK,CAAC,SAAS,EAAC,CAAC;IAK5B,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACiB,oBAAU;QACN,+CAAqB;GALxD,4BAA4B,CAkDxC","sourcesContent":["import { Injectable, Scope } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport { User } from \"src/entities/user.entity\";\nimport { ActiveUserData } from \"src/interfaces/active-user-data.interface\";\nimport { RequestContextService } from \"src/services/request-context.service\";\nimport { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from \"typeorm\";\n\n@Injectable({scope: Scope.TRANSIENT})\n// @EventSubscriber()\nexport class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {\n private dataSource: DataSource;\n constructor(\n @InjectDataSource()\n private readonly defaultDataSource: DataSource,\n private readonly requestContextService: RequestContextService,\n ) {\n // this.dataSource.subscribers.push(this);\n }\n\n bindToDataSource(dataSource: DataSource) {\n this.dataSource = dataSource;\n this.dataSource.subscribers.push(this);\n }\n\n async beforeInsert(event: InsertEvent<any>) {\n await this.stampUserField(event, true);\n }\n\n async beforeUpdate(event: UpdateEvent<any>) {\n await this.stampUserField(event, false);\n }\n\n private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean){\n if (!event.entity) {\n return;\n }\n // Get the current active user details from the request context\n const activeUserOrUndefined = this.requestContextService.getActiveUser();\n if (!activeUserOrUndefined) {\n return;\n }\n\n const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);\n if (isInsert) {\n event.entity.createdBy = loadedUser?.id;\n event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user\n }\n else {\n event.entity.updatedBy = loadedUser?.id;\n }\n }\n\n private async loadUser(activeUser: ActiveUserData): Promise<User> {\n const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application\n const loadedUser = await userRepo.findOne({\n where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token\n });\n return loadedUser;;\n }\n}"]}
1
+ {"version":3,"file":"created-by-updated-by.subscriber.js","sourceRoot":"","sources":["../../src/subscribers/created-by-updated-by.subscriber.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmD;AACnD,6CAAmD;AACnD,iFAA6E;AAC7E,qCAA0F;AAInF,IAAM,4BAA4B,GAAlC,MAAM,4BAA4B;IAErC,YAEqB,iBAA6B,EAC7B,qBAA4C;QAD5C,sBAAiB,GAAjB,iBAAiB,CAAY;QAC7B,0BAAqB,GAArB,qBAAqB,CAAuB;IAGjE,CAAC;IAED,gBAAgB,CAAC,UAAsB;QACnC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAuB;QACtC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,KAA0C,EAAE,QAAiB;QACtF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACX,CAAC;QAED,MAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,CAAC;QACzE,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QAWD,IAAI,QAAQ,EAAE,CAAC;YACX,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,qBAAqB,EAAE,GAAG,CAAC;YACpD,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,qBAAqB,EAAE,GAAG,CAAC;QACxD,CAAC;aACI,CAAC;YACF,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,qBAAqB,EAAE,GAAG,CAAC;QACxD,CAAC;IACL,CAAC;CASJ,CAAA;AA1DY,oEAA4B;uCAA5B,4BAA4B;IAFxC,IAAA,mBAAU,EAAC,EAAE,KAAK,EAAE,cAAK,CAAC,SAAS,EAAE,CAAC;IAK9B,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACiB,oBAAU;QACN,+CAAqB;GALxD,4BAA4B,CA0DxC","sourcesContent":["import { Injectable, Scope } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport { RequestContextService } from \"src/services/request-context.service\";\nimport { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from \"typeorm\";\n\n@Injectable({ scope: Scope.TRANSIENT })\n// @EventSubscriber()\nexport class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {\n private dataSource: DataSource;\n constructor(\n @InjectDataSource()\n private readonly defaultDataSource: DataSource,\n private readonly requestContextService: RequestContextService,\n ) {\n // this.dataSource.subscribers.push(this);\n }\n\n bindToDataSource(dataSource: DataSource) {\n this.dataSource = dataSource;\n this.dataSource.subscribers.push(this);\n }\n\n async beforeInsert(event: InsertEvent<any>) {\n await this.stampUserField(event, true);\n }\n\n async beforeUpdate(event: UpdateEvent<any>) {\n await this.stampUserField(event, false);\n }\n\n private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean) {\n if (!event.entity) {\n return;\n }\n // Get the current active user details from the request context\n const activeUserOrUndefined = this.requestContextService.getActiveUser();\n if (!activeUserOrUndefined) {\n return;\n }\n\n // const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);\n // if (isInsert) {\n // event.entity.createdBy = loadedUser?.id;\n // event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user\n // }\n // else {\n // event.entity.updatedBy = loadedUser?.id;\n // }\n\n if (isInsert) {\n event.entity.createdBy = activeUserOrUndefined?.sub;\n event.entity.updatedBy = activeUserOrUndefined?.sub; // For insert, we set both createdBy and updatedBy to the same user\n }\n else {\n event.entity.updatedBy = activeUserOrUndefined?.sub;\n }\n }\n\n // private async loadUser(activeUser: ActiveUserData): Promise<User> {\n // const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application\n // const loadedUser = await userRepo.findOne({\n // where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token\n // });\n // return loadedUser;;\n // }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidxai/core",
3
- "version": "0.1.6-beta.22",
3
+ "version": "0.1.6-beta.24",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -110,7 +110,7 @@ export async function bootstrapSolidApp(
110
110
  if (req.query) {
111
111
  req.query = qs.parse(req.url.split('?')[1], {
112
112
  allowDots: true,
113
- depth: 10,
113
+ depth: 20,
114
114
  arrayLimit: 100,
115
115
  });
116
116
  }
@@ -82,6 +82,7 @@ export class SolidRegistry {
82
82
  private securityRuleConfigProviders: Set<InstanceWrapper> = new Set();
83
83
  private errorCodeProviders: Set<InstanceWrapper> = new Set();
84
84
  private settingsProviders: Set<InstanceWrapper> = new Set();
85
+ private auditableModels: Set<string> = new Set();
85
86
 
86
87
  registerErrorCodeProvider(errorCodeProvider: InstanceWrapper): void {
87
88
  this.errorCodeProviders.add(errorCodeProvider);
@@ -338,6 +339,14 @@ export class SolidRegistry {
338
339
  });
339
340
  }
340
341
 
342
+ registerAuditableModels(models: Set<string>): void {
343
+ this.auditableModels = models;
344
+ }
345
+
346
+ isAuditableModel(modelSingularName: string): boolean {
347
+ return this.auditableModels.has(modelSingularName.toLowerCase());
348
+ }
349
+
341
350
  getCommonEntityKeys(): (keyof CommonEntity | 'createdBy' | 'updatedBy')[] {
342
351
  return ['id', 'createdAt', 'updatedAt', 'deletedAt', 'createdBy', 'updatedBy', 'deletedTracker', 'localeName', 'defaultEntityLocaleId', 'publishedAt'];
343
352
  // return Reflect.getMetadataKeys(CommonEntity.prototype) as (keyof CommonEntity)[];
package/src/interfaces.ts CHANGED
@@ -271,6 +271,7 @@ export interface QueuesModuleOptions {
271
271
  type: BrokerType;
272
272
  queueName: string;
273
273
  prefetch?: number;
274
+ persistToDatabase?: boolean;
274
275
  }
275
276
 
276
277
  export type MediaWithFullUrl = Media & {
@@ -7,4 +7,5 @@ export default {
7
7
  type: BrokerType.RabbitMQ,
8
8
  queueName: QUEUE_NAME,
9
9
  prefetch: 10,
10
+ persistToDatabase: false,
10
11
  };
@@ -34,6 +34,10 @@ export abstract class RabbitMqPublisher<T> implements OnModuleDestroy, QueuePubl
34
34
 
35
35
  abstract options(): QueuesModuleOptions;
36
36
 
37
+ protected shouldPersistToDatabase(): boolean {
38
+ return this.options().persistToDatabase ?? true;
39
+ }
40
+
37
41
  private async ensureConnectionAndChannel(): Promise<amqp.Channel> {
38
42
  if (this.channel) {
39
43
  return this.channel;
@@ -170,7 +174,9 @@ export abstract class RabbitMqPublisher<T> implements OnModuleDestroy, QueuePubl
170
174
  message.messageId = uuidv4();
171
175
 
172
176
  // Save the message to the DB so that we can then change its status in the subscriber...
173
- await this.persistToDatabase(namespacedQueueName, message);
177
+ if (this.shouldPersistToDatabase()) {
178
+ await this.persistToDatabase(namespacedQueueName, message);
179
+ }
174
180
 
175
181
  // wait for the channel to confirm
176
182
  try {
@@ -199,7 +205,7 @@ export abstract class RabbitMqPublisher<T> implements OnModuleDestroy, QueuePubl
199
205
 
200
206
  private async persistToDatabase(queueName: string, message: QueueMessage<T>) {
201
207
 
202
- // TODO: make an entry in the relevant database table, generate a unique id earlier.
208
+ // make an entry in the relevant database table, generate a unique id earlier.
203
209
  try {
204
210
  // 1. resolve the queue first
205
211
  const mqMessageQueue = await this.mqMessageQueueService.resolveQueue(queueName);
@@ -56,6 +56,10 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
56
56
 
57
57
  abstract options(): QueuesModuleOptions;
58
58
 
59
+ protected shouldPersistToDatabase(): boolean {
60
+ return this.options().persistToDatabase ?? true;
61
+ }
62
+
59
63
  async establishConnection(): Promise<amqp.Connection> {
60
64
 
61
65
  const url = new URL(this.url);
@@ -236,7 +240,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
236
240
  this.logger.error(`Error processing message on queue ${queueName}: ${errorMessage}`, (error as Error)?.stack);
237
241
 
238
242
  if (message.currentRetry < message.retryCount) {
239
- await this.updateStatusInDatabase('retrying', message);
243
+ if (this.shouldPersistToDatabase()) {
244
+ await this.updateStatusInDatabase('retrying', message);
245
+ }
240
246
 
241
247
  message.currentRetry++;
242
248
  const retryQueue = `${queueName}.retry`;
@@ -254,8 +260,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
254
260
  this.logger.warn(`Retrying message (${message.currentRetry}/${message.retryCount}) after ${message.retryInterval}ms on queue ${queueName}`);
255
261
  return;
256
262
  }
257
-
258
- await this.updateStatusInDatabase('failed', message, errorMessage, '');
263
+ if (this.shouldPersistToDatabase()) {
264
+ await this.updateStatusInDatabase('failed', message, errorMessage, '');
265
+ }
259
266
  channel.ack(rawMessage);
260
267
  await this.publishToFailedQueue(queueName, Buffer.from(JSON.stringify(message)), channel, error);
261
268
  this.logger.error(`Message failed after ${message.retryCount} attempts on queue ${queueName}: ${errorMessage}`);
@@ -355,7 +362,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
355
362
  * Abstract method for message processing logic.
356
363
  */
357
364
  protected async processMessage(message: QueueMessage<T>, rawMessage, channel, queueName: string): Promise<void> {
358
- await this.updateStatusInDatabase('started', message);
365
+ if (this.shouldPersistToDatabase()) {
366
+ await this.updateStatusInDatabase('started', message);
367
+ }
359
368
 
360
369
  // Capture the results of handling the task.
361
370
  const result = await this.subscribeWithTimeout(message, queueName);
@@ -364,7 +373,9 @@ export abstract class RabbitMqSubscriber<T> implements OnModuleInit, QueueSubscr
364
373
  channel.ack(rawMessage);
365
374
 
366
375
  // Persist success output and timing.
367
- await this.updateStatusInDatabase('succeeded', message, '', result ? JSON.stringify(result, null, 2) : '');
376
+ if (this.shouldPersistToDatabase()) {
377
+ await this.updateStatusInDatabase('succeeded', message, '', result ? JSON.stringify(result, null, 2) : '');
378
+ }
368
379
 
369
380
  }
370
381
 
@@ -2,6 +2,7 @@ import { Injectable } from "@nestjs/common";
2
2
  import { ClsService } from "nestjs-cls";
3
3
  import { REQUEST_USER_KEY } from "src/constants";
4
4
  import { BasicFilterDto } from "src/dtos/basic-filters.dto";
5
+ import { ActiveUserData } from "src/interfaces/active-user-data.interface";
5
6
 
6
7
  @Injectable()
7
8
  export class RequestContextService {
@@ -9,7 +10,7 @@ export class RequestContextService {
9
10
  }
10
11
 
11
12
  // This method i.e getActiveUser() will fetch the user from the request object in the context
12
- getActiveUser() {
13
+ getActiveUser(): ActiveUserData | undefined {
13
14
  return this.cls.get(REQUEST_USER_KEY);
14
15
  }
15
16
 
@@ -1,6 +1,8 @@
1
1
  import { classify } from '@angular-devkit/core/src/utils/strings';
2
2
  import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
3
3
  import { DiscoveryService, MetadataScanner, ModuleRef, Reflector } from '@nestjs/core';
4
+ import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
5
+ import { ModelMetadataRepository } from 'src/repository/model-metadata.repository';
4
6
  import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
5
7
  import { getDataSourceToken } from '@nestjs/typeorm';
6
8
  import { IS_COMPUTED_FIELD_PROVIDER } from 'src/decorators/computed-field-provider.decorator';
@@ -40,6 +42,8 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
40
42
  private readonly solidRegistry: SolidRegistry,
41
43
  private readonly moduleRef: ModuleRef,
42
44
  private readonly settingService: SettingService,
45
+ private readonly modelMetadataRepo: ModelMetadataRepository,
46
+ private readonly modelMetadataHelperService: ModelMetadataHelperService,
43
47
  ) { }
44
48
 
45
49
  private readonly logger = new Logger(SolidIntrospectService.name);
@@ -142,9 +146,33 @@ export class SolidIntrospectService implements OnApplicationBootstrap {
142
146
 
143
147
  // Register the core subscribers against all the configured database modules / datasources
144
148
  await this.bootstrapCoreTypeOrmSubscribers(solidDatabaseModules);
149
+ await this.cacheAuditableModels();
145
150
  await this.settingService.updateSettingsCache();
146
151
  }
147
152
 
153
+ private async cacheAuditableModels(): Promise<void> {
154
+ const models = await this.modelMetadataRepo.find({
155
+ where: { enableAuditTracking: true },
156
+ relations: { fields: true, module: true },
157
+ });
158
+
159
+ const auditableSet = new Set<string>();
160
+ for (const model of models) {
161
+ const allFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName);
162
+ const hasAuditableField = allFields.some(field =>
163
+ field.enableAuditTracking &&
164
+ !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
165
+ !(field.type === 'relation' && field.relationType === 'one-to-many')
166
+ );
167
+ if (hasAuditableField) {
168
+ auditableSet.add(model.singularName.toLowerCase());
169
+ }
170
+ }
171
+
172
+ this.solidRegistry.registerAuditableModels(auditableSet);
173
+ this.logger.debug(`Cached ${auditableSet.size} auditable model(s): ${[...auditableSet].join(', ')}`);
174
+ }
175
+
148
176
  async bootstrapCoreTypeOrmSubscribers(dbModules: Array<InstanceWrapper<any>>): Promise<void> {
149
177
  // Register core subscribers for each Solid database module
150
178
  for (const wrapper of dbModules) {
@@ -1,7 +1,6 @@
1
- import { forwardRef, Inject, Injectable, Scope } from '@nestjs/common';
2
- import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
1
+ import { Injectable, Scope } from '@nestjs/common';
3
2
  import { lowerFirst } from 'src/helpers/string.helper';
4
- import { ModelMetadataRepository } from 'src/repository/model-metadata.repository';
3
+ import { SolidRegistry } from 'src/helpers/solid-registry';
5
4
  import { DataSource, EntityMetadata, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
6
5
  import { ChatterMessageService } from '../services/chatter-message.service';
7
6
 
@@ -16,17 +15,9 @@ type DeferredCall =
16
15
  export class AuditSubscriber implements EntitySubscriberInterface {
17
16
  private dataSource: DataSource;
18
17
  constructor(
19
- // @InjectDataSource()
20
- // private readonly dataSource: DataSource,
21
18
  private readonly chatterMessageService: ChatterMessageService,
22
- // @InjectRepository(ModelMetadata)
23
- // private readonly modelMetadataRepo: Repository<ModelMetadata>,
24
- @Inject(forwardRef(() => ModelMetadataRepository))
25
- private readonly modelMetadataRepo: ModelMetadataRepository,
26
- private readonly modelMetadataHelperService: ModelMetadataHelperService,
27
- ) {
28
- // this.dataSource.subscribers.push(this);
29
- }
19
+ private readonly solidRegistry: SolidRegistry,
20
+ ) { }
30
21
 
31
22
  bindToDataSource(dataSource: DataSource) {
32
23
  this.dataSource = dataSource;
@@ -43,46 +34,12 @@ export class AuditSubscriber implements EntitySubscriberInterface {
43
34
  this.perTxn.set(qr, arr);
44
35
  }
45
36
 
46
- private async shouldTrackAudit(entity: any, metadata: EntityMetadata): Promise<boolean> {
47
- const model = await this.modelMetadataRepo.findOne({
48
- where: {
49
- singularName: lowerFirst(metadata.name)
50
- },
51
- relations: {
52
- fields: true,
53
- module: true
54
- }
55
- });
56
-
57
- if (!model || !model.enableAuditTracking) {
58
- return false;
59
- }
60
-
61
- const modelFields = await this.modelMetadataHelperService.loadFieldHierarchy(model.singularName)
62
-
63
- const auditFields = modelFields.filter(field =>
64
- field.enableAuditTracking &&
65
- !['mediaSingle', 'mediaMultiple', 'richText', 'json'].includes(field.type) &&
66
- !(field.type === 'relation' && field.relationType === 'one-to-many')
67
- );
68
-
69
- if (auditFields.length === 0) {
70
- return false;
71
- }
72
-
73
- // if (!entity) {
74
- // console.warn(`[AuditSubscriber] Skipping audit for ${metadata.name} – entity is undefined or null`);
75
- // return false;
76
- // }
77
-
78
- return entity && auditFields.some(field => {
79
- const fieldValue = entity[field.name];
80
- return fieldValue !== undefined && fieldValue !== null;
81
- });
37
+ private shouldTrackAudit(metadata: EntityMetadata): boolean {
38
+ return this.solidRegistry.isAuditableModel(lowerFirst(metadata.name));
82
39
  }
83
40
 
84
41
  async afterInsert(event: InsertEvent<any>) {
85
- if (await this.shouldTrackAudit(event.entity, event.metadata)) {
42
+ if (this.shouldTrackAudit(event.metadata)) {
86
43
  // await this.chatterMessageService.postAuditMessageOnInsert(event.entity, event.metadata);
87
44
  this.enqueue(event, {
88
45
  kind: 'insert',
@@ -92,7 +49,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
92
49
  }
93
50
 
94
51
  async afterUpdate(event: UpdateEvent<any>) {
95
- if (await this.shouldTrackAudit(event.entity, event.metadata)) {
52
+ if (this.shouldTrackAudit(event.metadata)) {
96
53
  // await this.chatterMessageService.postAuditMessageOnUpdate(event.entity, event.metadata, event.databaseEntity, event.updatedColumns || []);
97
54
  this.enqueue(event, {
98
55
  kind: 'update',
@@ -107,7 +64,7 @@ export class AuditSubscriber implements EntitySubscriberInterface {
107
64
  }
108
65
 
109
66
  async afterRemove(event: RemoveEvent<any>) {
110
- if (await this.shouldTrackAudit(event.entity, event.metadata)) {
67
+ if (this.shouldTrackAudit(event.metadata)) {
111
68
  // await this.chatterMessageService.postAuditMessageOnDelete(event.entity, event.metadata, event.databaseEntity);
112
69
  this.enqueue(event, {
113
70
  kind: 'delete',
@@ -1,11 +1,9 @@
1
1
  import { Injectable, Scope } from "@nestjs/common";
2
2
  import { InjectDataSource } from "@nestjs/typeorm";
3
- import { User } from "src/entities/user.entity";
4
- import { ActiveUserData } from "src/interfaces/active-user-data.interface";
5
3
  import { RequestContextService } from "src/services/request-context.service";
6
- import { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent } from "typeorm";
4
+ import { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from "typeorm";
7
5
 
8
- @Injectable({scope: Scope.TRANSIENT})
6
+ @Injectable({ scope: Scope.TRANSIENT })
9
7
  // @EventSubscriber()
10
8
  export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
11
9
  private dataSource: DataSource;
@@ -30,7 +28,7 @@ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
30
28
  await this.stampUserField(event, false);
31
29
  }
32
30
 
33
- private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean){
31
+ private async stampUserField(event: InsertEvent<any> | UpdateEvent<any>, isInsert: boolean) {
34
32
  if (!event.entity) {
35
33
  return;
36
34
  }
@@ -40,21 +38,29 @@ export class CreatedByUpdatedBySubscriber implements EntitySubscriberInterface {
40
38
  return;
41
39
  }
42
40
 
43
- const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
41
+ // const loadedUser = await this.loadUser(activeUserOrUndefined as unknown as ActiveUserData);
42
+ // if (isInsert) {
43
+ // event.entity.createdBy = loadedUser?.id;
44
+ // event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user
45
+ // }
46
+ // else {
47
+ // event.entity.updatedBy = loadedUser?.id;
48
+ // }
49
+
44
50
  if (isInsert) {
45
- event.entity.createdBy = loadedUser?.id;
46
- event.entity.updatedBy = loadedUser?.id; // For insert, we set both createdBy and updatedBy to the same user
51
+ event.entity.createdBy = activeUserOrUndefined?.sub;
52
+ event.entity.updatedBy = activeUserOrUndefined?.sub; // For insert, we set both createdBy and updatedBy to the same user
47
53
  }
48
54
  else {
49
- event.entity.updatedBy = loadedUser?.id;
55
+ event.entity.updatedBy = activeUserOrUndefined?.sub;
50
56
  }
51
57
  }
52
58
 
53
- private async loadUser(activeUser: ActiveUserData): Promise<User> {
54
- const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application
55
- const loadedUser = await userRepo.findOne({
56
- where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token
57
- });
58
- return loadedUser;;
59
- }
59
+ // private async loadUser(activeUser: ActiveUserData): Promise<User> {
60
+ // const userRepo = this.defaultDataSource.getRepository(User); // Assuming 'User' is the entity name for users in your application
61
+ // const loadedUser = await userRepo.findOne({
62
+ // where: { id: activeUser.sub }, // Assuming 'sub' is the user ID in the JWT token
63
+ // });
64
+ // return loadedUser;;
65
+ // }
60
66
  }