feat: revamp to dynamic database-first architecture and cleanup phase docs
CI / typecheck + build (turbo) (push) Canceled after 0s

- Migrate document fetching and CRUD to be PostgreSQL-authoritative
- Remove static section enums and add dynamic listSections query
- Support custom sections and metadata across API, MCP, and Web UI
- Add /api/sections endpoint and update Header, Sidebar, and Forms
- Remove obsolete phase planning docs and modernize README/AGENTS
This commit is contained in:
asepharyana
2026-08-21 11:35:42 +07:00
parent a937e8f51b
commit a9b67385c3
37 changed files with 720 additions and 2515 deletions
+13 -6
View File
@@ -39,20 +39,27 @@ export const EMBED_BASE_URL = process.env.EMBED_BASE_URL ?? "";
export const EMBED_API_KEY = process.env.EMBED_API_KEY ?? "";
export const EMBED_MODEL = process.env.EMBED_MODEL ?? "";
// Phase 3: Redis + BullMQ (shared imrnes Redis, no auth by default).
// Redis + BullMQ (shared Redis instance).
export const REDIS_URL = process.env.REDIS_URL ?? "redis://100.121.180.82:6379";
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD ?? "";
export const QUEUE_PREFIX = process.env.QUEUE_PREFIX ?? "mcpedia";
// Phase 4: git-sync webhook shared secret.
// Git-sync webhook and API write shared secret.
export const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET ?? "";
// Phase 11: Admin password for web-based CRUD.
// Admin password for web-based CRUD.
export const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD ?? "";
// Phase 14: Section registry — re-exported from a Node-free module so client
// components can safely import SECTIONS without pulling in node:fs.
export { SECTIONS, SECTIONS_BY_ID, SECTION_IDS, type SectionConfig } from "./sections";
// Dynamic Section registry & helpers — re-exported from a Node-free module.
export {
SECTIONS,
SECTIONS_BY_ID,
SECTION_IDS,
DEFAULT_SECTIONS,
SECTION_PRESETS,
getSectionMeta,
type SectionConfig,
} from "./sections";
// NOTE: we deliberately do NOT throw here if DATABASE_URL is empty.
// Throwing at import time breaks `next build` SSG and any runtime-injected env.
+61 -17
View File
@@ -1,10 +1,6 @@
/**
* Section registry — defines all content sections for the UI.
* This file is intentionally free of Node.js built-in imports (no fs/path)
* so it can be safely imported by client components.
*
* Adding a new section is as simple as adding an entry here + creating a
* `content/<id>/` directory with markdown files. No UI code changes needed.
* Dynamic Section Registry & Metadata Generator.
* Safe for client and server components.
*/
export interface SectionConfig {
id: string;
@@ -13,32 +9,80 @@ export interface SectionConfig {
desc: string;
}
export const SECTIONS: SectionConfig[] = [
{
id: "docs",
export const SECTION_PRESETS: Record<string, { label: string; icon: string; desc: string }> = {
docs: {
label: "Documentation",
icon: "📄",
desc: "Setup guides, API references, and protocol specs.",
},
{
id: "writeups",
writeups: {
label: "Writeups",
icon: "📝",
desc: "Post-mortems, debugging stories, and case studies.",
desc: "Post-mortems, CTF writeups, debugging stories, and case studies.",
},
{
id: "research",
research: {
label: "Research",
icon: "🔬",
desc: "Deep-dive analysis, architecture notes, and experiments.",
},
{
id: "notes",
notes: {
label: "Notes",
icon: "📌",
desc: "Quick references, patterns, and gotchas.",
desc: "Quick references, patterns, and cheat-sheets.",
},
guides: {
label: "Guides",
icon: "🧭",
desc: "Step-by-step guides and implementation walkthroughs.",
},
tutorials: {
label: "Tutorials",
icon: "🎓",
desc: "Educational lessons and practical tutorials.",
},
ctf: {
label: "CTF",
icon: "🚩",
desc: "Capture The Flag challenge writeups and exploit solutions.",
},
api: {
label: "API",
icon: "⚡",
desc: "API documentation and endpoint definitions.",
},
projects: {
label: "Projects",
icon: "🚀",
desc: "Project overviews and technical architecture roadmaps.",
},
};
export function getSectionMeta(id: string, count?: number): SectionConfig {
const cleanId = (id || "docs").toLowerCase().trim();
if (SECTION_PRESETS[cleanId]) {
return { id: cleanId, ...SECTION_PRESETS[cleanId] };
}
const formattedLabel = cleanId
.split(/[-_]/)
.map((w) => (w ? w.charAt(0).toUpperCase() + w.slice(1) : ""))
.join(" ");
return {
id: cleanId,
label: formattedLabel || "Custom Section",
icon: "📁",
desc: `Documents in the ${formattedLabel || cleanId} section.`,
};
}
export const DEFAULT_SECTIONS: SectionConfig[] = [
getSectionMeta("docs"),
getSectionMeta("writeups"),
getSectionMeta("research"),
getSectionMeta("notes"),
];
// Backwards-compatible constants:
export const SECTIONS: SectionConfig[] = DEFAULT_SECTIONS;
export const SECTIONS_BY_ID = new Map(SECTIONS.map((s) => [s.id, s]));
export const SECTION_IDS = SECTIONS.map((s) => s.id);