add control plane service

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Harivansh Rathi 2026-03-31 21:25:42 +00:00
parent 679fd504a2
commit 57f221fb72
8 changed files with 766 additions and 0 deletions

View file

@ -0,0 +1,14 @@
import type { NextcloudBackendStatus } from "@ainas/contracts";
export class NextcloudBackendAdapter {
constructor(private readonly baseUrl: string) {}
describe(): NextcloudBackendStatus {
return {
configured: this.baseUrl.length > 0,
baseUrl: this.baseUrl,
provider: "nextcloud"
};
}
}

View file

@ -0,0 +1,59 @@
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import {
CONTROL_PLANE_ROUTES,
type ControlPlaneHealthResponse,
type ControlPlaneVersionResponse
} from "@ainas/contracts";
import type { ControlPlaneConfig } from "./config.js";
import { NextcloudBackendAdapter } from "./adapters/nextcloud-backend.js";
export function createApp(config: ControlPlaneConfig) {
const startedAt = Date.now();
const nextcloudBackend = new NextcloudBackendAdapter(config.nextcloudBaseUrl);
return createServer((request, response) => {
if (request.method !== "GET" || !request.url) {
writeJson(response, 405, { error: "Method not allowed" });
return;
}
const url = new URL(request.url, "http://localhost");
if (url.pathname === CONTROL_PLANE_ROUTES.health) {
const payload: ControlPlaneHealthResponse = {
service: "control-plane",
status: "ok",
timestamp: new Date().toISOString(),
uptimeSeconds: Math.floor((Date.now() - startedAt) / 1000),
nextcloud: nextcloudBackend.describe()
};
writeJson(response, 200, payload);
return;
}
if (url.pathname === CONTROL_PLANE_ROUTES.version) {
const payload: ControlPlaneVersionResponse = {
service: "control-plane",
version: config.version,
apiVersion: "v1"
};
writeJson(response, 200, payload);
return;
}
writeJson(response, 404, {
error: "Not found"
});
});
}
function writeJson(response: ServerResponse<IncomingMessage>, statusCode: number, payload: unknown) {
response.writeHead(statusCode, {
"content-type": "application/json; charset=utf-8"
});
response.end(JSON.stringify(payload));
}

View file

@ -0,0 +1,25 @@
export interface ControlPlaneConfig {
port: number;
version: string;
nextcloudBaseUrl: string;
}
export function loadConfig(env: NodeJS.ProcessEnv = process.env): ControlPlaneConfig {
const portValue = env.PORT ?? "3000";
const port = Number.parseInt(portValue, 10);
if (Number.isNaN(port)) {
throw new Error(`Invalid PORT value: ${portValue}`);
}
return {
port,
version: env.AINAS_VERSION ?? "0.1.0-dev",
nextcloudBaseUrl: normalizeBaseUrl(env.NEXTCLOUD_BASE_URL ?? "http://nextcloud")
};
}
function normalizeBaseUrl(url: string): string {
return url.replace(/\/+$/, "");
}

View file

@ -0,0 +1,10 @@
import { createApp } from "./app.js";
import { loadConfig } from "./config.js";
const config = loadConfig();
const app = createApp(config);
app.listen(config.port, "0.0.0.0", () => {
console.log(`aiNAS control plane listening on port ${config.port}`);
});