import type { IBaseOsRuntimeInfo, ICloudlyHeartbeatResult, ICloudlyRegisterResult, } from './types.ts'; export interface ICloudlyConnectorOptions { cloudlyUrl?: string; joinToken?: string; nodeToken?: string; } export class CloudlyConnector { private readonly cloudlyUrl?: string; private readonly joinToken?: string; private nodeToken?: string; constructor(optionsArg: ICloudlyConnectorOptions) { this.cloudlyUrl = optionsArg.cloudlyUrl; this.joinToken = optionsArg.joinToken; this.nodeToken = optionsArg.nodeToken; } public isConfigured() { return Boolean(this.cloudlyUrl); } public setNodeToken(nodeTokenArg: string | undefined) { this.nodeToken = nodeTokenArg; } public async registerNode(statusArg: IBaseOsRuntimeInfo): Promise { if (!this.cloudlyUrl) { return { accepted: false, message: 'Cloudly URL is not configured', }; } if (!this.joinToken && !this.nodeToken) { return { accepted: false, message: 'Neither join token nor node token is configured', }; } return await this.postJson('/baseos/v1/nodes/register', { joinToken: this.joinToken, nodeToken: this.nodeToken, status: statusArg, }); } public async sendHeartbeat(statusArg: IBaseOsRuntimeInfo): Promise { if (!this.cloudlyUrl) { return { accepted: false, message: 'Cloudly URL is not configured', }; } if (!this.nodeToken) { return { accepted: false, message: 'Node token is not configured', }; } return await this.postJson('/baseos/v1/nodes/heartbeat', { nodeToken: this.nodeToken, status: statusArg, }); } private async postJson( pathArg: string, bodyArg: Record, ): Promise { if (!this.cloudlyUrl) { throw new Error('Cloudly URL is not configured'); } const url = new URL( pathArg, this.cloudlyUrl.endsWith('/') ? this.cloudlyUrl : `${this.cloudlyUrl}/`, ); const response = await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', }, body: JSON.stringify(bodyArg), }); if (!response.ok) { throw new Error(`Cloudly request failed: ${pathArg} -> HTTP ${response.status}`); } return await response.json() as TResponse; } }