Initial commit
This commit is contained in:
151
dist/shared/store/notificationStore.js
vendored
Normal file
151
dist/shared/store/notificationStore.js
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { getPool } from "../db/pool";
|
||||
function nowIso() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
function cloneNotification(notification) {
|
||||
return {
|
||||
...notification,
|
||||
payload_json: { ...notification.payload_json }
|
||||
};
|
||||
}
|
||||
function mapNotification(row) {
|
||||
return {
|
||||
id: row.id,
|
||||
transaction_id: row.transaction_id,
|
||||
device_id: row.device_id || null,
|
||||
delivery_channel: "mqtt",
|
||||
payload_type: "payment_success",
|
||||
delivery_status: row.delivery_status,
|
||||
retry_count: row.retry_count,
|
||||
ack_status: row.ack_status,
|
||||
event_id: row.event_id,
|
||||
reason: row.reason || undefined,
|
||||
payload_json: row.payload_json || {},
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at,
|
||||
sent_at: row.sent_at || undefined,
|
||||
ack_at: row.ack_at || undefined,
|
||||
next_retry_at: row.next_retry_at || undefined
|
||||
};
|
||||
}
|
||||
export async function createNotification(payload) {
|
||||
const now = nowIso();
|
||||
const insert = await getPool().query(`INSERT INTO notifications (
|
||||
id,
|
||||
transaction_id,
|
||||
device_id,
|
||||
delivery_status,
|
||||
retry_count,
|
||||
ack_status,
|
||||
event_id,
|
||||
reason,
|
||||
payload_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
|
||||
ON CONFLICT (transaction_id, event_id) DO UPDATE
|
||||
SET updated_at = EXCLUDED.updated_at
|
||||
RETURNING *`, [
|
||||
randomUUID(),
|
||||
payload.transaction_id,
|
||||
payload.device_id,
|
||||
payload.delivery_status,
|
||||
0,
|
||||
payload.ack_status || "not_needed",
|
||||
payload.event_id,
|
||||
payload.reason || null,
|
||||
payload.payload_json || {},
|
||||
now,
|
||||
now
|
||||
]);
|
||||
if (insert.rowCount && insert.rowCount > 0) {
|
||||
return mapNotification(insert.rows[0]);
|
||||
}
|
||||
const { rows } = await getPool().query("SELECT * FROM notifications WHERE transaction_id = $1 AND event_id = $2", [payload.transaction_id, payload.event_id]);
|
||||
return mapNotification(rows[0]);
|
||||
}
|
||||
export async function getNotificationById(notificationId) {
|
||||
const { rows } = await getPool().query("SELECT * FROM notifications WHERE id = $1", [notificationId]);
|
||||
return rows[0] ? cloneNotification(mapNotification(rows[0])) : null;
|
||||
}
|
||||
export async function updateNotification(notificationId, patch) {
|
||||
const existing = await getNotificationById(notificationId);
|
||||
if (!existing) {
|
||||
throw new Error("NOTIFICATION_NOT_FOUND");
|
||||
}
|
||||
const next = {
|
||||
...existing,
|
||||
...patch,
|
||||
id: existing.id,
|
||||
transaction_id: existing.transaction_id,
|
||||
device_id: existing.device_id,
|
||||
delivery_channel: existing.delivery_channel,
|
||||
payload_type: existing.payload_type,
|
||||
event_id: existing.event_id,
|
||||
payload_json: existing.payload_json,
|
||||
created_at: existing.created_at,
|
||||
updated_at: nowIso()
|
||||
};
|
||||
const { rows } = await getPool().query(`UPDATE notifications
|
||||
SET delivery_status = $2,
|
||||
retry_count = $3,
|
||||
ack_status = $4,
|
||||
device_id = COALESCE($5, device_id),
|
||||
reason = $6,
|
||||
sent_at = $7,
|
||||
ack_at = $8,
|
||||
next_retry_at = $9,
|
||||
updated_at = $10
|
||||
WHERE id = $1
|
||||
RETURNING *`, [
|
||||
notificationId,
|
||||
next.delivery_status,
|
||||
next.retry_count,
|
||||
next.ack_status,
|
||||
next.device_id ?? null,
|
||||
next.reason || null,
|
||||
next.sent_at || null,
|
||||
next.ack_at || null,
|
||||
next.next_retry_at || null,
|
||||
next.updated_at
|
||||
]);
|
||||
return cloneNotification(mapNotification(rows[0]));
|
||||
}
|
||||
export async function getNotificationByTransactionId(transactionId) {
|
||||
const { rows } = await getPool().query(`SELECT * FROM notifications
|
||||
WHERE transaction_id = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1`, [transactionId]);
|
||||
return rows[0] ? mapNotification(rows[0]) : null;
|
||||
}
|
||||
export async function getNotificationByTransactionAndEvent(transactionId, eventId) {
|
||||
const { rows } = await getPool().query("SELECT * FROM notifications WHERE transaction_id = $1 AND event_id = $2", [transactionId, eventId]);
|
||||
return rows[0] ? mapNotification(rows[0]) : null;
|
||||
}
|
||||
export async function listNotificationsByDevice(deviceId) {
|
||||
const { rows } = await getPool().query("SELECT * FROM notifications WHERE device_id = $1 ORDER BY created_at DESC", [deviceId]);
|
||||
return rows.map(mapNotification);
|
||||
}
|
||||
export async function listNotifications(filter) {
|
||||
const filters = [];
|
||||
const params = [];
|
||||
if (filter?.transaction_id) {
|
||||
params.push(filter.transaction_id);
|
||||
filters.push(`transaction_id = $${params.length}`);
|
||||
}
|
||||
if (filter?.device_id) {
|
||||
params.push(filter.device_id);
|
||||
filters.push(`device_id = $${params.length}`);
|
||||
}
|
||||
if (filter?.delivery_status) {
|
||||
params.push(filter.delivery_status);
|
||||
filters.push(`delivery_status = $${params.length}`);
|
||||
}
|
||||
const where = filters.length ? `WHERE ${filters.join(" AND ")}` : "";
|
||||
const { rows } = await getPool().query(`SELECT * FROM notifications ${where} ORDER BY created_at DESC`, params);
|
||||
return rows.map(mapNotification);
|
||||
}
|
||||
export function toNotificationPayload(notification) {
|
||||
return cloneNotification(notification);
|
||||
}
|
||||
Reference in New Issue
Block a user