121 lines
4.2 KiB
TypeScript
121 lines
4.2 KiB
TypeScript
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
|
|
import { HarborError } from '../../../shared/errors.js';
|
|
import { sendJson } from '../response.js';
|
|
|
|
interface DeviceInventoryPort {
|
|
snapshot(): unknown;
|
|
refresh(): Promise<unknown>;
|
|
update(deviceId: string, patch: Record<string, unknown>, expectedRevision: unknown): unknown;
|
|
createTag(name: unknown, expectedRevision: unknown): unknown;
|
|
renameTag(tagId: string, name: unknown, expectedRevision: unknown): unknown;
|
|
deleteTag(tagId: string, expectedRevision: unknown): unknown;
|
|
resetTraffic(expectedRevision: unknown): Promise<unknown>;
|
|
setPolicy(deviceId: string, mode: unknown, expectedRevision: unknown): Promise<unknown>;
|
|
}
|
|
|
|
interface DeviceInventoryRouteDependencies {
|
|
deviceInventory: DeviceInventoryPort | null;
|
|
readBody(req: IncomingMessage): Promise<Record<string, unknown>>;
|
|
}
|
|
|
|
const DEVICE_PATH = /^\/api\/devices\/(dev_[a-f0-9]{16})$/;
|
|
const DEVICE_POLICY_PATH = /^\/api\/devices\/(dev_[a-f0-9]{16})\/policy$/;
|
|
const DEVICE_TAG_PATH = /^\/api\/device-tags\/(tag_[a-f0-9]{16})$/;
|
|
|
|
export function createDeviceInventoryRoute(dependencies: DeviceInventoryRouteDependencies) {
|
|
return {
|
|
async handle(req: IncomingMessage, res: ServerResponse) {
|
|
const pathname = new URL(req.url || '/', 'http://localhost').pathname;
|
|
|
|
if (pathname === '/api/devices') {
|
|
if (!dependencies.deviceInventory || req.method !== 'GET') {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
sendJson(res, 200, dependencies.deviceInventory.snapshot());
|
|
return true;
|
|
}
|
|
|
|
if (pathname === '/api/devices/refresh') {
|
|
if (!dependencies.deviceInventory || req.method !== 'POST') {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
sendJson(res, 200, await dependencies.deviceInventory.refresh());
|
|
return true;
|
|
}
|
|
|
|
if (pathname === '/api/devices/traffic') {
|
|
if (!dependencies.deviceInventory || req.method !== 'DELETE') {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
const body = await dependencies.readBody(req);
|
|
sendJson(res, 200, await dependencies.deviceInventory.resetTraffic(body.expectedRevision));
|
|
return true;
|
|
}
|
|
|
|
if (pathname === '/api/device-tags') {
|
|
if (!dependencies.deviceInventory || req.method !== 'POST') {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
const body = await dependencies.readBody(req);
|
|
sendJson(
|
|
res,
|
|
200,
|
|
dependencies.deviceInventory.createTag(body.name, body.expectedRevision),
|
|
);
|
|
return true;
|
|
}
|
|
|
|
const tagMatch = pathname.match(DEVICE_TAG_PATH);
|
|
if (tagMatch) {
|
|
if (!dependencies.deviceInventory || !['PUT', 'DELETE'].includes(req.method || '')) {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
const body = await dependencies.readBody(req);
|
|
sendJson(
|
|
res,
|
|
200,
|
|
req.method === 'PUT'
|
|
? dependencies.deviceInventory.renameTag(tagMatch[1], body.name, body.expectedRevision)
|
|
: dependencies.deviceInventory.deleteTag(tagMatch[1], body.expectedRevision),
|
|
);
|
|
return true;
|
|
}
|
|
|
|
const deviceMatch = pathname.match(DEVICE_PATH);
|
|
if (deviceMatch) {
|
|
if (!dependencies.deviceInventory || req.method !== 'PUT') {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
const { expectedRevision, ...patch } = await dependencies.readBody(req);
|
|
sendJson(
|
|
res,
|
|
200,
|
|
dependencies.deviceInventory.update(deviceMatch[1], patch, expectedRevision),
|
|
);
|
|
return true;
|
|
}
|
|
|
|
const policyMatch = pathname.match(DEVICE_POLICY_PATH);
|
|
if (policyMatch) {
|
|
if (!dependencies.deviceInventory || req.method !== 'PUT') {
|
|
throw new HarborError('ENDPOINT_NOT_FOUND');
|
|
}
|
|
const body = await dependencies.readBody(req);
|
|
sendJson(
|
|
res,
|
|
200,
|
|
await dependencies.deviceInventory.setPolicy(
|
|
policyMatch[1],
|
|
body.mode,
|
|
body.expectedRevision,
|
|
),
|
|
);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
};
|
|
}
|