feat(guides) : add safer publishing and discovery
CI / Verify (push) Successful in 1m35s
CI / Build immutable images and deploy (push) Successful in 5m27s

This commit is contained in:
2026-09-19 13:32:57 +07:00 Unverified
parent 6698dc1302
commit 9896e8a4ad
27 changed files with 3213 additions and 108 deletions
+36 -10
View File
@@ -9,6 +9,10 @@ import {
deleteGlossaryAlias,
} from "@/lib/glossary-repository";
import { requireAdmin } from "@/lib/auth/server";
import { getCurrentCatalogVersion } from "@/lib/catalog/sync";
import { publicGuideSections } from "@/lib/guides/public-sections";
import { publicationIssues } from "@/lib/guides/publication";
import { getAdminGuideContentById } from "@/lib/guides/queries";
import {
createExtraSection,
createGuide,
@@ -20,7 +24,6 @@ import {
saveArtifacts,
saveConstellations,
saveExtraSection,
saveExtraSections,
saveTeams,
setGuideVisibility,
saveWeapons,
@@ -126,13 +129,6 @@ export async function loadTeamImportGuide(guideId: string) {
}
}
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 {
@@ -204,10 +200,40 @@ export async function restoreStructuredGuide(guideId: string) {
export async function setStructuredGuideVisibility(
guideId: string,
isPublic: boolean,
input: unknown,
) {
await requireAdmin();
return runGuideAction(() => setGuideVisibility(guideId, isPublic));
const parsed = z.strictObject({
isPublic: z.boolean(),
expectedVersion: z.number().int().positive(),
publicationNote: z.string().trim().max(500).optional(),
}).safeParse(input);
if (!parsed.success) return { status: "error" as const, message: "ข้อมูลการเผยแพร่ไม่ถูกต้อง" };
if (!parsed.data.isPublic) {
return runGuideAction(() => setGuideVisibility(guideId, parsed.data));
}
const [guide, catalogVersion] = await Promise.all([
getAdminGuideContentById(guideId),
getCurrentCatalogVersion(),
]);
const issues = publicationIssues({
exists: Boolean(guide),
overview: guide?.overview ?? "",
coverReady: guide?.coverStatus === "ready",
catalogVersion,
renderableSectionCount: guide ? publicGuideSections(guide).length : 0,
version: guide?.version ?? 0,
expectedVersion: parsed.data.expectedVersion,
});
if (issues.length || !catalogVersion) {
return { status: "error" as const, message: issues.join(" • "), issues };
}
return runGuideAction(() => setGuideVisibility(guideId, {
...parsed.data,
publicationNote: parsed.data.publicationNote || null,
catalogVersion,
}));
}
export async function deleteStructuredGuide(guideId: string) {