@multicloud-io/multicloud-connection-js
Version:
Shared TypeScript/JavaScript library for connecting to Multicloud servers with mTLS authentication
328 lines • 11.5 kB
JavaScript
;
/**
* Multicloud REST API client
*
* Provides a comprehensive client for interacting with Multicloud servers
* with mTLS authentication and proper error handling.
*
* IMPORTANT: This is for server-side use only - never expose to client/browser!
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MulticloudRESTClient = exports.TaskStatus = exports.Orchestration = exports.State = void 0;
const https_1 = __importDefault(require("https"));
const fs_1 = __importDefault(require("fs"));
const exceptions_1 = require("./exceptions");
/**
* State of the job
*/
var State;
(function (State) {
State["LOADED"] = "LOADED";
State["STARTING"] = "STARTING";
State["RUNNING"] = "RUNNING";
State["STOPPING"] = "STOPPING";
State["STOPPED"] = "STOPPED";
State["PENDING_DESTROY"] = "PENDING_DESTROY";
State["FINISHED"] = "FINISHED";
})(State || (exports.State = State = {}));
/**
* Orchestration types
*/
var Orchestration;
(function (Orchestration) {
Orchestration["EXTERNAL"] = "EXTERNAL";
Orchestration["ALL"] = "ALL";
Orchestration["EXACTLY"] = "EXACTLY";
Orchestration["EXACTLY_ONCE"] = "EXACTLY_ONCE";
Orchestration["ACTIVE_STANDBY"] = "ACTIVE_STANDBY";
// Add other values as needed
})(Orchestration || (exports.Orchestration = Orchestration = {}));
/**
* Status of the task
*/
var TaskStatus;
(function (TaskStatus) {
TaskStatus["UNKNOWN"] = "UNKNOWN";
TaskStatus["STARTING"] = "STARTING";
TaskStatus["STANDBY"] = "STANDBY";
TaskStatus["RUNNING"] = "RUNNING";
TaskStatus["RESTARTING"] = "RESTARTING";
TaskStatus["STOPPING"] = "STOPPING";
TaskStatus["STOPPED"] = "STOPPED";
TaskStatus["DONE"] = "DONE";
TaskStatus["ERROR"] = "ERROR";
TaskStatus["PENDING_SHUTDOWN"] = "PENDING_SHUTDOWN";
TaskStatus["PENDING_RESTART"] = "PENDING_RESTART";
TaskStatus["CANCELLED"] = "CANCELLED";
})(TaskStatus || (exports.TaskStatus = TaskStatus = {}));
/**
* REST client for Multicloud API
*/
class MulticloudRESTClient {
constructor(params) {
this.baseUrl = params.serverUrl.replace(/\/$/, ''); // Remove trailing slash
this.timeout = params.timeout;
this.debug = params.debug;
this.accessToken = params.accessToken;
// Create HTTPS agent with mTLS configuration
const agentOptions = {
rejectUnauthorized: params.verifySsl,
};
// Add client certificates if provided
if (params.clientCert && params.clientKey) {
try {
agentOptions.cert = fs_1.default.readFileSync(params.clientCert);
agentOptions.key = fs_1.default.readFileSync(params.clientKey);
if (this.debug) {
console.log('[MulticloudRESTClient] Loaded mTLS certificates');
}
}
catch (error) {
throw new exceptions_1.MulticloudAuthenticationError(`Failed to load client certificates: ${error}`, error instanceof Error ? error : undefined);
}
}
this.agent = new https_1.default.Agent(agentOptions);
if (this.debug) {
console.log('[MulticloudRESTClient] Initialized client:', {
baseUrl: this.baseUrl,
verifySsl: params.verifySsl,
timeout: this.timeout,
hasCertificates: !!(params.clientCert && params.clientKey)
});
}
}
/**
* Make an HTTP request to the Multicloud API
*/
async makeRequest(method, endpoint, body) {
const url = `${this.baseUrl}${endpoint}`;
if (this.debug) {
console.log(`[MulticloudRESTClient] ${method} ${url}`);
}
return new Promise((resolve, reject) => {
const options = {
method,
agent: this.agent,
timeout: this.timeout,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
};
// Add Authorization header if access token is available
if (this.accessToken) {
options.headers['Authorization'] = `Bearer ${this.accessToken}`;
}
const req = https_1.default.request(url, options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
if (this.debug) {
console.log(`[MulticloudRESTClient] Response status: ${res.statusCode}`);
}
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
reject(new exceptions_1.MulticloudNetworkError(`HTTP ${res.statusCode}: ${res.statusMessage}. Details: ${data}`, res.statusCode));
return;
}
// Handle empty responses
if (!data.trim()) {
resolve(null);
return;
}
const result = JSON.parse(data);
resolve(result);
}
catch (error) {
reject(new exceptions_1.MulticloudResponseError(`Failed to parse response: ${error}`, error instanceof Error ? error : undefined));
}
});
});
req.on('error', (error) => {
if (error.message.includes('ECONNREFUSED')) {
reject(new exceptions_1.MulticloudConnectionError(`Connection refused to ${this.baseUrl}. Is the Multicloud server running?`, error));
}
else if (error.message.includes('certificate')) {
reject(new exceptions_1.MulticloudAuthenticationError(`Certificate authentication failed: ${error.message}`, error));
}
else {
reject(new exceptions_1.MulticloudNetworkError(`Network error: ${error.message}`, undefined, error));
}
});
req.on('timeout', () => {
req.destroy();
reject(new exceptions_1.MulticloudNetworkError(`Request timeout after ${this.timeout}ms`));
});
// Send request body if provided
if (body) {
req.write(JSON.stringify(body));
}
req.end();
});
}
/**
* Update the access token for subsequent requests
* @param accessToken New JWT access token
*/
setAccessToken(accessToken) {
this.accessToken = accessToken;
if (this.debug) {
console.log('[MulticloudRESTClient] Access token updated');
}
}
/**
* Clear the access token
*/
clearAccessToken() {
this.accessToken = undefined;
if (this.debug) {
console.log('[MulticloudRESTClient] Access token cleared');
}
}
/**
* Test connection to the Multicloud server
*/
async testConnection() {
try {
// Try to get a simple endpoint to test connectivity
await this.makeRequest('GET', '/health');
return true;
}
catch (error) {
if (this.debug) {
console.log('[MulticloudRESTClient] Connection test failed:', error);
}
throw error;
}
}
/** get tasks for a cluster, or if null or it "*" for all clusters */
async getTasks(clusterId) {
if (clusterId === null || clusterId === "*" || clusterId === undefined) {
return this.makeRequest('GET', '/coordinator/tasks');
}
else {
return this.makeRequest('GET', `/coordinator/tasks?clusterId=${clusterId}`);
}
}
/**
* Get all clusters
*/
async getClusters() {
return this.makeRequest('GET', '/admin/clusters');
}
/**
* Get clusters for a specific application - Uses application-specific filtering
*/
async getApplicationClusters(applicationId) {
return this.makeRequest('GET', `/applications/clusters?applicationId=${applicationId}`);
}
/**
* Get a specific cluster by ID
*/
async getCluster(clusterId) {
return this.makeRequest('GET', `/admin/clusters/${clusterId}`);
}
/**
* Get all jobs
*/
async getJobs() {
return this.makeRequest('GET', '/coordinator/jobs');
}
/**
* Get jobs for a specific cluster
*/
async getClusterJobs(clusterId) {
return this.makeRequest('GET', `/coordinator/jobs?clusterId=${clusterId}`);
}
/**
* Get a specific job by ID
*/
async getJob(jobId) {
return this.makeRequest('GET', `/coordinator/jobs/${jobId}`);
}
/**
* Create a new job
*/
async createJob(clusterId, jobData) {
return this.makeRequest('POST', `/coordinator/job?clusterId=${clusterId}`, jobData);
}
/**
* Update a job
*/
async updateJob(action, jobId, clusterId) {
const params = new URLSearchParams({ action });
// Only append clusterId if it's not null
if (clusterId) {
params.append('clusterId', clusterId);
params.append('action', action);
}
return this.makeRequest('PUT', `/coordinator/job/${jobId}?${params.toString()}`); // Updated to return Response
}
/**
* Delete a job
*/
async deleteJob(jobId) {
return this.makeRequest('DELETE', `/coordinator/jobs/${jobId}`);
}
/**
* Get all servers
*/
async getServers() {
return this.makeRequest('GET', '/admin/servers');
}
/**
* Get servers for a specific cluster
*/
async getClusterServers(clusterId) {
return this.makeRequest('GET', `/admin/servers?clusterId=${clusterId}`);
}
/**
* Get a specific server by ID
*/
async getServer(serverId) {
return this.makeRequest('GET', `/admin/servers/${serverId}`);
}
/**
* Create a new server
*/
async createServer(serverData) {
return this.makeRequest('POST', '/admin/servers', serverData);
}
/**
* Update a server
*/
async updateServer(serverId, serverData) {
return this.makeRequest('PUT', `/admin/servers/${serverId}`, serverData);
}
/**
* Delete a server
*/
async deleteServer(serverId) {
return this.makeRequest('DELETE', `/admin/servers/${serverId}`);
}
/**
* Get server status
*/
async getServerStatus(serverId) {
return this.makeRequest('GET', `/admin/servers/${serverId}/status`);
}
/**
* Restart a server
*/
async restartServer(serverId) {
return this.makeRequest('POST', `/admin/servers/${serverId}/restart`);
}
/**
* Get all MetaLocations for a cluster (id, name, region, url)
*/
async getClusterLocations(clusterId) {
return this.makeRequest('GET', `/admin/cluster/${clusterId}/locations`);
}
}
exports.MulticloudRESTClient = MulticloudRESTClient;
//# sourceMappingURL=client.js.map