@paroicms/server
Version:
The ParoiCMS server
180 lines • 6.71 kB
JavaScript
import { getNodeTypeByName } from "@paroicms/internal-anywhere-lib";
import { parseSqliteDateTime, sqliteDateTime } from "@paroicms/internal-server-lib";
import { ApiError } from "@paroicms/public-server-lib";
import { type } from "arktype";
import { depKeysOfNodeRelationCache, invalidateMultipleDocumentsInCache, invalidateNodeInCache, makeCacheDependencyKey, makeDependencyKeysForDocuments, } from "../../common/text-cache.js";
import { getDocumentNodeIdOfPart } from "../part/part.queries.js";
const TypeNameRowAT = type({
typeName: "string",
"+": "reject",
});
export async function getTypeNameOf(siteContext, nodeId) {
const row = await siteContext
.cn("PaNode as n")
.select("n.typeName")
.where("n.id", nodeId)
.first();
if (!row)
throw new ApiError(`cannot find node '${nodeId}'`, 404);
const validated = TypeNameRowAT.assert(row);
return validated.typeName;
}
export async function getDocumentSchemaOf(siteContext, nodeId) {
const typeName = await getTypeNameOf(siteContext, nodeId);
const nodeType = getNodeTypeByName(siteContext.siteSchema, typeName);
if (nodeType.kind !== "document")
throw new Error(`unknown document type '${typeName}'`);
return nodeType;
}
const NodeSeedAT = type({
id: "number",
parentId: "number|null",
typeName: "string",
relativeId: "string",
publishDate: "Date|string|number|null",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
parentId: r.parentId === null ? undefined : String(r.parentId),
typeName: r.typeName,
relativeId: r.relativeId,
publishDate: parseSqliteDateTime(r.publishDate),
}));
export async function findOneNode({ cn }, id) {
const node = await cn("PaNode as n")
.select("n.id", "n.parentId", "n.typeName", "n.relativeId", "n.publishDate")
.where("n.id", id)
.first();
if (!node)
throw new ApiError(`can't find node ${id}`, 404);
return NodeSeedAT.assert(node);
}
const LanguageRowAT = type({
language: "string",
"+": "reject",
});
export async function getLanguagesOfNode({ cn, siteSchema }, nodeId) {
const rows = await cn("PaLNode as l")
.select("l.language")
.where("l.nodeId", nodeId)
.whereIn("l.language", siteSchema.languages);
return rows.map((row) => LanguageRowAT.assert(row).language);
}
const SetNodePublishDateRowAT = type({
typeName: "string",
publishDate: "string|null",
"+": "reject",
}).pipe((data) => ({
typeName: data.typeName,
publishDate: parseSqliteDateTime(data.publishDate),
}));
export async function setNodePublishDate(siteContext, { nodeId, publishDate }) {
const { cn, siteSchema } = siteContext;
const row = await siteContext
.cn("PaNode as n")
.select("n.typeName", "n.publishDate")
.where("n.id", nodeId)
.first();
if (!row)
throw new ApiError(`cannot find node '${nodeId}'`, 404);
const validated = SetNodePublishDateRowAT.assert(row);
const typeName = validated.typeName;
const prevPublishDate = validated.publishDate;
const nodeType = getNodeTypeByName(siteSchema, typeName);
await cn("PaNode")
.where("id", nodeId)
.update({ publishDate: publishDate === null ? null : sqliteDateTime(publishDate) });
if (nodeType.kind === "document") {
await invalidateOrScheduleDocument(siteContext, {
nodeId,
publishDate: publishDate ?? undefined,
prevPublishDate,
});
}
else {
await invalidateOrSchedulePart(siteContext, {
nodeId,
publishDate: publishDate ?? undefined,
prevPublishDate,
});
}
}
async function invalidateOrScheduleDocument(siteContext, options) {
const { nodeId, publishDate, prevPublishDate } = options;
const now = Date.now();
const wasPublished = prevPublishDate !== undefined && prevPublishDate.getTime() <= now;
const eventKey = makeCacheDependencyKey({ nodeId });
if (publishDate && publishDate.getTime() <= now) {
if (wasPublished) {
await invalidateNodeInCache(siteContext, { nodeId });
}
else {
const newDocDepKeys = await depKeysOfNodeRelationCache(siteContext, {
parentOfNodeId: nodeId,
relation: "children",
});
await siteContext.textCache.invalidate(newDocDepKeys);
await siteContext.textCache.cancelScheduledInvalidation(eventKey);
}
}
else {
if (wasPublished) {
await invalidateMultipleDocumentsInCache(siteContext, { nodeId, fully: true });
}
if (publishDate) {
const newDocDepKeys = await depKeysOfNodeRelationCache(siteContext, {
parentOfNodeId: nodeId,
relation: "children",
});
await siteContext.textCache.scheduleInvalidation(eventKey, newDocDepKeys, publishDate);
}
}
}
async function invalidateOrSchedulePart(siteContext, options) {
const { nodeId, publishDate } = options;
const documentNodeId = await getDocumentNodeIdOfPart(siteContext, nodeId);
const eventKey = makeCacheDependencyKey({ nodeId: documentNodeId, relation: "updated" });
if (publishDate && publishDate.getTime() <= Date.now()) {
await invalidateMultipleDocumentsInCache(siteContext, {
nodeId: documentNodeId,
});
}
else if (publishDate) {
const depKeys = makeDependencyKeysForDocuments(siteContext, documentNodeId);
await siteContext.textCache.scheduleInvalidation(eventKey, depKeys, publishDate);
}
}
const NodeChildrenCountRowAT = type({
cnt: "number",
"+": "reject",
});
export async function countNodeChildrenOf(siteContext, parendId) {
const result = await siteContext
.cn("PaNode as n")
.count("* as cnt")
.where("n.parentId", parendId)
.first();
return NodeChildrenCountRowAT.assert(result).cnt;
}
const ScheduledNodeRowAT = type({
id: "number",
publishDate: "Date|string|number",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
publishDate: parseSqliteDateTime(r.publishDate),
}));
export async function getScheduledNodes(siteContext) {
const rows = await siteContext
.cn("PaNode as n")
.select("n.id", "n.publishDate")
.whereRaw("n.publishDate > current_timestamp");
return rows.map((row) => {
const validated = ScheduledNodeRowAT.assert(row);
return {
nodeId: validated.id,
publishDate: validated.publishDate.toISOString(),
};
});
}
//# sourceMappingURL=node.queries.js.map