feat: initialize unidesk platform
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export interface UniDeskConfig {
|
||||
project: { name: string; timezone: string };
|
||||
runtime: { typescript: "bun"; bunVersion: string };
|
||||
network: {
|
||||
host: string;
|
||||
publicHost: string;
|
||||
core: { port: number; containerPort: number };
|
||||
frontend: { port: number; containerPort: number };
|
||||
database: { port: number; containerPort: number };
|
||||
};
|
||||
database: { user: string; password: string; name: string; volume: string; volumeSize: string };
|
||||
providerGateway: {
|
||||
id: string;
|
||||
name: string;
|
||||
token: string;
|
||||
labels: Record<string, unknown>;
|
||||
heartbeatIntervalMs: number;
|
||||
reconnectBaseMs: number;
|
||||
reconnectMaxMs: number;
|
||||
};
|
||||
docker: { composeFile: string; projectName: string };
|
||||
paths: { stateDir: string; logsDir: string; docsReferenceDir: string };
|
||||
sshForwarding: { mode: string; keyDir: string; host: string; port: number; user: string };
|
||||
}
|
||||
|
||||
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
||||
export const repoRoot = join(moduleDir, "..", "..");
|
||||
|
||||
export function rootPath(...parts: string[]): string {
|
||||
return join(repoRoot, ...parts);
|
||||
}
|
||||
|
||||
function asRecord(value: unknown, name: string): Record<string, unknown> {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
throw new Error(`${name} must be an object`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function stringField(obj: Record<string, unknown>, key: string, path: string): string {
|
||||
const value = obj[key];
|
||||
if (typeof value !== "string" || value.length === 0) throw new Error(`${path}.${key} must be a non-empty string`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function numberField(obj: Record<string, unknown>, key: string, path: string): number {
|
||||
const value = obj[key];
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new Error(`${path}.${key} must be a positive number`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function portPair(obj: Record<string, unknown>, key: string): { port: number; containerPort: number } {
|
||||
const value = asRecord(obj[key], `network.${key}`);
|
||||
return { port: numberField(value, "port", `network.${key}`), containerPort: numberField(value, "containerPort", `network.${key}`) };
|
||||
}
|
||||
|
||||
export function readConfig(): UniDeskConfig {
|
||||
const configPath = rootPath("config.json");
|
||||
if (!existsSync(configPath)) throw new Error(`config.json not found at ${configPath}`);
|
||||
const raw = readFileSync(configPath, "utf8");
|
||||
const parsed = asRecord(JSON.parse(raw) as unknown, "config.json");
|
||||
const project = asRecord(parsed.project, "project");
|
||||
const runtime = asRecord(parsed.runtime, "runtime");
|
||||
const network = asRecord(parsed.network, "network");
|
||||
const database = asRecord(parsed.database, "database");
|
||||
const providerGateway = asRecord(parsed.providerGateway, "providerGateway");
|
||||
const docker = asRecord(parsed.docker, "docker");
|
||||
const paths = asRecord(parsed.paths, "paths");
|
||||
const sshForwarding = asRecord(parsed.sshForwarding, "sshForwarding");
|
||||
const labels = asRecord(providerGateway.labels, "providerGateway.labels");
|
||||
const typescript = stringField(runtime, "typescript", "runtime");
|
||||
if (typescript !== "bun") throw new Error("runtime.typescript must be bun");
|
||||
return {
|
||||
project: { name: stringField(project, "name", "project"), timezone: stringField(project, "timezone", "project") },
|
||||
runtime: { typescript, bunVersion: stringField(runtime, "bunVersion", "runtime") },
|
||||
network: {
|
||||
host: stringField(network, "host", "network"),
|
||||
publicHost: stringField(network, "publicHost", "network"),
|
||||
core: portPair(network, "core"),
|
||||
frontend: portPair(network, "frontend"),
|
||||
database: portPair(network, "database"),
|
||||
},
|
||||
database: {
|
||||
user: stringField(database, "user", "database"),
|
||||
password: stringField(database, "password", "database"),
|
||||
name: stringField(database, "name", "database"),
|
||||
volume: stringField(database, "volume", "database"),
|
||||
volumeSize: stringField(database, "volumeSize", "database"),
|
||||
},
|
||||
providerGateway: {
|
||||
id: stringField(providerGateway, "id", "providerGateway"),
|
||||
name: stringField(providerGateway, "name", "providerGateway"),
|
||||
token: stringField(providerGateway, "token", "providerGateway"),
|
||||
labels,
|
||||
heartbeatIntervalMs: numberField(providerGateway, "heartbeatIntervalMs", "providerGateway"),
|
||||
reconnectBaseMs: numberField(providerGateway, "reconnectBaseMs", "providerGateway"),
|
||||
reconnectMaxMs: numberField(providerGateway, "reconnectMaxMs", "providerGateway"),
|
||||
},
|
||||
docker: { composeFile: stringField(docker, "composeFile", "docker"), projectName: stringField(docker, "projectName", "docker") },
|
||||
paths: {
|
||||
stateDir: stringField(paths, "stateDir", "paths"),
|
||||
logsDir: stringField(paths, "logsDir", "paths"),
|
||||
docsReferenceDir: stringField(paths, "docsReferenceDir", "paths"),
|
||||
},
|
||||
sshForwarding: {
|
||||
mode: stringField(sshForwarding, "mode", "sshForwarding"),
|
||||
keyDir: stringField(sshForwarding, "keyDir", "sshForwarding"),
|
||||
host: stringField(sshForwarding, "host", "sshForwarding"),
|
||||
port: numberField(sshForwarding, "port", "sshForwarding"),
|
||||
user: stringField(sshForwarding, "user", "sshForwarding"),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user