// Server-only fetchers for the public template marketplace. Used by the // server-rendered template detail page (SEO metadata + JSON-LD) and the // sitemap. Never import this into a client component. const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:4000'; export interface TemplateTool { name: string; description: string; inputSchema: Record; } export interface TemplateSecretHint { key: string; description: string; howToGetUrl?: string; } export interface TemplateDetail { id: string; slug: string; title: string; shortDescription: string; longDescription: string | null; category: string; status: 'draft' | 'public' | 'hidden' | 'takedown'; verified: boolean; forkCount: number; activeDeployments: number; toolsSchema: TemplateTool[]; generatedCode: string; requiredSecrets: TemplateSecretHint[]; scopes: string[]; ownerName: string | null; ownerOrgName: string | null; sourceServerId: string | null; createdAt: string; } /** * Fetch a single public template by slug for server rendering. Returns null * for missing / non-public templates so the page can `notFound()` — we only * want `public` templates indexed. */ export async function fetchTemplate(slug: string): Promise { try { const res = await fetch(`${API_BASE}/v1/templates/${encodeURIComponent(slug)}`, { // Cache server-side for 5 min so crawler hits don't hammer the API. next: { revalidate: 300 }, }); if (!res.ok) return null; const data = (await res.json()) as { template?: TemplateDetail }; const t = data.template; if (!t || t.status !== 'public') return null; return t; } catch { return null; } } /** Slugs of public templates, for the sitemap. Best-effort: returns [] on error. */ export async function fetchPublicTemplateSlugs(): Promise { try { const res = await fetch(`${API_BASE}/v1/templates?limit=100&sort=newest`, { next: { revalidate: 600 }, }); if (!res.ok) return []; const data = (await res.json()) as | { templates?: Array<{ slug?: string }> } | Array<{ slug?: string }>; const list = Array.isArray(data) ? data : (data.templates ?? []); return list.map((t) => t.slug).filter((s): s is string => typeof s === 'string'); } catch { return []; } }