feat(core): dynamic custom frontmatter fields for CTF writeup metadata
CI / typecheck + build (turbo) (push) Canceled after 0s
CI / typecheck + build (turbo) (push) Canceled after 0s
- Add extra_fields JSONB column to documents table (migration 0003) - CreateDocInput/UpdateDocInput: extraFields?: Record<string, string> - parseFile: extracts non-standard frontmatter keys as extraFields - stringifyFile: writes extraFields dynamically to YAML frontmatter - toMeta: spreads extraFields from DB row into DocumentMeta - DocForm: '+ Add field' UI for content creators to add any metadata - API routes: splitPayload() separates standard vs custom fields - Doc page: CustomFieldBadges dynamically renders any custom field - Add db:migrate / db:push scripts + deploy workflow migration step - Add CTF writeup template (content/writeups/ctf/template/) - Add sample DEF CON writeup with event/challenge/category/difficulty/points - Add @tailwindcss/typography for table rendering (prose classes) - Add remark-gfm for GitHub Flavored Markdown table parsing
This commit is contained in:
@@ -128,6 +128,7 @@ export interface CreateDocInput {
|
||||
status?: DocStatus;
|
||||
author?: string;
|
||||
tags?: string[];
|
||||
extraFields?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface UpdateDocInput {
|
||||
@@ -137,6 +138,7 @@ export interface UpdateDocInput {
|
||||
status?: DocStatus;
|
||||
tags?: string[];
|
||||
author?: string;
|
||||
extraFields?: Record<string, string>;
|
||||
}
|
||||
|
||||
/** Validate that a slug is safe (no path traversal, only [a-z0-9/_-]). */
|
||||
@@ -186,6 +188,7 @@ export async function createDocument(input: CreateDocInput): Promise<DocumentMet
|
||||
path: relPath,
|
||||
createdAt: nowIso,
|
||||
updatedAt: nowIso,
|
||||
extraFields: input.extraFields ?? {},
|
||||
};
|
||||
|
||||
// Write file to disk first (source of truth).
|
||||
@@ -204,6 +207,7 @@ export async function createDocument(input: CreateDocInput): Promise<DocumentMet
|
||||
tags: meta.tags,
|
||||
path: meta.path,
|
||||
body: input.body,
|
||||
extraFields: input.extraFields ?? {},
|
||||
createdAt: new Date(meta.createdAt),
|
||||
updatedAt: new Date(meta.updatedAt),
|
||||
});
|
||||
@@ -241,6 +245,11 @@ export async function updateDocument(
|
||||
tags: input.tags ?? doc.tags,
|
||||
author: input.author ?? doc.author,
|
||||
updatedAt,
|
||||
// Merge: new extraFields override old ones; merge with existing
|
||||
extraFields:
|
||||
input.extraFields !== undefined
|
||||
? { ...doc.extraFields, ...input.extraFields }
|
||||
: doc.extraFields,
|
||||
};
|
||||
const body = input.body ?? doc.body;
|
||||
|
||||
@@ -259,6 +268,7 @@ export async function updateDocument(
|
||||
status: updated.status,
|
||||
author: updated.author,
|
||||
tags: updated.tags,
|
||||
extraFields: input.extraFields ?? doc.extraFields ?? {},
|
||||
body,
|
||||
updatedAt: new Date(updatedAt),
|
||||
})
|
||||
|
||||
@@ -48,6 +48,7 @@ export async function indexContentFile(
|
||||
tags: meta.tags,
|
||||
path: meta.path,
|
||||
body,
|
||||
extraFields: meta.extraFields ?? {},
|
||||
createdAt: new Date(meta.createdAt || nowIso),
|
||||
updatedAt: new Date(nowIso),
|
||||
})
|
||||
@@ -62,6 +63,7 @@ export async function indexContentFile(
|
||||
tags: meta.tags,
|
||||
path: meta.path,
|
||||
body,
|
||||
extraFields: meta.extraFields ?? {},
|
||||
updatedAt: new Date(nowIso),
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user