feat(templates): persist independent guide applications
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import "server-only";
|
||||
|
||||
import { getDb } from "@/db";
|
||||
import {
|
||||
blocks,
|
||||
characters,
|
||||
dataSources,
|
||||
dataSourceVersions,
|
||||
outboxEvents,
|
||||
pageDataSources,
|
||||
pages,
|
||||
} from "@/db/schema";
|
||||
import { evaluateDataSource } from "@/lib/content/data-source";
|
||||
import { applyTemplate, type TemplateBlueprint } from "@/lib/templates";
|
||||
|
||||
export interface NewTemplateCharacter {
|
||||
slug: string;
|
||||
nameTh: string;
|
||||
nameEn?: string | null;
|
||||
description?: string | null;
|
||||
element?: string | null;
|
||||
role?: string | null;
|
||||
rarity?: number | null;
|
||||
}
|
||||
|
||||
function collectSourceIds(value: unknown, result = new Set<string>()): Set<string> {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) collectSourceIds(item, result);
|
||||
} else if (value && typeof value === "object") {
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
if (key === "dataSourceId" && typeof item === "string") result.add(item);
|
||||
collectSourceIds(item, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function persistTemplateApplication(
|
||||
template: TemplateBlueprint,
|
||||
identity: NewTemplateCharacter,
|
||||
authorId: string,
|
||||
): Promise<{ characterId: string; characterSlug: string; firstPageSlug: string }> {
|
||||
const characterId = crypto.randomUUID();
|
||||
const applied = applyTemplate(template, characterId);
|
||||
const db = getDb();
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.insert(characters).values({
|
||||
id: characterId,
|
||||
slug: identity.slug,
|
||||
nameTh: identity.nameTh,
|
||||
nameEn: identity.nameEn?.trim() || null,
|
||||
description: identity.description?.trim() || null,
|
||||
element: identity.element?.trim() || null,
|
||||
role: identity.role?.trim() || null,
|
||||
rarity: identity.rarity ?? null,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
for (const source of applied.dataSources) {
|
||||
const snapshot = {
|
||||
id: source.id,
|
||||
name: source.name,
|
||||
version: 1,
|
||||
fields: source.fields,
|
||||
rows: source.rows,
|
||||
};
|
||||
await tx.insert(dataSources).values({
|
||||
id: source.id,
|
||||
name: source.name,
|
||||
description: source.description,
|
||||
fields: source.fields,
|
||||
currentVersion: 1,
|
||||
});
|
||||
await tx.insert(dataSourceVersions).values({
|
||||
dataSourceId: source.id,
|
||||
version: 1,
|
||||
rows: source.rows,
|
||||
evaluation: evaluateDataSource(snapshot) as unknown as Record<string, unknown>,
|
||||
createdById: authorId,
|
||||
});
|
||||
}
|
||||
|
||||
for (const page of applied.pages) {
|
||||
await tx.insert(pages).values({
|
||||
id: page.id,
|
||||
characterId,
|
||||
slug: page.slug,
|
||||
title: page.title,
|
||||
publicNote: page.publicNote,
|
||||
navOrder: page.navOrder,
|
||||
visible: false,
|
||||
version: 1,
|
||||
});
|
||||
if (page.blocks.length > 0) {
|
||||
await tx.insert(blocks).values(
|
||||
page.blocks.map((block) => ({
|
||||
...block,
|
||||
pageId: page.id,
|
||||
})),
|
||||
);
|
||||
}
|
||||
const dependencies = [
|
||||
...collectSourceIds(page.blocks.map((block) => block.config)),
|
||||
];
|
||||
if (dependencies.length > 0) {
|
||||
await tx.insert(pageDataSources).values(
|
||||
dependencies.map((dataSourceId) => ({
|
||||
pageId: page.id,
|
||||
dataSourceId,
|
||||
pinnedVersion: null,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await tx.insert(outboxEvents).values([
|
||||
{
|
||||
topic: "directory",
|
||||
aggregateId: characterId,
|
||||
eventType: "directory.updated",
|
||||
payload: { id: characterId, version: 1 },
|
||||
},
|
||||
{
|
||||
topic: "admin",
|
||||
aggregateId: characterId,
|
||||
eventType: "admin.updated",
|
||||
payload: { id: characterId, version: 1 },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
return {
|
||||
characterId,
|
||||
characterSlug: identity.slug,
|
||||
firstPageSlug: applied.pages[0].slug,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user