@open-mercato/core 0.4.6-develop-6953d75a91 → 0.4.6-develop-90c3eb0e8a
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/notifications/events.js +30 -0
- package/dist/modules/notifications/events.js.map +7 -0
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +2 -3
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +2 -2
- package/dist/modules/notifications/lib/events.js +6 -1
- package/dist/modules/notifications/lib/events.js.map +2 -2
- package/dist/modules/notifications/lib/notificationMapper.js +10 -1
- package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
- package/dist/modules/notifications/lib/notificationService.js +26 -1
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/progress/events.js +6 -6
- package/dist/modules/progress/events.js.map +2 -2
- package/dist/modules/progress/lib/events.js.map +1 -1
- package/dist/modules/progress/lib/progressServiceImpl.js +38 -29
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/api/reindex.js +3 -0
- package/dist/modules/query_index/api/reindex.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +8 -10
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/query_index/subscribers/reindex.js +89 -1
- package/dist/modules/query_index/subscribers/reindex.js.map +2 -2
- package/package.json +2 -2
- package/src/modules/notifications/README.md +21 -0
- package/src/modules/notifications/events.ts +28 -0
- package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +2 -3
- package/src/modules/notifications/lib/events.ts +5 -0
- package/src/modules/notifications/lib/notificationMapper.ts +12 -1
- package/src/modules/notifications/lib/notificationService.ts +33 -1
- package/src/modules/progress/events.ts +6 -6
- package/src/modules/progress/lib/events.ts +60 -0
- package/src/modules/progress/lib/progressServiceImpl.ts +32 -22
- package/src/modules/query_index/api/reindex.ts +3 -0
- package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -10
- package/src/modules/query_index/subscribers/reindex.ts +99 -0
|
@@ -18,6 +18,15 @@ export type ProgressJobCreatedPayload = {
|
|
|
18
18
|
jobId: string
|
|
19
19
|
jobType: string
|
|
20
20
|
name: string
|
|
21
|
+
description?: string | null
|
|
22
|
+
status?: string
|
|
23
|
+
progressPercent?: number
|
|
24
|
+
processedCount?: number
|
|
25
|
+
totalCount?: number | null
|
|
26
|
+
etaSeconds?: number | null
|
|
27
|
+
cancellable?: boolean
|
|
28
|
+
startedAt?: string | null
|
|
29
|
+
finishedAt?: string | null
|
|
21
30
|
tenantId: string
|
|
22
31
|
organizationId?: string | null
|
|
23
32
|
}
|
|
@@ -25,36 +34,87 @@ export type ProgressJobCreatedPayload = {
|
|
|
25
34
|
export type ProgressJobStartedPayload = {
|
|
26
35
|
jobId: string
|
|
27
36
|
jobType: string
|
|
37
|
+
name?: string
|
|
38
|
+
description?: string | null
|
|
39
|
+
status?: string
|
|
40
|
+
progressPercent?: number
|
|
41
|
+
processedCount?: number
|
|
42
|
+
totalCount?: number | null
|
|
43
|
+
etaSeconds?: number | null
|
|
44
|
+
cancellable?: boolean
|
|
45
|
+
startedAt?: string | null
|
|
46
|
+
finishedAt?: string | null
|
|
28
47
|
tenantId: string
|
|
48
|
+
organizationId?: string | null
|
|
29
49
|
}
|
|
30
50
|
|
|
31
51
|
export type ProgressJobUpdatedPayload = {
|
|
32
52
|
jobId: string
|
|
33
53
|
jobType?: string
|
|
54
|
+
name?: string
|
|
55
|
+
description?: string | null
|
|
56
|
+
status?: string
|
|
34
57
|
progressPercent: number
|
|
35
58
|
processedCount: number
|
|
36
59
|
totalCount?: number | null
|
|
37
60
|
etaSeconds?: number | null
|
|
38
61
|
tenantId: string
|
|
62
|
+
organizationId?: string | null
|
|
63
|
+
cancellable?: boolean
|
|
64
|
+
startedAt?: string | null
|
|
65
|
+
finishedAt?: string | null
|
|
39
66
|
}
|
|
40
67
|
|
|
41
68
|
export type ProgressJobCompletedPayload = {
|
|
42
69
|
jobId: string
|
|
43
70
|
jobType: string
|
|
71
|
+
name?: string
|
|
72
|
+
description?: string | null
|
|
73
|
+
status?: string
|
|
74
|
+
progressPercent?: number
|
|
75
|
+
processedCount?: number
|
|
76
|
+
totalCount?: number | null
|
|
77
|
+
etaSeconds?: number | null
|
|
78
|
+
cancellable?: boolean
|
|
79
|
+
startedAt?: string | null
|
|
80
|
+
finishedAt?: string | null
|
|
44
81
|
resultSummary?: Record<string, unknown> | null
|
|
45
82
|
tenantId: string
|
|
83
|
+
organizationId?: string | null
|
|
46
84
|
}
|
|
47
85
|
|
|
48
86
|
export type ProgressJobFailedPayload = {
|
|
49
87
|
jobId: string
|
|
50
88
|
jobType: string
|
|
89
|
+
name?: string
|
|
90
|
+
description?: string | null
|
|
91
|
+
status?: string
|
|
92
|
+
progressPercent?: number
|
|
93
|
+
processedCount?: number
|
|
94
|
+
totalCount?: number | null
|
|
95
|
+
etaSeconds?: number | null
|
|
96
|
+
cancellable?: boolean
|
|
97
|
+
startedAt?: string | null
|
|
98
|
+
finishedAt?: string | null
|
|
51
99
|
errorMessage: string
|
|
52
100
|
tenantId: string
|
|
101
|
+
organizationId?: string | null
|
|
53
102
|
stale?: boolean
|
|
54
103
|
}
|
|
55
104
|
|
|
56
105
|
export type ProgressJobCancelledPayload = {
|
|
57
106
|
jobId: string
|
|
58
107
|
jobType: string
|
|
108
|
+
name?: string
|
|
109
|
+
description?: string | null
|
|
110
|
+
status?: string
|
|
111
|
+
progressPercent?: number
|
|
112
|
+
processedCount?: number
|
|
113
|
+
totalCount?: number | null
|
|
114
|
+
etaSeconds?: number | null
|
|
115
|
+
cancellable?: boolean
|
|
116
|
+
startedAt?: string | null
|
|
117
|
+
finishedAt?: string | null
|
|
59
118
|
tenantId: string
|
|
119
|
+
organizationId?: string | null
|
|
60
120
|
}
|
|
@@ -4,6 +4,23 @@ import type { ProgressService } from './progressService'
|
|
|
4
4
|
import { calculateEta, calculateProgressPercent, STALE_JOB_TIMEOUT_SECONDS } from './progressService'
|
|
5
5
|
import { PROGRESS_EVENTS } from './events'
|
|
6
6
|
|
|
7
|
+
function buildJobPayload(job: ProgressJob): Record<string, unknown> {
|
|
8
|
+
return {
|
|
9
|
+
jobId: job.id,
|
|
10
|
+
jobType: job.jobType,
|
|
11
|
+
name: job.name,
|
|
12
|
+
description: job.description ?? null,
|
|
13
|
+
status: job.status,
|
|
14
|
+
progressPercent: job.progressPercent,
|
|
15
|
+
processedCount: job.processedCount,
|
|
16
|
+
totalCount: job.totalCount ?? null,
|
|
17
|
+
etaSeconds: job.etaSeconds ?? null,
|
|
18
|
+
cancellable: job.cancellable,
|
|
19
|
+
startedAt: job.startedAt?.toISOString() ?? null,
|
|
20
|
+
finishedAt: job.finishedAt?.toISOString() ?? null,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
export function createProgressService(em: EntityManager, eventBus: { emit: (event: string, payload: Record<string, unknown>) => Promise<void> }): ProgressService {
|
|
8
25
|
return {
|
|
9
26
|
async createJob(input, ctx) {
|
|
@@ -26,9 +43,7 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
26
43
|
await em.persistAndFlush(job)
|
|
27
44
|
|
|
28
45
|
await eventBus.emit(PROGRESS_EVENTS.JOB_CREATED, {
|
|
29
|
-
|
|
30
|
-
jobType: job.jobType,
|
|
31
|
-
name: job.name,
|
|
46
|
+
...buildJobPayload(job),
|
|
32
47
|
tenantId: ctx.tenantId,
|
|
33
48
|
organizationId: ctx.organizationId,
|
|
34
49
|
})
|
|
@@ -46,9 +61,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
46
61
|
await em.flush()
|
|
47
62
|
|
|
48
63
|
await eventBus.emit(PROGRESS_EVENTS.JOB_STARTED, {
|
|
49
|
-
|
|
50
|
-
jobType: job.jobType,
|
|
64
|
+
...buildJobPayload(job),
|
|
51
65
|
tenantId: ctx.tenantId,
|
|
66
|
+
organizationId: job.organizationId ?? null,
|
|
52
67
|
})
|
|
53
68
|
|
|
54
69
|
return job
|
|
@@ -84,13 +99,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
84
99
|
await em.flush()
|
|
85
100
|
|
|
86
101
|
await eventBus.emit(PROGRESS_EVENTS.JOB_UPDATED, {
|
|
87
|
-
|
|
88
|
-
jobType: job.jobType,
|
|
89
|
-
progressPercent: job.progressPercent,
|
|
90
|
-
processedCount: job.processedCount,
|
|
91
|
-
totalCount: job.totalCount,
|
|
92
|
-
etaSeconds: job.etaSeconds,
|
|
102
|
+
...buildJobPayload(job),
|
|
93
103
|
tenantId: ctx.tenantId,
|
|
104
|
+
organizationId: job.organizationId ?? null,
|
|
94
105
|
})
|
|
95
106
|
|
|
96
107
|
return job
|
|
@@ -112,10 +123,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
112
123
|
await em.flush()
|
|
113
124
|
|
|
114
125
|
await eventBus.emit(PROGRESS_EVENTS.JOB_UPDATED, {
|
|
115
|
-
|
|
116
|
-
progressPercent: job.progressPercent,
|
|
117
|
-
processedCount: job.processedCount,
|
|
126
|
+
...buildJobPayload(job),
|
|
118
127
|
tenantId: ctx.tenantId,
|
|
128
|
+
organizationId: job.organizationId ?? null,
|
|
119
129
|
})
|
|
120
130
|
|
|
121
131
|
return job
|
|
@@ -136,10 +146,10 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
136
146
|
await em.flush()
|
|
137
147
|
|
|
138
148
|
await eventBus.emit(PROGRESS_EVENTS.JOB_COMPLETED, {
|
|
139
|
-
|
|
140
|
-
jobType: job.jobType,
|
|
149
|
+
...buildJobPayload(job),
|
|
141
150
|
resultSummary: job.resultSummary,
|
|
142
151
|
tenantId: ctx.tenantId,
|
|
152
|
+
organizationId: job.organizationId ?? null,
|
|
143
153
|
})
|
|
144
154
|
|
|
145
155
|
return job
|
|
@@ -157,10 +167,10 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
157
167
|
await em.flush()
|
|
158
168
|
|
|
159
169
|
await eventBus.emit(PROGRESS_EVENTS.JOB_FAILED, {
|
|
160
|
-
|
|
161
|
-
jobType: job.jobType,
|
|
170
|
+
...buildJobPayload(job),
|
|
162
171
|
errorMessage: job.errorMessage,
|
|
163
172
|
tenantId: ctx.tenantId,
|
|
173
|
+
organizationId: job.organizationId ?? null,
|
|
164
174
|
})
|
|
165
175
|
|
|
166
176
|
return job
|
|
@@ -185,9 +195,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
185
195
|
await em.flush()
|
|
186
196
|
|
|
187
197
|
await eventBus.emit(PROGRESS_EVENTS.JOB_CANCELLED, {
|
|
188
|
-
|
|
189
|
-
jobType: job.jobType,
|
|
198
|
+
...buildJobPayload(job),
|
|
190
199
|
tenantId: ctx.tenantId,
|
|
200
|
+
organizationId: job.organizationId ?? null,
|
|
191
201
|
})
|
|
192
202
|
|
|
193
203
|
return job
|
|
@@ -246,11 +256,11 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
246
256
|
job.errorMessage = `Job stale: no heartbeat for ${timeoutSeconds} seconds`
|
|
247
257
|
|
|
248
258
|
await eventBus.emit(PROGRESS_EVENTS.JOB_FAILED, {
|
|
249
|
-
|
|
250
|
-
jobType: job.jobType,
|
|
259
|
+
...buildJobPayload(job),
|
|
251
260
|
errorMessage: job.errorMessage,
|
|
252
261
|
tenantId: job.tenantId,
|
|
253
262
|
stale: true,
|
|
263
|
+
organizationId: job.organizationId ?? null,
|
|
254
264
|
})
|
|
255
265
|
}
|
|
256
266
|
|
|
@@ -71,6 +71,9 @@ export async function POST(req: Request) {
|
|
|
71
71
|
if (auth.orgId !== undefined) {
|
|
72
72
|
payload.organizationId = auth.orgId ?? null
|
|
73
73
|
}
|
|
74
|
+
if (typeof auth.sub === 'string' && auth.sub.length > 0) {
|
|
75
|
+
payload.requestedByUserId = auth.sub
|
|
76
|
+
}
|
|
74
77
|
return bus.emitEvent(
|
|
75
78
|
'query_index.reindex',
|
|
76
79
|
payload,
|
|
@@ -288,16 +288,14 @@ export default function QueryIndexesTable() {
|
|
|
288
288
|
: t('query_index.table.actions.vectorReindex')
|
|
289
289
|
const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })
|
|
290
290
|
try {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
await apiCallOrThrow(url, { method: 'DELETE' }, { errorMessage })
|
|
300
|
-
}
|
|
291
|
+
await apiCallOrThrow('/api/search/embeddings/reindex', {
|
|
292
|
+
method: 'POST',
|
|
293
|
+
headers: { 'content-type': 'application/json' },
|
|
294
|
+
body: JSON.stringify({
|
|
295
|
+
entityId,
|
|
296
|
+
purgeFirst: action === 'purge',
|
|
297
|
+
}),
|
|
298
|
+
}, { errorMessage })
|
|
301
299
|
} catch (err) {
|
|
302
300
|
console.error('query_index.table.vectorAction', err)
|
|
303
301
|
if (typeof window !== 'undefined') {
|
|
@@ -3,6 +3,7 @@ import { recordIndexerError } from '@open-mercato/shared/lib/indexers/error-log'
|
|
|
3
3
|
import { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'
|
|
4
4
|
import { reindexEntity } from '../lib/reindexer'
|
|
5
5
|
import type { VectorIndexService } from '@open-mercato/search/vector'
|
|
6
|
+
import type { ProgressService } from '@open-mercato/core/modules/progress/lib/progressService'
|
|
6
7
|
|
|
7
8
|
export const metadata = { event: 'query_index.reindex', persistent: true }
|
|
8
9
|
|
|
@@ -25,6 +26,93 @@ export default async function handle(payload: any, ctx: { resolve: <T=any>(name:
|
|
|
25
26
|
const partitionCount = Number.isFinite(payload?.partitionCount) ? Math.max(1, Math.trunc(payload.partitionCount)) : undefined
|
|
26
27
|
const partitionIndex = Number.isFinite(payload?.partitionIndex) ? Math.max(0, Math.trunc(payload.partitionIndex)) : undefined
|
|
27
28
|
const resetCoverage = typeof payload?.resetCoverage === 'boolean' ? payload.resetCoverage : undefined
|
|
29
|
+
const requestedByUserId = typeof payload?.requestedByUserId === 'string' ? payload.requestedByUserId : null
|
|
30
|
+
|
|
31
|
+
const progressTenantId = typeof tenantId === 'string' && tenantId.length > 0 ? tenantId : null
|
|
32
|
+
const progressOrganizationId = typeof organizationId === 'string' && organizationId.length > 0 ? organizationId : null
|
|
33
|
+
const progressPartitionIndex = Number.isFinite(partitionIndex) ? partitionIndex : null
|
|
34
|
+
const progressPartitionCount = Number.isFinite(partitionCount) ? partitionCount : null
|
|
35
|
+
let progressService: ProgressService | null = null
|
|
36
|
+
let progressJobId: string | null = null
|
|
37
|
+
let progressEnabled = false
|
|
38
|
+
try {
|
|
39
|
+
progressService = ctx.resolve<ProgressService>('progressService')
|
|
40
|
+
progressEnabled = progressService != null && progressTenantId != null
|
|
41
|
+
} catch {
|
|
42
|
+
progressService = null
|
|
43
|
+
progressEnabled = false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const updateProgress = async (
|
|
47
|
+
info: { processed: number; total: number },
|
|
48
|
+
options?: { complete?: boolean; failed?: boolean; errorMessage?: string },
|
|
49
|
+
): Promise<void> => {
|
|
50
|
+
if (!progressEnabled || !progressService || !progressTenantId) return
|
|
51
|
+
const progressCtx = {
|
|
52
|
+
tenantId: progressTenantId,
|
|
53
|
+
organizationId: progressOrganizationId,
|
|
54
|
+
userId: requestedByUserId,
|
|
55
|
+
}
|
|
56
|
+
const totalCount = Number.isFinite(info.total) ? Math.max(0, info.total) : 0
|
|
57
|
+
const processedCount = Number.isFinite(info.processed) ? Math.max(0, info.processed) : 0
|
|
58
|
+
const progressPercent = totalCount > 0 ? Math.min(100, Math.round((processedCount / totalCount) * 100)) : 0
|
|
59
|
+
try {
|
|
60
|
+
if (!progressJobId) {
|
|
61
|
+
const created = await progressService.createJob({
|
|
62
|
+
jobType: 'query_index.reindex',
|
|
63
|
+
name: `Query index reindex: ${entityType}`,
|
|
64
|
+
description: progressPartitionCount && progressPartitionCount > 1
|
|
65
|
+
? `Partition ${((progressPartitionIndex ?? 0) + 1).toString()} of ${progressPartitionCount.toString()}`
|
|
66
|
+
: undefined,
|
|
67
|
+
totalCount: totalCount > 0 ? totalCount : undefined,
|
|
68
|
+
cancellable: false,
|
|
69
|
+
meta: {
|
|
70
|
+
entityType,
|
|
71
|
+
partitionIndex: progressPartitionIndex,
|
|
72
|
+
partitionCount: progressPartitionCount,
|
|
73
|
+
},
|
|
74
|
+
partitionIndex: progressPartitionIndex ?? undefined,
|
|
75
|
+
partitionCount: progressPartitionCount ?? undefined,
|
|
76
|
+
}, progressCtx)
|
|
77
|
+
progressJobId = created.id
|
|
78
|
+
await progressService.startJob(progressJobId, progressCtx)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await progressService.updateProgress(
|
|
82
|
+
progressJobId,
|
|
83
|
+
{
|
|
84
|
+
processedCount,
|
|
85
|
+
totalCount: totalCount > 0 ? totalCount : undefined,
|
|
86
|
+
progressPercent,
|
|
87
|
+
},
|
|
88
|
+
progressCtx,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if (options?.complete) {
|
|
92
|
+
await progressService.completeJob(
|
|
93
|
+
progressJobId,
|
|
94
|
+
{
|
|
95
|
+
resultSummary: {
|
|
96
|
+
entityType,
|
|
97
|
+
processed: processedCount,
|
|
98
|
+
total: totalCount,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
progressCtx,
|
|
102
|
+
)
|
|
103
|
+
} else if (options?.failed) {
|
|
104
|
+
await progressService.failJob(
|
|
105
|
+
progressJobId,
|
|
106
|
+
{
|
|
107
|
+
errorMessage: options.errorMessage ?? `Reindex failed for ${entityType}`,
|
|
108
|
+
},
|
|
109
|
+
progressCtx,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// Never block query_index subscriber execution because of progress tracking.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
28
116
|
|
|
29
117
|
try {
|
|
30
118
|
await recordIndexerLog(
|
|
@@ -57,7 +145,14 @@ export default async function handle(payload: any, ctx: { resolve: <T=any>(name:
|
|
|
57
145
|
partitionIndex,
|
|
58
146
|
resetCoverage,
|
|
59
147
|
vectorService,
|
|
148
|
+
onProgress: (info) => {
|
|
149
|
+
void updateProgress(info)
|
|
150
|
+
},
|
|
60
151
|
})
|
|
152
|
+
await updateProgress(
|
|
153
|
+
{ processed: result.processed, total: result.total },
|
|
154
|
+
{ complete: true },
|
|
155
|
+
)
|
|
61
156
|
await recordIndexerLog(
|
|
62
157
|
{ em },
|
|
63
158
|
{
|
|
@@ -76,6 +171,10 @@ export default async function handle(payload: any, ctx: { resolve: <T=any>(name:
|
|
|
76
171
|
},
|
|
77
172
|
)
|
|
78
173
|
} catch (error) {
|
|
174
|
+
await updateProgress(
|
|
175
|
+
{ processed: 0, total: 0 },
|
|
176
|
+
{ failed: true, errorMessage: error instanceof Error ? error.message : String(error) },
|
|
177
|
+
)
|
|
79
178
|
await recordIndexerLog(
|
|
80
179
|
{ em },
|
|
81
180
|
{
|