139 lines
3.7 KiB
TypeScript
139 lines
3.7 KiB
TypeScript
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,
|
|
};
|
|
}
|