@neohm/nestend 3.3.8 → 3.3.10

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 (275) hide show
  1. package/README.md +238 -121
  2. package/dist/admin/admin.module.d.ts.map +1 -1
  3. package/dist/admin/admin.module.js.map +1 -1
  4. package/dist/admin/controllers/admin.api.key.controller.js +1 -1
  5. package/dist/admin/controllers/admin.api.key.controller.js.map +1 -1
  6. package/dist/admin/controllers/admin.email.template.controller.d.ts +24 -0
  7. package/dist/admin/controllers/admin.email.template.controller.d.ts.map +1 -0
  8. package/dist/admin/controllers/admin.email.template.controller.js +106 -0
  9. package/dist/admin/controllers/admin.email.template.controller.js.map +1 -0
  10. package/dist/admin/controllers/admin.pdf.template.controller.d.ts +24 -0
  11. package/dist/admin/controllers/admin.pdf.template.controller.d.ts.map +1 -0
  12. package/dist/admin/controllers/admin.pdf.template.controller.js +106 -0
  13. package/dist/admin/controllers/admin.pdf.template.controller.js.map +1 -0
  14. package/dist/admin/controllers/admin.scheduled.task.controller.d.ts.map +1 -1
  15. package/dist/admin/controllers/admin.scheduled.task.controller.js +1 -1
  16. package/dist/admin/controllers/admin.scheduled.task.controller.js.map +1 -1
  17. package/dist/admin/controllers/admin.scope.controller.d.ts.map +1 -1
  18. package/dist/admin/controllers/admin.scope.controller.js +1 -1
  19. package/dist/admin/controllers/admin.scope.controller.js.map +1 -1
  20. package/dist/admin/controllers/admin.system.user.controller.d.ts.map +1 -1
  21. package/dist/admin/controllers/admin.system.user.controller.js +1 -1
  22. package/dist/admin/controllers/admin.system.user.controller.js.map +1 -1
  23. package/dist/admin/controllers/admin.tenant.controller.d.ts.map +1 -1
  24. package/dist/admin/controllers/admin.tenant.controller.js +1 -1
  25. package/dist/admin/controllers/admin.tenant.controller.js.map +1 -1
  26. package/dist/admin/controllers/admin.tenant.user.controller.d.ts.map +1 -1
  27. package/dist/admin/controllers/admin.tenant.user.controller.js +1 -1
  28. package/dist/admin/controllers/admin.tenant.user.controller.js.map +1 -1
  29. package/dist/admin/controllers/admin.user.controller.d.ts.map +1 -1
  30. package/dist/admin/controllers/admin.user.controller.js +1 -1
  31. package/dist/admin/controllers/admin.user.controller.js.map +1 -1
  32. package/dist/admin/controllers/system.role.controller.d.ts.map +1 -1
  33. package/dist/admin/controllers/system.role.controller.js +1 -1
  34. package/dist/admin/controllers/system.role.controller.js.map +1 -1
  35. package/dist/admin/dtos/add.admin.user.dto.d.ts +2 -2
  36. package/dist/admin/dtos/add.admin.user.dto.d.ts.map +1 -1
  37. package/dist/admin/dtos/add.admin.user.dto.js +2 -2
  38. package/dist/admin/dtos/add.admin.user.dto.js.map +1 -1
  39. package/dist/admin/dtos/add.email.template.dto.d.ts +9 -0
  40. package/dist/admin/dtos/add.email.template.dto.d.ts.map +1 -0
  41. package/dist/admin/dtos/add.email.template.dto.js +68 -0
  42. package/dist/admin/dtos/add.email.template.dto.js.map +1 -0
  43. package/dist/admin/dtos/add.pdf.template.dto.d.ts +8 -0
  44. package/dist/admin/dtos/add.pdf.template.dto.d.ts.map +1 -0
  45. package/dist/admin/dtos/add.pdf.template.dto.js +58 -0
  46. package/dist/admin/dtos/add.pdf.template.dto.js.map +1 -0
  47. package/dist/admin/dtos/add.scheduled.task.dto.d.ts +2 -2
  48. package/dist/admin/dtos/add.scheduled.task.dto.d.ts.map +1 -1
  49. package/dist/admin/dtos/add.scheduled.task.dto.js +2 -2
  50. package/dist/admin/dtos/add.scheduled.task.dto.js.map +1 -1
  51. package/dist/admin/dtos/add.scope.dto.d.ts +2 -2
  52. package/dist/admin/dtos/add.scope.dto.d.ts.map +1 -1
  53. package/dist/admin/dtos/add.scope.dto.js +2 -2
  54. package/dist/admin/dtos/add.scope.dto.js.map +1 -1
  55. package/dist/admin/dtos/add.system.role.dto.d.ts +2 -2
  56. package/dist/admin/dtos/add.system.role.dto.d.ts.map +1 -1
  57. package/dist/admin/dtos/add.system.role.dto.js +2 -2
  58. package/dist/admin/dtos/add.system.role.dto.js.map +1 -1
  59. package/dist/admin/dtos/add.tenant.dto.d.ts +3 -2
  60. package/dist/admin/dtos/add.tenant.dto.d.ts.map +1 -1
  61. package/dist/admin/dtos/add.tenant.dto.js +9 -2
  62. package/dist/admin/dtos/add.tenant.dto.js.map +1 -1
  63. package/dist/admin/dtos/base.user.dto.d.ts +2 -2
  64. package/dist/admin/dtos/base.user.dto.d.ts.map +1 -1
  65. package/dist/admin/dtos/base.user.dto.js +2 -2
  66. package/dist/admin/dtos/base.user.dto.js.map +1 -1
  67. package/dist/admin/dtos/email.template.list.dto.d.ts +4 -0
  68. package/dist/admin/dtos/email.template.list.dto.d.ts.map +1 -0
  69. package/dist/admin/dtos/email.template.list.dto.js +8 -0
  70. package/dist/admin/dtos/email.template.list.dto.js.map +1 -0
  71. package/dist/admin/dtos/pdf.template.list.dto.d.ts +4 -0
  72. package/dist/admin/dtos/pdf.template.list.dto.d.ts.map +1 -0
  73. package/dist/admin/dtos/pdf.template.list.dto.js +8 -0
  74. package/dist/admin/dtos/pdf.template.list.dto.js.map +1 -0
  75. package/dist/admin/es6.classes.d.ts +12 -4
  76. package/dist/admin/es6.classes.d.ts.map +1 -1
  77. package/dist/admin/es6.classes.js +22 -1
  78. package/dist/admin/es6.classes.js.map +1 -1
  79. package/dist/admin/libraries/admin.user.data.processor.js +1 -1
  80. package/dist/admin/libraries/admin.user.data.processor.js.map +1 -1
  81. package/dist/admin/libraries/admin.user.delete.processor.js +2 -2
  82. package/dist/admin/libraries/admin.user.delete.processor.js.map +1 -1
  83. package/dist/admin/libraries/api.key.data.processor.js +1 -1
  84. package/dist/admin/libraries/api.key.data.processor.js.map +1 -1
  85. package/dist/admin/libraries/bulk.user.data.processor.js +1 -1
  86. package/dist/admin/libraries/bulk.user.data.processor.js.map +1 -1
  87. package/dist/admin/libraries/client.user.data.processor.js +1 -1
  88. package/dist/admin/libraries/client.user.data.processor.js.map +1 -1
  89. package/dist/admin/libraries/client.user.delete.processor.js +1 -1
  90. package/dist/admin/libraries/client.user.delete.processor.js.map +1 -1
  91. package/dist/admin/libraries/email.template.data.processor.d.ts +13 -0
  92. package/dist/admin/libraries/email.template.data.processor.d.ts.map +1 -0
  93. package/dist/admin/libraries/email.template.data.processor.js +51 -0
  94. package/dist/admin/libraries/email.template.data.processor.js.map +1 -0
  95. package/dist/admin/libraries/email.template.list.processor.d.ts +15 -0
  96. package/dist/admin/libraries/email.template.list.processor.d.ts.map +1 -0
  97. package/dist/admin/libraries/email.template.list.processor.js +31 -0
  98. package/dist/admin/libraries/email.template.list.processor.js.map +1 -0
  99. package/dist/admin/libraries/pdf.template.data.processor.d.ts +13 -0
  100. package/dist/admin/libraries/pdf.template.data.processor.d.ts.map +1 -0
  101. package/dist/admin/libraries/pdf.template.data.processor.js +48 -0
  102. package/dist/admin/libraries/pdf.template.data.processor.js.map +1 -0
  103. package/dist/admin/libraries/pdf.template.list.processor.d.ts +15 -0
  104. package/dist/admin/libraries/pdf.template.list.processor.d.ts.map +1 -0
  105. package/dist/admin/libraries/pdf.template.list.processor.js +30 -0
  106. package/dist/admin/libraries/pdf.template.list.processor.js.map +1 -0
  107. package/dist/admin/libraries/scheduled.task.data.processor.js +1 -1
  108. package/dist/admin/libraries/scheduled.task.data.processor.js.map +1 -1
  109. package/dist/admin/libraries/scheduled.task.delete.processor.js +1 -1
  110. package/dist/admin/libraries/scheduled.task.delete.processor.js.map +1 -1
  111. package/dist/admin/libraries/scope.data.processor.js +1 -1
  112. package/dist/admin/libraries/scope.data.processor.js.map +1 -1
  113. package/dist/admin/libraries/system.role.data.processor.js +1 -1
  114. package/dist/admin/libraries/system.role.data.processor.js.map +1 -1
  115. package/dist/admin/libraries/system.user.data.processor.js +1 -1
  116. package/dist/admin/libraries/system.user.data.processor.js.map +1 -1
  117. package/dist/admin/libraries/tenant.data.processor.d.ts.map +1 -1
  118. package/dist/admin/libraries/tenant.data.processor.js +2 -1
  119. package/dist/admin/libraries/tenant.data.processor.js.map +1 -1
  120. package/dist/admin/libraries/tenant.delete.processor.js +1 -1
  121. package/dist/admin/libraries/tenant.delete.processor.js.map +1 -1
  122. package/dist/admin/services/template.service.d.ts +7 -0
  123. package/dist/admin/services/template.service.d.ts.map +1 -0
  124. package/dist/admin/services/template.service.js +34 -0
  125. package/dist/admin/services/template.service.js.map +1 -0
  126. package/dist/auth/processors/change.password.data.processor.js +2 -2
  127. package/dist/auth/processors/change.password.data.processor.js.map +1 -1
  128. package/dist/auth/processors/register.user.data.processor.js +1 -1
  129. package/dist/auth/processors/register.user.data.processor.js.map +1 -1
  130. package/dist/auth/services/impersonation.service.d.ts.map +1 -1
  131. package/dist/auth/services/impersonation.service.js +4 -3
  132. package/dist/auth/services/impersonation.service.js.map +1 -1
  133. package/dist/common/dtos/common.data.dto.d.ts +4 -0
  134. package/dist/common/dtos/common.data.dto.d.ts.map +1 -0
  135. package/dist/common/dtos/{common.payload.dto.js → common.data.dto.js} +5 -5
  136. package/dist/common/dtos/common.data.dto.js.map +1 -0
  137. package/dist/common/dtos/common.list.filter.dto.d.ts.map +1 -1
  138. package/dist/common/dtos/common.list.filter.dto.js +22 -0
  139. package/dist/common/dtos/common.list.filter.dto.js.map +1 -1
  140. package/dist/common/dtos/common.params.dto.d.ts +1 -0
  141. package/dist/common/dtos/common.params.dto.d.ts.map +1 -1
  142. package/dist/common/dtos/common.params.dto.js +6 -0
  143. package/dist/common/dtos/common.params.dto.js.map +1 -1
  144. package/dist/common/dtos/filter.dto.d.ts.map +1 -1
  145. package/dist/common/dtos/filter.dto.js +15 -0
  146. package/dist/common/dtos/filter.dto.js.map +1 -1
  147. package/dist/common/dtos/sort.dto.d.ts.map +1 -1
  148. package/dist/common/dtos/sort.dto.js +8 -0
  149. package/dist/common/dtos/sort.dto.js.map +1 -1
  150. package/dist/common/entities/system.audit.log.entity.d.ts +3 -3
  151. package/dist/common/entities/system.audit.log.entity.d.ts.map +1 -1
  152. package/dist/common/entities/system.audit.log.entity.js +6 -6
  153. package/dist/common/entities/system.audit.log.entity.js.map +1 -1
  154. package/dist/common/es6.classes.d.ts +2 -2
  155. package/dist/common/es6.classes.d.ts.map +1 -1
  156. package/dist/common/es6.classes.js +2 -2
  157. package/dist/common/es6.classes.js.map +1 -1
  158. package/dist/common/libraries/common.data.processor.d.ts +1 -1
  159. package/dist/common/libraries/common.data.processor.d.ts.map +1 -1
  160. package/dist/common/libraries/common.data.processor.js +1 -1
  161. package/dist/common/libraries/common.data.processor.js.map +1 -1
  162. package/dist/common/services/audit.service.d.ts +4 -4
  163. package/dist/common/services/audit.service.js +15 -15
  164. package/dist/common/services/audit.service.js.map +1 -1
  165. package/dist/common/services/queue.service.d.ts +3 -2
  166. package/dist/common/services/queue.service.d.ts.map +1 -1
  167. package/dist/common/services/queue.service.js +15 -1
  168. package/dist/common/services/queue.service.js.map +1 -1
  169. package/dist/common/subscribers/common.subscriber.d.ts.map +1 -1
  170. package/dist/common/subscribers/common.subscriber.js.map +1 -1
  171. package/dist/common/types/audit.types.d.ts +4 -4
  172. package/dist/common/types/audit.types.d.ts.map +1 -1
  173. package/dist/common/types/queue.types.d.ts +10 -0
  174. package/dist/common/types/queue.types.d.ts.map +1 -1
  175. package/dist/constants/entity.constants.d.ts +1 -0
  176. package/dist/constants/entity.constants.d.ts.map +1 -1
  177. package/dist/constants/entity.constants.js +1 -0
  178. package/dist/constants/entity.constants.js.map +1 -1
  179. package/dist/constants/entity.maps.d.ts +2 -0
  180. package/dist/constants/entity.maps.d.ts.map +1 -1
  181. package/dist/constants/entity.maps.js +2 -0
  182. package/dist/constants/entity.maps.js.map +1 -1
  183. package/dist/constants/job.constants.d.ts +1 -0
  184. package/dist/constants/job.constants.d.ts.map +1 -1
  185. package/dist/constants/job.constants.js +1 -0
  186. package/dist/constants/job.constants.js.map +1 -1
  187. package/dist/document/controllers/document.controller.js +1 -1
  188. package/dist/document/controllers/document.controller.js.map +1 -1
  189. package/dist/document/document.module.d.ts.map +1 -1
  190. package/dist/document/document.module.js +18 -2
  191. package/dist/document/document.module.js.map +1 -1
  192. package/dist/document/dtos/pdf.document.options.dto.d.ts +17 -0
  193. package/dist/document/dtos/pdf.document.options.dto.d.ts.map +1 -0
  194. package/dist/document/dtos/pdf.document.options.dto.js +13 -0
  195. package/dist/document/dtos/pdf.document.options.dto.js.map +1 -0
  196. package/dist/document/dtos/pdf.template.attributes.dto.d.ts +3 -0
  197. package/dist/document/dtos/pdf.template.attributes.dto.d.ts.map +1 -0
  198. package/dist/document/dtos/pdf.template.attributes.dto.js +7 -0
  199. package/dist/document/dtos/pdf.template.attributes.dto.js.map +1 -0
  200. package/dist/document/entities/document.entity.d.ts +1 -0
  201. package/dist/document/entities/document.entity.d.ts.map +1 -1
  202. package/dist/document/entities/document.entity.js +4 -0
  203. package/dist/document/entities/document.entity.js.map +1 -1
  204. package/dist/document/entities/pdf.template.entity.d.ts +9 -0
  205. package/dist/document/entities/pdf.template.entity.d.ts.map +1 -0
  206. package/dist/document/entities/pdf.template.entity.js +38 -0
  207. package/dist/document/entities/pdf.template.entity.js.map +1 -0
  208. package/dist/document/enums/document.default.enum.d.ts +3 -1
  209. package/dist/document/enums/document.default.enum.d.ts.map +1 -1
  210. package/dist/document/enums/document.default.enum.js +2 -0
  211. package/dist/document/enums/document.default.enum.js.map +1 -1
  212. package/dist/document/enums/pdf.default.enum.d.ts +2 -1
  213. package/dist/document/enums/pdf.default.enum.d.ts.map +1 -1
  214. package/dist/document/enums/pdf.default.enum.js +1 -0
  215. package/dist/document/enums/pdf.default.enum.js.map +1 -1
  216. package/dist/document/es6.classes.d.ts +8 -6
  217. package/dist/document/es6.classes.d.ts.map +1 -1
  218. package/dist/document/es6.classes.js +16 -3
  219. package/dist/document/es6.classes.js.map +1 -1
  220. package/dist/document/jobs/clean.orphan.documents.job.d.ts +16 -0
  221. package/dist/document/jobs/clean.orphan.documents.job.d.ts.map +1 -0
  222. package/dist/document/jobs/clean.orphan.documents.job.js +63 -0
  223. package/dist/document/jobs/clean.orphan.documents.job.js.map +1 -0
  224. package/dist/document/libraries/process.pdf.document.d.ts +16 -0
  225. package/dist/document/libraries/process.pdf.document.d.ts.map +1 -0
  226. package/dist/document/libraries/process.pdf.document.js +47 -0
  227. package/dist/document/libraries/process.pdf.document.js.map +1 -0
  228. package/dist/document/services/document.service.d.ts +3 -2
  229. package/dist/document/services/document.service.d.ts.map +1 -1
  230. package/dist/document/services/document.service.js +14 -2
  231. package/dist/document/services/document.service.js.map +1 -1
  232. package/dist/document/services/pdf.template.service.d.ts +12 -0
  233. package/dist/document/services/pdf.template.service.d.ts.map +1 -0
  234. package/dist/document/services/pdf.template.service.js +57 -0
  235. package/dist/document/services/pdf.template.service.js.map +1 -0
  236. package/dist/document/subscribers/document.subscriber.d.ts +2 -0
  237. package/dist/document/subscribers/document.subscriber.d.ts.map +1 -1
  238. package/dist/document/subscribers/document.subscriber.js +13 -0
  239. package/dist/document/subscribers/document.subscriber.js.map +1 -1
  240. package/dist/document/types/document.types.d.ts +3 -0
  241. package/dist/document/types/document.types.d.ts.map +1 -1
  242. package/dist/document/types/pdf.types.d.ts +4 -0
  243. package/dist/document/types/pdf.types.d.ts.map +1 -1
  244. package/dist/index.d.ts +9 -4
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js +17 -5
  247. package/dist/index.js.map +1 -1
  248. package/dist/migrations/1749600000002.CreateSysTenantsTable.d.ts.map +1 -1
  249. package/dist/migrations/1749600000002.CreateSysTenantsTable.js +1 -0
  250. package/dist/migrations/1749600000002.CreateSysTenantsTable.js.map +1 -1
  251. package/dist/migrations/1749600000012.CreateSysAuditLogsTable.d.ts.map +1 -1
  252. package/dist/migrations/1749600000012.CreateSysAuditLogsTable.js +4 -4
  253. package/dist/migrations/1749600000012.CreateSysAuditLogsTable.js.map +1 -1
  254. package/dist/migrations/1749600000020.CreateSysEmailTemplatesTable.d.ts.map +1 -1
  255. package/dist/migrations/1749600000020.CreateSysEmailTemplatesTable.js +11 -1
  256. package/dist/migrations/1749600000020.CreateSysEmailTemplatesTable.js.map +1 -1
  257. package/dist/migrations/1749600000028.CreateSysDocumentsTable.d.ts.map +1 -1
  258. package/dist/migrations/1749600000028.CreateSysDocumentsTable.js +1 -0
  259. package/dist/migrations/1749600000028.CreateSysDocumentsTable.js.map +1 -1
  260. package/dist/migrations/1749600000029.CreateSysPdfTemplatesTable.d.ts +6 -0
  261. package/dist/migrations/1749600000029.CreateSysPdfTemplatesTable.d.ts.map +1 -0
  262. package/dist/migrations/1749600000029.CreateSysPdfTemplatesTable.js +33 -0
  263. package/dist/migrations/1749600000029.CreateSysPdfTemplatesTable.js.map +1 -0
  264. package/dist/notification/entities/email.template.entity.d.ts +2 -2
  265. package/dist/notification/entities/email.template.entity.d.ts.map +1 -1
  266. package/dist/notification/entities/email.template.entity.js +2 -2
  267. package/dist/notification/entities/email.template.entity.js.map +1 -1
  268. package/dist/tenant/entities/tenant.entity.d.ts +1 -0
  269. package/dist/tenant/entities/tenant.entity.d.ts.map +1 -1
  270. package/dist/tenant/entities/tenant.entity.js +5 -1
  271. package/dist/tenant/entities/tenant.entity.js.map +1 -1
  272. package/package.json +1 -1
  273. package/dist/common/dtos/common.payload.dto.d.ts +0 -4
  274. package/dist/common/dtos/common.payload.dto.d.ts.map +0 -1
  275. package/dist/common/dtos/common.payload.dto.js.map +0 -1
package/README.md CHANGED
@@ -101,33 +101,33 @@ these are free to change without a breaking release.
101
101
 
102
102
  ## Feature Map
103
103
 
104
- | Area | Key exports | Notes |
105
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
106
- | Entities | `RootEntity`, `CommonEntity`, `CommonAdminEntity`, `UserEntity`, `AdminUserEntity`, `SystemRoleEntity`, `SystemAdminRoleEntity`, ... | uuid PK, soft deletes, `created_by`/`updated_by` (+ `creator`/`updater` relations), jsonb `attributes`; entity-level `findOneOrNew`, `hardDelete`, `restore`, `paginate` statics plus a row-wise `softDelete()` |
107
- | Migrations | `MigrationUtility`, `SeederUtility` (the 28 table migrations ship in `dist/migrations` — glob them, they are not barrel exports) | fluent `this.string()`, `this.numeric()`, `addRecord()` seeding |
108
- | Queue | `QueueModule`, `QueueModule.withConnection()`, `QueueService`, `CommonJob` | BullMQ `@Processor`/`WorkerHost` consumer; auto-discovered jobs, duplicate prevention, recurring schedulers, completed jobs cleared from Redis on success (1-day TTL on failures), `clearQueue()`, retries, failures persisted to `sys_failed_jobs`; connection from `BULL_REDIS_*` or supplied by the app — never register `BullModule.forRoot()` yourself, two roots race over the worker's connection |
109
- | Scheduled events | `SystemScheduledTaskEntity`, `ScheduledEventEntity`, `ScheduleKind`, `ScheduleSpec`, `IsCronExpression` | schedule definitions in `sys_scheduled_tasks` → events in `sys_scheduled_events`; interval / cron / once specs with timezones, drift-free slots, horizon materialisation, catch-up + overlap policies, fire-once dispatch with backoff, cadence via `SCHEDULED_EVENT_MIN` |
110
- | Auth | `AuthModule`, `AuthService.validateRole()`, `RoleTier`, `TenantRole`, `Auth`, `JwtMiddleware` | imperative auth (open by default); `<module>.<tier>` role hierarchy (manager > writer > viewer), with `global.admin` overriding every role check; JwtMiddleware resolves the user into `Auth`'s AsyncLocalStorage context per request; access + rotating refresh tokens; session-based logout invalidation (`sys_auth_sessions`); Google SSO via ID token at `POST /auth/google`; impersonation — a tenantless `has_global_access` user signs into one organization at `POST /auth/impersonation/login` |
111
- | Admin auth | `AdminAuthService.validateRole()`, `AdminAuth`, `AdminJwtMiddleware` (endpoints at `/admin/auth`) | fully separate stack for back-office admins — own users table (`sys_admin_users`), own roles (`sys_admin_roles`/`sys_auser_roles`), own sessions/refresh tokens (shared tables, `is_admin` flag) |
112
- | API keys | `ApiKeyService`, `ApiKeyGuard` / `ApiKeyMiddleware`, `@ApiKeyScopes()`, `ApiKeyContext` | hashed keys in `sys_api_keys` (admin-owned), one tenant per key, expiry/revocation, `x-api-key` header; `validateAccess(scope)` returns the tenant the key acts for |
113
- | Scopes | `ScopeService`, `AuthService.validateAccess()`, `ScopeEntity`, `TenantScopeEntity`, `ApiScopeEntity` | one feature named once in `sys_scopes`, then granted per organization (`sys_tenant_scopes`) or per API key (`sys_api_scopes`) — never an array on the principal |
114
- | Multi-tenancy | `TenantModule`, `TenantEntity`, `CommonTenantEntity`, `TenantContextService`, `TenantContextMiddleware` | AsyncLocalStorage tenant context; resolve by header, slug, or domain; tenants are onboarded by admins (`TenantEntity.creator` → `AdminUserEntity`) |
115
- | Admin | `AdminModule` (its controllers, DTOs and processors are internal — import the module and hit the endpoints), `GlobalAdminRoles` | back-office APIs: tenant onboarding, client/admin/system user registration (single + bulk), the `has_global_access` grant that enables impersonation, tenant + scheduled-task listing; 4-tier `GlobalAdminRoles` gate over `sys_admin_roles`; Swagger at `/docs` (hidden in production) |
116
- | Notifications | `NotificationModule`, `NotificationService`, `FcmPushNotificationService` | in-app rows + FCM push (optional `firebase-admin`); device token registry |
117
- | Email (SES) | `EmailNotificationService`, `ProcessEmailNotification`, `EmailTemplateEntity`, `EmailNotificationLogEntity` | templates in `sys_email_templates` rendered by a remote service, dispatched via SES per recipient (to/cc/bcc); logged to `sys_email_notification_logs` outside production; dispatch skipped when `AWS_SES_FROM_ADDRESS` unset |
118
- | Audit | `AuditService` | `saveAuditLog(old, new)` with changed-value diffing into `sys_audit_logs` |
119
- | Cache | `CacheService` | Redis, TTL objects (`{days, hours, min}`), transparent encryption |
120
- | Encryption | `EncryptionService` | AES-256-GCM, scrypt-derived keys |
121
- | System properties | `SystemPropertyModule`, `SystemPropertyService` | cached + optionally encrypted key/value settings in `sys_properties` |
122
- | Documents | `DocumentModule`, `DocumentService`, `S3BucketDocumentService`, `MinioBucketDocumentService` | `POST /api/documents/upload` (multipart) or `uploadDocument(multerFiles) → links[]`; driver chosen via `DOCUMENT_STORAGE_DRIVER` |
123
- | PDF generation | `PdfService`, `PdfRenderOptions` | `renderHtmlToPdf(html) → Buffer` via headless Chromium; honours the document's own `@page` rules and paginates to any length; optional peer dep `puppeteer` |
124
- | HTTP | `CommonController`, `GlobalExceptionFilter`, common DTOs | health endpoint base controller, uniform error envelope |
125
- | Raw SQL | `SqlService`, `SqlTarget` | named `:key` placeholders always bound as driver params, `:...key` array expansion; runs on the read replica (`DB_READ_*`) with transparent primary fallback; `fetchOne`/`fetchScalar`/`exists`/`count`/`paginate` helpers |
126
- | List endpoints | `CommonListProcessor`, `CommonListFilterDto`, `FilterDto`, `CommonListFilterConfig` | allowlisted columns, parameterised operators (`eq`…`isNull`), typed value casting (`str`/`num`/`date`/`bool`/`null`), multi-sort + paginated on the read replica |
127
- | Outbound HTTP | `RemoteRequestService`, `ApiRequestTypeEnum`, `ApiContentTypeEnum` | injectable `fetch` wrapper (global via `CommonModule`); `getApiResponse<T>()` parses JSON/text and throws `OperationException` on non-2xx, `getApiRawResponse()` returns the raw `Response`; JSON + form-urlencoded body serialisation |
128
- | Observability | `LoggingInterceptor`, `MonitoringInterceptor`, `MonitoringService` | Sentry auto-registers when `@sentry/node` + `SENTRY_DSN` present; pluggable `MonitoringAdapter` |
129
- | Jobs/Events | `CommonJob`, `CommonSubscriber`, `CommonDataProcessor` | dispatch/handle separation, discovery-based registration, worker-event lifecycle, column-change detection, `created_by`/`updated_by` stamped from the `Auth` context in `beforeInsert`/`beforeUpdate`, field error collection |
130
- | Constants & maps | `baseEntityConstants`, `baseJobConstants`, `baseEntityMaps`, `baseJobMaps` | centralised dotted identifiers (`as const`) plus identifier → class maps of every concrete entity and job (abstract bases excluded); map keys are computed from the constants, so renames propagate at compile time |
104
+ | Area | Key exports | Notes |
105
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
106
+ | Entities | `RootEntity`, `CommonEntity`, `CommonAdminEntity`, `UserEntity`, `AdminUserEntity`, `SystemRoleEntity`, `SystemAdminRoleEntity`, ... | uuid PK, soft deletes, `created_by`/`updated_by` (+ `creator`/`updater` relations), jsonb `attributes`; entity-level `findOneOrNew`, `hardDelete`, `restore`, `paginate` statics plus a row-wise `softDelete()` |
107
+ | Migrations | `MigrationUtility`, `SeederUtility` (the 29 table migrations ship in `dist/migrations` — glob them, they are not barrel exports) | fluent `this.string()`, `this.numeric()`, `addRecord()` seeding |
108
+ | Queue | `QueueModule`, `QueueModule.withConnection()`, `QueueService`, `CommonJob` | BullMQ `@Processor`/`WorkerHost` consumer; auto-discovered jobs, duplicate prevention, recurring schedulers, completed jobs cleared from Redis on success (1-day TTL on failures), `clearQueue()`, retries, failures persisted to `sys_failed_jobs`; connection from `BULL_REDIS_*` or supplied by the app — never register `BullModule.forRoot()` yourself, two roots race over the worker's connection |
109
+ | Scheduled events | `SystemScheduledTaskEntity`, `ScheduledEventEntity`, `ScheduleKind`, `ScheduleSpec`, `IsCronExpression` | schedule definitions in `sys_scheduled_tasks` → events in `sys_scheduled_events`; interval / cron / once specs with timezones, drift-free slots, horizon materialisation, catch-up + overlap policies, fire-once dispatch with backoff, cadence via `SCHEDULED_EVENT_MIN` |
110
+ | Auth | `AuthModule`, `AuthService.validateRole()`, `RoleTier`, `TenantRole`, `Auth`, `JwtMiddleware` | imperative auth (open by default); `<module>.<tier>` role hierarchy (manager > writer > viewer), with `global.admin` overriding every role check; JwtMiddleware resolves the user into `Auth`'s AsyncLocalStorage context per request; access + rotating refresh tokens; session-based logout invalidation (`sys_auth_sessions`); Google SSO via ID token at `POST /auth/google`; impersonation — a tenantless `has_global_access` user signs into one organization at `POST /auth/impersonation/login` |
111
+ | Admin auth | `AdminAuthService.validateRole()`, `AdminAuth`, `AdminJwtMiddleware` (endpoints at `/admin/auth`) | fully separate stack for back-office admins — own users table (`sys_admin_users`), own roles (`sys_admin_roles`/`sys_auser_roles`), own sessions/refresh tokens (shared tables, `is_admin` flag) |
112
+ | API keys | `ApiKeyService`, `ApiKeyGuard` / `ApiKeyMiddleware`, `@ApiKeyScopes()`, `ApiKeyContext` | hashed keys in `sys_api_keys` (admin-owned), one tenant per key, expiry/revocation, `x-api-key` header; `validateAccess(scope)` returns the tenant the key acts for |
113
+ | Scopes | `ScopeService`, `AuthService.validateAccess()`, `ScopeEntity`, `TenantScopeEntity`, `ApiScopeEntity` | one feature named once in `sys_scopes`, then granted per organization (`sys_tenant_scopes`) or per API key (`sys_api_scopes`) — never an array on the principal |
114
+ | Multi-tenancy | `TenantModule`, `TenantEntity`, `CommonTenantEntity`, `TenantContextService`, `TenantContextMiddleware` | AsyncLocalStorage tenant context; resolve by header, slug, or domain; tenants are onboarded by admins (`TenantEntity.creator` → `AdminUserEntity`) |
115
+ | Admin | `AdminModule` (its controllers, DTOs and processors are internal — import the module and hit the endpoints), `GlobalAdminRoles` | back-office APIs: tenant onboarding, client/admin/system user registration (single + bulk), the `has_global_access` grant that enables impersonation, tenant + scheduled-task listing, the email/PDF template catalogues (read at content-reader, written at content-manager, deleted at global admin); 4-tier `GlobalAdminRoles` gate over `sys_admin_roles`; Swagger at `/docs` (hidden in production) |
116
+ | Notifications | `NotificationModule`, `NotificationService`, `FcmPushNotificationService` | in-app rows + FCM push (optional `firebase-admin`); device token registry |
117
+ | Email (SES) | `EmailNotificationService`, `ProcessEmailNotification`, `EmailTemplateEntity`, `EmailNotificationLogEntity` | templates in `sys_email_templates` (managed from the admin surface) rendered by a remote service, dispatched via SES per recipient (to/cc/bcc); logged to `sys_email_notification_logs` outside production; dispatch skipped when `AWS_SES_FROM_ADDRESS` unset |
118
+ | Audit | `AuditService` | `saveAuditLog(old, new)` with changed-value diffing into `sys_audit_logs` |
119
+ | Cache | `CacheService` | Redis, TTL objects (`{days, hours, min}`), transparent encryption |
120
+ | Encryption | `EncryptionService` | AES-256-GCM, scrypt-derived keys |
121
+ | System properties | `SystemPropertyModule`, `SystemPropertyService` | cached + optionally encrypted key/value settings in `sys_properties` |
122
+ | Documents | `DocumentModule`, `DocumentService`, `S3BucketDocumentService`, `MinioBucketDocumentService` | `POST /api/documents/upload` (multipart) or `uploadDocument(multerFiles) → links[]`; driver chosen via `DOCUMENT_STORAGE_DRIVER`; `sys_document_uploads` is the file, `sys_documents` the link to one of your records (`system_generated` marks the ones the backend produced itself) |
123
+ | PDF documents | `PdfService`, `PdfTemplateService`, `ProcessPdfDocument`, `PdfTemplateEntity`, `PdfRenderOptions` | templates in `sys_pdf_templates` rendered to HTML by a remote service, rasterized by headless Chromium (`renderHtmlToPdf(html) → Buffer`, honours the document's own `@page` rules and paginates to any length), then stored — `ProcessPdfDocument` writes both rows and flags the `sys_documents` one `system_generated`; optional peer dep `puppeteer` |
124
+ | HTTP | `CommonController`, `GlobalExceptionFilter`, common DTOs | health endpoint base controller, uniform error envelope |
125
+ | Raw SQL | `SqlService`, `SqlTarget` | named `:key` placeholders always bound as driver params, `:...key` array expansion; runs on the read replica (`DB_READ_*`) with transparent primary fallback; `fetchOne`/`fetchScalar`/`exists`/`count`/`paginate` helpers |
126
+ | List endpoints | `CommonListProcessor`, `CommonListFilterDto`, `FilterDto`, `CommonListFilterConfig` | allowlisted columns, parameterised operators (`eq`…`isNull`), typed value casting (`str`/`num`/`date`/`bool`/`null`), multi-sort + paginated on the read replica |
127
+ | Outbound HTTP | `RemoteRequestService`, `ApiRequestTypeEnum`, `ApiContentTypeEnum` | injectable `fetch` wrapper (global via `CommonModule`); `getApiResponse<T>()` parses JSON/text and throws `OperationException` on non-2xx, `getApiRawResponse()` returns the raw `Response`; JSON + form-urlencoded body serialisation |
128
+ | Observability | `LoggingInterceptor`, `MonitoringInterceptor`, `MonitoringService` | Sentry auto-registers when `@sentry/node` + `SENTRY_DSN` present; pluggable `MonitoringAdapter` |
129
+ | Jobs/Events | `CommonJob`, `CommonSubscriber`, `CommonDataProcessor` | dispatch/handle separation, discovery-based registration, worker-event lifecycle, column-change detection, `created_by`/`updated_by` stamped from the `Auth` context in `beforeInsert`/`beforeUpdate`, field error collection |
130
+ | Constants & maps | `baseEntityConstants`, `baseJobConstants`, `baseEntityMaps`, `baseJobMaps` | centralised dotted identifiers (`as const`) plus identifier → class maps of every concrete entity and job (abstract bases excluded); map keys are computed from the constants, so renames propagate at compile time |
131
131
 
132
132
  ## Environment Variables
133
133
 
@@ -209,7 +209,10 @@ sold to them?" and "is this person allowed to use it?" in that order.
209
209
 
210
210
  ```ts
211
211
  const { user } = await this.authService.validateAccess('billing.invoices');
212
- const { user } = await this.authService.validateAccess('billing.invoices', 'invoice.writer');
212
+ const { user } = await this.authService.validateAccess(
213
+ 'billing.invoices',
214
+ 'invoice.writer',
215
+ );
213
216
  ```
214
217
 
215
218
  `AuthController` (mounted at `/auth`) exposes the ready-made flows: `POST
@@ -308,8 +311,8 @@ admin with the `admin` role can grant `sys_admin_roles` to other admins via
308
311
 
309
312
  ### Encryption
310
313
 
311
- | Variable | Default | Description |
312
- | ------------------- | ------- | ------------------------------------------------------------------------------------- |
314
+ | Variable | Default | Description |
315
+ | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
313
316
  | `ENCRYPTION_SECRET` | — | **Required** when `EncryptionService` is used — which now includes issuing API keys (`key_encrypted`). AES-256-GCM key material. Min 32 chars |
314
317
 
315
318
  ### Cache (Redis)
@@ -323,21 +326,21 @@ admin with the `admin` role can grant `sys_admin_roles` to other admins via
323
326
 
324
327
  ### Queue (BullMQ / Redis)
325
328
 
326
- | Variable | Default | Description |
327
- | ----------------------------------- | ------------- | --------------------------------------------------------------------------------------------------- |
328
- | `BULL_REDIS_HOST` | `localhost` | Redis host for BullMQ |
329
- | `BULL_REDIS_PORT` | `6379` | Redis port |
330
- | `BULL_REDIS_PASSWORD` | _(empty)_ | Redis password |
331
- | `BULL_REDIS_DB` | `0` | Redis DB index |
332
- | `BULL_QUEUE_NAME` | `myapp-queue` | Name of the BullMQ queue |
333
- | `BULL_WORKERS` | `2` | Concurrent worker threads per process |
334
- | `BULL_QUEUE_WORKER` | `false` | Set `true` on consumer instances only — API servers should leave this `false` |
329
+ | Variable | Default | Description |
330
+ | ----------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
331
+ | `BULL_REDIS_HOST` | `localhost` | Redis host for BullMQ |
332
+ | `BULL_REDIS_PORT` | `6379` | Redis port |
333
+ | `BULL_REDIS_PASSWORD` | _(empty)_ | Redis password |
334
+ | `BULL_REDIS_DB` | `0` | Redis DB index |
335
+ | `BULL_QUEUE_NAME` | `myapp-queue` | Name of the BullMQ queue |
336
+ | `BULL_WORKERS` | `2` | Concurrent worker threads per process |
337
+ | `BULL_QUEUE_WORKER` | `false` | Set `true` on consumer instances only — API servers should leave this `false` |
335
338
  | `BULL_JOB_TTL_SECONDS` | `86400` | Retention (seconds) for **failed** jobs before removal from the queue + Redis (default 1 day); completed jobs are removed immediately |
336
- | `MAX_JOB_RETRIES` | `3` | Max attempts per job (BullMQ `attempts` default) and the scheduled-event retry ceiling |
337
- | `SCHEDULED_EVENT_MIN` | `0` | `SetScheduledEventJob` cadence in minutes (every N minutes); `0`/unset disables self-scheduling |
338
- | `SCHEDULED_EVENT_HORIZON_MIN` | _(derived)_ | How far ahead occurrences are materialised; defaults to 2× the cadence, floored at one minute |
339
- | `SCHEDULED_EVENT_BATCH` | `500` | Occurrences dispatched per pass |
340
- | `ENABLE_SCHEDULED_EVENTS_EXECUTION` | _unset_ | Must be exactly `true` to arm scheduled-event materialisation and dispatch |
339
+ | `MAX_JOB_RETRIES` | `3` | Max attempts per job (BullMQ `attempts` default) and the scheduled-event retry ceiling |
340
+ | `SCHEDULED_EVENT_MIN` | `0` | `SetScheduledEventJob` cadence in minutes (every N minutes); `0`/unset disables self-scheduling |
341
+ | `SCHEDULED_EVENT_HORIZON_MIN` | _(derived)_ | How far ahead occurrences are materialised; defaults to 2× the cadence, floored at one minute |
342
+ | `SCHEDULED_EVENT_BATCH` | `500` | Occurrences dispatched per pass |
343
+ | `ENABLE_SCHEDULED_EVENTS_EXECUTION` | _unset_ | Must be exactly `true` to arm scheduled-event materialisation and dispatch |
341
344
 
342
345
  #### Jobs
343
346
 
@@ -493,7 +496,7 @@ jitter lands on the occurrence, never on the cursor, so it cannot accumulate.
493
496
  ##### Creating and editing a task
494
497
 
495
498
  `POST /api/admin/scheduled-tasks` is an upsert. `AddScheduledTaskDto` extends
496
- `CommonPayloadDto`, so it carries an optional `id`: send one to edit that task, omit
499
+ `CommonDataDto`, so it carries an optional `id`: send one to edit that task, omit
497
500
  it to create. `identifier`, `name` and `schedule` are required by the DTO either way;
498
501
  `identifier` uniqueness is checked in the processor, excluding the row being edited.
499
502
 
@@ -571,12 +574,12 @@ a subscriber only starts firing once something constructs it.
571
574
 
572
575
  ### Document Storage
573
576
 
574
- | Variable | Default | Description |
575
- | --------------------------------------- | ------- | ------------------------------------------ |
576
- | `DOCUMENT_STORAGE_DRIVER` | `minio` | Active driver: `s3` or `minio` |
577
- | `DOCUMENT_PRESIGNED_URL_EXPIRY_SECONDS` | `3600` | Presigned download URL validity in seconds |
577
+ | Variable | Default | Description |
578
+ | --------------------------------------- | ------- | ---------------------------------------------- |
579
+ | `DOCUMENT_STORAGE_DRIVER` | `minio` | Active driver: `s3` or `minio` |
580
+ | `DOCUMENT_PRESIGNED_URL_EXPIRY_SECONDS` | `3600` | Presigned download URL validity in seconds |
578
581
  | `DOCUMENT_MAX_UPLOAD_FILES` | `10` | Files per `POST /api/documents/upload` request |
579
- | `DOCUMENT_MAX_UPLOAD_SIZE_MB` | `25` | Per-file size ceiling on that endpoint |
582
+ | `DOCUMENT_MAX_UPLOAD_SIZE_MB` | `25` | Per-file size ceiling on that endpoint |
580
583
 
581
584
  **S3 driver** (`DOCUMENT_STORAGE_DRIVER=s3`):
582
585
 
@@ -602,12 +605,12 @@ a subscriber only starts firing once something constructs it.
602
605
  | `MINIO_BUCKET` | `documents` | Bucket name (auto-created if missing) |
603
606
  | `MINIO_PUBLIC_URL` | _(empty)_ | CDN base URL when MinIO sits behind a proxy |
604
607
 
605
- ### PDF Generation
608
+ ### PDF Documents
606
609
 
607
610
  Requires the optional peer dependency `puppeteer`.
608
611
 
609
- | Variable | Default | Description |
610
- | ------------------------- | --------- | ---------------------------------------------------------------------------------- |
612
+ | Variable | Default | Description |
613
+ | ------------------------- | --------- | ----------------------------------------------------------------------------------- |
611
614
  | `PDF_CHROMIUM_PATH` | _(empty)_ | Path to an existing Chromium/Chrome binary; falls back to puppeteer's bundled one |
612
615
  | `PDF_CHROMIUM_NO_SANDBOX` | `false` | `true` passes `--no-sandbox`. Needed in most containers; only use with trusted HTML |
613
616
  | `PDF_RENDER_TIMEOUT_MS` | `30000` | Per-render page load timeout in milliseconds |
@@ -634,10 +637,10 @@ Requires the optional peer dependency `puppeteer`.
634
637
 
635
638
  ### API Keys
636
639
 
637
- | Variable | Default | Description |
638
- | --------------------- | ----------- | ---------------------------------------------------------------- |
639
- | `API_KEY_HEADER_NAME` | `x-api-key` | Request header carrying the API key |
640
- | `ENCRYPTION_SECRET` | — | **Required to issue keys** — `key_encrypted` is AES-256-GCM |
640
+ | Variable | Default | Description |
641
+ | --------------------- | ----------- | ----------------------------------------------------------- |
642
+ | `API_KEY_HEADER_NAME` | `x-api-key` | Request header carrying the API key |
643
+ | `ENCRYPTION_SECRET` | — | **Required to issue keys** — `key_encrypted` is AES-256-GCM |
641
644
 
642
645
  A key is stored three ways: its sha256 hash in `key_hash` (what lookups match), a
643
646
  short visible prefix in `key_prefix`, and the key itself encrypted in
@@ -691,17 +694,28 @@ Requires the optional peer dependency `firebase-admin`. Provide **one** of:
691
694
  Storage is split across two tables. `sys_document_uploads` (`DocumentUploadEntity`)
692
695
  is the stored object — name, mime type, size, driver, path, url, `is_public` — and
693
696
  every upload writes one row automatically. `sys_documents` (`DocumentEntity`) is just
694
- the link to one of your records: `upload_id` + `source_type` + `source_id`, no file
695
- metadata. So one file can hang off several records, and a file can exist before
696
- anything owns it.
697
+ the link to one of your records: `upload_id` + `source_type` + `source_id` +
698
+ `system_generated`, no file metadata. So one file can hang off several records, and a
699
+ file can exist before anything owns it.
700
+
701
+ `system_generated` answers a question the two tables otherwise cannot: whether anybody
702
+ uploaded this file. A generated invoice and an uploaded scan of one are the same pair of
703
+ rows, so the flag is what lets a listing tell them apart — and what lets a regeneration
704
+ know which of its records it may replace. It is set through
705
+ `setDocument(dto, { systemGenerated: true })`, which is how
706
+ [`ProcessPdfDocument`](#pdf-documents-pdfservice-pdftemplateservice-processpdfdocument)
707
+ marks what it produces; it is deliberately absent from `SetDocumentDto`, so the HTTP
708
+ endpoint cannot claim it.
697
709
 
698
710
  `DocumentModule` ships two endpoints. `POST /api/documents/upload` is multipart: it
699
711
  takes the files in a `files` field, uploads them through the active driver and returns
700
712
  the saved `sys_document_uploads` row of each. `POST /api/documents/set` then attaches
701
713
  one of those uploads to a record of yours — `{ upload_id, source_type, source_id }` —
702
714
  and returns the `sys_documents` link row, re-sending an existing link rather than
703
- duplicating it. In code that pairing is `documentService.setDocument(uploadId, {
704
- sourceType, sourceId })`.
715
+ duplicating it. `documentService.deleteDocument(id)` soft deletes such a link —
716
+ no endpoint, since detaching belongs to the flow that owns the record — and leaves the
717
+ upload and its object alone. `DocumentSubscriber` records who did it under
718
+ `attributes.deleted_by`, as `sys_documents` has no such column.
705
719
 
706
720
  Callers do not choose the folder. They send a `path_identifier` (`invoice`,
707
721
  `avatar`, ...) which resolves through the `document.path.<path_identifier>` system
@@ -739,40 +753,105 @@ async upload(@UploadedFiles() files: Express.Multer.File[], @CurrentUser() user:
739
753
  Set `DOCUMENT_STORAGE_DRIVER=minio` for a local MinIO bucket (auto-created) or
740
754
  `DOCUMENT_STORAGE_DRIVER=s3` for Amazon S3. Switching drivers requires no code change.
741
755
 
742
- ## PDF Generation (PdfService)
756
+ Because uploading and attaching are separate steps, a file can end up claimed by
757
+ nothing. `CleanOrphanDocumentsJob` (`clean.orphan.documents.job`) sweeps those. Run
758
+ with no payload it collects every upload that has no `sys_documents` row and is more
759
+ than a day old, then re-dispatches itself as `{ id }` per row; each of those deletes
760
+ that object from the bucket and soft deletes its row, so one unreachable file retries
761
+ alone instead of stalling the sweep. Schedule it with a `sys_scheduled_tasks` row under
762
+ that identifier — the module registers the handler but never dispatches it.
763
+
764
+ ## PDF Documents (PdfService, PdfTemplateService, ProcessPdfDocument)
765
+
766
+ The `/pdfs` counterpart of [Templated Email](#templated-email-aws-ses), and it reuses
767
+ that pipeline's shape exactly: a catalogue row names a template, a remote service
768
+ renders it, a processor drives the whole thing. What differs is the last mile — mail is
769
+ handed to SES, a document is rasterized and stored.
770
+
771
+ Three pieces, split along the one line that matters — where the browser binary lives:
743
772
 
744
- `PdfService` (exported by `DocumentModule`) turns an HTML document into PDF
745
- bytes using a headless Chromium.
773
+ - **`PdfTemplateService`** — resolves a `sys_pdf_templates` row by `identifier` and
774
+ POSTs your `data` to the remote template service, returning print-ready HTML. Exactly
775
+ `EmailNotificationService.getEmailBody()`, for documents.
776
+ - **`PdfService`** — turns that HTML into PDF bytes with a headless Chromium. Knows
777
+ nothing about templates or storage.
778
+ - **`ProcessPdfDocument`** — the entry point. Validates, renders, rasterizes, stores,
779
+ and returns the `sys_documents` row with its `upload` relation attached.
746
780
 
747
- It is the backend half of the document rendering service: that service serves
748
- print-ready, A4-locked HTML at `POST /pdfs/<version>/<path>/<template>`, and
749
- this service rasterizes it. The split keeps the template service free of a
750
- browser binary and puts the PDF bytes next to the storage and delivery code
751
- that needs them.
781
+ All three are exported by `DocumentModule`.
782
+
783
+ ### The whole thing in one call
752
784
 
753
785
  ```ts
754
- // 1. Fetch the rendered document from the template service.
755
- const html = await this.remoteRequestService.getApiResponse<string>({
756
- url: `${templateBaseUrl}/pdfs/v1/business/summary-report`,
757
- requestType: ApiRequestTypeEnum.POST,
758
- contentType: ApiContentTypeEnum.APPLICATION_JSON,
759
- headers: { 'x-client-key': clientKey },
760
- body: reportPayload,
786
+ const processor = new ProcessPdfDocument(
787
+ this.pdfTemplateService,
788
+ this.pdfService,
789
+ this.documentService,
790
+ );
791
+
792
+ const document = await processor.process({
793
+ identifier: 'purchase-order', // a sys_pdf_templates row
794
+ metadata: {
795
+ fileName: 'po-10432.pdf',
796
+ mimeType: 'application/pdf', // default
797
+ pathIdentifier: 'purchase.order', // → the document.path.purchase.order property
798
+ isPublic: false, // default
799
+ },
800
+ source: { // becomes the sys_documents row
801
+ source_type: 'purchase.order',
802
+ source_id: purchaseOrder.id,
803
+ },
804
+ data: { order: purchaseOrder, lines }, // payload for the remote renderer
761
805
  });
762
806
 
763
- // 2. Rasterize it.
807
+ const link = await this.documentService.getDocumentLink(document.upload);
808
+ ```
809
+
810
+ Four steps, in order:
811
+
812
+ 1. `PdfTemplateService.getPdfHtml()` — loads the template (`OperationException` if the
813
+ identifier is unknown), builds the URL as the `pdf.template.base.url` system
814
+ property + `template.path` + `identifier`, and POSTs `data` to it. The URL is a
815
+ plain concatenation, so the property and `path` carry their own trailing slashes —
816
+ same contract as `email.template.base.url`.
817
+ 2. `PdfService.renderHtmlToUploadableFile()` — the bytes, named (`.pdf` appended when
818
+ the file name lacks it).
819
+ 3. `DocumentService.uploadSingleDocument()` — the object goes to the active driver and
820
+ one `sys_document_uploads` row records it. `pathIdentifier` is resolved through
821
+ `resolvePathPrefix()` first, so the destination folder is a `document.path.*` system
822
+ property rather than a caller-supplied path.
823
+ 4. `DocumentService.setDocument(…, { systemGenerated: true })` — the `sys_documents`
824
+ row linking that upload to your record.
825
+
826
+ The upload is deliberately made _without_ `sourceType`/`sourceId` and linked in a
827
+ separate call: those options attach the document as an ordinary upload, and
828
+ `system_generated` is only settable on the explicit `setDocument()` — never from
829
+ `SetDocumentDto`, so the HTTP endpoint cannot claim it.
830
+
831
+ **Seed the catalogue.** A `sys_pdf_templates` row per document, plus the
832
+ `pdf.template.base.url` property; without either, `getPdfHtml()` raises. Manage them
833
+ through [the admin surface](#admin-module) — content manager to define or edit, global
834
+ admin to delete. There is no `PdfTemplateIdentifier` enum: the library dispatches no
835
+ documents of its own, so name yours in an enum in your app rather than inline.
836
+
837
+ ### Using PdfService on its own
838
+
839
+ Skip the processor when you already have the HTML, or want the bytes rather than a
840
+ stored document:
841
+
842
+ ```ts
764
843
  const pdf: Buffer = await this.pdfService.renderHtmlToPdf(html);
765
844
 
766
845
  // …or go straight to bucket storage.
767
- const file = await this.pdfService.renderHtmlToUploadableFile(html, 'summary-report.pdf');
846
+ const file = await this.pdfService.renderHtmlToUploadableFile(html, 'report.pdf');
768
847
  const [link] = await this.documentService.uploadDocument([file], {
769
848
  pathPrefix: 'reports',
770
849
  });
771
850
  ```
772
851
 
773
- **Install puppeteer.** It is an *optional* peer dependency — nothing imports it
774
- until a PDF is actually requested, so apps that never generate one skip the
775
- ~170MB Chromium download entirely:
852
+ **Install puppeteer.** It is an _optional_ peer dependency — nothing imports it until a
853
+ PDF is actually requested, so apps that never generate one skip the ~170MB Chromium
854
+ download entirely:
776
855
 
777
856
  ```bash
778
857
  yarn add puppeteer
@@ -784,7 +863,7 @@ yarn add puppeteer
784
863
 
785
864
  - **The template owns the page setup.** `preferCssPageSize` defaults to `true`,
786
865
  so an `@page { size: A4; margin: … }` rule in the HTML wins over the `format`
787
- option. Page *count* is never specified by anyone — Chromium paginates and
866
+ option. Page _count_ is never specified by anyone — Chromium paginates and
788
867
  emits as many pages as the content needs, which is what lets an A4-width
789
868
  template grow to any length.
790
869
  - **Print media is emulated**, so `@media screen` blocks (the sheet shadow a
@@ -799,11 +878,11 @@ yarn add puppeteer
799
878
 
800
879
  ### Configuration
801
880
 
802
- | Variable | Default | Description |
803
- | ------------------------- | --------- | ------------------------------------------------------------------------------------ |
804
- | `PDF_CHROMIUM_PATH` | _(empty)_ | Path to an existing Chromium/Chrome binary; falls back to puppeteer's bundled one |
805
- | `PDF_CHROMIUM_NO_SANDBOX` | `false` | `true` passes `--no-sandbox`. Needed in most containers; only use with trusted HTML |
806
- | `PDF_RENDER_TIMEOUT_MS` | `30000` | Per-render page load timeout |
881
+ | Variable | Default | Description |
882
+ | ------------------------- | --------- | ----------------------------------------------------------------------------------- |
883
+ | `PDF_CHROMIUM_PATH` | _(empty)_ | Path to an existing Chromium/Chrome binary; falls back to puppeteer's bundled one |
884
+ | `PDF_CHROMIUM_NO_SANDBOX` | `false` | `true` passes `--no-sandbox`. Needed in most containers; only use with trusted HTML |
885
+ | `PDF_RENDER_TIMEOUT_MS` | `30000` | Per-render page load timeout |
807
886
 
808
887
  ## Raw SQL (SqlService)
809
888
 
@@ -862,9 +941,15 @@ await new ProcessEmailNotification(emailNotificationService).process({
862
941
  });
863
942
  ```
864
943
 
944
+ The catalogue is managed from [the admin surface](#admin-module) —
945
+ `POST /admin/email-templates` and friends — and `EmailTemplateEntity` extends
946
+ `CommonAdminEntity` for that reason: a row is authored from the back office, never by a
947
+ tenant user.
948
+
865
949
  The templates the library itself dispatches are named by the
866
950
  `EmailTemplateIdentifier` enum — seed a matching `sys_email_templates` row for each
867
- one you want delivered:
951
+ one you want delivered, and note that these two are exactly the rows a delete must not
952
+ reach:
868
953
 
869
954
  | Identifier | Sent by | Recipient | `data` keys |
870
955
  | ----------------------- | ------------------------- | ------------------ | --------------------------------------------------------------------------- |
@@ -937,7 +1022,8 @@ SQL runs. Results come back as `{ records, total, page, limit, total_pages }`.
937
1022
  ## Admin Module
938
1023
 
939
1024
  `AdminModule` is the back-office surface — tenant onboarding, client/admin user
940
- registration, tenant listing and scheduled-task management. It uses the same
1025
+ registration, tenant listing, scheduled-task management and the email/PDF template
1026
+ catalogues. It uses the same
941
1027
  imperative auth model as the rest of the app: controllers are open by default and
942
1028
  gate each action through `AdminAccessService` before handing the work to a
943
1029
  processor.
@@ -963,42 +1049,73 @@ pass the actor to a processor.
963
1049
 
964
1050
  ### Endpoints
965
1051
 
966
- | Route | Min. role | Handler |
967
- | ---------------------------------------- | --------------- | ----------------------------------------------------------- |
968
- | `POST /admin/tenants` | super admin | `TenantDataProcessor` (upsert — send `id` to update) |
969
- | `POST /admin/tenants/list` | content reader | `TenantListProcessor` |
970
- | `GET /admin/tenants/:id` | content reader | the row plus its `scopes` |
971
- | `POST /admin/tenants/:id/delete` | super admin | `TenantDeleteProcessor` |
972
- | `POST /admin/tenant-users` | admin | `ClientUserDataProcessor` |
973
- | `POST /admin/tenant-users/bulk` | admin | `BulkUserDataProcessor` |
974
- | `POST /admin/tenant-users/list` | content reader | `ClientUserListProcessor` |
975
- | `GET /admin/tenant-users/:id` | content reader | the row |
976
- | `POST /admin/tenant-users/:id/delete` | admin | `ClientUserDeleteProcessor` |
977
- | `POST /admin/users` | super admin | `AdminUserDataProcessor` |
978
- | `POST /admin/users/list` | content reader | `AdminUserListProcessor` |
979
- | `GET /admin/users/:id` | content reader | the row |
980
- | `POST /admin/users/:id/delete` | super admin | `AdminUserDeleteProcessor` |
981
- | `POST /admin/roles/list` | admin | `AdminRoleListProcessor` |
982
- | `POST /admin/roles/tenant/list` | admin | `RoleListProcessor` |
983
- | `POST /admin/scheduled-tasks` | content manager | `ScheduledTaskDataProcessor` (upsert — send `id` to update) |
984
- | `POST /admin/scheduled-tasks/list` | content reader | `ScheduledTaskListProcessor` |
985
- | `GET /admin/scheduled-tasks/:id` | content reader | the row |
986
- | `POST /admin/scheduled-tasks/:id/delete` | content manager | `ScheduledTaskDeleteProcessor` |
987
- | `POST /admin/scopes` | content manager | `ScopeDataProcessor` (upsert — send `id` to update) |
988
- | `POST /admin/scopes/list` | content reader | `ScopeListProcessor` |
989
- | `GET /admin/scopes/:id` | content reader | the row |
990
- | `POST /admin/scopes/:id/delete` | content manager | `ScopeService.deleteScope` (cascades to every grant) |
991
- | `POST /admin/api-keys` | admin | `ApiKeyDataProcessor` — returns `{ raw_key, api_key }` |
992
- | `POST /admin/api-keys/list` | content reader | `ApiKeyListProcessor` |
993
- | `GET /admin/api-keys/:id` | content reader | the row, its `scopes`, and `key` from admin up |
994
- | `POST /admin/api-keys/:id/disable` | admin | `ApiKeyService.disableApiKey` (reversible) |
995
- | `POST /admin/api-keys/:id/delete` | admin | `ApiKeyService.deleteApiKey` (soft, and revokes) |
1052
+ | Route | Min. role | Handler |
1053
+ | ------------------------------------ | --------------- | --------------------------------------------------------------- |
1054
+ | `POST /admin/tenants` | super admin | `TenantDataProcessor` (upsert — send `id` to update) |
1055
+ | `POST /admin/tenants/list` | content reader | `TenantListProcessor` |
1056
+ | `GET /admin/tenants/:id` | content reader | the row plus its `scopes` |
1057
+ | `DELETE /admin/tenants/:id` | super admin | `TenantDeleteProcessor` (cascades to users, keys, grants) |
1058
+ | `POST /admin/tenant-users` | admin | `ClientUserDataProcessor` |
1059
+ | `POST /admin/tenant-users/bulk` | admin | `BulkUserDataProcessor` |
1060
+ | `POST /admin/tenant-users/list` | content reader | `ClientUserListProcessor` |
1061
+ | `GET /admin/tenant-users/:id` | content reader | the row |
1062
+ | `DELETE /admin/tenant-users/:id` | admin | `ClientUserDeleteProcessor` |
1063
+ | `POST /admin/system-users` | super admin | `SystemUserDataProcessor` (upsert — send `id` to update) |
1064
+ | `POST /admin/system-users/list` | content reader | `SystemUserListProcessor` (scoped to `user_type = system.user`) |
1065
+ | `GET /admin/system-users/:id` | content reader | the row |
1066
+ | `DELETE /admin/system-users/:id` | super admin | `SystemUserService.delete` (revokes live sessions) |
1067
+ | `POST /admin/users` | super admin | `AdminUserDataProcessor` |
1068
+ | `POST /admin/users/list` | content reader | `AdminUserListProcessor` |
1069
+ | `GET /admin/users/:id` | content reader | the row |
1070
+ | `DELETE /admin/users/:id` | super admin | `AdminUserDeleteProcessor` |
1071
+ | `POST /admin/roles/list` | admin | `AdminRoleListProcessor` — picker feed (`sys_admin_roles`) |
1072
+ | `POST /admin/roles/tenant/list` | admin | `RoleListProcessor` — picker feed (`sys_roles`) |
1073
+ | `POST /admin/system-roles` | content manager | `SystemRoleDataProcessor` (upsert — send `id` to update) |
1074
+ | `POST /admin/system-roles/list` | content reader | `RoleListProcessor` |
1075
+ | `GET /admin/system-roles/:id` | content reader | the row |
1076
+ | `DELETE /admin/system-roles/:id` | content manager | `RoleService.deleteRole` (cascades to every grant) |
1077
+ | `POST /admin/scheduled-tasks` | content manager | `ScheduledTaskDataProcessor` (upsert — send `id` to update) |
1078
+ | `POST /admin/scheduled-tasks/list` | content reader | `ScheduledTaskListProcessor` |
1079
+ | `GET /admin/scheduled-tasks/:id` | content reader | the row |
1080
+ | `DELETE /admin/scheduled-tasks/:id` | content manager | `ScheduledTaskDeleteProcessor` |
1081
+ | `POST /admin/email-templates` | content manager | `EmailTemplateDataProcessor` (upsert — send `id` to update) |
1082
+ | `POST /admin/email-templates/list` | content reader | `EmailTemplateListProcessor` |
1083
+ | `GET /admin/email-templates/:id` | content reader | the row |
1084
+ | `DELETE /admin/email-templates/:id` | **admin** | `TemplateService.deleteEmailTemplate` |
1085
+ | `POST /admin/pdf-templates` | content manager | `PdfTemplateDataProcessor` (upsert — send `id` to update) |
1086
+ | `POST /admin/pdf-templates/list` | content reader | `PdfTemplateListProcessor` |
1087
+ | `GET /admin/pdf-templates/:id` | content reader | the row |
1088
+ | `DELETE /admin/pdf-templates/:id` | **admin** | `TemplateService.deletePdfTemplate` |
1089
+ | `POST /admin/scopes` | content manager | `ScopeDataProcessor` (upsert — send `id` to update) |
1090
+ | `POST /admin/scopes/list` | content reader | `ScopeListProcessor` |
1091
+ | `GET /admin/scopes/:id` | content reader | the row |
1092
+ | `DELETE /admin/scopes/:id` | content manager | `ScopeService.deleteScope` (cascades to every grant) |
1093
+ | `POST /admin/api-keys` | admin | `ApiKeyDataProcessor` — returns `{ raw_key, api_key }` |
1094
+ | `POST /admin/api-keys/list` | content reader | `ApiKeyListProcessor` |
1095
+ | `GET /admin/api-keys/:id` | content reader | the row, its `scopes`, and `key` from admin up |
1096
+ | `POST /admin/api-keys/:id/disable` | admin | `ApiKeyService.disableApiKey` (reversible) |
1097
+ | `DELETE /admin/api-keys/:id` | admin | `ApiKeyService.deleteApiKey` (soft, and revokes) |
996
1098
 
997
1099
  List endpoints are `POST .../list` with the filter payload in the body (a
998
1100
  `CommonListFilterDto`) — see [List Endpoints & Filtering](#list-endpoints--filtering).
999
1101
  Deletes and the API-key disable are single-action, so they sit on the domain service
1000
1102
  rather than in a processor; processors are for the writes that validate a payload.
1001
1103
 
1104
+ **Template catalogues.** `sys_email_templates` and `sys_pdf_templates` are the same
1105
+ surface across two controllers — same routes, same DTO, and a PDF template simply has
1106
+ no `subject` because a document has no envelope to address. Neither row holds markup:
1107
+ `path` + `identifier` are appended to a base-URL system property to reach the remote
1108
+ template service, so the catalogue is a table of pointers.
1109
+
1110
+ Reading is content-reader work and writing content-manager work, the same split scopes
1111
+ get. **Deleting sits one level higher, at global admin** — the one place these differ
1112
+ from every other catalogue. An identifier is named by _code_
1113
+ (`EmailTemplateIdentifier.TENANT_ONBOARDED`, whatever `ProcessPdfDocument` asks for),
1114
+ so deleting a row does not change what a flow says, it stops the flow rendering at all
1115
+ — and only at send/generate time, as an `OperationException`. Nothing cascades: the
1116
+ remote template, every mail already sent and every document already generated are left
1117
+ exactly as they are.
1118
+
1002
1119
  **Scopes and grants.** A scope is defined once in `sys_scopes` (content manager),
1003
1120
  then granted to the principals that hold it. `AddTenantDto.scope_ids` carries the
1004
1121
  tenant's whole scope set — `setScopes()` grants what is new and soft-removes what is
@@ -1035,7 +1152,7 @@ registration deliberately sends nothing.
1035
1152
 
1036
1153
  ##### Create vs. update
1037
1154
 
1038
- Creation DTOs extend `CommonPayloadDto`, which carries an optional UUID `id`. Where a
1155
+ Creation DTOs extend `CommonDataDto`, which carries an optional UUID `id`. Where a
1039
1156
  processor supports it, sending an `id` turns the same `POST` into an update.
1040
1157
 
1041
1158
  The processor loads the row when an `id` is present, falls back to `Entity.create()`
@@ -1 +1 @@
1
- {"version":3,"file":"admin.module.d.ts","sourceRoot":"","sources":["../../src/admin/admin.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,gBAAgB,CAAC;AAIvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAY9D,qBAMa,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAE,kBAAuB,GAAG,aAAa;CAcjE"}
1
+ {"version":3,"file":"admin.module.d.ts","sourceRoot":"","sources":["../../src/admin/admin.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,gBAAgB,CAAC;AAIvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAa9D,qBAMa,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAE,kBAAuB,GAAG,aAAa;CAcjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"admin.module.js","sourceRoot":"","sources":["../../src/admin/admin.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,qDAAiD;AACjD,2DAAuD;AACvD,+CAA2C;AAE3C,6EAAyE;AACzE,6EAAwE;AAmBjE,IAAM,WAAW,mBAAjB,MAAM,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,UAA8B,EAAE;QAC9C,OAAO;YACL,MAAM,EAAE,aAAW;YACnB,OAAO,EAAE;gBACP,wBAAU,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC5B,4BAAY,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC9B,wCAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACpC,6CAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC;aACvC;YACD,WAAW,EAAE,CAAC,GAAG,wBAAU,CAAC,WAAW,CAAC;YACxC,SAAS,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;YACnC,OAAO,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;SAClC,CAAC;IACJ,CAAC;CACF,CAAA;AAfY,kCAAW;sBAAX,WAAW;IANvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,wBAAU,EAAE,4BAAY,EAAE,wCAAkB,EAAE,6CAAoB,CAAC;QAC7E,WAAW,EAAE,CAAC,GAAG,wBAAU,CAAC,WAAW,CAAC;QACxC,SAAS,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;QACnC,OAAO,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;KAClC,CAAC;GACW,WAAW,CAevB"}
1
+ {"version":3,"file":"admin.module.js","sourceRoot":"","sources":["../../src/admin/admin.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,qDAAiD;AACjD,2DAAuD;AACvD,+CAA2C;AAE3C,6EAAyE;AACzE,6EAAwE;AAoBjE,IAAM,WAAW,mBAAjB,MAAM,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,UAA8B,EAAE;QAC9C,OAAO;YACL,MAAM,EAAE,aAAW;YACnB,OAAO,EAAE;gBACP,wBAAU,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC5B,4BAAY,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC9B,wCAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACpC,6CAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC;aACvC;YACD,WAAW,EAAE,CAAC,GAAG,wBAAU,CAAC,WAAW,CAAC;YACxC,SAAS,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;YACnC,OAAO,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;SAClC,CAAC;IACJ,CAAC;CACF,CAAA;AAfY,kCAAW;sBAAX,WAAW;IANvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,wBAAU,EAAE,4BAAY,EAAE,wCAAkB,EAAE,6CAAoB,CAAC;QAC7E,WAAW,EAAE,CAAC,GAAG,wBAAU,CAAC,WAAW,CAAC;QACxC,SAAS,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;QACnC,OAAO,EAAE,CAAC,GAAG,wBAAU,CAAC,QAAQ,CAAC;KAClC,CAAC;GACW,WAAW,CAevB"}
@@ -98,7 +98,7 @@ __decorate([
98
98
  __metadata("design:returntype", Promise)
99
99
  ], AdminApiKeyController.prototype, "disable", null);
100
100
  __decorate([
101
- (0, common_1.Post)(':id/delete'),
101
+ (0, common_1.Delete)(':id'),
102
102
  (0, swagger_1.ApiOperation)({
103
103
  summary: 'Delete an API key.',
104
104
  description: 'Soft delete: the row keeps its data with `deleted_at` set and drops out of every listing. It is revoked in the same transaction, along with its scope grants — key validation matches on `is_active`/`revoked_at`, so a row that only left the listings would keep authenticating requests. Requires at least the global admin role.',
@@ -1 +1 @@
1
- {"version":3,"file":"admin.api.key.controller.js","sourceRoot":"","sources":["../../../src/admin/controllers/admin.api.key.controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admin.api.key.controller.js","sourceRoot":"","sources":["../../../src/admin/controllers/admin.api.key.controller.ts"],"names":[],"mappings":"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"}