UNPKG

@paroicms/server

Version:
265 lines 11.1 kB
import { getDocumentTypeByName, getNodeTypeByName, isRootOfRoutingCluster, isRootOfSubRoutingCluster, } from "@paroicms/internal-anywhere-lib"; import { ApiError } from "@paroicms/public-server-lib"; import { type } from "arktype"; import { getHandleOfFeaturedImage } from "../../common/media-handles.helpers.js"; import { invalidateDocumentInCache, invalidateDocumentRelationCache, invalidateMultipleDocumentsInCache, } from "../../common/text-cache.js"; import { createNode } from "../../connector/db-init/init-node-queries.js"; import { loadRoutingCluster } from "../../connector/db-init/load-routing-cluster.queries.js"; import { autoCreateCluster } from "../../connector/db-init/populate-routing-documents.js"; import { reloadHomeRoutingCluster } from "../../site-context/refresh-site-context.js"; import { countNodeChildrenOf, getLanguagesOfNode, getTypeNameOf } from "../node/node.queries.js"; import { createRoutingDocumentsInLanguageForCluster } from "../routing-cluster/cluster-management.js"; import { getParentDocumentIdsOf, getParentLNodeIdOf, getParentNodeIdOf, } from "./load-documents.queries.js"; export class CreateDocumentValues { slug; title; } export async function updateDocument(siteContext, documentId, values) { const { cn } = siteContext; await cn("PaDocument") .where({ nodeId: documentId.nodeId, language: documentId.language, }) .update(values); await invalidateDocumentInCache(siteContext, { documentId, perimeter: "slug" in values ? "slug" : undefined, }); } export async function createDocumentOnNode(siteContext, { language, nodeId, values, forceReady, }) { const { cn, siteSchema } = siteContext; const typeName = await getTypeNameOf({ cn: siteContext.cn }, nodeId); const documentType = getDocumentTypeByName(siteSchema, typeName); const autoPublish = documentType.documentKind === "regular" && documentType.autoPublish; const ready = forceReady ?? autoPublish; if (typeName !== "home") { const parentNodeId = await getParentNodeIdOf(siteContext, nodeId); if (parentNodeId === undefined) throw new Error(`missing parent for "${nodeId}"`); const acceptedLanguages = await getLanguagesOfNode(siteContext, parentNodeId); if (!acceptedLanguages.includes(language)) { throw new ApiError(`language "${language}" is not available on parent`, 400); } } const rootOfCluster = isRootOfRoutingCluster(documentType) ? await loadRoutingCluster(siteContext, { kind: typeName === "home" ? "home" : "sub", nodeId, typeName, }) : undefined; const documentId = await cn.transaction(async (tx) => { const result = await createDocumentWithManager({ language, nodeId, cn: tx, values, ready, }); if (rootOfCluster) { await createRoutingDocumentsInLanguageForCluster(siteContext, tx, rootOfCluster, language); } return result; }); if (ready) { const parentId = await getParentLNodeIdOf(siteContext, documentId); if (parentId) { await invalidateDocumentRelationCache(siteContext, { documentId: parentId, relation: "children", }); } } if (rootOfCluster && documentType.typeName === "home") { await reloadHomeRoutingCluster(siteContext.fqdn); } return documentId; } export async function createNodeWithDocument(siteContext, { parentId, values, forceReady, }) { const { siteSchema } = siteContext; const { language, nodeId: parentNodeId } = parentId; const documentType = getDocumentTypeByName(siteSchema, values.typeName); const autoPublish = documentType.documentKind === "regular" && documentType.autoPublish; const ready = forceReady ?? autoPublish; const parentTypeName = await getTypeNameOf(siteContext, parentNodeId); const parentType = getNodeTypeByName(siteContext.siteSchema, parentTypeName); if (parentType.kind === "document" && parentType.childLimit !== undefined) { const count = await countNodeChildrenOf(siteContext, parentNodeId); if (count >= parentType.childLimit) throw new ApiError("maximum children reached", 400); } if (parentType.kind !== "site") { const acceptedLanguages = await getLanguagesOfNode(siteContext, parentNodeId); if (!acceptedLanguages.includes(language)) { throw new ApiError(`language "${language}" is not available on parent`, 400); } } const documentId = await siteContext.cn.transaction(async (tx) => { const { id: nodeId } = await createNode(siteContext.siteSchema, tx, { parentId: parentNodeId, typeName: values.typeName, }); const newId = await createDocumentWithManager({ language, nodeId, values, cn: tx, ready, }); return newId; }); if (ready) { await invalidateDocumentRelationCache(siteContext, { documentId: parentId, relation: "children", }); } if (isRootOfSubRoutingCluster(documentType)) { await autoCreateCluster(siteContext, { nodeId: documentId.nodeId, documentType, }); } return documentId; } const DeleteDocumentNodeRowAT = type({ id: "number", "+": "reject", }).pipe((r) => ({ id: String(r.id), })); export async function deleteNodeAndDocuments(siteContext, nodeId) { const { cn, mediaStorage } = siteContext; const parentDocumentIds = await getParentDocumentIdsOf(siteContext, { nodeId }); const { partNodeIds } = await cn.transaction(async (tx) => { const nodeRows = await tx("PaNode as l") .select("l.id as id") .whereRaw("l.parentId = ?", [nodeId]) .whereNotExists(function () { this.select(1).from("PaDocument as d").where("d.nodeId", "l.id"); }); const partNodeIds = nodeRows.map((r) => DeleteDocumentNodeRowAT.assert(r).id); await fullDeleteParts(partNodeIds, tx); await tx("PaFieldVarchar").where("nodeId", nodeId).delete(); await tx("PaFieldText").where("nodeId", nodeId).delete(); await tx("PaDocument").where("nodeId", nodeId).delete(); await tx("PaLNode").where("nodeId", nodeId).delete(); await tx("PaNode").where("id", nodeId).delete(); return { partNodeIds }; }); await mediaStorage.deleteMedias({ handles: [...partNodeIds.map(getHandleOfFeaturedImage), getHandleOfFeaturedImage(nodeId)], }); await invalidateMultipleDocumentsInCache(siteContext, { nodeId, parentIds: parentDocumentIds, fully: true, }); } const DeleteDocumentCountRowAT = type({ cnt: "number", "+": "reject", }); export async function deleteDocument(siteContext, documentId) { const { cn, mediaStorage } = siteContext; const { nodeId, language } = documentId; const parentId = await getParentLNodeIdOf(siteContext, documentId); const countRow = await cn("PaLNode as s") .count("s.nodeId as cnt") .where("s.nodeId", nodeId) .andWhereNot("language", documentId.language) .first(); const count = countRow ? DeleteDocumentCountRowAT.assert(countRow).cnt : 0; const shouldDeleteNode = count === 0; const { partNodeIds } = await cn.transaction(async (tx) => { const nodeRows = await tx("PaNode as l") .select("l.id as id") .whereRaw("l.parentId = ?", [nodeId]) .whereNotExists(function () { this.select(1).from("PaDocument as d").where("d.nodeId", "l.id"); }); const partNodeIds = nodeRows.map((r) => DeleteDocumentNodeRowAT.assert(r).id); await deleteParts({ language, nodeIdList: partNodeIds, cn: tx, }); await tx("PaFieldVarchar") .where("nodeId", documentId.nodeId) .andWhere("language", documentId.language) .delete(); await tx("PaFieldText") .where("nodeId", documentId.nodeId) .andWhere("language", documentId.language) .delete(); await tx("PaDocument") .where("nodeId", documentId.nodeId) .andWhere("language", documentId.language) .delete(); await tx("PaLNode") .where("nodeId", documentId.nodeId) .andWhere("language", documentId.language) .delete(); if (shouldDeleteNode) { await tx("PaFieldVarchar").where("nodeId", nodeId).delete(); await tx("PaFieldText").where("nodeId", nodeId).delete(); await tx("PaFieldLabeling").where("nodeId", nodeId).orWhere("termId", nodeId).delete(); await tx("PaPartNode").where("documentNodeId", nodeId).delete(); await tx("PaNode").where("id", nodeId).delete(); } return { partNodeIds }; }); await invalidateDocumentInCache(siteContext, { documentId, fully: true, parentId }); if (shouldDeleteNode) { const handles = partNodeIds.map(getHandleOfFeaturedImage); handles.push(getHandleOfFeaturedImage(nodeId)); const deletedIds = await mediaStorage.deleteMedias({ handles }); await siteContext.imageCache.invalidate({ mediaIds: deletedIds }); } } const DeletePartCountRowAT = type({ cnt: "number", "+": "reject", }); async function deleteParts({ nodeIdList, cn, language, }) { if (nodeIdList.length === 0) return; await cn("PaLNode").whereIn("nodeId", nodeIdList).andWhere("language", language).delete(); for (const nodeId of nodeIdList) { const countRow = await cn("PaLNode as nl") .count("nl.nodeId as cnt") .where("nl.nodeId", nodeId) .first(); if (!countRow) continue; if (DeletePartCountRowAT.assert(countRow).cnt === 0) { await cn("PaFieldVarchar").where("nodeId", nodeId).delete(); await cn("PaFieldText").where("nodeId", nodeId).delete(); await cn("PaNode").where("id", nodeId).delete(); } } } async function fullDeleteParts(nodeIdList, cn) { if (nodeIdList.length === 0) return; await cn("PaFieldText").whereIn("nodeId", nodeIdList).delete(); await cn("PaFieldVarchar").whereIn("nodeId", nodeIdList).delete(); await cn("PaLNode").whereIn("nodeId", nodeIdList).delete(); await cn("PaNode").whereIn("id", nodeIdList).delete(); } async function createDocumentWithManager({ language, nodeId, values, ready, cn, }) { await cn("PaLNode").insert({ language, nodeId, ready: !!ready, }); await cn("PaDocument").insert({ language, nodeId, slug: values?.slug, title: values?.title, }); return { language, nodeId }; } //# sourceMappingURL=save-documents.queries.js.map