gadget/gadget-drone/src/services/platform.ts
2026-04-28 09:20:37 -04:00

169 lines
4.2 KiB
TypeScript

// src/services/platform.ts
// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
// 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<void> {
this.log.info("started");
}
async stop(): Promise<void> {
this.log.info("stopped");
}
async register(
email: string,
password: string,
): Promise<PlatformRegistration> {
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<void> {
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<void> {
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();