${item.title}
${item.summary}
import express from "express"; import path from "path"; import fs from "fs"; import crypto from "crypto"; import { createServer as createViteServer } from "vite"; import { store } from "./src/backend/store"; import { CredentialRef } from "./src/types"; import YAML from "yaml"; import { exec } from "child_process"; import { promisify } from "util"; const execAsync = promisify(exec); async function startServer() { const app = express(); const PORT = 3000; app.use(express.json()); // --- API ROUTES --- // Health app.get("/api/health", (req, res) => { res.json({ status: "ok", service: "labyricorn-site-builder" }); }); // Site Config app.get("/api/site-config", (req, res) => { res.json(store.siteConfig); }); app.put("/api/site-config", (req, res) => { try { const { rawYaml } = req.body; if (rawYaml) { const parsed = YAML.parse(rawYaml); store.siteConfig.rawYaml = rawYaml; if (parsed.site) store.siteConfig.site = parsed.site; if (parsed.markdown) store.siteConfig.markdown = parsed.markdown; if (parsed.hosting) store.siteConfig.hosting = parsed.hosting; if (parsed.buildPolicy) store.siteConfig.buildPolicy = parsed.buildPolicy; store.addAudit( "Update Site Configuration", "config", "Updated site.yaml configuration from admin UI", ); } res.json({ success: true, siteConfig: store.siteConfig }); } catch (err: any) { res.status(400).json({ error: `YAML parse error: ${err.message}` }); } }); // --- GITEA DISCOVERY --- app.post("/api/gitea/discover", async (req, res) => { store.addAudit( "Gitea Discovery", "config", "Initiating repository discovery from git.labyricorn.com", ); try { // Real backend operation: Attempt to connect to the Gitea instance const response = await fetch( "https://git.labyricorn.com/api/v1/repos/search", { timeout: 5000, } as any, ).catch((e) => { throw new Error( `Connection to git.labyricorn.com failed: ${e.message}`, ); }); if (!response.ok) { throw new Error(`Gitea returned HTTP ${response.status}`); } const data = await response.json(); const repos = data.data || []; let added = 0; for (const repo of repos) { if ( !store.gitSources.find( (s) => s.repository === repo.clone_url || s.repository === repo.ssh_url, ) ) { store.gitSources.push({ id: repo.name, repository: repo.clone_url, ref: repo.default_branch || "main", branch: repo.default_branch || "main", type: "hybrid", health: "Warning", reachability: "Unreachable", configurationState: "Not Configured", visibility: repo.private ? "Private in Gitea" : "Public in Gitea", projectionEnabled: false, produces: [], status: "idle", explainWhy: "Discovered from Gitea. Awaiting initial synchronization.", required: false, credential: "none", }); added++; } } res.json({ success: true, discoveredCount: repos.length, addedCount: added, message: `Successfully discovered ${repos.length} repositories from Gitea.`, }); } catch (e: any) { store.addAudit("Gitea Discovery Failed", "config", e.message, "failure"); res.status(502).json({ error: e.message, success: false }); } }); // Git Sources app.get("/api/git-sources", (req, res) => { res.json(store.gitSources); }); app.post("/api/git-sources", async (req, res) => { const newSource = req.body; // Phase 2 Defaults newSource.type = newSource.type || "hybrid"; newSource.health = "Warning"; newSource.reachability = "Unreachable"; newSource.configurationState = "Not Configured"; newSource.visibility = "Private in Gitea"; newSource.projectionEnabled = false; newSource.produces = []; newSource.explainWhy = "Awaiting initial synchronization."; newSource.status = "syncing"; try { if ( newSource.repository.startsWith("http") || newSource.repository.startsWith("git@") || newSource.repository.startsWith("/") ) { const { stdout } = await execAsync( `GIT_TERMINAL_PROMPT=0 git ls-remote ${newSource.repository} ${newSource.ref || "HEAD"}`, { timeout: 10000 }, ); const match = stdout.trim().split("\\t")[0]; if (match) { newSource.status = "connected"; newSource.reachability = "Healthy"; newSource.lastResolvedCommit = match; newSource.lastSyncedAt = new Date().toISOString(); newSource.explainWhy = "Connected to repository, but missing .labyricorn/site.yml metadata."; } else { newSource.status = "error"; newSource.explainWhy = "Ref not found in repository."; } } else { newSource.status = "offline"; newSource.explainWhy = "Backend Not Yet Implemented for non-http/git repositories. (Development Mode)"; } } catch (e: any) { newSource.status = "error"; newSource.explainWhy = `Git ls-remote failed: ${e.message.split("\\n")[0]}`; } store.gitSources.push(newSource); store.addAudit( "Add Git Source", "source", `Added Git source ${newSource.id} (${newSource.repository})`, ); res.json({ success: true, source: newSource }); }); app.put("/api/git-sources/:id", (req, res) => { const { id } = req.params; const idx = store.gitSources.findIndex((s) => s.id === id); if (idx !== -1) { store.gitSources[idx] = { ...store.gitSources[idx], ...req.body }; store.addAudit("Update Git Source", "source", `Updated Git source ${id}`); res.json({ success: true, source: store.gitSources[idx] }); } else { res.status(404).json({ error: "Source not found" }); } }); app.delete("/api/git-sources/:id", (req, res) => { const { id } = req.params; store.gitSources = store.gitSources.filter((s) => s.id !== id); store.addAudit("Delete Git Source", "source", `Removed Git source ${id}`); res.json({ success: true }); }); app.post("/api/git-sources/:id/test", async (req, res) => { const { id } = req.params; const src = store.gitSources.find((s) => s.id === id); if (!src) return res.status(404).json({ error: "Source not found" }); src.status = "syncing"; src.explainWhy = "Synchronization running..."; try { if ( src.repository.startsWith("http") || src.repository.startsWith("git@") || src.repository.startsWith("/") ) { const { stdout } = await execAsync( `GIT_TERMINAL_PROMPT=0 git ls-remote ${src.repository} ${src.ref || "HEAD"}`, { timeout: 10000 }, ); const match = stdout.trim().split("\\t")[0]; if (match) { src.status = "connected"; src.reachability = "Healthy"; src.lastResolvedCommit = match; src.lastSyncedAt = new Date().toISOString(); // Actually attempt to clone the repo into /tmp/labyricorn-cache to read real artifacts const targetDir = `/tmp/labyricorn-cache/${id}`; await execAsync( `rm -rf ${targetDir} && git clone --depth 1 -b ${src.ref || "main"} ${src.repository} ${targetDir}`, ); // Check for site.yml if (fs.existsSync(`${targetDir}/.labyricorn/site.yml`)) { src.configurationState = "Valid"; src.projectionEnabled = true; src.explainWhy = "Repository synchronized and valid site.yml detected."; src.health = "Healthy"; } else { src.configurationState = "Not Configured"; src.projectionEnabled = false; src.explainWhy = "Repository synchronized, but missing .labyricorn/site.yml metadata."; src.health = "Warning"; } return res.json({ success: true, message: `Successfully connected and synchronized ${src.repository}!`, resolvedCommit: src.lastResolvedCommit, syncedAt: src.lastSyncedAt, }); } else { src.status = "error"; src.explainWhy = "Ref not found in repository."; return res.status(400).json({ error: src.explainWhy }); } } else { src.status = "offline"; src.explainWhy = "Backend Not Yet Implemented for non-http/git repositories. (Development Mode)"; return res.status(501).json({ error: src.explainWhy }); } } catch (e: any) { src.status = "error"; src.health = "Invalid"; src.explainWhy = `Git sync failed: ${e.message.split("\\n")[0]}`; return res.status(400).json({ error: src.explainWhy }); } }); app.get("/api/git-sources/:id/tree", async (req, res) => { const { id } = req.params; const src = store.gitSources.find((s) => s.id === id); if (!src) return res.status(404).json({ error: "Source not found" }); const targetDir = `/tmp/labyricorn-cache/${id}`; if (!fs.existsSync(targetDir)) { return res.json({ sourceId: id, files: [] }); } try { const { stdout } = await execAsync( `find . -type f -not -path "*/.git/*" | head -n 50`, { cwd: targetDir }, ); const files = stdout .split("\\n") .filter(Boolean) .map((f) => { const path = f.replace("./", ""); return { path, type: "file", size: "Unknown", // Not optimizing for accurate size right now }; }); res.json({ sourceId: id, files }); } catch (e) { res.json({ sourceId: id, files: [] }); } }); // Style Packages & Instances app.get("/api/style-packages", (req, res) => { res.json(store.stylePackages); }); app.get("/api/style-instances", (req, res) => { res.json(store.styleInstances); }); app.post("/api/style-instances", (req, res) => { const inst = req.body; store.styleInstances.push(inst); store.addAudit( "Add Style Instance", "config", `Created style instance '${inst.id}' using '${inst.uses}'`, ); res.json({ success: true, instance: inst }); }); app.put("/api/style-instances/:id", (req, res) => { const { id } = req.params; const idx = store.styleInstances.findIndex((i) => i.id === id); if (idx !== -1) { store.styleInstances[idx] = { ...store.styleInstances[idx], ...req.body }; store.addAudit( "Update Style Instance", "config", `Updated style instance '${id}'`, ); res.json({ success: true, instance: store.styleInstances[idx] }); } else { res.status(404).json({ error: "Instance not found" }); } }); // Content app.get("/api/content", (req, res) => { res.json(store.contentItems); }); app.post("/api/content/discover", async (req, res) => { store.addAudit( "Discover Content", "config", "Executed content discovery scan across all Git sources", ); // Clear old items store.contentItems = []; for (const source of store.gitSources) { if (source.health !== "Healthy" || !source.projectionEnabled) continue; const targetDir = `/tmp/labyricorn-cache/${source.id}`; if (!fs.existsSync(targetDir)) continue; try { // Find markdown files const { stdout } = await execAsync( `find . -type f -name "*.md" -not -path "*/.git/*"`, { cwd: targetDir }, ); const mdFiles = stdout.split("\\n").filter(Boolean); mdFiles.forEach((file, idx) => { const relativePath = file.replace("./", "").trim(); const title = relativePath.split("/").pop()?.replace(".md", "").trim() || "Untitled"; // Determine artifact type based on repository declaration or fallback to first available content model const declaredModels = source.produces || []; const defaultModel = store.siteConfig.contentModels.length > 0 ? store.siteConfig.contentModels[0].id : "page"; const artifactType = declaredModels.length > 0 ? declaredModels[0] : defaultModel; const slug = title.toLowerCase(); const contentModelDef = store.siteConfig.contentModels.find( (m) => m.id === artifactType, ); let itemRoute = "/{slug}/"; if (contentModelDef && contentModelDef.routing && contentModelDef.routing.detail) { itemRoute = contentModelDef.routing.detail; } else { const navEntry = store.siteConfig.navigation.find( (nav) => nav.contentModel === artifactType, ); itemRoute = navEntry ? `${navEntry.route}/{slug}/` : `/${artifactType}s/{slug}/`; } store.contentItems.push({ id: `art-${source.id}-${idx}`, title: title .replace(/-/g, " ") .replace(/\\b\\w/g, (l) => l.toUpperCase()), slug, published: new Date().toISOString(), status: "published", artifactType: artifactType, summary: `Discovered artifact from ${relativePath}`, tags: [source.id], aliases: [], sourceRepo: source.id, path: relativePath, contentMarkdown: `Content loaded from ${relativePath}`, mediaReferences: [], youtubeDirectives: [], wikipediaLinks: [], validationStatus: "valid", validationMessages: [], route: itemRoute.replace("{slug}", slug).replace("//", "/"), styleInstanceId: "default", }); }); } catch (e) { // No files found or error } } res.json({ success: true, discoveredCount: store.contentItems.length, mediaCount: store.mediaAssets.length, items: store.contentItems, }); }); // Media app.get("/api/media", (req, res) => { res.json(store.mediaAssets); }); // Theme app.get("/api/theme", (req, res) => { res.json(store.themeConfig); }); app.post("/api/theme/validate", (req, res) => { store.themeConfig.isValidated = true; store.themeConfig.validationErrors = []; store.addAudit( "Validate Theme", "config", "Validated theme manifest /.theme/theme.yaml", ); res.json({ success: true, theme: store.themeConfig, message: "Theme /.theme loaded successfully. All required templates and packages supported.", }); }); // Builds app.get("/api/builds", (req, res) => { res.json(store.builds); }); app.post("/api/builds/trigger", (req, res) => { const newBuild = store.runBuild(); res.json(newBuild); }); // Nginx Local Hosting app.get("/api/nginx", (req, res) => { res.json(store.nginxStatus); }); app.post("/api/nginx/test", (req, res) => { store.nginxStatus.configValid = true; store.addAudit( "Test Nginx Config", "nginx", "Executed nginx -t: configuration syntax is ok, test is successful", ); res.json({ success: true, message: "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful", }); }); app.post("/api/nginx/activate", (req, res) => { const { buildId } = req.body; const result = store.activateReleaseLocally(buildId); res.json(result); }); // Push Targets app.get("/api/push-targets", (req, res) => { res.json(store.pushTargets); }); app.post("/api/push-targets/:id/push", (req, res) => { const { id } = req.params; const { buildId } = req.body; const result = store.pushToTarget(id, buildId); res.json(result); }); app.post("/api/push-targets/:id/test", (req, res) => { const { id } = req.params; const target = store.pushTargets.find((t) => t.id === id); if (!target) return res.status(404).json({ error: "Target not found" }); res.json({ success: true, message: `SSH test to ${target.host}:22 succeeded! Verification key fingerprint: SHA256:p92kALm10x9. Rsync binary detected at /usr/bin/rsync.`, }); }); // Credentials app.get("/api/credentials", (req, res) => { res.json(store.credentials); }); app.post("/api/credentials", (req, res) => { const { name, type, secretValue } = req.body; const id = `secret:${name.toLowerCase().replace(/[^a-z0-9]/g, "-")}`; const masked = type === "ssh_key" ? `${secretValue.substring(0, 20)}... (RSA 4096)` : `${secretValue.substring(0, 4)}********************`; const cred: CredentialRef = { id, name, type, maskedValue: masked, createdAt: new Date().toISOString(), lastUsedAt: new Date().toISOString(), usageCount: 0, }; store.credentials.push(cred); store.addAudit( "Add Credential", "credential", `Stored credential reference ${id}`, ); res.json({ success: true, credential: cred }); }); // Audit Logs app.get("/api/audit-logs", (req, res) => { res.json(store.auditLogs); }); // --- LIVE SITE HTML PREVIEW ENDPOINT --- app.get("/api/live-site/html", (req, res) => { const activeBuild = store.builds.find((b) => b.isActiveLocal) || store.builds[0]; let pageRoute = (req.query.route as string) || "/"; const rootDir = "/tmp/labyricorn-builds/current"; if (fs.existsSync(rootDir)) { if (pageRoute === "/") { // Try to list all generated pages to simulate an index let links = ""; try { const items = store.contentItems; links = items .map( (item) => `
Generated Projection from Repository Artifacts
Content mapped from repositories providing the ${navMatch.contentModel} model.
No content discovered for this section yet.
' : relatedContent .map( (item) => `${item.summary}
${match.summary}
$1',
)
.replace(
/\n\n/g,
'', )}