Files
gunshiz 249af29e9b
CI / Verify (push) Successful in 1m21s
CI / Build immutable images and deploy (push) Successful in 4m32s
feat : remove legacy feature
2026-09-10 19:45:06 +07:00

260 lines
8.3 KiB
TypeScript

"use server";
import { updateTag } from "next/cache";
import * as z from "zod";
import { GLOSSARY_CACHE_TAG, pageCacheTag } from "@/lib/cache/tags";
import {
createGlossaryAlias,
deleteGlossaryAlias,
} from "@/lib/glossary-repository";
import { requireAdmin } from "@/lib/auth/server";
import {
createExtraSection,
createGuide,
deleteExtraSection,
GuideVersionConflictError,
permanentlyDeleteGuide,
reorderExtraSections,
restoreGuide,
saveArtifacts,
saveConstellations,
saveExtraSection,
saveExtraSections,
saveTeams,
setGuideVisibility,
saveWeapons,
trashGuide,
updateGuideBasics,
} from "@/lib/guides/mutations";
import {
cancelCatalogSyncJob,
getCatalogSyncJob,
startCatalogSyncJob,
} from "@/lib/catalog/sync";
import {
getAdminTeamImportGuide,
listAdminTeamImportGuides,
} from "@/lib/guides/queries";
type GuideActionResult =
| { status: "saved"; version?: number; location?: string }
| { status: "conflict"; currentVersion: number; message: string }
| { status: "error"; message: string };
type CreateGuideActionResult = Exclude<GuideActionResult, { status: "conflict" }>;
async function runGuideAction(action: () => Promise<{ version?: number }>): Promise<GuideActionResult> {
try {
const result = await action();
return { status: "saved", version: result.version };
} catch (cause) {
if (cause instanceof GuideVersionConflictError) {
return { status: "conflict", currentVersion: cause.currentVersion, message: cause.message };
}
if (cause instanceof z.ZodError) {
return { status: "error", message: cause.issues.map((issue) => issue.message).join(" • ") };
}
return { status: "error", message: cause instanceof Error ? cause.message : "บันทึกไม่สำเร็จ" };
}
}
export async function createStructuredGuide(input: unknown): Promise<CreateGuideActionResult> {
await requireAdmin();
try {
const guide = await createGuide(input);
return { status: "saved", version: guide.version, location: `/admin/${guide.characterKey}` };
} catch (cause) {
return { status: "error", message: cause instanceof Error ? cause.message : "สร้าง Guide ไม่สำเร็จ" };
}
}
export async function saveWeaponSection(guideId: string, input: unknown) {
await requireAdmin();
return runGuideAction(() => saveWeapons(guideId, input));
}
export async function saveGuideOverview(guideId: string, input: unknown) {
await requireAdmin();
return runGuideAction(() => updateGuideBasics(guideId, input));
}
export async function saveArtifactSection(guideId: string, input: unknown) {
await requireAdmin();
return runGuideAction(() => saveArtifacts(guideId, input));
}
export async function saveConstellationSection(guideId: string, input: unknown) {
await requireAdmin();
return runGuideAction(() => saveConstellations(guideId, input));
}
export async function saveTeamSection(guideId: string, input: unknown) {
await requireAdmin();
return runGuideAction(() => saveTeams(guideId, input));
}
export async function listTeamImportGuides(currentGuideId: string) {
await requireAdmin();
const parsed = z.string().uuid().safeParse(currentGuideId);
if (!parsed.success) return { status: "error" as const, message: "Guide ID ไม่ถูกต้อง" };
try {
return { status: "success" as const, guides: await listAdminTeamImportGuides(parsed.data) };
} catch {
return { status: "error" as const, message: "โหลดรายการ Guide ไม่สำเร็จ" };
}
}
export async function loadTeamImportGuide(guideId: string) {
await requireAdmin();
const parsed = z.string().uuid().safeParse(guideId);
if (!parsed.success) return { status: "error" as const, message: "Guide ID ไม่ถูกต้อง" };
try {
const guide = await getAdminTeamImportGuide(parsed.data);
return guide
? { status: "success" as const, guide }
: { status: "error" as const, message: "ไม่พบ Guide นี้" };
} catch {
return { status: "error" as const, message: "โหลด Team จาก Guide ไม่สำเร็จ" };
}
}
export async function saveExtraSectionList(guideId: string, input: unknown) {
await requireAdmin();
const result = await runGuideAction(() => saveExtraSections(guideId, input));
if (result.status === "saved") updateTag(pageCacheTag(guideId));
return result;
}
export async function createGuideExtra(guideId: string, input: unknown) {
await requireAdmin();
try {
const section = await createExtraSection(z.string().uuid().parse(guideId), input);
updateTag(pageCacheTag(guideId));
return {
status: "saved" as const,
version: section.version,
section: { id: section.id, slug: section.slug, title: section.title },
};
} catch (cause) {
return {
status: "error" as const,
message: cause instanceof z.ZodError
? cause.issues.map((issue) => issue.message).join(" • ")
: cause instanceof Error
? cause.message
: "สร้าง Extra section ไม่สำเร็จ",
};
}
}
export async function saveGuideExtra(
guideId: string,
sectionId: string,
input: unknown,
) {
await requireAdmin();
const parsedGuideId = z.string().uuid().parse(guideId);
const parsedSectionId = z.string().uuid().parse(sectionId);
const result = await runGuideAction(() =>
saveExtraSection(parsedGuideId, parsedSectionId, input),
);
if (result.status === "saved") updateTag(pageCacheTag(guideId));
return result;
}
export async function deleteGuideExtra(guideId: string, sectionId: string) {
await requireAdmin();
const result = await runGuideAction(() =>
deleteExtraSection(
z.string().uuid().parse(guideId),
z.string().uuid().parse(sectionId),
),
);
if (result.status === "saved") updateTag(pageCacheTag(guideId));
return result;
}
export async function reorderGuideExtras(guideId: string, input: unknown) {
await requireAdmin();
const result = await runGuideAction(() =>
reorderExtraSections(z.string().uuid().parse(guideId), input),
);
if (result.status === "saved") updateTag(pageCacheTag(guideId));
return result;
}
export async function moveGuideToTrash(guideId: string) {
await requireAdmin();
return runGuideAction(() => trashGuide(guideId));
}
export async function restoreStructuredGuide(guideId: string) {
await requireAdmin();
return runGuideAction(() => restoreGuide(guideId));
}
export async function setStructuredGuideVisibility(
guideId: string,
isPublic: boolean,
) {
await requireAdmin();
return runGuideAction(() => setGuideVisibility(guideId, isPublic));
}
export async function deleteStructuredGuide(guideId: string) {
await requireAdmin();
return runGuideAction(() => permanentlyDeleteGuide(guideId));
}
export async function startCatalogSync() {
await requireAdmin();
try {
return { status: "started" as const, job: await startCatalogSyncJob() };
} catch (cause) {
return { status: "error" as const, message: cause instanceof Error ? cause.message : "เริ่ม Sync ไม่สำเร็จ" };
}
}
export async function getCatalogSyncProgress(jobId: string) {
await requireAdmin();
return getCatalogSyncJob(jobId);
}
export async function cancelCatalogSync(jobId: string) {
await requireAdmin();
return cancelCatalogSyncJob(jobId);
}
export async function addGlossaryAlias(input: unknown) {
await requireAdmin();
try {
const alias = await createGlossaryAlias(input);
updateTag(GLOSSARY_CACHE_TAG);
return { status: "saved" as const, alias };
} catch (cause) {
const message = cause instanceof Error && cause.message.includes("unique")
? "Shortcut นี้ถูกใช้งานแล้ว"
: cause instanceof z.ZodError
? cause.issues.map((issue) => issue.message).join(" • ")
: cause instanceof Error
? cause.message
: "เพิ่ม Shortcut ไม่สำเร็จ";
return { status: "error" as const, message };
}
}
export async function removeGlossaryAlias(id: string) {
await requireAdmin();
try {
await deleteGlossaryAlias(z.string().uuid().parse(id));
updateTag(GLOSSARY_CACHE_TAG);
return { status: "saved" as const };
} catch (cause) {
return {
status: "error" as const,
message: cause instanceof Error ? cause.message : "ลบ Shortcut ไม่สำเร็จ",
};
}
}