@open-mercato/core 0.6.8-develop.6983.1.e4f794099e → 0.6.8-develop.6985.1.fb93574faa

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 (597) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/notification/index.js +6 -0
  3. package/dist/generated/entities/notification/index.js.map +2 -2
  4. package/dist/generated/entities/notification_preference/index.js +19 -0
  5. package/dist/generated/entities/notification_preference/index.js.map +7 -0
  6. package/dist/generated/entities/notification_type/index.js +21 -0
  7. package/dist/generated/entities/notification_type/index.js.map +7 -0
  8. package/dist/generated/entities/notification_type_override/index.js +17 -0
  9. package/dist/generated/entities/notification_type_override/index.js.map +7 -0
  10. package/dist/generated/entities/push_notification_delivery/index.js +41 -0
  11. package/dist/generated/entities/push_notification_delivery/index.js.map +7 -0
  12. package/dist/generated/entities/user_device/index.js +35 -0
  13. package/dist/generated/entities/user_device/index.js.map +7 -0
  14. package/dist/generated/entities.ids.generated.js +12 -1
  15. package/dist/generated/entities.ids.generated.js.map +2 -2
  16. package/dist/generated/entity-fields-registry.js +72 -0
  17. package/dist/generated/entity-fields-registry.js.map +2 -2
  18. package/dist/helpers/integration/api.js +2 -1
  19. package/dist/helpers/integration/api.js.map +2 -2
  20. package/dist/helpers/integration/appRoot.js +9 -0
  21. package/dist/helpers/integration/appRoot.js.map +7 -0
  22. package/dist/helpers/integration/dbFixtures.js +1 -4
  23. package/dist/helpers/integration/dbFixtures.js.map +2 -2
  24. package/dist/helpers/integration/pushFake.js +126 -0
  25. package/dist/helpers/integration/pushFake.js.map +7 -0
  26. package/dist/modules/auth/notifications.js +16 -0
  27. package/dist/modules/auth/notifications.js.map +2 -2
  28. package/dist/modules/business_rules/notifications.js +2 -0
  29. package/dist/modules/business_rules/notifications.js.map +2 -2
  30. package/dist/modules/catalog/notifications.js +2 -0
  31. package/dist/modules/catalog/notifications.js.map +2 -2
  32. package/dist/modules/communication_channels/acl.js +9 -0
  33. package/dist/modules/communication_channels/acl.js.map +2 -2
  34. package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js +133 -0
  35. package/dist/modules/communication_channels/api/delete/admin/channels/[id]/route.js.map +7 -0
  36. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +6 -2
  37. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +2 -2
  38. package/dist/modules/communication_channels/api/get/channels/[id]/route.js +6 -2
  39. package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +2 -2
  40. package/dist/modules/communication_channels/api/get/channels/route.js +2 -1
  41. package/dist/modules/communication_channels/api/get/channels/route.js.map +2 -2
  42. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +10 -5
  43. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
  44. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +29 -1
  45. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +2 -2
  46. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js +157 -0
  47. package/dist/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.js.map +7 -0
  48. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +8 -1
  49. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
  50. package/dist/modules/communication_channels/commands/admin-delete-channel.js +141 -0
  51. package/dist/modules/communication_channels/commands/admin-delete-channel.js.map +7 -0
  52. package/dist/modules/communication_channels/commands/connect-credential-channel.js +25 -8
  53. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
  54. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +2 -2
  55. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
  56. package/dist/modules/communication_channels/commands/push-register.js +1 -1
  57. package/dist/modules/communication_channels/commands/push-register.js.map +2 -2
  58. package/dist/modules/communication_channels/commands/push-unregister.js +4 -3
  59. package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
  60. package/dist/modules/communication_channels/data/entities.js +4 -0
  61. package/dist/modules/communication_channels/data/entities.js.map +2 -2
  62. package/dist/modules/communication_channels/data/validators.js +2 -1
  63. package/dist/modules/communication_channels/data/validators.js.map +2 -2
  64. package/dist/modules/communication_channels/lib/access-control.js +11 -1
  65. package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
  66. package/dist/modules/communication_channels/lib/connect-channel.js +20 -12
  67. package/dist/modules/communication_channels/lib/connect-channel.js.map +2 -2
  68. package/dist/modules/communication_channels/lib/push-adapter.js +68 -0
  69. package/dist/modules/communication_channels/lib/push-adapter.js.map +7 -0
  70. package/dist/modules/communication_channels/lib/push-capabilities.js +31 -0
  71. package/dist/modules/communication_channels/lib/push-capabilities.js.map +7 -0
  72. package/dist/modules/communication_channels/lib/push-connect-error.js +20 -0
  73. package/dist/modules/communication_channels/lib/push-connect-error.js.map +7 -0
  74. package/dist/modules/communication_channels/lib/push-credential-errors.js +41 -0
  75. package/dist/modules/communication_channels/lib/push-credential-errors.js.map +7 -0
  76. package/dist/modules/communication_channels/lib/push-envelope.js +46 -0
  77. package/dist/modules/communication_channels/lib/push-envelope.js.map +7 -0
  78. package/dist/modules/communication_channels/lib/refcounted-client-cache.js +64 -0
  79. package/dist/modules/communication_channels/lib/refcounted-client-cache.js.map +7 -0
  80. package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js +13 -0
  81. package/dist/modules/communication_channels/migrations/Migration20260703123630_communication_channels.js.map +7 -0
  82. package/dist/modules/communication_channels/notifications.js +4 -0
  83. package/dist/modules/communication_channels/notifications.js.map +2 -2
  84. package/dist/modules/communication_channels/setup.js +2 -0
  85. package/dist/modules/communication_channels/setup.js.map +2 -2
  86. package/dist/modules/configs/lib/upgrade-actions.js +21 -0
  87. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  88. package/dist/modules/customer_accounts/notifications.js +12 -0
  89. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  90. package/dist/modules/customers/notifications.js +4 -0
  91. package/dist/modules/customers/notifications.js.map +2 -2
  92. package/dist/modules/devices/acl.js +11 -0
  93. package/dist/modules/devices/acl.js.map +7 -0
  94. package/dist/modules/devices/api/[id]/route.js +167 -0
  95. package/dist/modules/devices/api/[id]/route.js.map +7 -0
  96. package/dist/modules/devices/api/admin/devices/[id]/route.js +219 -0
  97. package/dist/modules/devices/api/admin/devices/[id]/route.js.map +7 -0
  98. package/dist/modules/devices/api/admin/devices/route.js +140 -0
  99. package/dist/modules/devices/api/admin/devices/route.js.map +7 -0
  100. package/dist/modules/devices/api/auth.js +9 -0
  101. package/dist/modules/devices/api/auth.js.map +7 -0
  102. package/dist/modules/devices/api/deviceList.js +53 -0
  103. package/dist/modules/devices/api/deviceList.js.map +7 -0
  104. package/dist/modules/devices/api/deviceOps.js +118 -0
  105. package/dist/modules/devices/api/deviceOps.js.map +7 -0
  106. package/dist/modules/devices/api/openapi.js +19 -0
  107. package/dist/modules/devices/api/openapi.js.map +7 -0
  108. package/dist/modules/devices/api/route.js +143 -0
  109. package/dist/modules/devices/api/route.js.map +7 -0
  110. package/dist/modules/devices/backend/devices/[id]/page.js +135 -0
  111. package/dist/modules/devices/backend/devices/[id]/page.js.map +7 -0
  112. package/dist/modules/devices/backend/devices/[id]/page.meta.js +16 -0
  113. package/dist/modules/devices/backend/devices/[id]/page.meta.js.map +7 -0
  114. package/dist/modules/devices/backend/devices/create/page.js +68 -0
  115. package/dist/modules/devices/backend/devices/create/page.js.map +7 -0
  116. package/dist/modules/devices/backend/devices/create/page.meta.js +16 -0
  117. package/dist/modules/devices/backend/devices/create/page.meta.js.map +7 -0
  118. package/dist/modules/devices/backend/devices/page.js +235 -0
  119. package/dist/modules/devices/backend/devices/page.js.map +7 -0
  120. package/dist/modules/devices/backend/devices/page.meta.js +23 -0
  121. package/dist/modules/devices/backend/devices/page.meta.js.map +7 -0
  122. package/dist/modules/devices/commands/deactivate.js +107 -0
  123. package/dist/modules/devices/commands/deactivate.js.map +7 -0
  124. package/dist/modules/devices/commands/index.js +4 -0
  125. package/dist/modules/devices/commands/index.js.map +7 -0
  126. package/dist/modules/devices/commands/register.js +183 -0
  127. package/dist/modules/devices/commands/register.js.map +7 -0
  128. package/dist/modules/devices/commands/shared.js +74 -0
  129. package/dist/modules/devices/commands/shared.js.map +7 -0
  130. package/dist/modules/devices/commands/update.js +126 -0
  131. package/dist/modules/devices/commands/update.js.map +7 -0
  132. package/dist/modules/devices/data/entities.js +83 -0
  133. package/dist/modules/devices/data/entities.js.map +7 -0
  134. package/dist/modules/devices/data/validators.js +49 -0
  135. package/dist/modules/devices/data/validators.js.map +7 -0
  136. package/dist/modules/devices/di.js +11 -0
  137. package/dist/modules/devices/di.js.map +7 -0
  138. package/dist/modules/devices/encryption.js +12 -0
  139. package/dist/modules/devices/encryption.js.map +7 -0
  140. package/dist/modules/devices/events.js +19 -0
  141. package/dist/modules/devices/events.js.map +7 -0
  142. package/dist/modules/devices/index.js +17 -0
  143. package/dist/modules/devices/index.js.map +7 -0
  144. package/dist/modules/devices/lib/operationMetadata.js +20 -0
  145. package/dist/modules/devices/lib/operationMetadata.js.map +7 -0
  146. package/dist/modules/devices/migrations/Migration20260602120000.js +15 -0
  147. package/dist/modules/devices/migrations/Migration20260602120000.js.map +7 -0
  148. package/dist/modules/devices/migrations/Migration20260630120000.js +15 -0
  149. package/dist/modules/devices/migrations/Migration20260630120000.js.map +7 -0
  150. package/dist/modules/devices/migrations/Migration20260701120000.js +13 -0
  151. package/dist/modules/devices/migrations/Migration20260701120000.js.map +7 -0
  152. package/dist/modules/devices/migrations/Migration20260721120000.js +15 -0
  153. package/dist/modules/devices/migrations/Migration20260721120000.js.map +7 -0
  154. package/dist/modules/devices/setup.js +13 -0
  155. package/dist/modules/devices/setup.js.map +7 -0
  156. package/dist/modules/entities/cli.js +8 -3
  157. package/dist/modules/entities/cli.js.map +2 -2
  158. package/dist/modules/entities/migrations/Migration20260722120000.js +28 -0
  159. package/dist/modules/entities/migrations/Migration20260722120000.js.map +7 -0
  160. package/dist/modules/inbox_ops/notifications.js +2 -0
  161. package/dist/modules/inbox_ops/notifications.js.map +2 -2
  162. package/dist/modules/messages/notifications.js +2 -0
  163. package/dist/modules/messages/notifications.js.map +2 -2
  164. package/dist/modules/notifications/acl.js +3 -1
  165. package/dist/modules/notifications/acl.js.map +2 -2
  166. package/dist/modules/notifications/api/admin/preferences/route.js +167 -0
  167. package/dist/modules/notifications/api/admin/preferences/route.js.map +7 -0
  168. package/dist/modules/notifications/api/channels/route.js +54 -0
  169. package/dist/modules/notifications/api/channels/route.js.map +7 -0
  170. package/dist/modules/notifications/api/preferences/route.js +126 -0
  171. package/dist/modules/notifications/api/preferences/route.js.map +7 -0
  172. package/dist/modules/notifications/api/route.js +8 -1
  173. package/dist/modules/notifications/api/route.js.map +2 -2
  174. package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js +187 -0
  175. package/dist/modules/notifications/api/types/[id]/channels/[channel]/route.js.map +7 -0
  176. package/dist/modules/notifications/api/types/route.js +225 -0
  177. package/dist/modules/notifications/api/types/route.js.map +7 -0
  178. package/dist/modules/notifications/api/unread-count/route.js +5 -1
  179. package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
  180. package/dist/modules/notifications/backend/notifications/user-preferences/page.js +10 -0
  181. package/dist/modules/notifications/backend/notifications/user-preferences/page.js.map +7 -0
  182. package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js +28 -0
  183. package/dist/modules/notifications/backend/notifications/user-preferences/page.meta.js.map +7 -0
  184. package/dist/modules/notifications/backend/profile/notification-preferences/page.js +10 -0
  185. package/dist/modules/notifications/backend/profile/notification-preferences/page.js.map +7 -0
  186. package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js +33 -0
  187. package/dist/modules/notifications/backend/profile/notification-preferences/page.meta.js.map +7 -0
  188. package/dist/modules/notifications/data/entities.js +128 -1
  189. package/dist/modules/notifications/data/entities.js.map +2 -2
  190. package/dist/modules/notifications/data/validators.js +95 -2
  191. package/dist/modules/notifications/data/validators.js.map +2 -2
  192. package/dist/modules/notifications/di.js +7 -0
  193. package/dist/modules/notifications/di.js.map +2 -2
  194. package/dist/modules/notifications/events.js +17 -0
  195. package/dist/modules/notifications/events.js.map +2 -2
  196. package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js +190 -0
  197. package/dist/modules/notifications/frontend/NotificationPreferenceMatrix.js.map +7 -0
  198. package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js +124 -0
  199. package/dist/modules/notifications/frontend/NotificationPreferencesPageClient.js.map +7 -0
  200. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +166 -1
  201. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
  202. package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js +172 -0
  203. package/dist/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.js.map +7 -0
  204. package/dist/modules/notifications/generators.js +63 -0
  205. package/dist/modules/notifications/generators.js.map +7 -0
  206. package/dist/modules/notifications/lib/delivery-strategies-registry.js +19 -0
  207. package/dist/modules/notifications/lib/delivery-strategies-registry.js.map +7 -0
  208. package/dist/modules/notifications/lib/deliveryStrategies.js.map +2 -2
  209. package/dist/modules/notifications/lib/derive-category.js +8 -0
  210. package/dist/modules/notifications/lib/derive-category.js.map +7 -0
  211. package/dist/modules/notifications/lib/notification-channel-registry.js +35 -0
  212. package/dist/modules/notifications/lib/notification-channel-registry.js.map +7 -0
  213. package/dist/modules/notifications/lib/notification-type-registry.js +87 -0
  214. package/dist/modules/notifications/lib/notification-type-registry.js.map +7 -0
  215. package/dist/modules/notifications/lib/notificationCopy.js +14 -0
  216. package/dist/modules/notifications/lib/notificationCopy.js.map +7 -0
  217. package/dist/modules/notifications/lib/notificationFactory.js +4 -1
  218. package/dist/modules/notifications/lib/notificationFactory.js.map +2 -2
  219. package/dist/modules/notifications/lib/notificationMapper.js +2 -0
  220. package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
  221. package/dist/modules/notifications/lib/notificationPreferenceService.js +83 -0
  222. package/dist/modules/notifications/lib/notificationPreferenceService.js.map +7 -0
  223. package/dist/modules/notifications/lib/notificationService.js +83 -28
  224. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  225. package/dist/modules/notifications/lib/notificationVisibility.js +24 -0
  226. package/dist/modules/notifications/lib/notificationVisibility.js.map +7 -0
  227. package/dist/modules/notifications/lib/routeHelpers.js +11 -1
  228. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  229. package/dist/modules/notifications/lib/shouldDeliver.js +46 -0
  230. package/dist/modules/notifications/lib/shouldDeliver.js.map +7 -0
  231. package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js +54 -0
  232. package/dist/modules/notifications/lib/strategies/email-delivery-strategy.js.map +7 -0
  233. package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js +12 -0
  234. package/dist/modules/notifications/lib/strategies/in-app-delivery-strategy.js.map +7 -0
  235. package/dist/modules/notifications/lib/typeCatalogue.js +71 -0
  236. package/dist/modules/notifications/lib/typeCatalogue.js.map +7 -0
  237. package/dist/modules/notifications/lib/typeChannelSettings.js +12 -0
  238. package/dist/modules/notifications/lib/typeChannelSettings.js.map +7 -0
  239. package/dist/modules/notifications/lib/typeOverrides.js +19 -0
  240. package/dist/modules/notifications/lib/typeOverrides.js.map +7 -0
  241. package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js +14 -0
  242. package/dist/modules/notifications/migrations/Migration20260625122947_notifications.js.map +7 -0
  243. package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js +15 -0
  244. package/dist/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.js.map +7 -0
  245. package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js +13 -0
  246. package/dist/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.js.map +7 -0
  247. package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js +15 -0
  248. package/dist/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.js.map +7 -0
  249. package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js +13 -0
  250. package/dist/modules/notifications/migrations/Migration20260706120000_notifications_channels.js.map +7 -0
  251. package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js +14 -0
  252. package/dist/modules/notifications/migrations/Migration20260716123121_notifications.js.map +7 -0
  253. package/dist/modules/notifications/notification-channels.js +26 -0
  254. package/dist/modules/notifications/notification-channels.js.map +7 -0
  255. package/dist/modules/notifications/notifications.delivery-strategies.js +12 -0
  256. package/dist/modules/notifications/notifications.delivery-strategies.js.map +7 -0
  257. package/dist/modules/notifications/setup.js +16 -1
  258. package/dist/modules/notifications/setup.js.map +2 -2
  259. package/dist/modules/notifications/subscribers/deliver-notification.js +66 -57
  260. package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
  261. package/dist/modules/notifications/subscribers/sync-notification-types.js +16 -0
  262. package/dist/modules/notifications/subscribers/sync-notification-types.js.map +7 -0
  263. package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js +23 -0
  264. package/dist/modules/notifications/widgets/injection/profile-preferences-menu/widget.js.map +7 -0
  265. package/dist/modules/notifications/widgets/injection-table.js +14 -0
  266. package/dist/modules/notifications/widgets/injection-table.js.map +7 -0
  267. package/dist/modules/push_notifications/acl.js +13 -0
  268. package/dist/modules/push_notifications/acl.js.map +7 -0
  269. package/dist/modules/push_notifications/api/custom-send/route.js +141 -0
  270. package/dist/modules/push_notifications/api/custom-send/route.js.map +7 -0
  271. package/dist/modules/push_notifications/api/deliveries/[id]/route.js +110 -0
  272. package/dist/modules/push_notifications/api/deliveries/[id]/route.js.map +7 -0
  273. package/dist/modules/push_notifications/api/deliveries/route.js +56 -0
  274. package/dist/modules/push_notifications/api/deliveries/route.js.map +7 -0
  275. package/dist/modules/push_notifications/api/openapi.js +19 -0
  276. package/dist/modules/push_notifications/api/openapi.js.map +7 -0
  277. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js +103 -0
  278. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.js.map +7 -0
  279. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js +13 -0
  280. package/dist/modules/push_notifications/backend/push_notifications/[id]/page.meta.js.map +7 -0
  281. package/dist/modules/push_notifications/backend/push_notifications/page.js +213 -0
  282. package/dist/modules/push_notifications/backend/push_notifications/page.js.map +7 -0
  283. package/dist/modules/push_notifications/backend/push_notifications/page.meta.js +23 -0
  284. package/dist/modules/push_notifications/backend/push_notifications/page.meta.js.map +7 -0
  285. package/dist/modules/push_notifications/backend/push_notifications/send/page.js +245 -0
  286. package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +7 -0
  287. package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js +26 -0
  288. package/dist/modules/push_notifications/backend/push_notifications/send/page.meta.js.map +7 -0
  289. package/dist/modules/push_notifications/data/entities.js +92 -0
  290. package/dist/modules/push_notifications/data/entities.js.map +7 -0
  291. package/dist/modules/push_notifications/data/extensions.js +23 -0
  292. package/dist/modules/push_notifications/data/extensions.js.map +7 -0
  293. package/dist/modules/push_notifications/data/validators.js +89 -0
  294. package/dist/modules/push_notifications/data/validators.js.map +7 -0
  295. package/dist/modules/push_notifications/di.js +17 -0
  296. package/dist/modules/push_notifications/di.js.map +7 -0
  297. package/dist/modules/push_notifications/events.js +24 -0
  298. package/dist/modules/push_notifications/events.js.map +7 -0
  299. package/dist/modules/push_notifications/index.js +16 -0
  300. package/dist/modules/push_notifications/index.js.map +7 -0
  301. package/dist/modules/push_notifications/lib/fake-provider-recorder.js +80 -0
  302. package/dist/modules/push_notifications/lib/fake-provider-recorder.js.map +7 -0
  303. package/dist/modules/push_notifications/lib/push-delivery-strategy.js +54 -0
  304. package/dist/modules/push_notifications/lib/push-delivery-strategy.js.map +7 -0
  305. package/dist/modules/push_notifications/lib/push-delivery.js +297 -0
  306. package/dist/modules/push_notifications/lib/push-delivery.js.map +7 -0
  307. package/dist/modules/push_notifications/lib/push-fanout.js +138 -0
  308. package/dist/modules/push_notifications/lib/push-fanout.js.map +7 -0
  309. package/dist/modules/push_notifications/lib/push-health.js +32 -0
  310. package/dist/modules/push_notifications/lib/push-health.js.map +7 -0
  311. package/dist/modules/push_notifications/lib/push-reaper.js +87 -0
  312. package/dist/modules/push_notifications/lib/push-reaper.js.map +7 -0
  313. package/dist/modules/push_notifications/lib/push-receipt-reaper.js +136 -0
  314. package/dist/modules/push_notifications/lib/push-receipt-reaper.js.map +7 -0
  315. package/dist/modules/push_notifications/lib/push-stub-adapter.js +63 -0
  316. package/dist/modules/push_notifications/lib/push-stub-adapter.js.map +7 -0
  317. package/dist/modules/push_notifications/lib/queue.js +53 -0
  318. package/dist/modules/push_notifications/lib/queue.js.map +7 -0
  319. package/dist/modules/push_notifications/lib/reclaim-window.js +13 -0
  320. package/dist/modules/push_notifications/lib/reclaim-window.js.map +7 -0
  321. package/dist/modules/push_notifications/lib/send-custom-push.js +48 -0
  322. package/dist/modules/push_notifications/lib/send-custom-push.js.map +7 -0
  323. package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js +16 -0
  324. package/dist/modules/push_notifications/migrations/Migration20260625150049_push_notifications.js.map +7 -0
  325. package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js +13 -0
  326. package/dist/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.js.map +7 -0
  327. package/dist/modules/push_notifications/notifications.delivery-strategies.js +8 -0
  328. package/dist/modules/push_notifications/notifications.delivery-strategies.js.map +7 -0
  329. package/dist/modules/push_notifications/notifications.js +40 -0
  330. package/dist/modules/push_notifications/notifications.js.map +7 -0
  331. package/dist/modules/push_notifications/setup.js +52 -0
  332. package/dist/modules/push_notifications/setup.js.map +7 -0
  333. package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js +57 -0
  334. package/dist/modules/push_notifications/workers/reclaim-stuck.worker.js.map +7 -0
  335. package/dist/modules/push_notifications/workers/send-push.worker.js +27 -0
  336. package/dist/modules/push_notifications/workers/send-push.worker.js.map +7 -0
  337. package/dist/modules/sales/notifications.js +8 -0
  338. package/dist/modules/sales/notifications.js.map +2 -2
  339. package/dist/modules/staff/notifications.js +6 -0
  340. package/dist/modules/staff/notifications.js.map +2 -2
  341. package/dist/modules/workflows/notifications.js +2 -0
  342. package/dist/modules/workflows/notifications.js.map +2 -2
  343. package/generated/entities/notification/index.ts +3 -0
  344. package/generated/entities/notification_preference/index.ts +8 -0
  345. package/generated/entities/notification_type/index.ts +9 -0
  346. package/generated/entities/notification_type_override/index.ts +7 -0
  347. package/generated/entities/push_notification_delivery/index.ts +19 -0
  348. package/generated/entities/user_device/index.ts +16 -0
  349. package/generated/entities.ids.generated.ts +12 -1
  350. package/generated/entity-fields-registry.ts +72 -0
  351. package/package.json +7 -7
  352. package/src/helpers/integration/api.ts +2 -1
  353. package/src/helpers/integration/appRoot.ts +13 -0
  354. package/src/helpers/integration/dbFixtures.ts +1 -5
  355. package/src/helpers/integration/pushFake.ts +219 -0
  356. package/src/modules/auth/i18n/de.json +1 -0
  357. package/src/modules/auth/i18n/en.json +1 -0
  358. package/src/modules/auth/i18n/es.json +1 -0
  359. package/src/modules/auth/i18n/ko.json +1 -0
  360. package/src/modules/auth/i18n/pl.json +1 -0
  361. package/src/modules/auth/notifications.ts +16 -0
  362. package/src/modules/business_rules/i18n/de.json +2 -1
  363. package/src/modules/business_rules/i18n/en.json +2 -1
  364. package/src/modules/business_rules/i18n/es.json +2 -1
  365. package/src/modules/business_rules/i18n/ko.json +2 -1
  366. package/src/modules/business_rules/i18n/pl.json +2 -1
  367. package/src/modules/business_rules/notifications.ts +2 -0
  368. package/src/modules/catalog/i18n/de.json +2 -1
  369. package/src/modules/catalog/i18n/en.json +2 -1
  370. package/src/modules/catalog/i18n/es.json +2 -1
  371. package/src/modules/catalog/i18n/ko.json +2 -1
  372. package/src/modules/catalog/i18n/pl.json +2 -1
  373. package/src/modules/catalog/notifications.ts +2 -0
  374. package/src/modules/communication_channels/acl.ts +9 -0
  375. package/src/modules/communication_channels/api/delete/admin/channels/[id]/route.ts +168 -0
  376. package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +6 -2
  377. package/src/modules/communication_channels/api/get/channels/[id]/route.ts +6 -2
  378. package/src/modules/communication_channels/api/get/channels/route.ts +2 -1
  379. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +16 -5
  380. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +37 -1
  381. package/src/modules/communication_channels/api/post/channels/connect/tenant-credentials/route.ts +175 -0
  382. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +11 -1
  383. package/src/modules/communication_channels/commands/admin-delete-channel.ts +203 -0
  384. package/src/modules/communication_channels/commands/connect-credential-channel.ts +63 -15
  385. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +10 -2
  386. package/src/modules/communication_channels/commands/push-register.ts +7 -1
  387. package/src/modules/communication_channels/commands/push-unregister.ts +14 -3
  388. package/src/modules/communication_channels/data/entities.ts +10 -0
  389. package/src/modules/communication_channels/data/validators.ts +1 -0
  390. package/src/modules/communication_channels/i18n/de.json +35 -1
  391. package/src/modules/communication_channels/i18n/en.json +35 -1
  392. package/src/modules/communication_channels/i18n/es.json +35 -1
  393. package/src/modules/communication_channels/i18n/ko.json +35 -1
  394. package/src/modules/communication_channels/i18n/pl.json +35 -1
  395. package/src/modules/communication_channels/lib/access-control.ts +42 -0
  396. package/src/modules/communication_channels/lib/adapter.ts +25 -0
  397. package/src/modules/communication_channels/lib/connect-channel.ts +61 -26
  398. package/src/modules/communication_channels/lib/push-adapter.ts +139 -0
  399. package/src/modules/communication_channels/lib/push-capabilities.ts +42 -0
  400. package/src/modules/communication_channels/lib/push-connect-error.ts +52 -0
  401. package/src/modules/communication_channels/lib/push-credential-errors.ts +58 -0
  402. package/src/modules/communication_channels/lib/push-envelope.ts +104 -0
  403. package/src/modules/communication_channels/lib/refcounted-client-cache.ts +123 -0
  404. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +9 -0
  405. package/src/modules/communication_channels/migrations/Migration20260703123630_communication_channels.ts +20 -0
  406. package/src/modules/communication_channels/notifications.ts +4 -0
  407. package/src/modules/communication_channels/setup.ts +2 -0
  408. package/src/modules/configs/i18n/de.json +5 -1
  409. package/src/modules/configs/i18n/en.json +5 -1
  410. package/src/modules/configs/i18n/es.json +5 -1
  411. package/src/modules/configs/i18n/ko.json +5 -1
  412. package/src/modules/configs/i18n/pl.json +5 -1
  413. package/src/modules/configs/lib/upgrade-actions.ts +21 -0
  414. package/src/modules/customer_accounts/i18n/de.json +2 -1
  415. package/src/modules/customer_accounts/i18n/en.json +2 -1
  416. package/src/modules/customer_accounts/i18n/es.json +2 -1
  417. package/src/modules/customer_accounts/i18n/ko.json +2 -1
  418. package/src/modules/customer_accounts/i18n/pl.json +2 -1
  419. package/src/modules/customer_accounts/notifications.ts +12 -0
  420. package/src/modules/customers/i18n/de.json +2 -1
  421. package/src/modules/customers/i18n/en.json +2 -1
  422. package/src/modules/customers/i18n/es.json +2 -1
  423. package/src/modules/customers/i18n/ko.json +2 -1
  424. package/src/modules/customers/i18n/pl.json +2 -1
  425. package/src/modules/customers/notifications.ts +4 -0
  426. package/src/modules/devices/AGENTS.md +101 -0
  427. package/src/modules/devices/acl.ts +7 -0
  428. package/src/modules/devices/api/[id]/route.ts +180 -0
  429. package/src/modules/devices/api/admin/devices/[id]/route.ts +235 -0
  430. package/src/modules/devices/api/admin/devices/route.ts +160 -0
  431. package/src/modules/devices/api/auth.ts +13 -0
  432. package/src/modules/devices/api/deviceList.ts +54 -0
  433. package/src/modules/devices/api/deviceOps.ts +186 -0
  434. package/src/modules/devices/api/openapi.ts +23 -0
  435. package/src/modules/devices/api/route.ts +165 -0
  436. package/src/modules/devices/backend/devices/[id]/page.meta.ts +12 -0
  437. package/src/modules/devices/backend/devices/[id]/page.tsx +164 -0
  438. package/src/modules/devices/backend/devices/create/page.meta.ts +12 -0
  439. package/src/modules/devices/backend/devices/create/page.tsx +81 -0
  440. package/src/modules/devices/backend/devices/page.meta.ts +21 -0
  441. package/src/modules/devices/backend/devices/page.tsx +259 -0
  442. package/src/modules/devices/commands/deactivate.ts +126 -0
  443. package/src/modules/devices/commands/index.ts +3 -0
  444. package/src/modules/devices/commands/register.ts +199 -0
  445. package/src/modules/devices/commands/shared.ts +119 -0
  446. package/src/modules/devices/commands/update.ts +142 -0
  447. package/src/modules/devices/data/entities.ts +79 -0
  448. package/src/modules/devices/data/validators.ts +65 -0
  449. package/src/modules/devices/di.ts +9 -0
  450. package/src/modules/devices/encryption.ts +15 -0
  451. package/src/modules/devices/events.ts +16 -0
  452. package/src/modules/devices/i18n/de.json +45 -0
  453. package/src/modules/devices/i18n/en.json +45 -0
  454. package/src/modules/devices/i18n/es.json +45 -0
  455. package/src/modules/devices/i18n/ko.json +45 -0
  456. package/src/modules/devices/i18n/pl.json +45 -0
  457. package/src/modules/devices/index.ts +15 -0
  458. package/src/modules/devices/lib/operationMetadata.ts +35 -0
  459. package/src/modules/devices/migrations/.snapshot-open-mercato.json +200 -0
  460. package/src/modules/devices/migrations/Migration20260602120000.ts +15 -0
  461. package/src/modules/devices/migrations/Migration20260630120000.ts +20 -0
  462. package/src/modules/devices/migrations/Migration20260701120000.ts +13 -0
  463. package/src/modules/devices/migrations/Migration20260721120000.ts +18 -0
  464. package/src/modules/devices/setup.ts +11 -0
  465. package/src/modules/entities/cli.ts +16 -2
  466. package/src/modules/entities/migrations/Migration20260722120000.ts +38 -0
  467. package/src/modules/inbox_ops/i18n/de.json +2 -1
  468. package/src/modules/inbox_ops/i18n/en.json +2 -1
  469. package/src/modules/inbox_ops/i18n/es.json +2 -1
  470. package/src/modules/inbox_ops/i18n/ko.json +2 -1
  471. package/src/modules/inbox_ops/i18n/pl.json +2 -1
  472. package/src/modules/inbox_ops/notifications.ts +2 -0
  473. package/src/modules/messages/i18n/de.json +2 -1
  474. package/src/modules/messages/i18n/en.json +2 -1
  475. package/src/modules/messages/i18n/es.json +2 -1
  476. package/src/modules/messages/i18n/ko.json +2 -1
  477. package/src/modules/messages/i18n/pl.json +2 -1
  478. package/src/modules/messages/notifications.ts +2 -0
  479. package/src/modules/notifications/AGENTS.md +54 -0
  480. package/src/modules/notifications/acl.ts +2 -0
  481. package/src/modules/notifications/api/admin/preferences/route.ts +193 -0
  482. package/src/modules/notifications/api/channels/route.ts +54 -0
  483. package/src/modules/notifications/api/preferences/route.ts +131 -0
  484. package/src/modules/notifications/api/route.ts +8 -1
  485. package/src/modules/notifications/api/types/[id]/channels/[channel]/route.ts +226 -0
  486. package/src/modules/notifications/api/types/route.ts +239 -0
  487. package/src/modules/notifications/api/unread-count/route.ts +5 -1
  488. package/src/modules/notifications/backend/notifications/user-preferences/page.meta.ts +26 -0
  489. package/src/modules/notifications/backend/notifications/user-preferences/page.tsx +12 -0
  490. package/src/modules/notifications/backend/profile/notification-preferences/page.meta.ts +31 -0
  491. package/src/modules/notifications/backend/profile/notification-preferences/page.tsx +12 -0
  492. package/src/modules/notifications/data/entities.ts +144 -0
  493. package/src/modules/notifications/data/validators.ts +116 -0
  494. package/src/modules/notifications/di.ts +7 -0
  495. package/src/modules/notifications/events.ts +23 -0
  496. package/src/modules/notifications/frontend/NotificationPreferenceMatrix.tsx +287 -0
  497. package/src/modules/notifications/frontend/NotificationPreferencesPageClient.tsx +155 -0
  498. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +273 -2
  499. package/src/modules/notifications/frontend/NotificationUserPreferencesAdminPageClient.tsx +214 -0
  500. package/src/modules/notifications/generators.ts +82 -0
  501. package/src/modules/notifications/i18n/de.json +43 -0
  502. package/src/modules/notifications/i18n/en.json +43 -0
  503. package/src/modules/notifications/i18n/es.json +43 -0
  504. package/src/modules/notifications/i18n/ko.json +43 -0
  505. package/src/modules/notifications/i18n/pl.json +44 -1
  506. package/src/modules/notifications/lib/delivery-strategies-registry.ts +30 -0
  507. package/src/modules/notifications/lib/deliveryStrategies.ts +40 -4
  508. package/src/modules/notifications/lib/derive-category.ts +15 -0
  509. package/src/modules/notifications/lib/notification-channel-registry.ts +59 -0
  510. package/src/modules/notifications/lib/notification-type-registry.ts +182 -0
  511. package/src/modules/notifications/lib/notificationCopy.ts +34 -0
  512. package/src/modules/notifications/lib/notificationFactory.ts +5 -1
  513. package/src/modules/notifications/lib/notificationMapper.ts +2 -0
  514. package/src/modules/notifications/lib/notificationPreferenceService.ts +138 -0
  515. package/src/modules/notifications/lib/notificationService.ts +129 -25
  516. package/src/modules/notifications/lib/notificationVisibility.ts +48 -0
  517. package/src/modules/notifications/lib/routeHelpers.ts +14 -1
  518. package/src/modules/notifications/lib/shouldDeliver.ts +112 -0
  519. package/src/modules/notifications/lib/strategies/email-delivery-strategy.ts +63 -0
  520. package/src/modules/notifications/lib/strategies/in-app-delivery-strategy.ts +21 -0
  521. package/src/modules/notifications/lib/typeCatalogue.ts +141 -0
  522. package/src/modules/notifications/lib/typeChannelSettings.ts +29 -0
  523. package/src/modules/notifications/lib/typeOverrides.ts +46 -0
  524. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +874 -100
  525. package/src/modules/notifications/migrations/Migration20260625122947_notifications.ts +14 -0
  526. package/src/modules/notifications/migrations/Migration20260626120500_notifications_push_payload.ts +15 -0
  527. package/src/modules/notifications/migrations/Migration20260626130000_notifications_non_opt_out.ts +13 -0
  528. package/src/modules/notifications/migrations/Migration20260626140000_notifications_type_category_silent.ts +15 -0
  529. package/src/modules/notifications/migrations/Migration20260706120000_notifications_channels.ts +13 -0
  530. package/src/modules/notifications/migrations/Migration20260716123121_notifications.ts +14 -0
  531. package/src/modules/notifications/notification-channels.ts +30 -0
  532. package/src/modules/notifications/notifications.delivery-strategies.ts +15 -0
  533. package/src/modules/notifications/setup.ts +23 -1
  534. package/src/modules/notifications/subscribers/deliver-notification.ts +105 -71
  535. package/src/modules/notifications/subscribers/sync-notification-types.ts +18 -0
  536. package/src/modules/notifications/widgets/injection/profile-preferences-menu/widget.ts +35 -0
  537. package/src/modules/notifications/widgets/injection-table.ts +18 -0
  538. package/src/modules/push_notifications/AGENTS.md +92 -0
  539. package/src/modules/push_notifications/acl.ts +9 -0
  540. package/src/modules/push_notifications/api/custom-send/route.ts +175 -0
  541. package/src/modules/push_notifications/api/deliveries/[id]/route.ts +122 -0
  542. package/src/modules/push_notifications/api/deliveries/route.ts +58 -0
  543. package/src/modules/push_notifications/api/openapi.ts +22 -0
  544. package/src/modules/push_notifications/backend/push_notifications/[id]/page.meta.ts +9 -0
  545. package/src/modules/push_notifications/backend/push_notifications/[id]/page.tsx +149 -0
  546. package/src/modules/push_notifications/backend/push_notifications/page.meta.ts +21 -0
  547. package/src/modules/push_notifications/backend/push_notifications/page.tsx +239 -0
  548. package/src/modules/push_notifications/backend/push_notifications/send/page.meta.ts +24 -0
  549. package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +327 -0
  550. package/src/modules/push_notifications/data/entities.ts +111 -0
  551. package/src/modules/push_notifications/data/extensions.ts +29 -0
  552. package/src/modules/push_notifications/data/validators.ts +102 -0
  553. package/src/modules/push_notifications/di.ts +20 -0
  554. package/src/modules/push_notifications/events.ts +21 -0
  555. package/src/modules/push_notifications/i18n/de.json +79 -0
  556. package/src/modules/push_notifications/i18n/en.json +79 -0
  557. package/src/modules/push_notifications/i18n/es.json +79 -0
  558. package/src/modules/push_notifications/i18n/ko.json +79 -0
  559. package/src/modules/push_notifications/i18n/pl.json +79 -0
  560. package/src/modules/push_notifications/index.ts +15 -0
  561. package/src/modules/push_notifications/lib/fake-provider-recorder.ts +154 -0
  562. package/src/modules/push_notifications/lib/push-delivery-strategy.ts +90 -0
  563. package/src/modules/push_notifications/lib/push-delivery.ts +455 -0
  564. package/src/modules/push_notifications/lib/push-fanout.ts +237 -0
  565. package/src/modules/push_notifications/lib/push-health.ts +47 -0
  566. package/src/modules/push_notifications/lib/push-reaper.ts +140 -0
  567. package/src/modules/push_notifications/lib/push-receipt-reaper.ts +210 -0
  568. package/src/modules/push_notifications/lib/push-stub-adapter.ts +80 -0
  569. package/src/modules/push_notifications/lib/queue.ts +74 -0
  570. package/src/modules/push_notifications/lib/reclaim-window.ts +16 -0
  571. package/src/modules/push_notifications/lib/send-custom-push.ts +100 -0
  572. package/src/modules/push_notifications/migrations/.snapshot-open-mercato.json +386 -0
  573. package/src/modules/push_notifications/migrations/Migration20260625150049_push_notifications.ts +16 -0
  574. package/src/modules/push_notifications/migrations/Migration20260626120000_push_notifications_silent.ts +13 -0
  575. package/src/modules/push_notifications/notifications.delivery-strategies.ts +8 -0
  576. package/src/modules/push_notifications/notifications.ts +50 -0
  577. package/src/modules/push_notifications/setup.ts +92 -0
  578. package/src/modules/push_notifications/workers/reclaim-stuck.worker.ts +78 -0
  579. package/src/modules/push_notifications/workers/send-push.worker.ts +34 -0
  580. package/src/modules/sales/i18n/de.json +1 -0
  581. package/src/modules/sales/i18n/en.json +1 -0
  582. package/src/modules/sales/i18n/es.json +1 -0
  583. package/src/modules/sales/i18n/ko.json +1 -0
  584. package/src/modules/sales/i18n/pl.json +1 -0
  585. package/src/modules/sales/notifications.ts +8 -0
  586. package/src/modules/staff/i18n/de.json +1 -0
  587. package/src/modules/staff/i18n/en.json +1 -0
  588. package/src/modules/staff/i18n/es.json +1 -0
  589. package/src/modules/staff/i18n/ko.json +1 -0
  590. package/src/modules/staff/i18n/pl.json +1 -0
  591. package/src/modules/staff/notifications.ts +6 -0
  592. package/src/modules/workflows/i18n/de.json +1 -0
  593. package/src/modules/workflows/i18n/en.json +1 -0
  594. package/src/modules/workflows/i18n/es.json +1 -0
  595. package/src/modules/workflows/i18n/ko.json +1 -0
  596. package/src/modules/workflows/i18n/pl.json +1 -0
  597. package/src/modules/workflows/notifications.ts +2 -0
@@ -0,0 +1,203 @@
1
+ import { z } from 'zod'
2
+ import type { EntityManager } from '@mikro-orm/postgresql'
3
+ import type { CommandHandler } from '@open-mercato/shared/lib/commands'
4
+ import { registerCommand } from '@open-mercato/shared/lib/commands'
5
+ import { extractUndoPayload as extractSharedUndoPayload } from '@open-mercato/shared/lib/commands/undo'
6
+ import { createLogger } from '@open-mercato/shared/lib/logger'
7
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
8
+ import { CommunicationChannel } from '../data/entities'
9
+ import { channelOrgScopeWhere } from '../lib/access-control'
10
+ import { pushUnregister } from './push-unregister'
11
+ import { emitCommunicationChannelsEvent } from '../events'
12
+
13
+ const logger = createLogger('communication_channels')
14
+
15
+ const adminDeleteChannelSchema = z.object({
16
+ channelId: z.string().uuid(),
17
+ scope: z.object({
18
+ tenantId: z.string().uuid(),
19
+ organizationId: z.string().uuid().nullable(),
20
+ }),
21
+ })
22
+
23
+ export type AdminDeleteChannelInput = z.infer<typeof adminDeleteChannelSchema>
24
+
25
+ export type AdminDeleteChannelResult =
26
+ | { status: 'deleted'; channelId: string; undo: AdminDeleteChannelUndoSnapshot }
27
+ | { status: 'noop'; reason: string }
28
+ | { status: 'not_tenant_wide'; reason: string }
29
+
30
+ export interface AdminDeleteChannelUndoSnapshot {
31
+ channelId: string
32
+ // Optional only for back-compat with any pre-existing log entries; new
33
+ // snapshots always set it so the undo lookup stays tenant-scoped.
34
+ tenantId?: string
35
+ }
36
+
37
+ export const COMMUNICATION_CHANNELS_ADMIN_DELETE_CHANNEL_COMMAND_ID =
38
+ 'communication_channels.channel.admin_delete'
39
+
40
+ /**
41
+ * Admin soft-delete for a TENANT-WIDE channel (`user_id IS NULL`) — the
42
+ * companion to `delete-channel.ts` (owner-only, per-user path). Tenant-wide
43
+ * channels (shared inboxes and the push providers FCM/APNs/Expo) have no owner,
44
+ * so the owner path rejects them as `not_owner`; this path deletes them under
45
+ * the `communication_channels.admin` feature.
46
+ *
47
+ * The channel is loaded org-agnostically (tenant-wide push channels store
48
+ * `organization_id IS NULL`, but shared inboxes may pin an org) via
49
+ * `channelOrgScopeWhere`, so it resolves from any session org in the tenant.
50
+ * Per-user channels are refused with `not_tenant_wide` — they MUST go through
51
+ * the owner path so personal-mailbox privacy holds.
52
+ *
53
+ * Provider-side push delivery is torn down best-effort BEFORE the soft-delete,
54
+ * keyed on the CHANNEL's own org (`channel.organizationId ?? tenantId`) so
55
+ * credentials resolve where they were written (see push-register.ts /
56
+ * connect-credential-channel.ts). Mirrors `delete-channel`'s teardown.
57
+ *
58
+ * Undoable: undo clears `deleted_at` to restore the row. Parity with
59
+ * `delete-channel` — same event, payload, buildLog, and undo shape.
60
+ */
61
+ const adminDeleteChannelCommand: CommandHandler<
62
+ AdminDeleteChannelInput,
63
+ AdminDeleteChannelResult
64
+ > = {
65
+ id: COMMUNICATION_CHANNELS_ADMIN_DELETE_CHANNEL_COMMAND_ID,
66
+ // Explicitly undoable (the bus also infers this from `undo` below, but
67
+ // declaring it keeps undoability from silently dropping under a refactor).
68
+ isUndoable: true,
69
+ async execute(rawInput, ctx) {
70
+ const input = adminDeleteChannelSchema.parse(rawInput) as AdminDeleteChannelInput
71
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
72
+ const dscope = {
73
+ tenantId: input.scope.tenantId,
74
+ organizationId: input.scope.organizationId ?? null,
75
+ }
76
+
77
+ const channel = await findOneWithDecryption(
78
+ em,
79
+ CommunicationChannel,
80
+ {
81
+ id: input.channelId,
82
+ tenantId: input.scope.tenantId,
83
+ ...channelOrgScopeWhere(input.scope.organizationId),
84
+ deletedAt: null,
85
+ },
86
+ undefined,
87
+ dscope,
88
+ )
89
+ if (!channel) {
90
+ return { status: 'noop', reason: 'channel not found' }
91
+ }
92
+ if (channel.userId != null) {
93
+ return {
94
+ status: 'not_tenant_wide',
95
+ reason: 'channel is a per-user channel; use the owner delete path',
96
+ }
97
+ }
98
+
99
+ // Tear down provider-side push delivery before the soft-delete, while
100
+ // credentials are still resolvable. Keyed on the channel's OWN org (push
101
+ // channels store credentials at `organization_id = tenantId`; shared inboxes
102
+ // at their pinned org). Best-effort — failures are logged inside
103
+ // pushUnregister and never re-raised.
104
+ if (channel.credentialsRef) {
105
+ const channelOrganizationId = channel.organizationId ?? input.scope.tenantId
106
+ try {
107
+ await pushUnregister({
108
+ container: ctx.container,
109
+ scope: {
110
+ tenantId: input.scope.tenantId,
111
+ organizationId: channelOrganizationId,
112
+ userId: channel.userId ?? null,
113
+ },
114
+ input: { channelId: channel.id },
115
+ })
116
+ } catch (err) {
117
+ logger.warn('admin-delete-channel push unregister failed', { channelId: channel.id, err })
118
+ }
119
+ }
120
+
121
+ channel.deletedAt = new Date()
122
+ channel.isPrimary = false
123
+ await em.flush()
124
+
125
+ // Emit AFTER flush so subscribers observe the committed soft-delete. Same
126
+ // event/payload shape as delete-channel so workflows/audit observe the full
127
+ // channel lifecycle. Persistent so the delivery can retry on failure.
128
+ await emitCommunicationChannelsEvent(
129
+ 'communication_channels.channel.deleted',
130
+ {
131
+ channelId: channel.id,
132
+ userId: channel.userId ?? null,
133
+ providerKey: channel.providerKey,
134
+ channelType: channel.channelType,
135
+ tenantId: input.scope.tenantId,
136
+ organizationId: channel.organizationId ?? null,
137
+ },
138
+ { persistent: true },
139
+ )
140
+
141
+ return {
142
+ status: 'deleted',
143
+ channelId: channel.id,
144
+ undo: { channelId: channel.id, tenantId: channel.tenantId },
145
+ }
146
+ },
147
+ // Persist the undo snapshot into the action log. Without this, the command bus
148
+ // mints an undo token (so the UI offers "Undo") but the snapshot returned from
149
+ // execute() is never stored, and undo() (which clears deleted_at) would no-op.
150
+ async buildLog({ input, result }) {
151
+ if (result.status !== 'deleted') return null
152
+ return {
153
+ resourceKind: 'communication_channels.channel',
154
+ resourceId: result.channelId,
155
+ tenantId: result.undo.tenantId ?? input.scope.tenantId,
156
+ organizationId: input.scope.organizationId ?? null,
157
+ payload: { undo: result.undo },
158
+ }
159
+ },
160
+ async undo({ ctx, logEntry }) {
161
+ const snapshot = extractSnapshotFromLog(logEntry)
162
+ if (!snapshot || !snapshot.tenantId) return
163
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
164
+ // Re-find WITHOUT the `deletedAt: null` filter — the row we are restoring is
165
+ // soft-deleted. Tenant-scoped to avoid cross-tenant resolution by bare id.
166
+ const channel = await findOneWithDecryption(
167
+ em,
168
+ CommunicationChannel,
169
+ { id: snapshot.channelId, tenantId: snapshot.tenantId },
170
+ undefined,
171
+ { tenantId: snapshot.tenantId, organizationId: null },
172
+ )
173
+ if (!channel) return
174
+ channel.deletedAt = null
175
+ await em.flush()
176
+ },
177
+ }
178
+
179
+ /**
180
+ * Defensive shape-validator for the delete snapshot — mirrors the helper in
181
+ * `delete-channel.ts` so tests can construct snapshots directly.
182
+ */
183
+ export function extractUndoPayload(value: unknown): AdminDeleteChannelUndoSnapshot | null {
184
+ if (!value || typeof value !== 'object') return null
185
+ const candidate = (value as { undo?: unknown; channelId?: unknown }).undo ?? value
186
+ if (!candidate || typeof candidate !== 'object') return null
187
+ const obj = candidate as Record<string, unknown>
188
+ if (typeof obj.channelId !== 'string') return null
189
+ return {
190
+ channelId: obj.channelId,
191
+ tenantId: typeof obj.tenantId === 'string' ? obj.tenantId : undefined,
192
+ }
193
+ }
194
+
195
+ function extractSnapshotFromLog(logEntry: unknown): AdminDeleteChannelUndoSnapshot | null {
196
+ const undo = extractSharedUndoPayload<AdminDeleteChannelUndoSnapshot>((logEntry ?? null) as never)
197
+ if (undo) return extractUndoPayload(undo)
198
+ return extractUndoPayload(logEntry)
199
+ }
200
+
201
+ registerCommand(adminDeleteChannelCommand)
202
+
203
+ export default adminDeleteChannelCommand
@@ -15,7 +15,12 @@ const connectCredentialChannelSchema = z.object({
15
15
  credentials: z.record(z.string(), z.unknown()),
16
16
  /** Optional polling interval (seconds) — set when adapter is polling-based. */
17
17
  pollIntervalSeconds: z.number().int().positive().max(86_400).optional(),
18
- userId: z.string().uuid(),
18
+ /**
19
+ * Connecting user. `null` for tenant-wide (admin-connected) channels whose
20
+ * provider declares `channelScope: 'tenant'` (push: FCM/APNs/Expo) — the
21
+ * resulting channel + credentials are stored with `user_id = NULL`.
22
+ */
23
+ userId: z.string().uuid().nullable(),
19
24
  scope: z.object({
20
25
  tenantId: z.string().uuid(),
21
26
  organizationId: z.string().uuid().nullable(),
@@ -26,9 +31,10 @@ export type ConnectCredentialChannelInput = z.infer<typeof connectCredentialChan
26
31
 
27
32
  export type ConnectCredentialChannelResult =
28
33
  | { status: 'connected'; channelId: string; externalIdentifier: string | null }
29
- | { status: 'validation_failed'; errors: Record<string, string> }
34
+ | { status: 'validation_failed'; errors: Record<string, string>; errorCodes?: Record<string, string> }
30
35
  | { status: 'no_adapter'; reason: string }
31
36
  | { status: 'duplicate_mailbox'; externalIdentifier: string; existingProviderKey: string }
37
+ | { status: 'wrong_scope_for_route'; providerKey: string }
32
38
 
33
39
  export const COMMUNICATION_CHANNELS_CONNECT_CREDENTIAL_CHANNEL_COMMAND_ID =
34
40
  'communication_channels.channel.connect_credential'
@@ -42,21 +48,26 @@ type CredentialsServiceLike = {
42
48
  }
43
49
 
44
50
  /**
45
- * Connect a per-user credential-based channel (IMAP, and future basic-auth providers).
51
+ * Connect a credential-based channel (IMAP, push providers, and future basic-auth
52
+ * providers).
46
53
  *
47
54
  * Flow:
48
55
  * 1. Resolve the adapter for `providerKey`.
49
- * 2. Call `adapter.validateCredentials?` adapters that don't implement it
56
+ * 2. Decide scope: `adapter.channelScope === 'tenant'` the channel + credentials
57
+ * are stored with `user_id = NULL` (one shared channel per tenant, e.g. push
58
+ * FCM/APNs/Expo); otherwise they are stored under the connecting `userId`.
59
+ * 3. Call `adapter.validateCredentials?` — adapters that don't implement it
50
60
  * are accepted optimistically (the hub trusts the adapter to fail on first
51
61
  * use). Adapters with the method return field-level errors via Zod-like
52
62
  * `{ ok: false, errors: { fieldName: 'message' } }`; we forward those to
53
63
  * the caller for `createCrudFormError`.
54
- * 3. Persist the credentials encrypted via `integrationCredentialsService.save?` if available.
55
- * 4. Create the `CommunicationChannel` row with `userId = currentUser.id`,
64
+ * 4. Persist the credentials encrypted via `integrationCredentialsService.save?` if available.
65
+ * 5. Create the `CommunicationChannel` row with the effective `userId`,
56
66
  * `status = 'connected'`, and the adapter's declared capabilities snapshot.
57
67
  *
58
- * The route handler binds the `userId` field from the authenticated session;
59
- * the command refuses inputs whose `userId` doesn't pass UUID validation.
68
+ * The per-user route binds `userId` from the authenticated session; the tenant
69
+ * route passes `userId: null`. Either way the command re-derives the effective
70
+ * scope from the adapter, so a tenant provider is never stamped per-user.
60
71
  */
61
72
  const connectCredentialChannelCommand: CommandHandler<
62
73
  ConnectCredentialChannelInput,
@@ -75,6 +86,23 @@ const connectCredentialChannelCommand: CommandHandler<
75
86
  }
76
87
  }
77
88
 
89
+ // Tenant-wide providers (push: FCM/APNs/Expo) store one shared channel +
90
+ // credential row per tenant with `user_id = NULL`; per-user providers
91
+ // (Gmail/IMAP) keep the connecting user's id.
92
+ //
93
+ // SECURITY: a tenant-scoped provider must only be connected through the
94
+ // admin-gated tenant route (which passes `userId: null`). If it arrives with
95
+ // a non-null `userId` it came from the per-user route (feature
96
+ // `connect_user_channel`, granted to manager/employee) — refuse rather than
97
+ // silently minting a privileged tenant-wide channel that bypasses the
98
+ // admin-only `connect_tenant_channel` gate. The tenant route passes
99
+ // `userId: null`, so legitimate tenant connects still pass.
100
+ const tenantScoped = adapter.channelScope === 'tenant'
101
+ if (tenantScoped && input.userId !== null) {
102
+ return { status: 'wrong_scope_for_route', providerKey: input.providerKey }
103
+ }
104
+ const effectiveUserId = tenantScoped ? null : input.userId
105
+
78
106
  // Optional credential validation.
79
107
  if (typeof adapter.validateCredentials === 'function') {
80
108
  const validation = await adapter.validateCredentials({
@@ -89,6 +117,7 @@ const connectCredentialChannelCommand: CommandHandler<
89
117
  return {
90
118
  status: 'validation_failed',
91
119
  errors: validation.errors ?? { _form: 'Credential validation failed' },
120
+ errorCodes: validation.errorCodes,
92
121
  }
93
122
  }
94
123
  }
@@ -106,11 +135,24 @@ const connectCredentialChannelCommand: CommandHandler<
106
135
  //
107
136
  // `scope.userId` is set so the credentials service writes a per-user row.
108
137
  // Without this, two users on the same tenant share one credentials row
109
- // (see review R2-C1 / N1, 2026-05-26).
138
+ // (see review R2-C1 / N1, 2026-05-26). Tenant-wide providers pass `null`
139
+ // here so the service writes/reads the shared `user_id = NULL` row (same
140
+ // pattern as Stripe/Akeneo tenant-wide credentials).
141
+ //
142
+ // `organizationId` for tenant scope is pinned to `tenantId` (not the
143
+ // connecting admin's selected org). The channel dedup/heal is org-agnostic
144
+ // (index + heal key ignore org) and the channel row is stored with
145
+ // `organization_id = NULL`, so push-delivery resolves credentials at
146
+ // `channel.organizationId ?? tenantId` = `tenantId`. Pinning the write to the
147
+ // same key keeps a cross-org reconnect (e.g. key rotation from another org)
148
+ // overwriting the one credential row the delivery path actually reads, rather
149
+ // than orphaning it under a per-org key.
110
150
  const credentialsScope = {
111
151
  tenantId: input.scope.tenantId,
112
- organizationId: input.scope.organizationId ?? input.scope.tenantId,
113
- userId: input.userId,
152
+ organizationId: tenantScoped
153
+ ? input.scope.tenantId
154
+ : input.scope.organizationId ?? input.scope.tenantId,
155
+ userId: effectiveUserId,
114
156
  }
115
157
  let credentialsRefId: string | null = null
116
158
  let credentialsPersisted = false
@@ -118,7 +160,7 @@ const connectCredentialChannelCommand: CommandHandler<
118
160
  try {
119
161
  await credentialsService.save(
120
162
  `channel_${input.providerKey}`,
121
- { ...input.credentials, userId: input.userId },
163
+ { ...input.credentials, ...(effectiveUserId ? { userId: effectiveUserId } : {}) },
122
164
  credentialsScope,
123
165
  )
124
166
  credentialsPersisted = true
@@ -182,8 +224,14 @@ const connectCredentialChannelCommand: CommandHandler<
182
224
  displayName: input.displayName,
183
225
  externalIdentifier,
184
226
  credentialsRefId,
185
- userId: input.userId,
186
- scope: { tenantId: input.scope.tenantId, organizationId: input.scope.organizationId ?? null },
227
+ userId: effectiveUserId,
228
+ // Tenant-wide channels store `organization_id = NULL` (truly tenant-scoped,
229
+ // matching the entity's `user_id NULL` semantics) so the org-agnostic heal
230
+ // key stays stable across orgs and delivery reads creds at `?? tenantId`.
231
+ scope: {
232
+ tenantId: input.scope.tenantId,
233
+ organizationId: tenantScoped ? null : input.scope.organizationId ?? null,
234
+ },
187
235
  pollIntervalSeconds: input.pollIntervalSeconds,
188
236
  })
189
237
  } catch (err) {
@@ -205,7 +253,7 @@ const connectCredentialChannelCommand: CommandHandler<
205
253
  // covers the channel until the operator clicks "Re-register push".
206
254
  // Imported lazily to avoid a circular module load (push-register reads
207
255
  // the channel adapter registry, which is initialised after this module).
208
- if (credentialsAvailable && input.providerKey === 'gmail') {
256
+ if (credentialsAvailable && input.providerKey === 'gmail' && input.userId) {
209
257
  const adapterSupportsPush =
210
258
  typeof adapter.registerPush === 'function' && typeof adapter.unregisterPush === 'function'
211
259
  const organizationId = input.scope.organizationId
@@ -282,9 +282,17 @@ const deliverOutboundMessageCommand: CommandHandler<
282
282
  // Per-user credentials scope: pass `channel.userId` so the credentials
283
283
  // service returns this user's row, not whoever connected last. See
284
284
  // review R2-C1 / N1 (2026-05-26).
285
+ //
286
+ // Key the org on the CHANNEL's own org, not the message's org. Tenant-wide
287
+ // channels store `organization_id = NULL` and their credentials live at
288
+ // `organization_id = tenantId` (see connect-credential-channel.ts), so
289
+ // `channel.organizationId ?? tenantId` matches the write key for both
290
+ // org-scoped and tenant-wide channels. Keying on the message org would
291
+ // resolve `{}` the moment a tenant-wide provider delivers from a message
292
+ // that carries a non-null org.
285
293
  const credentialsScope = {
286
294
  tenantId: input.scope.tenantId,
287
- organizationId: input.scope.organizationId ?? input.scope.tenantId,
295
+ organizationId: channel.organizationId ?? input.scope.tenantId,
288
296
  userId: channel.userId ?? null,
289
297
  }
290
298
  let credentials: Record<string, unknown> = {}
@@ -406,7 +414,7 @@ const deliverOutboundMessageCommand: CommandHandler<
406
414
  credentials,
407
415
  scope: {
408
416
  tenantId: input.scope.tenantId,
409
- organizationId: input.scope.organizationId ?? input.scope.tenantId,
417
+ organizationId: channel.organizationId ?? input.scope.tenantId,
410
418
  },
411
419
  metadata: converted.metadata,
412
420
  })
@@ -113,9 +113,15 @@ export async function pushRegister(params: {
113
113
  } catch {
114
114
  credentialsService = null
115
115
  }
116
+ // Resolve credentials at the channel's OWN org, not the caller's scope org.
117
+ // Tenant-wide channels store `organization_id = NULL` and their credentials
118
+ // live at `organization_id = tenantId` (see connect-credential-channel.ts).
119
+ // Keying on `channel.organizationId ?? tenantId` matches the write key for
120
+ // both org-scoped (per-user) and tenant-wide channels; keying on the caller's
121
+ // scope org would silently resolve `{}` for a tenant-wide channel.
116
122
  const credentialsScope = {
117
123
  tenantId: scope.tenantId,
118
- organizationId: scope.organizationId,
124
+ organizationId: channel.organizationId ?? scope.tenantId,
119
125
  userId: channel.userId ?? null,
120
126
  }
121
127
  let credentials: Record<string, unknown> = {}
@@ -3,6 +3,7 @@ import type { AwilixContainer } from 'awilix'
3
3
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
4
  import type { EntityManager } from '@mikro-orm/postgresql'
5
5
  import { CommunicationChannel } from '../data/entities'
6
+ import { channelOrgScopeWhere } from '../lib/access-control'
6
7
  import { getChannelAdapterRegistry } from '../lib/adapter-registry-singleton'
7
8
  import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
8
9
  import { POLLING_ONLY_DEFAULT_INTERVAL_SECONDS } from '../lib/connect-channel'
@@ -64,7 +65,14 @@ export async function pushUnregister(params: {
64
65
  const { container, scope } = params
65
66
 
66
67
  const em = (container.resolve('em') as EntityManager).fork()
67
- const dscope = { tenantId: scope.tenantId, organizationId: scope.organizationId }
68
+ // The channel ROW and the credentials store use DIFFERENT orgs. A tenant-wide push channel is
69
+ // stored with `organization_id = NULL` (its encrypted columns are keyed at org=null) even though
70
+ // its credentials are pinned at `organization_id = tenantId`. `scope.organizationId` here is the
71
+ // credentials/delivery org (tenantId for tenant-wide) — so it must NOT be used as an exact row
72
+ // filter (it would never match a null-org row) nor as the row decryption org. Look the row up
73
+ // null-aware and decrypt at the row's own org (null column ⇒ null fallback); credential resolution
74
+ // below keeps using the credentials org.
75
+ const dscope = { tenantId: scope.tenantId, organizationId: null }
68
76
 
69
77
  const channel = await findOneWithDecryption(
70
78
  em,
@@ -72,7 +80,7 @@ export async function pushUnregister(params: {
72
80
  {
73
81
  id: input.channelId,
74
82
  tenantId: scope.tenantId,
75
- organizationId: scope.organizationId,
83
+ ...channelOrgScopeWhere(scope.organizationId),
76
84
  },
77
85
  undefined,
78
86
  dscope,
@@ -98,9 +106,12 @@ export async function pushUnregister(params: {
98
106
  } catch {
99
107
  credentialsService = null
100
108
  }
109
+ // Resolve credentials at the channel's OWN org (tenant-wide channels store
110
+ // their credentials at `organization_id = tenantId`; see push-register.ts /
111
+ // connect-credential-channel.ts), never the caller's scope org.
101
112
  const credentialsScope = {
102
113
  tenantId: scope.tenantId,
103
- organizationId: scope.organizationId,
114
+ organizationId: channel.organizationId ?? scope.tenantId,
104
115
  userId: channel.userId ?? null,
105
116
  }
106
117
  let credentials: Record<string, unknown> = {}
@@ -63,6 +63,16 @@ export type CommunicationChannelStatus =
63
63
  expression:
64
64
  `create unique index "communication_channels_user_provider_external_uq" on "communication_channels" ("tenant_id", "user_id", "provider_key", "external_identifier") where "deleted_at" is null and "user_id" is not null and "external_identifier" is not null`,
65
65
  })
66
+ // One tenant-wide push channel per (tenant, provider): push providers (FCM/APNs/
67
+ // Expo) have no `external_identifier` and `user_id IS NULL`, so the mailbox
68
+ // unique index above does not cover them. This keeps an admin reconnect healing
69
+ // the single shared row (see `createConnectedChannelRow`) instead of inserting
70
+ // duplicates the fan-out would silently ignore.
71
+ @Index({
72
+ name: 'communication_channels_tenant_push_provider_uq',
73
+ expression:
74
+ `create unique index "communication_channels_tenant_push_provider_uq" on "communication_channels" ("tenant_id", "provider_key") where "channel_type" = 'push' and "user_id" is null and "deleted_at" is null`,
75
+ })
66
76
  export class CommunicationChannel {
67
77
  [OptionalProps]?:
68
78
  | 'createdAt'
@@ -74,6 +74,7 @@ export const validateCredentialsInputSchema = z.object({
74
74
  export const validateCredentialsResultSchema = z.object({
75
75
  ok: z.boolean(),
76
76
  errors: z.record(z.string(), z.string()).optional(),
77
+ errorCodes: z.record(z.string(), z.string()).optional(),
77
78
  })
78
79
 
79
80
  export const refreshCredentialsInputSchema = z.object({
@@ -125,6 +125,39 @@
125
125
  "communication_channels.profile.tableTitle": "Ihre Kanäle",
126
126
  "communication_channels.profile.title": "Meine Kommunikationskanäle",
127
127
  "communication_channels.push.button.reregister": "Push neu registrieren",
128
+ "communication_channels.push.connect.blocked": "Verbindung durch Validierung blockiert",
129
+ "communication_channels.push.connect.button.apns": "APNs verbinden",
130
+ "communication_channels.push.connect.button.expo": "Expo verbinden",
131
+ "communication_channels.push.connect.button.fcm": "FCM verbinden",
132
+ "communication_channels.push.connect.cancel": "Abbrechen",
133
+ "communication_channels.push.connect.connected": "Push-Anbieter verbunden.",
134
+ "communication_channels.push.connect.connecting": "Verbinden…",
135
+ "communication_channels.push.connect.description": "Dies ist ein gemeinsamer, mandantenweiter Kanal — er bedient die Geräte aller Benutzer in diesem Arbeitsbereich.",
136
+ "communication_channels.push.connect.displayName": "Anzeigename",
137
+ "communication_channels.push.connect.errors.fields.invalid": "Dieser Wert ist ungültig.",
138
+ "communication_channels.push.connect.errors.fields.invalid_bundle_id": "Eine Bundle-ID sieht aus wie com.example.app.",
139
+ "communication_channels.push.connect.errors.fields.invalid_json": "Das ist kein gültiges JSON. Fügen Sie die Dienstkontodatei genau so ein, wie Firebase sie erzeugt hat.",
140
+ "communication_channels.push.connect.errors.fields.invalid_key_id": "Eine APNs-Key-ID besteht aus 10 alphanumerischen Zeichen.",
141
+ "communication_channels.push.connect.errors.fields.invalid_p8": "Das ist kein lesbarer APNs-.p8-Signaturschlüssel. Fügen Sie die vollständige Datei einschließlich der BEGIN- und END-Zeilen ein.",
142
+ "communication_channels.push.connect.errors.fields.invalid_team_id": "Eine Apple-Team-ID besteht aus 10 alphanumerischen Zeichen.",
143
+ "communication_channels.push.connect.errors.fields.missing_fields": "In der Dienstkonto-JSON fehlen erforderliche Felder (project_id, client_email, private_key).",
144
+ "communication_channels.push.connect.errors.fields.required": "Dieses Feld ist erforderlich.",
145
+ "communication_channels.push.connect.errors.mailbox_already_connected": "Dieser Kanal ist bereits über einen anderen Anbieter verbunden. Trennen Sie ihn dort, bevor Sie ihn erneut verbinden.",
146
+ "communication_channels.push.connect.errors.provider_not_tenant_scoped": "Dieser Anbieter verbindet benutzerspezifische Kanäle; verwenden Sie stattdessen den persönlichen Kanalverbindungsvorgang.",
147
+ "communication_channels.push.connect.errors.wrong_scope_for_route": "Unerwarteter Kanalbereich. Bitte versuchen Sie es erneut oder wenden Sie sich an den Support.",
148
+ "communication_channels.push.connect.failed": "Push-Anbieter konnte nicht verbunden werden.",
149
+ "communication_channels.push.connect.fields.apns.bundleId": "Bundle-ID",
150
+ "communication_channels.push.connect.fields.apns.keyId": "Key-ID",
151
+ "communication_channels.push.connect.fields.apns.p8Key": "APNs .p8-Schlüssel",
152
+ "communication_channels.push.connect.fields.apns.production": "Produktionsumgebung",
153
+ "communication_channels.push.connect.fields.apns.teamId": "Team-ID",
154
+ "communication_channels.push.connect.fields.expo.accessToken": "Zugriffstoken (optional)",
155
+ "communication_channels.push.connect.fields.fcm.appName": "App-Name (optional)",
156
+ "communication_channels.push.connect.fields.fcm.serviceAccountJson": "Dienstkonto-JSON",
157
+ "communication_channels.push.connect.save": "Verbinden",
158
+ "communication_channels.push.connect.title.apns": "Apple Push Notification service verbinden",
159
+ "communication_channels.push.connect.title.expo": "Expo Push verbinden",
160
+ "communication_channels.push.connect.title.fcm": "Firebase Cloud Messaging verbinden",
128
161
  "communication_channels.push.flash.registerFailed": "Push-Registrierung fehlgeschlagen",
129
162
  "communication_channels.push.status.active": "Push aktiv",
130
163
  "communication_channels.push.status.failed": "Push fehlgeschlagen — nutze Polling",
@@ -140,5 +173,6 @@
140
173
  "communication_channels.status.disconnected": "Getrennt",
141
174
  "communication_channels.status.error": "Fehler",
142
175
  "communication_channels.status.inactive": "Inaktiv",
143
- "communication_channels.status.requiresReauth": "Erneutes Verbinden erforderlich"
176
+ "communication_channels.status.requiresReauth": "Erneutes Verbinden erforderlich",
177
+ "notifications.categories.communication_channels": "Kommunikation"
144
178
  }
@@ -125,6 +125,39 @@
125
125
  "communication_channels.profile.tableTitle": "Your channels",
126
126
  "communication_channels.profile.title": "My communication channels",
127
127
  "communication_channels.push.button.reregister": "Re-register push",
128
+ "communication_channels.push.connect.blocked": "Connection blocked by validation",
129
+ "communication_channels.push.connect.button.apns": "Connect APNs",
130
+ "communication_channels.push.connect.button.expo": "Connect Expo",
131
+ "communication_channels.push.connect.button.fcm": "Connect FCM",
132
+ "communication_channels.push.connect.cancel": "Cancel",
133
+ "communication_channels.push.connect.connected": "Push provider connected.",
134
+ "communication_channels.push.connect.connecting": "Connecting…",
135
+ "communication_channels.push.connect.description": "This is a shared, tenant-wide channel — every user's devices in this workspace are served by it.",
136
+ "communication_channels.push.connect.displayName": "Display name",
137
+ "communication_channels.push.connect.errors.fields.invalid": "This value is invalid.",
138
+ "communication_channels.push.connect.errors.fields.invalid_bundle_id": "A Bundle ID looks like com.example.app.",
139
+ "communication_channels.push.connect.errors.fields.invalid_json": "This is not valid JSON. Paste the service account file exactly as Firebase generated it.",
140
+ "communication_channels.push.connect.errors.fields.invalid_key_id": "An APNs Key ID is 10 alphanumeric characters.",
141
+ "communication_channels.push.connect.errors.fields.invalid_p8": "This is not a readable APNs .p8 signing key. Paste the whole file including the BEGIN and END lines.",
142
+ "communication_channels.push.connect.errors.fields.invalid_team_id": "An Apple Team ID is 10 alphanumeric characters.",
143
+ "communication_channels.push.connect.errors.fields.missing_fields": "The service account JSON is missing required fields (project_id, client_email, private_key).",
144
+ "communication_channels.push.connect.errors.fields.required": "This field is required.",
145
+ "communication_channels.push.connect.errors.mailbox_already_connected": "This channel is already connected via another provider. Disconnect it there before reconnecting.",
146
+ "communication_channels.push.connect.errors.provider_not_tenant_scoped": "This provider connects per-user channels; use the personal channel connect flow instead.",
147
+ "communication_channels.push.connect.errors.wrong_scope_for_route": "Unexpected channel scope. Please try again or contact support.",
148
+ "communication_channels.push.connect.failed": "Could not connect push provider.",
149
+ "communication_channels.push.connect.fields.apns.bundleId": "Bundle ID",
150
+ "communication_channels.push.connect.fields.apns.keyId": "Key ID",
151
+ "communication_channels.push.connect.fields.apns.p8Key": "APNs .p8 key",
152
+ "communication_channels.push.connect.fields.apns.production": "Production environment",
153
+ "communication_channels.push.connect.fields.apns.teamId": "Team ID",
154
+ "communication_channels.push.connect.fields.expo.accessToken": "Access token (optional)",
155
+ "communication_channels.push.connect.fields.fcm.appName": "App name (optional)",
156
+ "communication_channels.push.connect.fields.fcm.serviceAccountJson": "Service Account JSON",
157
+ "communication_channels.push.connect.save": "Connect",
158
+ "communication_channels.push.connect.title.apns": "Connect Apple Push Notification service",
159
+ "communication_channels.push.connect.title.expo": "Connect Expo Push",
160
+ "communication_channels.push.connect.title.fcm": "Connect Firebase Cloud Messaging",
128
161
  "communication_channels.push.flash.registerFailed": "Failed to register push",
129
162
  "communication_channels.push.status.active": "Push active",
130
163
  "communication_channels.push.status.failed": "Push failed — using polling",
@@ -140,5 +173,6 @@
140
173
  "communication_channels.status.disconnected": "Disconnected",
141
174
  "communication_channels.status.error": "Error",
142
175
  "communication_channels.status.inactive": "Inactive",
143
- "communication_channels.status.requiresReauth": "Needs reconnection"
176
+ "communication_channels.status.requiresReauth": "Needs reconnection",
177
+ "notifications.categories.communication_channels": "Communication"
144
178
  }
@@ -125,6 +125,39 @@
125
125
  "communication_channels.profile.tableTitle": "Tus canales",
126
126
  "communication_channels.profile.title": "Mis canales de comunicación",
127
127
  "communication_channels.push.button.reregister": "Re-registrar push",
128
+ "communication_channels.push.connect.blocked": "Conexión bloqueada por la validación",
129
+ "communication_channels.push.connect.button.apns": "Conectar APNs",
130
+ "communication_channels.push.connect.button.expo": "Conectar Expo",
131
+ "communication_channels.push.connect.button.fcm": "Conectar FCM",
132
+ "communication_channels.push.connect.cancel": "Cancelar",
133
+ "communication_channels.push.connect.connected": "Proveedor push conectado.",
134
+ "communication_channels.push.connect.connecting": "Conectando…",
135
+ "communication_channels.push.connect.description": "Este es un canal compartido para todo el inquilino: atiende los dispositivos de todos los usuarios de este espacio.",
136
+ "communication_channels.push.connect.displayName": "Nombre visible",
137
+ "communication_channels.push.connect.errors.fields.invalid": "Este valor no es válido.",
138
+ "communication_channels.push.connect.errors.fields.invalid_bundle_id": "Un Bundle ID se parece a com.example.app.",
139
+ "communication_channels.push.connect.errors.fields.invalid_json": "Esto no es JSON válido. Pega el archivo de la cuenta de servicio tal y como lo generó Firebase.",
140
+ "communication_channels.push.connect.errors.fields.invalid_key_id": "Un Key ID de APNs tiene 10 caracteres alfanuméricos.",
141
+ "communication_channels.push.connect.errors.fields.invalid_p8": "Esta no es una clave de firma .p8 de APNs legible. Pega el archivo completo, incluidas las líneas BEGIN y END.",
142
+ "communication_channels.push.connect.errors.fields.invalid_team_id": "Un Team ID de Apple tiene 10 caracteres alfanuméricos.",
143
+ "communication_channels.push.connect.errors.fields.missing_fields": "Al JSON de la cuenta de servicio le faltan campos obligatorios (project_id, client_email, private_key).",
144
+ "communication_channels.push.connect.errors.fields.required": "Este campo es obligatorio.",
145
+ "communication_channels.push.connect.errors.mailbox_already_connected": "Este canal ya está conectado a través de otro proveedor. Desconéctalo allí antes de volver a conectarlo.",
146
+ "communication_channels.push.connect.errors.provider_not_tenant_scoped": "Este proveedor conecta canales por usuario; utiliza en su lugar el flujo de conexión de canal personal.",
147
+ "communication_channels.push.connect.errors.wrong_scope_for_route": "Ámbito de canal inesperado. Vuelve a intentarlo o contacta con soporte.",
148
+ "communication_channels.push.connect.failed": "No se pudo conectar el proveedor push.",
149
+ "communication_channels.push.connect.fields.apns.bundleId": "Bundle ID",
150
+ "communication_channels.push.connect.fields.apns.keyId": "Key ID",
151
+ "communication_channels.push.connect.fields.apns.p8Key": "Clave .p8 de APNs",
152
+ "communication_channels.push.connect.fields.apns.production": "Entorno de producción",
153
+ "communication_channels.push.connect.fields.apns.teamId": "Team ID",
154
+ "communication_channels.push.connect.fields.expo.accessToken": "Token de acceso (opcional)",
155
+ "communication_channels.push.connect.fields.fcm.appName": "Nombre de la app (opcional)",
156
+ "communication_channels.push.connect.fields.fcm.serviceAccountJson": "JSON de cuenta de servicio",
157
+ "communication_channels.push.connect.save": "Conectar",
158
+ "communication_channels.push.connect.title.apns": "Conectar Apple Push Notification service",
159
+ "communication_channels.push.connect.title.expo": "Conectar Expo Push",
160
+ "communication_channels.push.connect.title.fcm": "Conectar Firebase Cloud Messaging",
128
161
  "communication_channels.push.flash.registerFailed": "No se pudo registrar push",
129
162
  "communication_channels.push.status.active": "Push activo",
130
163
  "communication_channels.push.status.failed": "Push fallido — usando sondeo",
@@ -140,5 +173,6 @@
140
173
  "communication_channels.status.disconnected": "Desconectado",
141
174
  "communication_channels.status.error": "Error",
142
175
  "communication_channels.status.inactive": "Inactivo",
143
- "communication_channels.status.requiresReauth": "Necesita volver a conectarse"
176
+ "communication_channels.status.requiresReauth": "Necesita volver a conectarse",
177
+ "notifications.categories.communication_channels": "Comunicación"
144
178
  }