Files
echo/src/services/notificationDebugService.js

177 lines
4.7 KiB
JavaScript

import { v4 as uuidv4 } from 'uuid'
import { getDb, saveDbToStore } from '../lib/sqlite.js'
export const NOTIFICATION_DEBUG_STATUS = {
review: 'review',
unmatched: 'unmatched',
matched: 'matched',
ignored: 'ignored',
error: 'error',
}
const mapRow = (row) => ({
id: row.id,
sourceId: row.source_id || '',
channel: row.channel || '',
title: row.title || '',
text: row.text || '',
postedAt: row.posted_at || '',
status: row.status || NOTIFICATION_DEBUG_STATUS.unmatched,
ruleId: row.rule_id || '',
transactionId: row.transaction_id || '',
errorMessage: row.error_message || '',
})
const dedupeRecords = (rows = []) => {
const bucket = new Map()
rows.forEach((row) => {
const item = mapRow(row)
const key = item.sourceId || item.id
if (!bucket.has(key)) {
bucket.set(key, item)
return
}
const existing = bucket.get(key)
const existingScore = Number(Boolean(existing.transactionId)) + Number(existing.status === NOTIFICATION_DEBUG_STATUS.matched) + Number(Boolean(existing.ruleId))
const nextScore = Number(Boolean(item.transactionId)) + Number(item.status === NOTIFICATION_DEBUG_STATUS.matched) + Number(Boolean(item.ruleId))
if (nextScore > existingScore) {
bucket.set(key, item)
}
})
return Array.from(bucket.values())
}
const getLatestBySourceId = async (sourceId) => {
if (!sourceId) return null
const db = await getDb()
const result = await db.query(
`
SELECT *
FROM notifications_raw
WHERE source_id = ?
ORDER BY posted_at DESC
LIMIT 1
`,
[sourceId],
)
return result?.values?.[0] ? mapRow(result.values[0]) : null
}
export const ensureNotificationRaw = async (notification, options = {}) => {
const sourceId = notification?.id || notification?.sourceId || ''
const existing = await getLatestBySourceId(sourceId)
if (existing) return existing
const db = await getDb()
const id = notification?.rawId || uuidv4()
await db.run(
`
INSERT INTO notifications_raw (
id,
source_id,
channel,
title,
text,
posted_at,
status,
rule_id,
transaction_id,
error_message
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`,
[
id,
sourceId || null,
notification?.channel || null,
notification?.title || null,
notification?.text || '',
notification?.createdAt || notification?.postedAt || new Date().toISOString(),
options.status || NOTIFICATION_DEBUG_STATUS.unmatched,
options.ruleId || null,
options.transactionId || null,
options.errorMessage || null,
],
)
await saveDbToStore()
return {
id,
sourceId,
channel: notification?.channel || '',
title: notification?.title || '',
text: notification?.text || '',
postedAt: notification?.createdAt || notification?.postedAt || '',
status: options.status || NOTIFICATION_DEBUG_STATUS.unmatched,
ruleId: options.ruleId || '',
transactionId: options.transactionId || '',
errorMessage: options.errorMessage || '',
}
}
export const updateNotificationRawStatus = async (sourceId, next = {}) => {
if (!sourceId) return null
const existing = await getLatestBySourceId(sourceId)
if (!existing) return null
const db = await getDb()
await db.run(
`
UPDATE notifications_raw
SET status = ?, rule_id = ?, transaction_id = ?, error_message = ?
WHERE id = ?
`,
[
next.status || existing.status,
next.ruleId ?? existing.ruleId ?? null,
next.transactionId ?? existing.transactionId ?? null,
next.errorMessage ?? existing.errorMessage ?? null,
existing.id,
],
)
await saveDbToStore()
return {
...existing,
status: next.status || existing.status,
ruleId: next.ruleId ?? existing.ruleId,
transactionId: next.transactionId ?? existing.transactionId,
errorMessage: next.errorMessage ?? existing.errorMessage,
}
}
export const fetchNotificationDebugRecords = async (options = {}) => {
const db = await getDb()
const params = []
const where = []
if (options.status && options.status !== 'all') {
where.push('status = ?')
params.push(options.status)
}
if (options.keyword) {
where.push('(channel LIKE ? OR title LIKE ? OR text LIKE ?)')
const keyword = `%${String(options.keyword).trim()}%`
params.push(keyword, keyword, keyword)
}
const whereClause = where.length ? `WHERE ${where.join(' AND ')}` : ''
const limit = Math.max(1, Math.min(Number(options.limit) || 80, 200))
params.push(limit)
const result = await db.query(
`
SELECT *
FROM notifications_raw
${whereClause}
ORDER BY posted_at DESC
LIMIT ?
`,
params,
)
return dedupeRecords(result?.values || [])
}