169 lines
4.2 KiB
TypeScript
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();
|