Record project continuation changes
This commit is contained in:
+814
-3
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
AuditEvent,
|
||||
AuditSummary,
|
||||
AuthResult,
|
||||
BootstrapOwnerResult,
|
||||
Cluster,
|
||||
@@ -12,19 +13,49 @@ import type {
|
||||
FabricEntryPointNode,
|
||||
FabricEgressPool,
|
||||
FabricEgressPoolNode,
|
||||
FabricServiceChannelAdaptivePolicy,
|
||||
FabricServiceChannelBreadcrumbWindowPolicy,
|
||||
FabricServiceChannelPoolPolicy,
|
||||
FabricServiceChannelRecoveryPolicy,
|
||||
FabricServiceChannelRouteFeedbackObservation,
|
||||
FabricServiceChannelRouteRebuildAlertSilence,
|
||||
FabricServiceChannelRouteRebuildAttempt,
|
||||
FabricServiceChannelRouteRebuildHealthSummary,
|
||||
FabricServiceChannelRouteRebuildIncident,
|
||||
FabricServiceChannelRebuildInvestigationBreadcrumbs,
|
||||
ExpireFabricServiceChannelRouteFeedbackResult,
|
||||
FabricServiceChannelReadiness,
|
||||
FabricServiceChannelRebuildSnapshotMaintenanceHealth,
|
||||
FabricServiceChannelRebuildSnapshotWarmup,
|
||||
FabricServiceChannelLeaseMaintenance,
|
||||
FabricServiceChannelAccessTelemetry,
|
||||
FabricServiceChannelSchemaStatus,
|
||||
FabricTestingFlag,
|
||||
InstallationStatus,
|
||||
JoinRequest,
|
||||
MeshLink,
|
||||
MeshRouteIntent,
|
||||
NodeJoinToken,
|
||||
NodeHeartbeat,
|
||||
NodeSyntheticMeshConfig,
|
||||
NodeTelemetryObservation,
|
||||
NodeUpdatePlan,
|
||||
NodeUpdatePolicy,
|
||||
NodeUpdateStatus,
|
||||
NodeWorkloadDesiredState,
|
||||
OrganizationAdminSummary,
|
||||
Organization,
|
||||
OrganizationMembership,
|
||||
QoSPolicy,
|
||||
ReleaseVersion,
|
||||
Resource,
|
||||
RoleAssignment,
|
||||
UserAccount,
|
||||
VPNClientDiagnosticCommand,
|
||||
VPNClientDiagnosticStatus,
|
||||
VPNConnection,
|
||||
VPNConnectionLease,
|
||||
VPNPacketStats,
|
||||
WorkloadStatus,
|
||||
} from "../types";
|
||||
|
||||
@@ -69,6 +100,83 @@ export type CreateVPNConnectionPayload = {
|
||||
placementPolicy: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type UpsertNodeUpdatePolicyPayload = {
|
||||
product: string;
|
||||
channel?: string;
|
||||
targetVersion?: string | null;
|
||||
strategy?: string;
|
||||
enabled?: boolean;
|
||||
rollbackAllowed?: boolean;
|
||||
healthWindowSeconds?: number;
|
||||
};
|
||||
|
||||
export type UpdateFabricServiceChannelRecoveryPolicyPayload = {
|
||||
hysteresisPenalty?: number;
|
||||
promotionMinSamples?: number;
|
||||
demotionFailureThreshold?: number;
|
||||
demotionDropThreshold?: number;
|
||||
demotionSlowThreshold?: number;
|
||||
demotionRebuildEnabled?: boolean;
|
||||
demotionFencedEnabled?: boolean;
|
||||
};
|
||||
|
||||
export type UpdateFabricServiceChannelAdaptivePolicyPayload = {
|
||||
maxParallelWindow?: number;
|
||||
bulkPressureChannelThreshold?: number;
|
||||
queuePressureHighWatermark?: number;
|
||||
queuePressureMaxInFlight?: number;
|
||||
classWindows?: Record<string, number>;
|
||||
};
|
||||
|
||||
export type UpdateFabricServiceChannelPoolPolicyPayload = {
|
||||
entryPoolNodeIds?: string[];
|
||||
exitPoolNodeIds?: string[];
|
||||
preferredEntryNodeId?: string;
|
||||
preferredExitNodeId?: string;
|
||||
selectionStrategy?: string;
|
||||
routeRebuild?: string;
|
||||
entryFailover?: string;
|
||||
exitFailover?: string;
|
||||
backendFallbackAllowed?: boolean;
|
||||
stickySession?: boolean;
|
||||
};
|
||||
|
||||
export type UpdateFabricServiceChannelBreadcrumbWindowPolicyPayload = {
|
||||
currentWindowSeconds?: number;
|
||||
historyWindowSeconds?: number;
|
||||
};
|
||||
|
||||
export type CreateOrganizationPayload = {
|
||||
slug: string;
|
||||
name: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type CreateUserPayload = {
|
||||
email: string;
|
||||
password: string;
|
||||
platformRole?: string;
|
||||
};
|
||||
|
||||
export type CreateResourcePayload = {
|
||||
organizationId: string;
|
||||
name: string;
|
||||
address: string;
|
||||
protocol: string;
|
||||
secretRef?: string | null;
|
||||
certificateVerificationMode?: string;
|
||||
renderQualityProfile?: string;
|
||||
clipboardMode?: string;
|
||||
fileTransferMode?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type UpsertResourceSecretPayload = {
|
||||
username?: string;
|
||||
password?: string;
|
||||
domain?: string;
|
||||
};
|
||||
|
||||
export type CreateFabricEntryPointPayload = {
|
||||
name: string;
|
||||
endpointType: string;
|
||||
@@ -110,6 +218,12 @@ export class AdminApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
async refresh(input: { refreshToken: string }): Promise<AuthResult> {
|
||||
return this.post<AuthResult>("/auth/refresh", {
|
||||
refresh_token: input.refreshToken,
|
||||
});
|
||||
}
|
||||
|
||||
async getInstallationStatus(): Promise<InstallationStatus> {
|
||||
const payload = await this.request<{ installation: InstallationStatus }>("/installation/status", {
|
||||
method: "GET",
|
||||
@@ -244,6 +358,13 @@ export class AdminApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
async deleteClusterNode(clusterId: string, nodeId: string, reason: string): Promise<void> {
|
||||
await this.delete(`/clusters/${clusterId}/nodes/${nodeId}`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
|
||||
async listJoinRequests(clusterId: string): Promise<JoinRequest[]> {
|
||||
const payload = await this.get<{ join_requests: JoinRequest[] }>(`/clusters/${clusterId}/join-requests`);
|
||||
return payload.join_requests ?? [];
|
||||
@@ -260,6 +381,18 @@ export class AdminApiClient {
|
||||
return payload.join_token;
|
||||
}
|
||||
|
||||
async listJoinTokens(clusterId: string): Promise<NodeJoinToken[]> {
|
||||
const payload = await this.get<{ join_tokens: NodeJoinToken[] }>(`/clusters/${clusterId}/join-tokens`);
|
||||
return payload.join_tokens ?? [];
|
||||
}
|
||||
|
||||
async revokeJoinToken(clusterId: string, tokenId: string): Promise<NodeJoinToken> {
|
||||
const payload = await this.post<{ join_token: NodeJoinToken }>(`/clusters/${clusterId}/join-tokens/${tokenId}/revoke`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
});
|
||||
return payload.join_token;
|
||||
}
|
||||
|
||||
async approveJoinRequest(clusterId: string, requestId: string): Promise<void> {
|
||||
await this.post(`/clusters/${clusterId}/join-requests/${requestId}/approve`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
@@ -323,6 +456,49 @@ export class AdminApiClient {
|
||||
return payload.telemetry ?? [];
|
||||
}
|
||||
|
||||
async listReleaseVersions(clusterId: string, product = "rap-node-agent", channel = "dev"): Promise<ReleaseVersion[]> {
|
||||
const params = new URLSearchParams({ product, channel });
|
||||
const payload = await this.get<{ release_versions: ReleaseVersion[] }>(`/clusters/${clusterId}/updates/releases?${params.toString()}`);
|
||||
return payload.release_versions ?? [];
|
||||
}
|
||||
|
||||
async getNodeUpdatePlan(
|
||||
clusterId: string,
|
||||
nodeId: string,
|
||||
input: { product?: string; currentVersion?: string | null; os?: string; arch?: string; installType?: string; channel?: string },
|
||||
): Promise<NodeUpdatePlan> {
|
||||
const params = new URLSearchParams({
|
||||
product: input.product || "rap-node-agent",
|
||||
current_version: input.currentVersion || "",
|
||||
os: input.os || "linux",
|
||||
arch: input.arch || "amd64",
|
||||
install_type: input.installType || "docker",
|
||||
channel: input.channel || "dev",
|
||||
});
|
||||
const payload = await this.get<{ node_update_plan: NodeUpdatePlan }>(`/clusters/${clusterId}/nodes/${nodeId}/updates/plan?${params.toString()}`);
|
||||
return payload.node_update_plan;
|
||||
}
|
||||
|
||||
async upsertNodeUpdatePolicy(clusterId: string, nodeId: string, input: UpsertNodeUpdatePolicyPayload): Promise<NodeUpdatePolicy> {
|
||||
const payload = await this.put<{ node_update_policy: NodeUpdatePolicy }>(`/clusters/${clusterId}/nodes/${nodeId}/updates/policy`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
product: input.product,
|
||||
channel: input.channel || "dev",
|
||||
target_version: input.targetVersion ?? null,
|
||||
strategy: input.strategy || "rolling",
|
||||
enabled: input.enabled ?? true,
|
||||
rollback_allowed: input.rollbackAllowed ?? true,
|
||||
health_window_seconds: input.healthWindowSeconds || 180,
|
||||
});
|
||||
return payload.node_update_policy;
|
||||
}
|
||||
|
||||
async listNodeUpdateStatuses(clusterId: string, nodeId: string, limit = 80): Promise<NodeUpdateStatus[]> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId, limit: String(limit) });
|
||||
const payload = await this.get<{ node_update_statuses: NodeUpdateStatus[] }>(`/clusters/${clusterId}/nodes/${nodeId}/updates/statuses?${params.toString()}`);
|
||||
return payload.node_update_statuses ?? [];
|
||||
}
|
||||
|
||||
async listFabricTestingFlags(): Promise<FabricTestingFlag[]> {
|
||||
const payload = await this.get<{ testing_flags: FabricTestingFlag[] }>("/fabric/testing-flags");
|
||||
return payload.testing_flags ?? [];
|
||||
@@ -374,6 +550,28 @@ export class AdminApiClient {
|
||||
return payload.mesh_links ?? [];
|
||||
}
|
||||
|
||||
async listRouteIntents(clusterId: string): Promise<MeshRouteIntent[]> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ route_intents: MeshRouteIntent[] }>(`/clusters/${clusterId}/mesh/route-intents?${params.toString()}`);
|
||||
return payload.route_intents ?? [];
|
||||
}
|
||||
|
||||
async expireRouteIntent(clusterId: string, routeIntentId: string, reason: string): Promise<MeshRouteIntent> {
|
||||
const payload = await this.post<{ route_intent: MeshRouteIntent }>(`/clusters/${clusterId}/mesh/route-intents/${routeIntentId}/expire`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
reason,
|
||||
});
|
||||
return payload.route_intent;
|
||||
}
|
||||
|
||||
async disableRouteIntent(clusterId: string, routeIntentId: string, reason: string): Promise<MeshRouteIntent> {
|
||||
const payload = await this.post<{ route_intent: MeshRouteIntent }>(`/clusters/${clusterId}/mesh/route-intents/${routeIntentId}/disable`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
reason,
|
||||
});
|
||||
return payload.route_intent;
|
||||
}
|
||||
|
||||
async getNodeSyntheticMeshConfig(clusterId: string, nodeId: string): Promise<NodeSyntheticMeshConfig> {
|
||||
const payload = await this.get<{ synthetic_mesh_config: NodeSyntheticMeshConfig }>(
|
||||
`/clusters/${clusterId}/nodes/${nodeId}/mesh/synthetic-config`,
|
||||
@@ -381,6 +579,443 @@ export class AdminApiClient {
|
||||
return payload.synthetic_mesh_config;
|
||||
}
|
||||
|
||||
async listFabricServiceChannelRouteFeedback(
|
||||
clusterId: string,
|
||||
input: { reporterNodeId?: string; routeId?: string; serviceClass?: string; feedbackStatus?: string; includeExpired?: boolean } = {},
|
||||
): Promise<FabricServiceChannelRouteFeedbackObservation[]> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.reporterNodeId) {
|
||||
params.set("reporter_node_id", input.reporterNodeId);
|
||||
}
|
||||
if (input.routeId) {
|
||||
params.set("route_id", input.routeId);
|
||||
}
|
||||
if (input.serviceClass) {
|
||||
params.set("service_class", input.serviceClass);
|
||||
}
|
||||
if (input.feedbackStatus) {
|
||||
params.set("feedback_status", input.feedbackStatus);
|
||||
}
|
||||
if (input.includeExpired) {
|
||||
params.set("include_expired", "true");
|
||||
}
|
||||
const payload = await this.get<{ route_feedback: FabricServiceChannelRouteFeedbackObservation[] }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/route-feedback?${params.toString()}`,
|
||||
);
|
||||
return payload.route_feedback ?? [];
|
||||
}
|
||||
|
||||
async expireFabricServiceChannelRouteFeedback(
|
||||
clusterId: string,
|
||||
input: { routeId: string; reporterNodeId?: string; serviceClass?: string; reason?: string },
|
||||
): Promise<ExpireFabricServiceChannelRouteFeedbackResult> {
|
||||
const payload = await this.post<{ route_feedback_expire: ExpireFabricServiceChannelRouteFeedbackResult }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/route-feedback/expire`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
route_id: input.routeId,
|
||||
reporter_node_id: input.reporterNodeId || "",
|
||||
service_class: input.serviceClass || "",
|
||||
reason: input.reason || "expired from admin fabric diagnostics",
|
||||
},
|
||||
);
|
||||
return payload.route_feedback_expire;
|
||||
}
|
||||
|
||||
async listFabricServiceChannelRouteRebuildAttempts(
|
||||
clusterId: string,
|
||||
input: {
|
||||
reporterNodeId?: string;
|
||||
routeId?: string;
|
||||
replacementRouteId?: string;
|
||||
serviceClass?: string;
|
||||
rebuildStatus?: string;
|
||||
rebuildRequestId?: string;
|
||||
generation?: string;
|
||||
feedbackSource?: string;
|
||||
feedbackChannelId?: string;
|
||||
feedbackViolationStatus?: string;
|
||||
enrichment?: "summary" | "deep";
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
} = {},
|
||||
): Promise<FabricServiceChannelRouteRebuildAttempt[]> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.reporterNodeId) {
|
||||
params.set("reporter_node_id", input.reporterNodeId);
|
||||
}
|
||||
if (input.routeId) {
|
||||
params.set("route_id", input.routeId);
|
||||
}
|
||||
if (input.replacementRouteId) {
|
||||
params.set("replacement_route_id", input.replacementRouteId);
|
||||
}
|
||||
if (input.serviceClass) {
|
||||
params.set("service_class", input.serviceClass);
|
||||
}
|
||||
if (input.rebuildStatus) {
|
||||
params.set("rebuild_status", input.rebuildStatus);
|
||||
}
|
||||
if (input.rebuildRequestId) {
|
||||
params.set("rebuild_request_id", input.rebuildRequestId);
|
||||
}
|
||||
if (input.generation) {
|
||||
params.set("generation", input.generation);
|
||||
}
|
||||
if (input.feedbackSource) {
|
||||
params.set("feedback_source", input.feedbackSource);
|
||||
}
|
||||
if (input.feedbackChannelId) {
|
||||
params.set("feedback_channel_id", input.feedbackChannelId);
|
||||
}
|
||||
if (input.feedbackViolationStatus) {
|
||||
params.set("feedback_violation_status", input.feedbackViolationStatus);
|
||||
}
|
||||
if (input.enrichment) {
|
||||
params.set("enrichment", input.enrichment);
|
||||
}
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
if (input.offset) {
|
||||
params.set("offset", String(input.offset));
|
||||
}
|
||||
const payload = await this.get<{ rebuild_attempts: FabricServiceChannelRouteRebuildAttempt[] }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-attempts?${params.toString()}`,
|
||||
);
|
||||
return payload.rebuild_attempts ?? [];
|
||||
}
|
||||
|
||||
async getFabricServiceChannelRouteRebuildHealthSummary(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelRouteRebuildHealthSummary> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
const payload = await this.get<{ rebuild_health: FabricServiceChannelRouteRebuildHealthSummary }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-health?${params.toString()}`,
|
||||
);
|
||||
return payload.rebuild_health;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelReadiness(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelReadiness> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
const payload = await this.get<{ fabric_service_channel_readiness: FabricServiceChannelReadiness }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/readiness?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_readiness;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelSchemaStatus(clusterId: string): Promise<FabricServiceChannelSchemaStatus> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ fabric_service_channel_schema_status: FabricServiceChannelSchemaStatus }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/schema-status?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_schema_status;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelRebuildSnapshotMaintenanceHealth(
|
||||
clusterId: string,
|
||||
input: { limit?: number; minAgeSeconds?: number; heartbeatThreshold?: number } = {},
|
||||
): Promise<FabricServiceChannelRebuildSnapshotMaintenanceHealth> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
if (input.minAgeSeconds) {
|
||||
params.set("min_age_seconds", String(input.minAgeSeconds));
|
||||
}
|
||||
if (input.heartbeatThreshold) {
|
||||
params.set("heartbeat_threshold", String(input.heartbeatThreshold));
|
||||
}
|
||||
const payload = await this.get<{ rebuild_snapshot_health: FabricServiceChannelRebuildSnapshotMaintenanceHealth }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-snapshots/health?${params.toString()}`,
|
||||
);
|
||||
return payload.rebuild_snapshot_health;
|
||||
}
|
||||
|
||||
async warmupFabricServiceChannelRebuildSnapshots(
|
||||
clusterId: string,
|
||||
input: { limit?: number; staleAfterSeconds?: number } = {},
|
||||
): Promise<FabricServiceChannelRebuildSnapshotWarmup> {
|
||||
const payload = await this.post<{ rebuild_snapshot_warmup: FabricServiceChannelRebuildSnapshotWarmup }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-snapshots/warmup`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
limit: input.limit || 10,
|
||||
stale_after_seconds: input.staleAfterSeconds || 60,
|
||||
},
|
||||
);
|
||||
return payload.rebuild_snapshot_warmup;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelLeaseMaintenance(
|
||||
clusterId: string,
|
||||
input: { limit?: number; includeExpired?: boolean } = {},
|
||||
): Promise<FabricServiceChannelLeaseMaintenance> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
if (input.includeExpired) {
|
||||
params.set("include_expired", "true");
|
||||
}
|
||||
const payload = await this.get<{ fabric_service_channel_lease_maintenance: FabricServiceChannelLeaseMaintenance }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/leases?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_lease_maintenance;
|
||||
}
|
||||
|
||||
async cleanupFabricServiceChannelLeases(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelLeaseMaintenance> {
|
||||
const payload = await this.post<{ fabric_service_channel_lease_maintenance: FabricServiceChannelLeaseMaintenance }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/leases/cleanup`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
limit: input.limit || 100,
|
||||
},
|
||||
);
|
||||
return payload.fabric_service_channel_lease_maintenance;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelAccessTelemetry(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelAccessTelemetry> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
const payload = await this.get<{ fabric_service_channel_access_telemetry: FabricServiceChannelAccessTelemetry }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/access-telemetry?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_access_telemetry;
|
||||
}
|
||||
|
||||
async listFabricServiceChannelRouteRebuildIncidents(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelRouteRebuildIncident[]> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
const payload = await this.get<{ rebuild_incidents: FabricServiceChannelRouteRebuildIncident[] }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-incidents?${params.toString()}`,
|
||||
);
|
||||
return payload.rebuild_incidents ?? [];
|
||||
}
|
||||
|
||||
async getFabricServiceChannelRebuildInvestigationBreadcrumbs(
|
||||
clusterId: string,
|
||||
input: { limit?: number; currentWindowSeconds?: number; historyWindowSeconds?: number } = {},
|
||||
): Promise<FabricServiceChannelRebuildInvestigationBreadcrumbs> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
if (input.limit) {
|
||||
params.set("limit", String(input.limit));
|
||||
}
|
||||
if (input.currentWindowSeconds) {
|
||||
params.set("current_window_seconds", String(input.currentWindowSeconds));
|
||||
}
|
||||
if (input.historyWindowSeconds) {
|
||||
params.set("history_window_seconds", String(input.historyWindowSeconds));
|
||||
}
|
||||
const payload = await this.get<{ rebuild_investigation_breadcrumbs: FabricServiceChannelRebuildInvestigationBreadcrumbs }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-investigations/breadcrumbs?${params.toString()}`,
|
||||
);
|
||||
return payload.rebuild_investigation_breadcrumbs;
|
||||
}
|
||||
|
||||
async recordFabricServiceChannelRouteRebuildInvestigation(
|
||||
clusterId: string,
|
||||
input: {
|
||||
reporterNodeId?: string;
|
||||
routeId?: string;
|
||||
serviceClass?: string;
|
||||
generation?: string;
|
||||
guardStatus?: string;
|
||||
incidentId?: string;
|
||||
feedbackSource?: string;
|
||||
feedbackChannelId?: string;
|
||||
feedbackViolationStatus?: string;
|
||||
drilldownSource?: string;
|
||||
reason?: string;
|
||||
},
|
||||
): Promise<void> {
|
||||
await this.post(`/clusters/${clusterId}/fabric/service-channels/rebuild-incidents/investigations`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
reporter_node_id: input.reporterNodeId,
|
||||
route_id: input.routeId,
|
||||
service_class: input.serviceClass || "",
|
||||
generation: input.generation || "",
|
||||
guard_status: input.guardStatus || "",
|
||||
incident_id: input.incidentId || "",
|
||||
feedback_source: input.feedbackSource || "",
|
||||
feedback_channel_id: input.feedbackChannelId || "",
|
||||
feedback_violation_status: input.feedbackViolationStatus || "",
|
||||
drilldown_source: input.drilldownSource || "",
|
||||
reason: input.reason || "operator opened deep rebuild ledger",
|
||||
});
|
||||
}
|
||||
|
||||
async silenceFabricServiceChannelRouteRebuildAlert(
|
||||
clusterId: string,
|
||||
input: {
|
||||
incidentSource?: string;
|
||||
channelId?: string;
|
||||
reporterNodeId: string;
|
||||
routeId: string;
|
||||
guardStatus: string;
|
||||
generation?: string;
|
||||
reason?: string;
|
||||
ttlSeconds?: number;
|
||||
},
|
||||
): Promise<FabricServiceChannelRouteRebuildAlertSilence> {
|
||||
const payload = await this.post<{ rebuild_alert_silence: FabricServiceChannelRouteRebuildAlertSilence }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-health/silences`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
incident_source: input.incidentSource || "",
|
||||
channel_id: input.channelId || "",
|
||||
reporter_node_id: input.reporterNodeId,
|
||||
route_id: input.routeId,
|
||||
guard_status: input.guardStatus,
|
||||
generation: input.generation || "",
|
||||
reason: input.reason || "operator acknowledged rebuild alert",
|
||||
ttl_seconds: input.ttlSeconds || 21600,
|
||||
},
|
||||
);
|
||||
return payload.rebuild_alert_silence;
|
||||
}
|
||||
|
||||
async listFabricServiceChannelRouteRebuildAlertSilences(
|
||||
clusterId: string,
|
||||
): Promise<FabricServiceChannelRouteRebuildAlertSilence[]> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ rebuild_alert_silences: FabricServiceChannelRouteRebuildAlertSilence[] }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-health/silences?${params.toString()}`,
|
||||
);
|
||||
return payload.rebuild_alert_silences ?? [];
|
||||
}
|
||||
|
||||
async unsilenceFabricServiceChannelRouteRebuildAlert(
|
||||
clusterId: string,
|
||||
silenceId: string,
|
||||
reason?: string,
|
||||
): Promise<FabricServiceChannelRouteRebuildAlertSilence> {
|
||||
const payload = await this.delete<{ rebuild_alert_silence: FabricServiceChannelRouteRebuildAlertSilence }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/rebuild-health/silences/${encodeURIComponent(silenceId)}`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
reason: reason || "operator removed rebuild alert silence",
|
||||
},
|
||||
);
|
||||
return payload.rebuild_alert_silence;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelRecoveryPolicy(clusterId: string): Promise<FabricServiceChannelRecoveryPolicy> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ fabric_service_channel_recovery_policy: FabricServiceChannelRecoveryPolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/recovery-policy?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_recovery_policy;
|
||||
}
|
||||
|
||||
async updateFabricServiceChannelRecoveryPolicy(
|
||||
clusterId: string,
|
||||
input: UpdateFabricServiceChannelRecoveryPolicyPayload,
|
||||
): Promise<FabricServiceChannelRecoveryPolicy> {
|
||||
const payload = await this.put<{ fabric_service_channel_recovery_policy: FabricServiceChannelRecoveryPolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/recovery-policy`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
hysteresis_penalty: input.hysteresisPenalty,
|
||||
promotion_min_samples: input.promotionMinSamples,
|
||||
demotion_failure_threshold: input.demotionFailureThreshold,
|
||||
demotion_drop_threshold: input.demotionDropThreshold,
|
||||
demotion_slow_threshold: input.demotionSlowThreshold,
|
||||
demotion_rebuild_enabled: input.demotionRebuildEnabled,
|
||||
demotion_fenced_enabled: input.demotionFencedEnabled,
|
||||
},
|
||||
);
|
||||
return payload.fabric_service_channel_recovery_policy;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelAdaptivePolicy(clusterId: string): Promise<FabricServiceChannelAdaptivePolicy> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ fabric_service_channel_adaptive_policy: FabricServiceChannelAdaptivePolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/adaptive-policy?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_adaptive_policy;
|
||||
}
|
||||
|
||||
async updateFabricServiceChannelAdaptivePolicy(
|
||||
clusterId: string,
|
||||
input: UpdateFabricServiceChannelAdaptivePolicyPayload,
|
||||
): Promise<FabricServiceChannelAdaptivePolicy> {
|
||||
const payload = await this.put<{ fabric_service_channel_adaptive_policy: FabricServiceChannelAdaptivePolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/adaptive-policy`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
max_parallel_window: input.maxParallelWindow,
|
||||
bulk_pressure_channel_threshold: input.bulkPressureChannelThreshold,
|
||||
queue_pressure_high_watermark: input.queuePressureHighWatermark,
|
||||
queue_pressure_max_in_flight: input.queuePressureMaxInFlight,
|
||||
class_windows: input.classWindows,
|
||||
},
|
||||
);
|
||||
return payload.fabric_service_channel_adaptive_policy;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelPoolPolicy(clusterId: string): Promise<FabricServiceChannelPoolPolicy> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ fabric_service_channel_pool_policy: FabricServiceChannelPoolPolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/pool-policy?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_pool_policy;
|
||||
}
|
||||
|
||||
async updateFabricServiceChannelPoolPolicy(
|
||||
clusterId: string,
|
||||
input: UpdateFabricServiceChannelPoolPolicyPayload,
|
||||
): Promise<FabricServiceChannelPoolPolicy> {
|
||||
const payload = await this.put<{ fabric_service_channel_pool_policy: FabricServiceChannelPoolPolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/pool-policy`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
entry_pool_node_ids: input.entryPoolNodeIds,
|
||||
exit_pool_node_ids: input.exitPoolNodeIds,
|
||||
preferred_entry_node_id: input.preferredEntryNodeId,
|
||||
preferred_exit_node_id: input.preferredExitNodeId,
|
||||
selection_strategy: input.selectionStrategy,
|
||||
route_rebuild: input.routeRebuild,
|
||||
entry_failover: input.entryFailover,
|
||||
exit_failover: input.exitFailover,
|
||||
backend_fallback_allowed: input.backendFallbackAllowed,
|
||||
sticky_session: input.stickySession,
|
||||
},
|
||||
);
|
||||
return payload.fabric_service_channel_pool_policy;
|
||||
}
|
||||
|
||||
async getFabricServiceChannelBreadcrumbWindowPolicy(clusterId: string): Promise<FabricServiceChannelBreadcrumbWindowPolicy> {
|
||||
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
|
||||
const payload = await this.get<{ fabric_service_channel_breadcrumb_window_policy: FabricServiceChannelBreadcrumbWindowPolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/breadcrumb-window-policy?${params.toString()}`,
|
||||
);
|
||||
return payload.fabric_service_channel_breadcrumb_window_policy;
|
||||
}
|
||||
|
||||
async updateFabricServiceChannelBreadcrumbWindowPolicy(
|
||||
clusterId: string,
|
||||
input: UpdateFabricServiceChannelBreadcrumbWindowPolicyPayload,
|
||||
): Promise<FabricServiceChannelBreadcrumbWindowPolicy> {
|
||||
const payload = await this.put<{ fabric_service_channel_breadcrumb_window_policy: FabricServiceChannelBreadcrumbWindowPolicy }>(
|
||||
`/clusters/${clusterId}/fabric/service-channels/breadcrumb-window-policy`,
|
||||
{
|
||||
actor_user_id: this.actorUserId,
|
||||
current_window_seconds: input.currentWindowSeconds,
|
||||
history_window_seconds: input.historyWindowSeconds,
|
||||
},
|
||||
);
|
||||
return payload.fabric_service_channel_breadcrumb_window_policy;
|
||||
}
|
||||
|
||||
async listQoSPolicies(clusterId: string): Promise<QoSPolicy[]> {
|
||||
const payload = await this.get<{ qos_policies: QoSPolicy[] }>(`/clusters/${clusterId}/mesh/qos-policies`);
|
||||
return payload.qos_policies ?? [];
|
||||
@@ -519,6 +1154,46 @@ export class AdminApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
async getVPNPacketStats(clusterId: string, vpnConnectionId: string): Promise<VPNPacketStats> {
|
||||
const payload = await this.get<{ vpn_packet_stats: VPNPacketStats }>(
|
||||
`/clusters/${clusterId}/vpn-connections/${vpnConnectionId}/tunnel/stats`,
|
||||
);
|
||||
return payload.vpn_packet_stats ?? {};
|
||||
}
|
||||
|
||||
async getVPNClientDiagnosticStatus(clusterId: string, deviceId: string): Promise<VPNClientDiagnosticStatus | null> {
|
||||
if (!deviceId.trim()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const payload = await this.get<{ vpn_client_diagnostic_status: VPNClientDiagnosticStatus }>(
|
||||
`/clusters/${clusterId}/vpn/client-diagnostics/${encodeURIComponent(deviceId.trim())}/status`,
|
||||
);
|
||||
return payload.vpn_client_diagnostic_status ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async listVPNClientDiagnosticStatuses(clusterId: string): Promise<VPNClientDiagnosticStatus[]> {
|
||||
const payload = await this.get<{ vpn_client_diagnostic_statuses: VPNClientDiagnosticStatus[] }>(
|
||||
`/clusters/${clusterId}/vpn/client-diagnostics`,
|
||||
);
|
||||
return payload.vpn_client_diagnostic_statuses ?? [];
|
||||
}
|
||||
|
||||
async enqueueVPNClientDiagnosticCommand(
|
||||
clusterId: string,
|
||||
deviceId: string,
|
||||
command: Record<string, unknown>,
|
||||
): Promise<VPNClientDiagnosticCommand> {
|
||||
const payload = await this.post<{ vpn_client_diagnostic_command: VPNClientDiagnosticCommand }>(
|
||||
`/clusters/${clusterId}/vpn/client-diagnostics/${encodeURIComponent(deviceId.trim())}/commands`,
|
||||
command,
|
||||
);
|
||||
return payload.vpn_client_diagnostic_command;
|
||||
}
|
||||
|
||||
async expireStaleVPNLeases(clusterId: string): Promise<VPNConnectionLease[]> {
|
||||
const payload = await this.post<{ expired_leases: VPNConnectionLease[] }>(
|
||||
`/clusters/${clusterId}/vpn-connection-leases/expire-stale`,
|
||||
@@ -529,9 +1204,47 @@ export class AdminApiClient {
|
||||
return payload.expired_leases ?? [];
|
||||
}
|
||||
|
||||
async listAudit(clusterId: string): Promise<AuditEvent[]> {
|
||||
const payload = await this.get<{ audit_events: AuditEvent[] }>(`/clusters/${clusterId}/audit?limit=100`);
|
||||
return payload.audit_events ?? [];
|
||||
async listAudit(
|
||||
clusterId: string,
|
||||
input: {
|
||||
eventTypes?: string[];
|
||||
targetTypes?: string[];
|
||||
correlation?: string;
|
||||
limit?: number;
|
||||
} = {},
|
||||
): Promise<AuditEvent[]> {
|
||||
return (await this.listAuditDetailed(clusterId, input)).events;
|
||||
}
|
||||
|
||||
async listAuditDetailed(
|
||||
clusterId: string,
|
||||
input: {
|
||||
eventTypes?: string[];
|
||||
targetTypes?: string[];
|
||||
correlation?: string;
|
||||
limit?: number;
|
||||
} = {},
|
||||
): Promise<{ events: AuditEvent[]; summary?: AuditSummary }> {
|
||||
const params = new URLSearchParams({ limit: String(input.limit || 100) });
|
||||
for (const eventType of input.eventTypes || []) {
|
||||
if (eventType) {
|
||||
params.append("event_type", eventType);
|
||||
}
|
||||
}
|
||||
for (const targetType of input.targetTypes || []) {
|
||||
if (targetType) {
|
||||
params.append("target_type", targetType);
|
||||
}
|
||||
}
|
||||
if (input.correlation) {
|
||||
params.set("correlation", input.correlation);
|
||||
}
|
||||
const payload = await this.get<{ audit_events: AuditEvent[]; audit_summary?: AuditSummary }>(`/clusters/${clusterId}/audit?${params.toString()}`);
|
||||
return { events: payload.audit_events ?? [], summary: payload.audit_summary };
|
||||
}
|
||||
|
||||
clusterEventsURL(clusterId: string): string {
|
||||
return `${this.baseUrl}/clusters/${encodeURIComponent(clusterId)}/events?actor_user_id=${encodeURIComponent(this.actorUserId)}`;
|
||||
}
|
||||
|
||||
async getOrganizationAdminSummary(organizationId: string): Promise<OrganizationAdminSummary> {
|
||||
@@ -541,6 +1254,96 @@ export class AdminApiClient {
|
||||
return payload.admin_summary;
|
||||
}
|
||||
|
||||
async listOrganizations(): Promise<Organization[]> {
|
||||
const payload = await this.request<{ organizations: Organization[] }>(
|
||||
`/organizations?user_id=${encodeURIComponent(this.actorUserId)}`,
|
||||
{ method: "GET" },
|
||||
);
|
||||
return payload.organizations ?? [];
|
||||
}
|
||||
|
||||
async createOrganization(input: CreateOrganizationPayload): Promise<Organization> {
|
||||
const payload = await this.post<{ organization: Organization }>("/organizations/", {
|
||||
actor_user_id: this.actorUserId,
|
||||
slug: input.slug,
|
||||
name: input.name,
|
||||
metadata: input.metadata || {},
|
||||
});
|
||||
return payload.organization;
|
||||
}
|
||||
|
||||
async listUsers(): Promise<UserAccount[]> {
|
||||
const payload = await this.get<{ users: UserAccount[] }>("/users/");
|
||||
return payload.users ?? [];
|
||||
}
|
||||
|
||||
async createUser(input: CreateUserPayload): Promise<UserAccount> {
|
||||
const payload = await this.post<{ user: UserAccount }>("/users/", {
|
||||
actor_user_id: this.actorUserId,
|
||||
email: input.email,
|
||||
password: input.password,
|
||||
platform_role: input.platformRole || "user",
|
||||
});
|
||||
return payload.user;
|
||||
}
|
||||
|
||||
async listOrganizationMemberships(organizationId: string): Promise<OrganizationMembership[]> {
|
||||
const payload = await this.request<{ memberships: OrganizationMembership[] }>(
|
||||
`/organizations/${organizationId}/memberships?user_id=${encodeURIComponent(this.actorUserId)}`,
|
||||
{ method: "GET" },
|
||||
);
|
||||
return payload.memberships ?? [];
|
||||
}
|
||||
|
||||
async addOrganizationMembership(organizationId: string, input: { userId: string; roleId: string }): Promise<OrganizationMembership> {
|
||||
const payload = await this.post<{ membership: OrganizationMembership }>(`/organizations/${organizationId}/memberships`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
user_id: input.userId,
|
||||
role_id: input.roleId,
|
||||
});
|
||||
return payload.membership;
|
||||
}
|
||||
|
||||
async listResources(organizationId?: string): Promise<Resource[]> {
|
||||
const params = new URLSearchParams({ user_id: this.actorUserId });
|
||||
if (organizationId) {
|
||||
params.set("organization_id", organizationId);
|
||||
}
|
||||
const payload = await this.request<{ resources: Resource[] }>(`/resources?${params.toString()}`, { method: "GET" });
|
||||
return payload.resources ?? [];
|
||||
}
|
||||
|
||||
async createResource(input: CreateResourcePayload): Promise<Resource> {
|
||||
const payload = await this.post<{ resource: Resource }>("/resources/", {
|
||||
actor_user_id: this.actorUserId,
|
||||
organization_id: input.organizationId,
|
||||
name: input.name,
|
||||
address: input.address,
|
||||
protocol: input.protocol || "rdp",
|
||||
secret_ref: input.secretRef || null,
|
||||
certificate_verification_mode: input.certificateVerificationMode || "strict",
|
||||
render_quality_profile: input.renderQualityProfile || "balanced",
|
||||
clipboard_mode: input.clipboardMode || "disabled",
|
||||
file_transfer_mode: input.fileTransferMode || "disabled",
|
||||
metadata: input.metadata || {},
|
||||
});
|
||||
return payload.resource;
|
||||
}
|
||||
|
||||
async upsertResourceSecret(resourceId: string, input: UpsertResourceSecretPayload): Promise<void> {
|
||||
await this.put(`/resources/${resourceId}/secret`, {
|
||||
actor_user_id: this.actorUserId,
|
||||
payload: {
|
||||
username: input.username || "",
|
||||
password: input.password || "",
|
||||
domain: input.domain || "",
|
||||
},
|
||||
metadata: {
|
||||
source: "web-admin",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async get<T>(path: string): Promise<T> {
|
||||
const separator = path.includes("?") ? "&" : "?";
|
||||
return this.request<T>(`${path}${separator}actor_user_id=${encodeURIComponent(this.actorUserId)}`, {
|
||||
@@ -564,6 +1367,14 @@ export class AdminApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
private async delete<T>(path: string, body: unknown): Promise<T> {
|
||||
return this.request<T>(path, {
|
||||
method: "DELETE",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
private async request<T>(path: string, init: RequestInit): Promise<T> {
|
||||
const response = await fetch(`${this.baseUrl}${path}`, init);
|
||||
if (!response.ok) {
|
||||
|
||||
Reference in New Issue
Block a user