398 lines
10 KiB
TypeScript
398 lines
10 KiB
TypeScript
import type {
|
|
NbDnsNameserverGroup,
|
|
NbEvent,
|
|
NbGroup,
|
|
NbNetwork,
|
|
NbNetworkResource,
|
|
NbNetworkRouter,
|
|
NbPeer,
|
|
NbPolicy,
|
|
NbPostureCheck,
|
|
NbRoute,
|
|
NbSetupKey,
|
|
NbUser,
|
|
} from "./types.ts";
|
|
|
|
/** Narrowed fetch signature used for dependency injection. */
|
|
export type FetchFn = (
|
|
input: string | URL | Request,
|
|
init?: RequestInit,
|
|
) => Promise<Response>;
|
|
|
|
/** Thrown when the NetBird API returns a non-2xx status. */
|
|
export class NetbirdApiError extends Error {
|
|
constructor(
|
|
public readonly status: number,
|
|
public readonly method: string,
|
|
public readonly path: string,
|
|
public readonly body: unknown,
|
|
) {
|
|
super(`NetBird API error: ${method} ${path} returned ${status}`);
|
|
this.name = "NetbirdApiError";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Thin HTTP client for the NetBird Management API.
|
|
*
|
|
* Accepts an injectable fetch function so callers (and tests) can swap
|
|
* the transport without touching the client logic.
|
|
*/
|
|
export class NetbirdClient {
|
|
constructor(
|
|
private readonly baseUrl: string,
|
|
private readonly token: string,
|
|
private readonly fetchFn: FetchFn = fetch,
|
|
) {}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Internal
|
|
// ---------------------------------------------------------------------------
|
|
|
|
private async request<T>(
|
|
method: string,
|
|
path: string,
|
|
body?: unknown,
|
|
): Promise<T> {
|
|
const url = `${this.baseUrl}${path}`;
|
|
const headers: Record<string, string> = {
|
|
"Authorization": `Token ${this.token}`,
|
|
"Accept": "application/json",
|
|
};
|
|
if (body !== undefined) {
|
|
headers["Content-Type"] = "application/json";
|
|
}
|
|
|
|
const resp = await this.fetchFn(url, {
|
|
method,
|
|
headers,
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
});
|
|
|
|
if (!resp.ok) {
|
|
const text = await resp.text();
|
|
let errorBody: unknown;
|
|
try {
|
|
errorBody = JSON.parse(text);
|
|
} catch {
|
|
errorBody = text;
|
|
}
|
|
throw new NetbirdApiError(resp.status, method, path, errorBody);
|
|
}
|
|
|
|
// 204 No Content — nothing to parse
|
|
if (resp.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
|
|
return (await resp.json()) as T;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Groups
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listGroups(): Promise<NbGroup[]> {
|
|
return this.request("GET", "/groups");
|
|
}
|
|
|
|
createGroup(data: { name: string; peers?: string[] }): Promise<NbGroup> {
|
|
return this.request("POST", "/groups", data);
|
|
}
|
|
|
|
updateGroup(
|
|
id: string,
|
|
data: { name?: string; peers?: string[] },
|
|
): Promise<NbGroup> {
|
|
return this.request("PUT", `/groups/${id}`, data);
|
|
}
|
|
|
|
deleteGroup(id: string): Promise<void> {
|
|
return this.request("DELETE", `/groups/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Setup Keys
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listSetupKeys(): Promise<NbSetupKey[]> {
|
|
return this.request("GET", "/setup-keys");
|
|
}
|
|
|
|
createSetupKey(data: {
|
|
name: string;
|
|
type: "one-off" | "reusable";
|
|
expires_in: number;
|
|
auto_groups?: string[];
|
|
usage_limit?: number;
|
|
}): Promise<NbSetupKey> {
|
|
return this.request("POST", "/setup-keys", data);
|
|
}
|
|
|
|
deleteSetupKey(id: number | string): Promise<void> {
|
|
return this.request("DELETE", `/setup-keys/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Peers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listPeers(): Promise<NbPeer[]> {
|
|
return this.request("GET", "/peers");
|
|
}
|
|
|
|
updatePeer(
|
|
id: string,
|
|
data: {
|
|
name?: string;
|
|
ssh_enabled?: boolean;
|
|
login_expiration_enabled?: boolean;
|
|
inactivity_expiration_enabled?: boolean;
|
|
},
|
|
): Promise<NbPeer> {
|
|
return this.request("PUT", `/peers/${id}`, data);
|
|
}
|
|
|
|
deletePeer(id: string): Promise<void> {
|
|
return this.request("DELETE", `/peers/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Policies
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listPolicies(): Promise<NbPolicy[]> {
|
|
return this.request("GET", "/policies");
|
|
}
|
|
|
|
createPolicy(data: Omit<NbPolicy, "id">): Promise<NbPolicy> {
|
|
return this.request("POST", "/policies", data);
|
|
}
|
|
|
|
updatePolicy(id: string, data: Omit<NbPolicy, "id">): Promise<NbPolicy> {
|
|
return this.request("PUT", `/policies/${id}`, data);
|
|
}
|
|
|
|
deletePolicy(id: string): Promise<void> {
|
|
return this.request("DELETE", `/policies/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Routes
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listRoutes(): Promise<NbRoute[]> {
|
|
return this.request("GET", "/routes");
|
|
}
|
|
|
|
createRoute(data: Omit<NbRoute, "id">): Promise<NbRoute> {
|
|
return this.request("POST", "/routes", data);
|
|
}
|
|
|
|
updateRoute(id: string, data: Omit<NbRoute, "id">): Promise<NbRoute> {
|
|
return this.request("PUT", `/routes/${id}`, data);
|
|
}
|
|
|
|
deleteRoute(id: string): Promise<void> {
|
|
return this.request("DELETE", `/routes/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DNS Nameserver Groups
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listDnsNameserverGroups(): Promise<NbDnsNameserverGroup[]> {
|
|
return this.request("GET", "/dns/nameservers");
|
|
}
|
|
|
|
createDnsNameserverGroup(
|
|
data: Omit<NbDnsNameserverGroup, "id">,
|
|
): Promise<NbDnsNameserverGroup> {
|
|
return this.request("POST", "/dns/nameservers", data);
|
|
}
|
|
|
|
updateDnsNameserverGroup(
|
|
id: string,
|
|
data: Omit<NbDnsNameserverGroup, "id">,
|
|
): Promise<NbDnsNameserverGroup> {
|
|
return this.request("PUT", `/dns/nameservers/${id}`, data);
|
|
}
|
|
|
|
deleteDnsNameserverGroup(id: string): Promise<void> {
|
|
return this.request("DELETE", `/dns/nameservers/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Events
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listEvents(): Promise<NbEvent[]> {
|
|
return this.request("GET", "/events/audit");
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Posture Checks
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listPostureChecks(): Promise<NbPostureCheck[]> {
|
|
return this.request("GET", "/posture-checks");
|
|
}
|
|
|
|
createPostureCheck(
|
|
data: Omit<NbPostureCheck, "id">,
|
|
): Promise<NbPostureCheck> {
|
|
return this.request("POST", "/posture-checks", data);
|
|
}
|
|
|
|
updatePostureCheck(
|
|
id: string,
|
|
data: Omit<NbPostureCheck, "id">,
|
|
): Promise<NbPostureCheck> {
|
|
return this.request("PUT", `/posture-checks/${id}`, data);
|
|
}
|
|
|
|
deletePostureCheck(id: string): Promise<void> {
|
|
return this.request("DELETE", `/posture-checks/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Networks
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listNetworks(): Promise<NbNetwork[]> {
|
|
return this.request("GET", "/networks");
|
|
}
|
|
|
|
createNetwork(
|
|
data: { name: string; description?: string },
|
|
): Promise<NbNetwork> {
|
|
return this.request("POST", "/networks", data);
|
|
}
|
|
|
|
updateNetwork(
|
|
id: string,
|
|
data: { name: string; description?: string },
|
|
): Promise<NbNetwork> {
|
|
return this.request("PUT", `/networks/${id}`, data);
|
|
}
|
|
|
|
deleteNetwork(id: string): Promise<void> {
|
|
return this.request("DELETE", `/networks/${id}`);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Network Resources
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listNetworkResources(networkId: string): Promise<NbNetworkResource[]> {
|
|
return this.request("GET", `/networks/${networkId}/resources`);
|
|
}
|
|
|
|
createNetworkResource(
|
|
networkId: string,
|
|
data: {
|
|
name: string;
|
|
description?: string;
|
|
address: string;
|
|
enabled: boolean;
|
|
groups: string[];
|
|
},
|
|
): Promise<NbNetworkResource> {
|
|
return this.request("POST", `/networks/${networkId}/resources`, data);
|
|
}
|
|
|
|
updateNetworkResource(
|
|
networkId: string,
|
|
resourceId: string,
|
|
data: {
|
|
name: string;
|
|
description?: string;
|
|
address: string;
|
|
enabled: boolean;
|
|
groups: string[];
|
|
},
|
|
): Promise<NbNetworkResource> {
|
|
return this.request(
|
|
"PUT",
|
|
`/networks/${networkId}/resources/${resourceId}`,
|
|
data,
|
|
);
|
|
}
|
|
|
|
deleteNetworkResource(
|
|
networkId: string,
|
|
resourceId: string,
|
|
): Promise<void> {
|
|
return this.request(
|
|
"DELETE",
|
|
`/networks/${networkId}/resources/${resourceId}`,
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Network Routers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listNetworkRouters(networkId: string): Promise<NbNetworkRouter[]> {
|
|
return this.request("GET", `/networks/${networkId}/routers`);
|
|
}
|
|
|
|
createNetworkRouter(
|
|
networkId: string,
|
|
data: Omit<NbNetworkRouter, "id">,
|
|
): Promise<NbNetworkRouter> {
|
|
return this.request("POST", `/networks/${networkId}/routers`, data);
|
|
}
|
|
|
|
updateNetworkRouter(
|
|
networkId: string,
|
|
routerId: string,
|
|
data: Omit<NbNetworkRouter, "id">,
|
|
): Promise<NbNetworkRouter> {
|
|
return this.request(
|
|
"PUT",
|
|
`/networks/${networkId}/routers/${routerId}`,
|
|
data,
|
|
);
|
|
}
|
|
|
|
deleteNetworkRouter(
|
|
networkId: string,
|
|
routerId: string,
|
|
): Promise<void> {
|
|
return this.request(
|
|
"DELETE",
|
|
`/networks/${networkId}/routers/${routerId}`,
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Users
|
|
// ---------------------------------------------------------------------------
|
|
|
|
listUsers(): Promise<NbUser[]> {
|
|
return this.request("GET", "/users");
|
|
}
|
|
|
|
createUser(data: {
|
|
email: string;
|
|
name?: string;
|
|
role: string;
|
|
auto_groups: string[];
|
|
is_service_user: boolean;
|
|
}): Promise<NbUser> {
|
|
return this.request("POST", "/users", data);
|
|
}
|
|
|
|
updateUser(
|
|
id: string,
|
|
data: { name?: string; role?: string; auto_groups?: string[] },
|
|
): Promise<NbUser> {
|
|
return this.request("PUT", `/users/${id}`, data);
|
|
}
|
|
|
|
deleteUser(id: string): Promise<void> {
|
|
return this.request("DELETE", `/users/${id}`);
|
|
}
|
|
}
|