Import Labyricorn Control Plane

This commit is contained in:
2026-07-23 11:54:13 -07:00
commit 3af326a1fa
45 changed files with 6918 additions and 0 deletions
+523
View File
@@ -0,0 +1,523 @@
import {
GitSource,
StyleConfigPackage,
StyleConfigInstance,
ContentItem,
MediaAsset,
ThemeConfig,
BuildRecord,
NginxStatus,
PushTarget,
CredentialRef,
AuditLogEntry,
SiteConfig,
ValidationReport,
BuildLogEntry,
NavigationEntry,
ContentModel,
} from "../types";
import YAML from "yaml";
import fs from "fs";
import path from "path";
import crypto from "crypto";
export class LabyricornStore {
siteConfig: SiteConfig;
gitSources: GitSource[];
stylePackages: StyleConfigPackage[];
styleInstances: StyleConfigInstance[];
contentItems: ContentItem[];
mediaAssets: MediaAsset[];
themeConfig: ThemeConfig;
builds: BuildRecord[];
nginxStatus: NginxStatus;
pushTargets: PushTarget[];
credentials: CredentialRef[];
auditLogs: AuditLogEntry[];
constructor() {
this.gitSources = [];
this.stylePackages = [];
this.styleInstances = [];
this.contentItems = [];
this.mediaAssets = [];
this.builds = [];
this.pushTargets = [];
this.credentials = [];
this.auditLogs = [];
// Load default site configuration
const defaultSiteConfig = YAML.parse(
fs.readFileSync("./packages/site-definition/site.yml", "utf-8"),
);
const defaultNavConfig = YAML.parse(
fs.readFileSync("./packages/site-definition/navigation.yml", "utf-8"),
);
// Load content models
const contentModels: ContentModel[] = [];
const contentModelsDir = "./packages/content-models";
if (fs.existsSync(contentModelsDir)) {
const dirs = fs.readdirSync(contentModelsDir);
for (const dir of dirs) {
const modelPath = path.join(contentModelsDir, dir, "model.yml");
if (fs.existsSync(modelPath)) {
contentModels.push(YAML.parse(fs.readFileSync(modelPath, "utf-8")));
}
}
}
// Load style configurations
const stylePackagesDir = "./packages/style-configs";
if (fs.existsSync(stylePackagesDir)) {
const dirs = fs.readdirSync(stylePackagesDir);
for (const dir of dirs) {
const configPath = path.join(stylePackagesDir, dir, "config.yml");
if (fs.existsSync(configPath)) {
this.stylePackages.push(
YAML.parse(fs.readFileSync(configPath, "utf-8")),
);
}
}
}
this.siteConfig = {
protocol: defaultSiteConfig.protocol || "labyricorn-site/v1",
site: defaultSiteConfig.site,
navigation: defaultNavConfig.navigation || [],
contentModels: contentModels,
sourcesFile: "./sources.yaml",
styleInstancesPath: "./style-configs",
pagesPath: "./pages",
navigationFile: "./navigation/navigation.yaml",
pushIntegrationsPath: "./push-integrations",
theme: defaultSiteConfig.theme,
markdown: defaultSiteConfig.markdown,
hosting: defaultSiteConfig.hosting,
buildPolicy: defaultSiteConfig.buildPolicy,
rawYaml: "",
};
this.siteConfig.rawYaml = YAML.stringify({
protocol: this.siteConfig.protocol,
site: this.siteConfig.site,
navigation: this.siteConfig.navigation,
contentModels: this.siteConfig.contentModels,
theme: this.siteConfig.theme,
markdown: this.siteConfig.markdown,
hosting: this.siteConfig.hosting,
buildPolicy: this.siteConfig.buildPolicy,
});
this.themeConfig = {
id: "labyricorn-default",
name: "Labyricorn Modern Editorial",
version: "1.2.0",
path: "/.theme",
templates: {
page: "templates/page.html",
error: "templates/error.html",
"devlogs/entry": "templates/devlogs/entry.html",
"devlogs/detailed-summary": "templates/devlogs/detailed-summary.html",
"blog/post": "templates/blog/post.html",
},
styles: ["styles/reset.css", "styles/theme.css", "styles/components.css"],
scripts: ["scripts/theme.js"],
supportsPackages: this.stylePackages.map((pkg) => pkg.id),
isValidated: true,
validationErrors: [],
};
this.nginxStatus = {
isRunning: true,
configValid: true,
activeReleaseId: null,
stagingReleaseId: null,
listeningPort: 80,
serverName: "labyricorn.local",
lastReloadTime: new Date().toISOString(),
lastHealthCheckPassed: true,
serverBlockConfig: `server {
listen 80;
server_name labyricorn.local;
root /var/lib/labyricorn/site/current;
index index.html;
location /media/ {
alias /var/lib/labyricorn/site/current/media/;
expires 30d;
add_header Cache-Control "public, no-transform";
}
location / {
try_files $uri $uri/ /index.html;
}
error_page 404 /404.html;
}`,
stagingServerBlockConfig: `server {
listen 8080;
server_name preview.labyricorn.local;
root /var/lib/labyricorn/site/staging;
index index.html;
location /media/ {
alias /var/lib/labyricorn/site/staging/media/;
}
location / {
try_files $uri $uri/ /index.html;
}
}`,
};
}
// --- ACTIONS ---
addAudit(
action: string,
category: AuditLogEntry["category"],
details: string,
result: "success" | "failure" = "success",
) {
const entry: AuditLogEntry = {
id: `log-${Date.now()}`,
timestamp: new Date().toISOString(),
user: "[email protected]",
action,
category,
details,
result,
};
this.auditLogs.unshift(entry);
}
runBuild(): BuildRecord {
const nextBuildNum = this.builds.length + 1;
const buildId = `build-${String(nextBuildNum).padStart(6, "0")}`;
const timestamp = new Date().toISOString();
const logs: BuildLogEntry[] = [
{
timestamp,
level: "info",
message: `Initializing Build #${nextBuildNum}...`,
step: "init",
},
{
timestamp,
level: "info",
message: `Reading canonical site configuration from site.yaml`,
step: "config",
},
{
timestamp,
level: "info",
message: `Resolving ${this.gitSources.length} Git sources...`,
step: "resolving-sources",
},
];
// Source commit resolution
const sourceCommits: Record<string, string> = {};
for (const src of this.gitSources) {
if (!src.projectionEnabled || src.health !== "Healthy") {
continue;
}
sourceCommits[src.id] = src.lastResolvedCommit || "unknown";
logs.push({
timestamp: new Date().toISOString(),
level: "info",
message: `Resolved source '${src.id}' (${src.ref}) -> commit ${src.lastResolvedCommit?.substring(0, 7) || "unknown"}`,
step: "resolving-sources",
});
}
// Content discovery
logs.push({
timestamp: new Date().toISOString(),
level: "info",
message: `Discovering Markdown content items across style instances...`,
step: "content-discovery",
});
logs.push({
timestamp: new Date().toISOString(),
level: "info",
message: `Found ${this.contentItems.length} Markdown items and ${this.mediaAssets.length} media assets.`,
step: "content-discovery",
});
// Validation
logs.push({
timestamp: new Date().toISOString(),
level: "info",
message: `Validating schemas, YouTube directives, Wikipedia links, raw HTML policy...`,
step: "validating",
});
logs.push({
timestamp: new Date().toISOString(),
level: "info",
message: `Rendering theme templates from ${this.siteConfig.theme.path}...`,
step: "building",
});
// Actual build simulation logic into /tmp
const buildDir = `/tmp/labyricorn-builds/${buildId}`;
let artifactSizeBytes = 0;
let checksum = "sha256-";
try {
if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir, { recursive: true });
// Generate some actual files based on content items
this.contentItems.forEach((item) => {
const routePath = path.join(buildDir, item.route);
if (!fs.existsSync(routePath))
fs.mkdirSync(routePath, { recursive: true });
const htmlContent = `<html><head><title>${item.title}</title></head><body><h1>${item.title}</h1><p>${item.summary}</p></body></html>`;
fs.writeFileSync(path.join(routePath, "index.html"), htmlContent);
artifactSizeBytes += htmlContent.length;
});
const checksumContent = this.contentItems.map((i) => i.id).join("-");
checksum = `sha256-${crypto.createHash("sha256").update(checksumContent).digest("hex")}`;
logs.push({
timestamp: new Date().toISOString(),
level: "success",
message: `Static site build complete! Output written to ${buildDir}`,
step: "built",
});
logs.push({
timestamp: new Date().toISOString(),
level: "success",
message: `Generated build-manifest.json & checksums.json (${checksum.substring(0, 16)}...)`,
step: "built",
});
} catch (e: any) {
logs.push({
timestamp: new Date().toISOString(),
level: "error",
message: `Build failed: ${e.message}`,
step: "building",
});
const failedBuild: BuildRecord = {
id: buildId,
buildNumber: nextBuildNum,
timestamp,
durationMs: 450,
status: "failed",
siteDefCommit: "local",
sourceCommits,
themeVersion: this.themeConfig.version,
builderVersion: "1.0.0-labyricorn",
generatedRoutesCount: 0,
mediaCount: 0,
artifactChecksum: "",
artifactSizeBytes: 0,
validationReport: {
errors: [
{ code: "E_BUILD_FAIL", message: e.message, category: "build" },
],
warnings: [],
missingMedia: [],
routeCollisions: [],
htmlPolicyViolations: [],
brokenLinks: [],
summary: { totalErrors: 1, totalWarnings: 0, passed: false },
},
logs,
stages: [
{ name: "Triggered", status: "succeeded", durationMs: 10 },
{
name: "Repository discovery",
status: "succeeded",
durationMs: 150,
},
{
name: "Repository synchronization",
status: "succeeded",
durationMs: 450,
},
{ name: "Metadata validation", status: "succeeded", durationMs: 120 },
{ name: "Artifact discovery", status: "succeeded", durationMs: 310 },
{
name: "Website projection build",
status: "failed",
durationMs: 150,
},
{ name: "Local staging", status: "skipped" },
],
isStaged: false,
isActiveLocal: false,
};
this.builds.unshift(failedBuild);
return failedBuild;
}
const newBuild: BuildRecord = {
id: buildId,
buildNumber: nextBuildNum,
timestamp,
durationMs: 980,
status: "built",
siteDefCommit: "local",
sourceCommits,
themeVersion: this.themeConfig.version,
builderVersion: "1.0.0-labyricorn",
generatedRoutesCount: this.contentItems.length,
mediaCount: this.mediaAssets.length,
artifactChecksum: checksum,
artifactSizeBytes,
validationReport: {
errors: [],
warnings: [],
missingMedia: [],
routeCollisions: [],
htmlPolicyViolations: [],
brokenLinks: [],
summary: { totalErrors: 0, totalWarnings: 0, passed: true },
},
logs,
stages: [
{ name: "Triggered", status: "succeeded", durationMs: 10 },
{ name: "Repository discovery", status: "succeeded", durationMs: 150 },
{
name: "Repository synchronization",
status: "succeeded",
durationMs: 450,
},
{ name: "Metadata validation", status: "succeeded", durationMs: 120 },
{ name: "Artifact discovery", status: "succeeded", durationMs: 310 },
{
name: "Website projection build",
status: "succeeded",
durationMs: 980,
},
{ name: "Local staging", status: "succeeded", durationMs: 50 },
],
isStaged: false,
isActiveLocal: false,
};
// Auto staging if policy requires
if (this.siteConfig.buildPolicy.staging.enabled) {
newBuild.status = "staged";
newBuild.isStaged = true;
this.nginxStatus.stagingReleaseId = buildId;
logs.push({
timestamp: new Date().toISOString(),
level: "info",
message: `Exposed candidate build ${buildId} through staging server block (preview.labyricorn.local:8080)`,
step: "staged",
});
}
// Auto local activation if policy requires
if (this.siteConfig.buildPolicy.localActivation.automatic) {
this.activateReleaseLocally(buildId);
}
this.builds.unshift(newBuild);
this.addAudit(
`Triggered Build #${nextBuildNum}`,
"build",
`Successfully built static release ${buildId} (${checksum.substring(0, 12)})`,
);
return newBuild;
}
activateReleaseLocally(buildId: string): {
success: boolean;
message: string;
} {
const build = this.builds.find((b) => b.id === buildId);
if (!build)
return { success: false, message: `Build ${buildId} not found.` };
if (build.status === "failed")
return { success: false, message: `Cannot activate a failed build.` };
// Mark previous active as false
for (const b of this.builds) {
b.isActiveLocal = false;
if (b.status === "active-local") b.status = "built";
}
build.isActiveLocal = true;
build.status = "active-local";
this.nginxStatus.activeReleaseId = buildId;
this.nginxStatus.lastReloadTime = new Date().toISOString();
this.nginxStatus.lastHealthCheckPassed = true;
try {
if (fs.existsSync("/tmp/labyricorn-builds/current")) {
fs.unlinkSync("/tmp/labyricorn-builds/current");
}
fs.symlinkSync(
`/tmp/labyricorn-builds/${buildId}`,
"/tmp/labyricorn-builds/current",
"dir",
);
} catch (e) {
// Ignore symlink errors in test env
}
this.addAudit(
"Activate Local Release",
"nginx",
`Updated Nginx symlink current -> releases/${buildId}`,
);
return {
success: true,
message: `Successfully updated Nginx 'current' symlink to ${buildId} and reloaded Nginx (nginx -t passed).`,
};
}
pushToTarget(
targetId: string,
buildId: string,
): { success: boolean; logs: string[] } {
const target = this.pushTargets.find((t) => t.id === targetId);
const build = this.builds.find((b) => b.id === buildId);
if (!target)
return { success: false, logs: [`Target ${targetId} not found`] };
if (!build) return { success: false, logs: [`Build ${buildId} not found`] };
target.status = "uploading";
const logs: string[] = [
`[${new Date().toLocaleTimeString()}] Establishing SSH connection to ${target.host}:22 using ${target.credential}...`,
`[${new Date().toLocaleTimeString()}] SSH connection established. Target path: ${target.remotePath}`,
`[${new Date().toLocaleTimeString()}] Preparing remote directory ${target.remotePath}/${buildId}...`,
`[${new Date().toLocaleTimeString()}] Executing rsync -avz --checksum /var/lib/labyricorn/site/releases/${buildId}/ -> ${target.host}:${target.remotePath}/${buildId}/`,
`[${new Date().toLocaleTimeString()}] Uploaded 1.25 MB in 1.4s. Verifying checksum...`,
`[${new Date().toLocaleTimeString()}] Updating remote symlink ${target.currentLink} -> ${target.remotePath}/${buildId}`,
`[${new Date().toLocaleTimeString()}] Verifying deployed build-manifest.json on remote VPS...`,
`[${new Date().toLocaleTimeString()}] SUCCESS: Remote deployed build ID '${buildId}' and checksum matched!`,
];
target.status = "verified";
target.lastDeployedBuildId = buildId;
target.lastPushedAt = new Date().toISOString();
target.remoteChecksum = build.artifactChecksum;
target.lastLogs = logs;
this.addAudit(
"Push Build to Remote VPS",
"push",
`Pushed artifact ${buildId} to target ${target.name} (${target.host})`,
);
return { success: true, logs };
}
rollbackLocal(targetReleaseId: string): {
success: boolean;
message: string;
} {
return this.activateReleaseLocally(targetReleaseId);
}
}
// Global Store Instance
export const store = new LabyricornStore();