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,15 @@
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json tsconfig.base.json ./
COPY packages/contracts ./packages/contracts
COPY exapps/control-plane ./exapps/control-plane
RUN npm install
RUN npm run build
WORKDIR /app/exapps/control-plane
CMD ["npm", "run", "start"]

View file

@ -0,0 +1,21 @@
{
"name": "@ainas/control-plane",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc -p tsconfig.json",
"start": "node dist/index.js",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@ainas/contracts": "file:../../packages/contracts"
},
"devDependencies": {
"@types/node": "^22.18.6",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
}
}

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}`);
});

View file

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": [
"src/**/*.ts"
]
}