// src/services/platform.ts // Copyright (C) 2026 Rob Colbert // Licensed under the Apache License, Version 2.0 import env from "../config/env.ts"; import assert from "node:assert"; import path from "node:path"; import os from "node:os"; import { GadgetService } from "../lib/service.ts"; export enum DroneStatus { Starting = "starting", Available = "available", Busy = "busy", Offline = "offline", } export interface PlatformRegistration { _id: string; // your drone's registration ID, channel, and queue user: { _id: string; username: string; }; } interface PlatformApiResponse { success: boolean; message?: string; } interface PlatformRegistrationResponse extends PlatformApiResponse { data: PlatformRegistration; } class PlatformService extends GadgetService { registration: PlatformRegistration | undefined; get name(): string { return "PlatformService"; } get slug(): string { return "svc:platform"; } async start(): Promise { this.log.info("started"); } async stop(): Promise { this.log.info("stopped"); } async register( email: string, password: string, ): Promise { const url = this.getApiUrl("/drone/registration"); const body = JSON.stringify({ email, password, hostname: os.hostname(), }); const response = await fetch(url, { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", "Content-Length": body.length.toString(), "X-Gadget-Key": env.platform.apiKey, }, body, }); const json = (await response.json()) as PlatformRegistrationResponse; if (!json.success) { const error = new Error("failed to register drone with Platform"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } if (!json.data || !json.data._id) { const error = new Error( "registration response did not contain required data", ); error.name = "PlatformError"; throw error; } if (!json.data.user || !json.data.user._id) { const error = new Error( "registration response did not contain required user account data", ); error.name = "PlatformError"; throw error; } this.registration = json.data; return json.data; } async unregister(): Promise { if (!this.registration) { return; } const url = this.getApiUrl(`/drone/registration/${this.registration._id}`); const body = JSON.stringify({ registration: this.registration }); const response = await fetch(url, { method: "DELETE", headers: { Accept: "application/json", "Content-Type": "application/json", "Content-Length": body.length.toString(), "X-Gadget-Key": env.platform.apiKey, }, body, }); const json = (await response.json()) as PlatformRegistrationResponse; if (!json.success) { const error = new Error("failed to register drone with Platform"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } } async setStatus(status: DroneStatus): Promise { assert( this.registration, "must register with platform before setting status", ); const url = this.getApiUrl( `/drone/registration/${this.registration._id}/status`, ); const body = JSON.stringify({ status }); const response = await fetch(url, { method: "PUT", headers: { Accept: "application/json", "Content-Type": "application/json", "Content-Length": body.length.toString(), "X-Gadget-Key": env.platform.apiKey, }, body, }); const json = (await response.json()) as PlatformRegistrationResponse; if (!json.success) { const error = new Error("failed to register drone with Platform"); error.name = "PlatformError"; error.statusCode = response.status; throw error; } this.log.info("drone status updated on platform", { status }); } getApiUrl(url: string): string { return `${env.platform.baseUrl}/api/v1${url}`; } } export default new PlatformService();