feat: complete search REST route, restore disk sync, and extraFields support across API/MCP

This commit is contained in:
asepharyana
2026-08-21 11:03:04 +07:00
parent 989ee9c464
commit a67a8c6469
11 changed files with 319 additions and 34 deletions
+37 -1
View File
@@ -3,6 +3,8 @@ import { documents, documentRevisions, documentChunks } from "@mcpedia/db/schema
import { reindexChunks } from "./index.service";
import { eq, desc, and, sql } from "drizzle-orm";
import { toMeta } from "./row-map";
import { CONTENT_ROOT } from "@mcpedia/config";
import { join } from "node:path";
import type { DocumentMeta } from "@mcpedia/types";
export interface RevisionSummary {
@@ -77,7 +79,8 @@ export async function getRevision(
}
/**
* Restore a revision: write its body+metadata back into the live `documents` row.
* Restore a revision: write its body+metadata back into the live `documents` row
* and update the on-disk markdown file.
*
* @param id revision UUID
* @param opts optional seam for testing — override the chunk-rebuild step so
@@ -110,6 +113,11 @@ export async function restoreRevision(
tags?: string[];
};
const [docRow] = await db
.select({ path: documents.path })
.from(documents)
.where(eq(documents.id, rev.documentId));
await db
.update(documents)
.set({
@@ -124,6 +132,34 @@ export async function restoreRevision(
})
.where(eq(documents.id, rev.documentId));
// Sync back to disk (source of truth for file-based reads)
if (docRow?.path) {
const absPath = join(CONTENT_ROOT, docRow.path);
try {
const { stringifyFile } = await import("@mcpedia/parser");
stringifyFile(
absPath,
docRow.path,
{
id: rev.slug,
slug: rev.slug,
title: rev.title,
type: (m.type as any) ?? "documentation",
section: (m.section as any) ?? "docs",
status: (m.status as any) ?? "published",
author: m.author ?? "",
tags: m.tags ?? [],
path: docRow.path,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
rev.body,
);
} catch (err) {
console.error(`restoreRevision: failed to write file to disk for ${rev.slug}:`, err);
}
}
// Rebuild semantic chunks + embeddings from the restored body so semantic
// and hybrid search stay consistent (otherwise document_chunks would hold
// the NEW body's chunks while documents.body holds the OLD/restore body).