@paroicms/server
Version:
The ParoiCMS server
151 lines • 5.37 kB
JavaScript
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