@open-mercato/core 0.4.2-canary-49d47ff90e → 0.4.2-canary-0ba39cdeb6
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.
- package/dist/modules/auth/backend/auth/profile/page.js.map +1 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +4 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +4 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/cli.js +13 -12
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/business_rules/api/execute/route.js +7 -1
- package/dist/modules/business_rules/api/execute/route.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +33 -3
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/configs/components/CachePanel.js +4 -4
- package/dist/modules/configs/components/CachePanel.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +48 -1
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/dashboards/cli.js +12 -4
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
- package/dist/modules/dashboards/services/widgetDataService.js +110 -3
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/notifications/data/validators.js +5 -1
- package/dist/modules/notifications/data/validators.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +2 -1
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/notifications/lib/deliveryConfig.js +4 -2
- package/dist/modules/notifications/lib/deliveryConfig.js.map +2 -2
- package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js +33 -7
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +14 -6
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/package.json +2 -2
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
- package/src/modules/auth/backend/auth/profile/page.tsx +2 -2
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +4 -1
- package/src/modules/auth/cli.ts +25 -12
- package/src/modules/business_rules/api/execute/route.ts +8 -1
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
- package/src/modules/business_rules/lib/rule-engine.ts +57 -3
- package/src/modules/configs/components/CachePanel.tsx +4 -4
- package/src/modules/configs/i18n/en.json +12 -2
- package/src/modules/configs/i18n/pl.json +12 -2
- package/src/modules/configs/lib/system-status.ts +48 -1
- package/src/modules/configs/lib/system-status.types.ts +1 -0
- package/src/modules/dashboards/cli.ts +14 -4
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
- package/src/modules/dashboards/services/widgetDataService.ts +132 -4
- package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
- package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
- package/src/modules/notifications/data/validators.ts +5 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +2 -0
- package/src/modules/notifications/lib/deliveryConfig.ts +8 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
- package/src/modules/notifications/subscribers/deliver-notification.ts +39 -10
- package/src/modules/workflows/lib/transition-handler.ts +18 -6
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Notification } from '../data/entities'
|
|
2
|
+
import type { NotificationDeliveryConfig } from './deliveryConfig'
|
|
3
|
+
|
|
4
|
+
export type NotificationDeliveryStrategyConfig = {
|
|
5
|
+
enabled?: boolean
|
|
6
|
+
config?: unknown
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type NotificationDeliveryRecipient = {
|
|
10
|
+
email?: string | null
|
|
11
|
+
name?: string | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type NotificationDeliveryContext = {
|
|
15
|
+
notification: Notification
|
|
16
|
+
recipient: NotificationDeliveryRecipient
|
|
17
|
+
title: string
|
|
18
|
+
body: string | null
|
|
19
|
+
panelUrl: string | null
|
|
20
|
+
panelLink: string | null
|
|
21
|
+
actionLinks: Array<{ id: string; label: string; href: string }>
|
|
22
|
+
deliveryConfig: NotificationDeliveryConfig
|
|
23
|
+
config: NotificationDeliveryStrategyConfig
|
|
24
|
+
resolve: <T = unknown>(name: string) => T
|
|
25
|
+
t: (key: string, fallback?: string, variables?: Record<string, string>) => string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type NotificationDeliveryStrategy = {
|
|
29
|
+
id: string
|
|
30
|
+
label?: string
|
|
31
|
+
defaultEnabled?: boolean
|
|
32
|
+
deliver: (ctx: NotificationDeliveryContext) => Promise<void> | void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type RegisteredStrategy = NotificationDeliveryStrategy & { priority: number }
|
|
36
|
+
|
|
37
|
+
const registry: RegisteredStrategy[] = []
|
|
38
|
+
|
|
39
|
+
export function registerNotificationDeliveryStrategy(
|
|
40
|
+
strategy: NotificationDeliveryStrategy,
|
|
41
|
+
options?: { priority?: number }
|
|
42
|
+
): void {
|
|
43
|
+
const priority = options?.priority ?? 0
|
|
44
|
+
registry.push({ ...strategy, priority })
|
|
45
|
+
registry.sort((a, b) => b.priority - a.priority)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getNotificationDeliveryStrategies(): NotificationDeliveryStrategy[] {
|
|
49
|
+
return registry
|
|
50
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { EntityManager } from '@mikro-orm/postgresql'
|
|
|
2
2
|
import { Notification } from '../data/entities'
|
|
3
3
|
import { NOTIFICATION_EVENTS } from '../lib/events'
|
|
4
4
|
import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, resolveNotificationDeliveryConfig, resolveNotificationPanelUrl } from '../lib/deliveryConfig'
|
|
5
|
+
import { getNotificationDeliveryStrategies } from '../lib/deliveryStrategies'
|
|
5
6
|
import { sendEmail } from '@open-mercato/shared/lib/email/send'
|
|
6
7
|
import NotificationEmail from '../emails/NotificationEmail'
|
|
7
8
|
import { loadDictionary } from '@open-mercato/shared/lib/i18n/server'
|
|
@@ -100,7 +101,6 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
|
|
|
100
101
|
const deliveryConfig = await resolveNotificationDeliveryConfig(ctx, { defaultValue: DEFAULT_NOTIFICATION_DELIVERY_CONFIG })
|
|
101
102
|
if (!deliveryConfig.strategies.email.enabled) {
|
|
102
103
|
debug('email delivery disabled')
|
|
103
|
-
return
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const em = ctx.resolve('em') as EntityManager
|
|
@@ -121,7 +121,7 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
|
|
|
121
121
|
encryptionService = null
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
const recipient = await resolveRecipient(em, notification, encryptionService)
|
|
124
|
+
const recipient = (await resolveRecipient(em, notification, encryptionService)) ?? { email: null, name: null }
|
|
125
125
|
if (!recipient?.email) {
|
|
126
126
|
debug('recipient has no email', notification.recipientUserId)
|
|
127
127
|
}
|
|
@@ -129,17 +129,18 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
|
|
|
129
129
|
const panelUrl = resolveNotificationPanelUrl(deliveryConfig)
|
|
130
130
|
if (!panelUrl) {
|
|
131
131
|
debug('missing panelUrl; check appUrl/panelPath settings')
|
|
132
|
-
return
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
const panelLink = buildPanelLink(panelUrl, notification.id)
|
|
136
|
-
const actionLinks =
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
const panelLink = panelUrl ? buildPanelLink(panelUrl, notification.id) : null
|
|
135
|
+
const actionLinks = panelLink
|
|
136
|
+
? (notification.actionData?.actions ?? []).map((action) => ({
|
|
137
|
+
id: action.id,
|
|
138
|
+
label: action.labelKey ? t(action.labelKey, action.label) : action.label,
|
|
139
|
+
href: panelLink,
|
|
140
|
+
}))
|
|
141
|
+
: []
|
|
141
142
|
|
|
142
|
-
if (deliveryConfig.strategies.email.enabled && recipient?.email) {
|
|
143
|
+
if (deliveryConfig.strategies.email.enabled && recipient?.email && panelLink) {
|
|
143
144
|
const subjectPrefix = deliveryConfig.strategies.email.subjectPrefix?.trim()
|
|
144
145
|
const subject = subjectPrefix ? `${subjectPrefix} ${title}` : title
|
|
145
146
|
const copy = {
|
|
@@ -171,5 +172,33 @@ export default async function handle(payload: NotificationCreatedPayload, ctx: R
|
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
const strategyConfigs = deliveryConfig.strategies.custom ?? {}
|
|
176
|
+
const strategies = getNotificationDeliveryStrategies()
|
|
177
|
+
for (const strategy of strategies) {
|
|
178
|
+
const strategyConfig = strategyConfigs[strategy.id]
|
|
179
|
+
const enabled = strategyConfig?.enabled ?? strategy.defaultEnabled ?? false
|
|
180
|
+
if (!enabled) {
|
|
181
|
+
debug('custom delivery disabled', strategy.id)
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
await strategy.deliver({
|
|
186
|
+
notification,
|
|
187
|
+
recipient,
|
|
188
|
+
title,
|
|
189
|
+
body,
|
|
190
|
+
panelUrl,
|
|
191
|
+
panelLink,
|
|
192
|
+
actionLinks,
|
|
193
|
+
deliveryConfig,
|
|
194
|
+
config: strategyConfig ?? {},
|
|
195
|
+
resolve: ctx.resolve,
|
|
196
|
+
t,
|
|
197
|
+
})
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error(`[notifications] delivery strategy failed (${strategy.id})`, error)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
174
203
|
return
|
|
175
204
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { EntityManager } from '@mikro-orm/core'
|
|
14
14
|
import type { AwilixContainer } from 'awilix'
|
|
15
|
+
import type { EventBus } from '@open-mercato/events'
|
|
15
16
|
import {
|
|
16
17
|
WorkflowInstance,
|
|
17
18
|
WorkflowDefinition,
|
|
@@ -271,6 +272,13 @@ export async function executeTransition(
|
|
|
271
272
|
context: TransitionExecutionContext
|
|
272
273
|
): Promise<TransitionExecutionResult> {
|
|
273
274
|
try {
|
|
275
|
+
let eventBus: Pick<EventBus, 'emitEvent'> | null = null
|
|
276
|
+
try {
|
|
277
|
+
eventBus = container.resolve('eventBus') as EventBus
|
|
278
|
+
} catch {
|
|
279
|
+
eventBus = null
|
|
280
|
+
}
|
|
281
|
+
|
|
274
282
|
// First, evaluate if transition is valid
|
|
275
283
|
const evaluation = await evaluateTransition(
|
|
276
284
|
em,
|
|
@@ -294,7 +302,8 @@ export async function executeTransition(
|
|
|
294
302
|
em,
|
|
295
303
|
instance,
|
|
296
304
|
transition,
|
|
297
|
-
context
|
|
305
|
+
context,
|
|
306
|
+
eventBus
|
|
298
307
|
)
|
|
299
308
|
|
|
300
309
|
if (!preConditionsResult.allowed) {
|
|
@@ -505,7 +514,8 @@ export async function executeTransition(
|
|
|
505
514
|
em,
|
|
506
515
|
instance,
|
|
507
516
|
transition,
|
|
508
|
-
context
|
|
517
|
+
context,
|
|
518
|
+
eventBus
|
|
509
519
|
)
|
|
510
520
|
|
|
511
521
|
if (!postConditionsResult.allowed) {
|
|
@@ -659,7 +669,8 @@ async function evaluatePreConditions(
|
|
|
659
669
|
em: EntityManager,
|
|
660
670
|
instance: WorkflowInstance,
|
|
661
671
|
transition: any,
|
|
662
|
-
context: TransitionExecutionContext
|
|
672
|
+
context: TransitionExecutionContext,
|
|
673
|
+
eventBus: Pick<EventBus, 'emitEvent'> | null
|
|
663
674
|
): Promise<ruleEngine.RuleEngineResult> {
|
|
664
675
|
try {
|
|
665
676
|
// Load workflow definition to get workflow ID
|
|
@@ -698,7 +709,7 @@ async function evaluatePreConditions(
|
|
|
698
709
|
}
|
|
699
710
|
|
|
700
711
|
// Execute rules - only GUARD rules will affect the 'allowed' status
|
|
701
|
-
const result = await ruleEngine.executeRules(em, ruleContext)
|
|
712
|
+
const result = await ruleEngine.executeRules(em, ruleContext, { eventBus })
|
|
702
713
|
|
|
703
714
|
return result
|
|
704
715
|
} catch (error) {
|
|
@@ -728,7 +739,8 @@ async function evaluatePostConditions(
|
|
|
728
739
|
em: EntityManager,
|
|
729
740
|
instance: WorkflowInstance,
|
|
730
741
|
transition: any,
|
|
731
|
-
context: TransitionExecutionContext
|
|
742
|
+
context: TransitionExecutionContext,
|
|
743
|
+
eventBus: Pick<EventBus, 'emitEvent'> | null
|
|
732
744
|
): Promise<ruleEngine.RuleEngineResult> {
|
|
733
745
|
try {
|
|
734
746
|
// Load workflow definition to get workflow ID
|
|
@@ -767,7 +779,7 @@ async function evaluatePostConditions(
|
|
|
767
779
|
}
|
|
768
780
|
|
|
769
781
|
// Execute rules
|
|
770
|
-
const result = await ruleEngine.executeRules(em, ruleContext)
|
|
782
|
+
const result = await ruleEngine.executeRules(em, ruleContext, { eventBus })
|
|
771
783
|
|
|
772
784
|
return result
|
|
773
785
|
} catch (error) {
|