diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0a25718..7d7a8b2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -48,6 +48,7 @@ jobs: /home/code/.bun/bin/bun install --frozen-lockfile /home/code/.bun/bin/bun --cwd apps/web run build /home/code/.bun/bin/bun --cwd packages/db run db:migrate + /home/code/.bun/bin/bun run index sudo systemctl restart mcpedia-web mcpedia-api mcpedia-mcp mcpedia-worker sleep 3 systemctl --no-pager status mcpedia-web mcpedia-api mcpedia-mcp mcpedia-worker --no-legend diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index e246632..dec49df 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -17,6 +17,16 @@ const VALID_TYPES: DocType[] = [ "note", ]; +// Standard frontmatter keys that are rendered explicitly in the UI template. +// Any other key in frontmatter becomes a dynamic "extra field" badge. +// This makes the system fully content-driven — creators add whatever +// metadata they need (event, challenge, category, difficulty, points, etc.) +// without code changes. +const STANDARD_FRONTMATTER_KEYS = new Set([ + "id", "slug", "title", "type", "section", "status", + "author", "tags", "path", "created_at", "updated_at", +]); + export interface ParsedFile { meta: DocumentMeta; body: string; @@ -48,17 +58,13 @@ export function parseFile(absPath: string, relPath: string): ParsedFile { const createdAt = data.created_at ?? nowIso; const updatedAt = data.updated_at ?? data.created_at ?? nowIso; - // Extract any additional frontmatter keys as dynamic extra fields. - // These are written to DB as JSONB + rendered as dynamic badges in the UI. - const STANDARD_FRONTMATTER_KEYS = new Set([ - "id", "slug", "title", "type", "section", "status", - "author", "tags", "path", "created_at", "updated_at", "event", - "challenge", "category", "difficulty", "points", - ]); + // Extract any non-standard frontmatter keys as dynamic extra fields. + // These are stored in DB as JSONB + rendered as dynamic badges in the UI. const extraFields: Record = {}; for (const [k, v] of Object.entries(data)) { if (!STANDARD_FRONTMATTER_KEYS.has(k) && v !== undefined && v !== null) { - extraFields[k] = String(v); + // Serialize non-string values (numbers, booleans) to string + extraFields[k] = typeof v === "string" ? v : JSON.stringify(v); } } @@ -74,17 +80,6 @@ export function parseFile(absPath: string, relPath: string): ParsedFile { path: relPath, createdAt: String(createdAt), updatedAt: String(updatedAt), - // CTF writeup metadata (optional) - event: typeof data.event === "string" ? data.event : undefined, - challenge: typeof data.challenge === "string" ? data.challenge : undefined, - category: typeof data.category === "string" ? data.category : undefined, - difficulty: - data.difficulty === "easy" || - data.difficulty === "medium" || - data.difficulty === "hard" - ? data.difficulty - : undefined, - points: typeof data.points === "number" ? data.points : undefined, extraFields, }; @@ -118,13 +113,11 @@ export function stringifyFile( path: relPath, created_at: meta.createdAt, updated_at: meta.updatedAt, - // CTF writeup metadata (only written if present) - ...(meta.event && { event: meta.event }), - ...(meta.challenge && { challenge: meta.challenge }), - ...(meta.category && { category: meta.category }), - ...(meta.difficulty && { difficulty: meta.difficulty }), - ...(meta.points !== undefined && { points: meta.points }), - // Dynamic custom fields (any key the content creator added) + // Dynamic custom fields — any key the content creator added. + // These are written as-is to YAML frontmatter, and read back via parseFile + // into meta.extraFields. Fully content-driven: no code changes needed + // to support new metadata fields (e.g. CTF: event, challenge, category, + // difficulty, points, or anything else). ...(meta.extraFields ?? {}), }; const yaml = "---\n" + diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 30d5dab..a15349f 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -14,14 +14,8 @@ export interface DocumentMeta { path: string; // relative path under content/ createdAt: string; // ISO updatedAt: string; // ISO - // CTF writeup metadata (optional — only set for writeups) - event?: string; - challenge?: string; - category?: string; - difficulty?: "easy" | "medium" | "hard"; - points?: number; - // Dynamic custom frontmatter fields (serialized as-is to YAML frontmatter). - // Allows content creators to add arbitrary metadata keys without code changes. + // Dynamic custom frontmatter fields (e.g. CTF: event, challenge, category, + // difficulty, points). Content creators add arbitrary key-value pairs. extraFields?: Record; }