UNPKG

@paroicms/server

Version:
151 lines 5.37 kB
import { parseSqliteDateTime, sqliteDateTime } from "@paroicms/internal-server-lib"; import { type } from "arktype"; import { uuidv7 } from "uuidv7"; import { ORDERED_TIERS } from "./history.types.js"; const HistoryEntryRowAT = type({ id: "string", nodeId: "number", language: "string", fieldValues: "string", documentValues: "string|null", changeSummary: "string|null", updatedAt: "string|number|Date", expireAt: "string|number|Date|null", timeTier: "string", "+": "reject", }).pipe((r) => { const updatedAt = parseSqliteDateTime(r.updatedAt); if (!updatedAt) throw new Error("updatedAt should not be null"); return { id: r.id, nodeId: String(r.nodeId), language: r.language, fieldValues: JSON.parse(r.fieldValues), documentValues: r.documentValues ?? undefined, changeSummary: r.changeSummary ? JSON.parse(r.changeSummary) : undefined, updatedAt, expireAt: parseSqliteDateTime(r.expireAt), timeTier: r.timeTier, }; }); const CountRowAT = type({ cnt: "number", "+": "reject", }); export async function getLatestHistoryEntryInTiers(siteContext, options) { const { nodeId, language, minTier, thresholdMinutes } = options; const minTierIndex = ORDERED_TIERS.indexOf(minTier); const tiersToCheck = ORDERED_TIERS.slice(minTierIndex); let query = selectHistoryEntry(siteContext) .where("h.nodeId", nodeId) .andWhere("h.language", language) .whereIn("h.timeTier", tiersToCheck); if (thresholdMinutes !== undefined) { query = query.whereRaw(`h.updatedAt > datetime(current_timestamp, '-${thresholdMinutes} minutes')`); } const row = await query.orderBy("h.id", "desc").first(); if (!row) return undefined; return HistoryEntryRowAT.assert(row); } export async function createHistoryEntry(siteContext, entry) { const { cn } = siteContext; const id = uuidv7(); const updatedAtValue = entry.updatedAt ? sqliteDateTime(entry.updatedAt) : cn.raw("current_timestamp"); const expireAtValue = entry.expireAt ? cn.raw(`datetime(current_timestamp, '${entry.expireAt}')`) : null; await cn("PaHistoryEntry").insert({ id, nodeId: entry.nodeId, language: entry.language, fieldValues: entry.fieldValues, documentValues: entry.documentValues, changeSummary: entry.changeSummary, updatedAt: updatedAtValue, expireAt: expireAtValue, timeTier: entry.timeTier, }); return id; } export async function getHistoryEntries(siteContext, options) { const { nodeId, language } = options; const rows = await selectHistoryEntry(siteContext) .where("h.nodeId", nodeId) .andWhere("h.language", language) .orderBy("h.id", "desc"); return rows.map((row) => HistoryEntryRowAT.assert(row)); } export async function getHistoryEntry(siteContext, entryId) { const row = await selectHistoryEntry(siteContext).where("h.id", entryId).first(); if (!row) return undefined; return HistoryEntryRowAT.assert(row); } export async function cleanupExpiredHistoryEntries(siteContext) { const deletedCount = await siteContext .cn("PaHistoryEntry") .whereNotNull("expireAt") .whereRaw("expireAt < datetime('now')") .delete(); return deletedCount; } export async function enforceEntryLimit(siteContext, options) { const { nodeId, language, maxEntries } = options; const countResult = await siteContext .cn("PaHistoryEntry as h") .count("* as cnt") .where("h.nodeId", nodeId) .andWhere("h.language", language) .first(); const count = countResult ? CountRowAT.assert(countResult).cnt : 0; if (count <= maxEntries) return 0; const rowsToDelete = count - maxEntries; const deletedCount = await siteContext .cn("PaHistoryEntry as h") .whereExists(function () { this.select(1) .from(siteContext .cn("PaHistoryEntry") .select("id") .where("nodeId", nodeId) .andWhere("language", language) .orderBy("id", "asc") .limit(rowsToDelete) .as("old")) .whereRaw("h.id = old.id"); }) .delete(); return deletedCount; } const LNodeUpdatedAtRowAT = type({ updatedAt: "string|number|Date", "+": "reject", }).pipe((r) => { const updatedAt = parseSqliteDateTime(r.updatedAt); if (!updatedAt) throw new Error("updatedAt should not be null"); return { updatedAt }; }); export async function getLNodeUpdatedAt(siteContext, options) { const { nodeId, language } = options; const row = await siteContext .cn("PaLNode as l") .select("l.updatedAt") .where("l.nodeId", nodeId) .andWhere("l.language", language) .first(); if (!row) return undefined; return LNodeUpdatedAtRowAT.assert(row).updatedAt; } function selectHistoryEntry(siteContext) { return siteContext .cn("PaHistoryEntry as h") .select("h.id", "h.nodeId", "h.language", "h.fieldValues", "h.documentValues", "h.changeSummary", "h.updatedAt", "h.expireAt", "h.timeTier"); } //# sourceMappingURL=history.queries.js.map