рабочий вариант, но скороть 10 МБит
This commit is contained in:
+17
-8
@@ -9,7 +9,7 @@ organization users.
|
||||
Architecture boundary:
|
||||
|
||||
- WEB is HTTP/HTTPS ingress and presentation.
|
||||
- Cluster configuration belongs to Control Plane APIs.
|
||||
- Cluster configuration is reached through the panel service gateway; the farm transport behind it remains QUIC fabric.
|
||||
- PostgreSQL remains the source of truth.
|
||||
- Dynamic admin pages must be safe, scoped, schema-driven projections.
|
||||
- Secrets, internal topology, peer caches, route caches, and raw credentials
|
||||
@@ -33,6 +33,7 @@ Implemented platform-owner sections:
|
||||
- node inventory
|
||||
- node membership disable and identity revoke boundaries
|
||||
- join token creation with signed authority envelope visibility
|
||||
- signed join bundle generation for docker/linux/windows node installs
|
||||
- join request approve/reject with signed approval envelope visibility
|
||||
- role assignment
|
||||
- workload desired-state setting
|
||||
@@ -86,18 +87,18 @@ http://127.0.0.1:5173
|
||||
The admin console should run on a dedicated admin host/port. It is not intended
|
||||
to be the public product landing page on generic `80/443` web ingress.
|
||||
|
||||
Default backend API inside the panel:
|
||||
Panel service gateway inside the UI:
|
||||
|
||||
```text
|
||||
/api/v1
|
||||
```
|
||||
|
||||
The local Vite dev server proxies `/api` to the remote test backend
|
||||
`http://192.168.200.61:8080` by default, avoiding browser CORS issues while
|
||||
keeping the Control Plane API unchanged. Override the proxy target when needed:
|
||||
The browser never asks the operator for a farm HTTP endpoint. During local
|
||||
development the Vite dev server proxies `/api` to the panel service gateway.
|
||||
Override the local proxy target only for developer work:
|
||||
|
||||
```powershell
|
||||
$env:RAP_ADMIN_API_PROXY = "http://192.168.200.61:8080"
|
||||
$env:RAP_ADMIN_API_PROXY = "http://<panel-service-host>:<port>"
|
||||
npm run dev -- --port 5173
|
||||
```
|
||||
|
||||
@@ -108,7 +109,7 @@ password fields; it does not expose API URLs or language/settings to
|
||||
unauthenticated users.
|
||||
|
||||
After authentication the panel verifies platform-owner/platform-admin access
|
||||
through Control Plane APIs before opening the console. Users without product-owner
|
||||
through the panel service gateway before opening the console. Users without product-owner
|
||||
scope must not see this panel. Organization admins and organization users require
|
||||
separate scoped panels.
|
||||
|
||||
@@ -116,10 +117,18 @@ Language selection is available only after login in the profile area. It is stor
|
||||
as a user-scoped browser preference for this MVP. Backend user-profile persistence
|
||||
for language/locale is a later Control Plane profile setting.
|
||||
|
||||
The panel shows real Control Plane data only. If cluster counts are zero, the
|
||||
The panel shows real farm data only. If cluster counts are zero, the
|
||||
cluster has no approved node-agent nodes, roles, workloads, VPN records, or mesh
|
||||
observations yet.
|
||||
|
||||
Current node enrollment surface is bundle-first:
|
||||
|
||||
- the panel creates a one-time install token
|
||||
- the panel requests a signed join bundle from the panel service gateway
|
||||
- the operator downloads or posts that bundle to the target machine
|
||||
- `rap-host-agent install* --join-bundle ...` performs first install
|
||||
- after first start, node enrollment, update, and control traffic use QUIC fabric
|
||||
|
||||
## Safety Rules
|
||||
|
||||
- The console is platform-owner/platform-admin only.
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,11 +3,97 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Панель Secure Access Fabric</title>
|
||||
<script type="module" crossorigin src="/assets/index-CiNvRobk.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Cur_BAkX.css">
|
||||
<title>Вход в панель фермы</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--bg: #eef2ec;
|
||||
--panel: #fffdf7;
|
||||
--ink: #102018;
|
||||
--muted: #66716a;
|
||||
--line: #dce2d8;
|
||||
--accent: #1f6b4c;
|
||||
--soft: #f6f4ea;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: Inter, "Segoe UI", Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
main {
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 28px;
|
||||
}
|
||||
.shell { width: min(460px, 100%); }
|
||||
h1 { margin: 0 0 8px; font-size: 26px; letter-spacing: 0; }
|
||||
p { margin: 0; color: var(--muted); }
|
||||
form, .note {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 18px;
|
||||
}
|
||||
form { margin-top: 22px; box-shadow: 0 14px 34px rgba(20, 35, 25, .08); }
|
||||
label {
|
||||
display: block;
|
||||
margin: 0 0 14px;
|
||||
color: #3d4a42;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
margin-top: 7px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
background: white;
|
||||
color: var(--ink);
|
||||
padding: 12px 13px;
|
||||
font: inherit;
|
||||
}
|
||||
input:focus { outline: 2px solid rgba(31, 107, 76, .22); border-color: var(--accent); }
|
||||
button {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 12px 14px;
|
||||
font: 800 15px Inter, "Segoe UI", Arial, sans-serif;
|
||||
cursor: pointer;
|
||||
}
|
||||
.note {
|
||||
margin-top: 10px;
|
||||
background: var(--soft);
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<main>
|
||||
<section class="shell" aria-labelledby="title">
|
||||
<h1 id="title">Вход в панель фермы</h1>
|
||||
<p>Администратор откроет управление фермой. Пользователь получит страницу установки VPN.</p>
|
||||
<form method="post" action="/api/v1/auth/ui/login">
|
||||
<label>
|
||||
Email или логин
|
||||
<input name="email" autocomplete="username" required autofocus />
|
||||
</label>
|
||||
<label>
|
||||
Пароль
|
||||
<input name="password" type="password" autocomplete="current-password" required />
|
||||
</label>
|
||||
<button type="submit">Войти</button>
|
||||
</form>
|
||||
<p class="note">Вход выполняется сервером. В браузере нет хранения ID оператора и тяжелой клиентской логики.</p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+82
-3
@@ -3,10 +3,89 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Панель Secure Access Fabric</title>
|
||||
<title>Панель управления фермой</title>
|
||||
<style>
|
||||
:root { color: #172019; background: #eef0ea; font-family: system-ui, "Segoe UI", sans-serif; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; }
|
||||
main { max-width: 1180px; margin: 0 auto; padding: 20px; }
|
||||
header { display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 420px); gap: 14px; align-items: stretch; margin-bottom: 12px; }
|
||||
h1 { margin: 0; font-size: 1.65rem; letter-spacing: 0; }
|
||||
h2 { margin: 0 0 8px; font-size: 1rem; }
|
||||
p { margin: 0; color: #667064; line-height: 1.45; }
|
||||
.hero, .identity, .panel { border: 1px solid #1a271b26; border-radius: 8px; background: #fffdf5; padding: 14px; }
|
||||
.hero { display: grid; align-content: space-between; min-height: 180px; }
|
||||
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
|
||||
.pill { display: inline-flex; width: fit-content; border-radius: 8px; padding: .24rem .5rem; font-size: .74rem; font-weight: 850; background: #36556c1a; color: #36556c; }
|
||||
.good { color: #236c4a; background: #2f6f4f1f; }
|
||||
.warn { color: #9a5b1c; background: #b86f2324; }
|
||||
form { display: grid; gap: 10px; }
|
||||
label { display: grid; gap: 6px; color: #667064; font-size: .78rem; font-weight: 850; }
|
||||
input, select, button { min-height: 38px; border: 1px solid #1a271b26; border-radius: 8px; padding: .5rem .65rem; color: #172019; background: #fff; font: inherit; }
|
||||
button { border-color: #2f6f4f; background: #2f6f4f; color: #fffaf0; font-weight: 850; cursor: pointer; }
|
||||
.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
|
||||
.panel { min-height: 120px; }
|
||||
.panel strong { display: block; margin-bottom: 4px; }
|
||||
.panel span { color: #667064; font-size: .84rem; }
|
||||
.footer { margin-top: 12px; color: #667064; font-size: .82rem; }
|
||||
@media (max-width: 860px) { main { padding: 10px; } header, .grid { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<main>
|
||||
<header>
|
||||
<section class="hero">
|
||||
<div>
|
||||
<h1>Панель управления фермой</h1>
|
||||
<p>Рабочая консоль для идентификации оператора, обзора узлов, обновлений, QUIC fabric, веб-контроля и аудита.</p>
|
||||
</div>
|
||||
<div class="badges">
|
||||
<span class="pill good">HTML5</span>
|
||||
<span class="pill good">HTMX-lite</span>
|
||||
<span class="pill">серверные расчеты</span>
|
||||
<span class="pill warn">внутри фермы QUIC fabric</span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="identity">
|
||||
<h2>Идентификация оператора</h2>
|
||||
<form id="admin-form" action="/api/v1/ui/admin" method="get">
|
||||
<label>
|
||||
ID пользователя
|
||||
<input id="actor-user-id" name="actor_user_id" autocomplete="username" required placeholder="uuid оператора" />
|
||||
</label>
|
||||
<label>
|
||||
Область входа
|
||||
<select name="scope">
|
||||
<option value="farm">ферма</option>
|
||||
<option value="cluster">кластер</option>
|
||||
<option value="organization">организация</option>
|
||||
</select>
|
||||
</label>
|
||||
<button type="submit">Открыть консоль</button>
|
||||
</form>
|
||||
</section>
|
||||
</header>
|
||||
<section class="grid">
|
||||
<div class="panel"><strong>Узлы</strong><span>живость, версии, heartbeat, роли, детали обновлений.</span></div>
|
||||
<div class="panel"><strong>Fabric</strong><span>визуальная работа связей, service channels, readiness и политики восстановления.</span></div>
|
||||
<div class="panel"><strong>Веб-контроль</strong><span>настройка admin-ingress и public-ingress как workloads фермы.</span></div>
|
||||
<div class="panel"><strong>Обновления</strong><span>массовые signed update hints, релизы, статусы и rollback-контуры.</span></div>
|
||||
<div class="panel"><strong>Аналитика</strong><span>маршруты, rebuild health, access telemetry, активные каналы.</span></div>
|
||||
<div class="panel"><strong>Аудит</strong><span>операторские действия, изменения политик, события восстановления.</span></div>
|
||||
</section>
|
||||
<div class="footer">ID сохраняется только в локальном профиле браузера для быстрого повторного входа.</div>
|
||||
</main>
|
||||
<script>
|
||||
(() => {
|
||||
const input = document.getElementById("actor-user-id");
|
||||
const form = document.getElementById("admin-form");
|
||||
if (!input || !form) return;
|
||||
const stored = localStorage.getItem("rap.webAdmin.actorUserId");
|
||||
if (stored) input.value = stored;
|
||||
form.addEventListener("submit", () => {
|
||||
if (input.value.trim()) localStorage.setItem("rap.webAdmin.actorUserId", input.value.trim());
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+807
-587
File diff suppressed because it is too large
Load Diff
+71
-12
@@ -9,6 +9,7 @@ import type {
|
||||
ClusterAuthorityState,
|
||||
ClusterNode,
|
||||
CreatedJoinToken,
|
||||
InstallJoinBundle,
|
||||
FabricServiceChannelAdaptivePolicy,
|
||||
FabricServiceChannelBreadcrumbWindowPolicy,
|
||||
FabricServiceChannelPoolPolicy,
|
||||
@@ -58,10 +59,11 @@ import type {
|
||||
} from "../types";
|
||||
|
||||
export type AdminClientConfig = {
|
||||
baseUrl: string;
|
||||
actorUserId: string;
|
||||
};
|
||||
|
||||
const panelGatewayBasePath = "/api/v1";
|
||||
|
||||
type ApiErrorPayload = {
|
||||
error?: {
|
||||
code?: string;
|
||||
@@ -155,7 +157,7 @@ export type UpdateFabricServiceChannelPoolPolicyPayload = {
|
||||
routeRebuild?: string;
|
||||
entryFailover?: string;
|
||||
exitFailover?: string;
|
||||
backendFallbackAllowed?: boolean;
|
||||
degradedRouteAllowed?: boolean;
|
||||
stickySession?: boolean;
|
||||
};
|
||||
|
||||
@@ -196,11 +198,9 @@ export type UpsertResourceSecretPayload = {
|
||||
};
|
||||
|
||||
export class AdminApiClient {
|
||||
private readonly baseUrl: string;
|
||||
private readonly actorUserId: string;
|
||||
|
||||
constructor(config: AdminClientConfig) {
|
||||
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
||||
this.actorUserId = config.actorUserId.trim();
|
||||
}
|
||||
|
||||
@@ -377,6 +377,51 @@ export class AdminApiClient {
|
||||
return payload.join_token;
|
||||
}
|
||||
|
||||
async getDockerJoinBundle(input: {
|
||||
clusterId: string;
|
||||
installToken: string;
|
||||
nodeName?: string;
|
||||
hostFacts?: Record<string, unknown>;
|
||||
}): Promise<InstallJoinBundle> {
|
||||
const payload = await this.post<{ join_bundle: InstallJoinBundle }>(`/node-agents/docker-join-bundle`, {
|
||||
cluster_id: input.clusterId,
|
||||
install_token: input.installToken,
|
||||
node_name: input.nodeName || "",
|
||||
host_facts: input.hostFacts || {},
|
||||
});
|
||||
return payload.join_bundle;
|
||||
}
|
||||
|
||||
async getWindowsJoinBundle(input: {
|
||||
clusterId: string;
|
||||
installToken: string;
|
||||
nodeName?: string;
|
||||
hostFacts?: Record<string, unknown>;
|
||||
}): Promise<InstallJoinBundle> {
|
||||
const payload = await this.post<{ join_bundle: InstallJoinBundle }>(`/node-agents/windows-join-bundle`, {
|
||||
cluster_id: input.clusterId,
|
||||
install_token: input.installToken,
|
||||
node_name: input.nodeName || "",
|
||||
host_facts: input.hostFacts || {},
|
||||
});
|
||||
return payload.join_bundle;
|
||||
}
|
||||
|
||||
async getLinuxJoinBundle(input: {
|
||||
clusterId: string;
|
||||
installToken: string;
|
||||
nodeName?: string;
|
||||
hostFacts?: Record<string, unknown>;
|
||||
}): Promise<InstallJoinBundle> {
|
||||
const payload = await this.post<{ join_bundle: InstallJoinBundle }>(`/node-agents/linux-join-bundle`, {
|
||||
cluster_id: input.clusterId,
|
||||
install_token: input.installToken,
|
||||
node_name: input.nodeName || "",
|
||||
host_facts: input.hostFacts || {},
|
||||
});
|
||||
return payload.join_bundle;
|
||||
}
|
||||
|
||||
async listJoinTokens(clusterId: string): Promise<NodeJoinToken[]> {
|
||||
const payload = await this.get<{ join_tokens: NodeJoinToken[] }>(`/clusters/${clusterId}/join-tokens`);
|
||||
return payload.join_tokens ?? [];
|
||||
@@ -1021,7 +1066,7 @@ export class AdminApiClient {
|
||||
route_rebuild: input.routeRebuild,
|
||||
entry_failover: input.entryFailover,
|
||||
exit_failover: input.exitFailover,
|
||||
backend_fallback_allowed: input.backendFallbackAllowed,
|
||||
degraded_route_allowed: input.degradedRouteAllowed,
|
||||
sticky_session: input.stickySession,
|
||||
},
|
||||
);
|
||||
@@ -1193,7 +1238,7 @@ export class AdminApiClient {
|
||||
}
|
||||
|
||||
clusterEventsURL(clusterId: string): string {
|
||||
return `${this.baseUrl}/clusters/${encodeURIComponent(clusterId)}/events?actor_user_id=${encodeURIComponent(this.actorUserId)}`;
|
||||
return `${panelGatewayBasePath}/clusters/${encodeURIComponent(clusterId)}/events?actor_user_id=${encodeURIComponent(this.actorUserId)}`;
|
||||
}
|
||||
|
||||
async getOrganizationAdminSummary(organizationId: string): Promise<OrganizationAdminSummary> {
|
||||
@@ -1325,14 +1370,20 @@ export class AdminApiClient {
|
||||
}
|
||||
|
||||
private async request<T>(path: string, init: RequestInit): Promise<T> {
|
||||
const response = await fetch(`${this.baseUrl}${path}`, init);
|
||||
const response = await fetch(`${panelGatewayBasePath}${path}`, {
|
||||
...init,
|
||||
headers: {
|
||||
"X-RAP-Panel-Gateway": "fabric",
|
||||
...(init.headers || {}),
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
let message = `Запрос завершился ошибкой HTTP ${response.status}`;
|
||||
let message = `Запрос завершился ошибкой ${response.status}`;
|
||||
try {
|
||||
const payload = (await response.json()) as ApiErrorPayload;
|
||||
message = formatApiErrorMessage(payload, response.status) || payload.error?.fallback_message || payload.error?.code || message;
|
||||
} catch {
|
||||
// Keep generic HTTP message if backend did not return JSON.
|
||||
// Keep generic message if backend did not return JSON.
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
@@ -1345,9 +1396,9 @@ function formatApiErrorMessage(payload: ApiErrorPayload, status: number) {
|
||||
if (!error) {
|
||||
return "";
|
||||
}
|
||||
if (status === 409 && error.code === "conflict.legacy_compatibility_removal_is_blocked_while_stale_recovery_risk_nodes_remain") {
|
||||
if (status === 409 && error.code === "conflict.fabric_standard_cleanup_is_blocked_while_stale_recovery_risk_nodes_remain") {
|
||||
const details = error.details || {};
|
||||
const parts: string[] = ["Compatibility cleanup заблокирован."];
|
||||
const parts: string[] = ["Fabric standard cleanup заблокирован."];
|
||||
const blockedOperation = stringDetail(details, "blocked_operation");
|
||||
if (blockedOperation) {
|
||||
parts.push(`Операция: ${blockedOperation}.`);
|
||||
@@ -1356,10 +1407,18 @@ function formatApiErrorMessage(payload: ApiErrorPayload, status: number) {
|
||||
numberDetail(details, "blocked_nodes") ? `blockers ${numberDetail(details, "blocked_nodes")}` : "",
|
||||
numberDetail(details, "stale_nodes") ? `stale ${numberDetail(details, "stale_nodes")}` : "",
|
||||
numberDetail(details, "artifact_gap_nodes") ? `artifact gap ${numberDetail(details, "artifact_gap_nodes")}` : "",
|
||||
numberDetail(details, "area_diversity_alert_nodes") ? `area diversity ${numberDetail(details, "area_diversity_alert_nodes")}` : "",
|
||||
numberDetail(details, "independent_ingress_alert_nodes") ? `independent ingress ${numberDetail(details, "independent_ingress_alert_nodes")}` : "",
|
||||
numberDetail(details, "updater_wake_unsupported_nodes") ? `updater wake unsupported ${numberDetail(details, "updater_wake_unsupported_nodes")}` : "",
|
||||
numberDetail(details, "updater_runtime_missing_nodes") ? `updater runtime missing ${numberDetail(details, "updater_runtime_missing_nodes")}` : "",
|
||||
numberDetail(details, "standard_updater_line_nodes") ? `standard updater line ${numberDetail(details, "standard_updater_line_nodes")}` : "",
|
||||
numberDetail(details, "staged_self_update_pending_nodes") ? `staged self-update ${numberDetail(details, "staged_self_update_pending_nodes")}` : "",
|
||||
numberDetail(details, "standard_control_dependency_nodes") ? `standard control ${numberDetail(details, "standard_control_dependency_nodes")}` : "",
|
||||
numberDetail(details, "registry_candidate_only_nodes") ? `registry candidate-only ${numberDetail(details, "registry_candidate_only_nodes")}` : "",
|
||||
numberDetail(details, "unknown_profile_nodes") ? `profile unknown ${numberDetail(details, "unknown_profile_nodes")}` : "",
|
||||
numberDetail(details, "waiting_update_status_nodes") ? `waiting status ${numberDetail(details, "waiting_update_status_nodes")}` : "",
|
||||
numberDetail(details, "unknown_version_nodes") ? `version unknown ${numberDetail(details, "unknown_version_nodes")}` : "",
|
||||
numberDetail(details, "legacy_recovery_contract_nodes") ? `legacy contract ${numberDetail(details, "legacy_recovery_contract_nodes")}` : "",
|
||||
numberDetail(details, "standard_recovery_contract_nodes") ? `standard contract ${numberDetail(details, "standard_recovery_contract_nodes")}` : "",
|
||||
numberDetail(details, "recovery_bridge_required_nodes") ? `recovery bridge ${numberDetail(details, "recovery_bridge_required_nodes")}` : "",
|
||||
numberDetail(details, "recovery_bridge_replay_ready_nodes") ? `bridge replay ready ${numberDetail(details, "recovery_bridge_replay_ready_nodes")}` : "",
|
||||
numberDetail(details, "waiting_recovery_heartbeat_nodes") ? `waiting heartbeat ${numberDetail(details, "waiting_recovery_heartbeat_nodes")}` : "",
|
||||
|
||||
+314
-210
File diff suppressed because it is too large
Load Diff
+119
-16
@@ -150,6 +150,74 @@ export type CreatedJoinToken = {
|
||||
|
||||
export type NodeJoinToken = Omit<CreatedJoinToken, "token">;
|
||||
|
||||
export type DockerArtifact = {
|
||||
kind: string;
|
||||
image?: string;
|
||||
media_type?: string;
|
||||
file_name?: string;
|
||||
urls?: string[];
|
||||
sha256?: string;
|
||||
size_bytes?: number;
|
||||
};
|
||||
|
||||
export type BootstrapInstallProfile = {
|
||||
schema_version: string;
|
||||
cluster_id: string;
|
||||
cluster_authority_public_key?: string;
|
||||
artifact_endpoints?: string[];
|
||||
fabric_registry_records?: unknown[];
|
||||
join_token: string;
|
||||
node_name: string;
|
||||
state_dir: string;
|
||||
mesh_advertise_endpoint?: string;
|
||||
mesh_advertise_endpoints_json?: unknown;
|
||||
mesh_advertise_transport?: string;
|
||||
mesh_connectivity_mode?: string;
|
||||
mesh_nat_type?: string;
|
||||
mesh_region?: string;
|
||||
fabric_listen_addr?: string;
|
||||
fabric_listen_port_mode?: string;
|
||||
fabric_listen_auto_port_start?: number;
|
||||
fabric_listen_auto_port_end?: number;
|
||||
roles?: string[];
|
||||
};
|
||||
|
||||
export type DockerJoinInstallProfile = BootstrapInstallProfile & {
|
||||
image: string;
|
||||
container_name: string;
|
||||
network: string;
|
||||
restart_policy: string;
|
||||
pull_image: boolean;
|
||||
replace: boolean;
|
||||
docker_image_artifact?: DockerArtifact;
|
||||
};
|
||||
|
||||
export type WindowsJoinInstallProfile = BootstrapInstallProfile & {
|
||||
install_dir: string;
|
||||
startup_mode: string;
|
||||
node_agent_artifact?: DockerArtifact;
|
||||
};
|
||||
|
||||
export type LinuxJoinInstallProfile = BootstrapInstallProfile & {
|
||||
install_dir: string;
|
||||
startup_mode: string;
|
||||
replace?: boolean;
|
||||
node_agent_artifact?: DockerArtifact;
|
||||
};
|
||||
|
||||
export type InstallJoinBundle = {
|
||||
schema_version: string;
|
||||
bundle_kind: string;
|
||||
cluster_id: string;
|
||||
cluster_authority?: ClusterAuthorityDescriptor;
|
||||
authority_payload?: Record<string, unknown>;
|
||||
authority_signature?: ClusterSignature;
|
||||
issued_at: string;
|
||||
docker_install_profile?: DockerJoinInstallProfile;
|
||||
windows_install_profile?: WindowsJoinInstallProfile;
|
||||
linux_install_profile?: LinuxJoinInstallProfile;
|
||||
};
|
||||
|
||||
export type RoleAssignment = {
|
||||
id: string;
|
||||
cluster_id: string;
|
||||
@@ -412,6 +480,8 @@ export type StaleNodeRiskProduct = {
|
||||
last_status_phase?: string | null;
|
||||
last_status_value?: string | null;
|
||||
last_status_reason?: string | null;
|
||||
staged_self_update_pending?: boolean;
|
||||
post_update_heartbeat_gap?: boolean;
|
||||
recovery_bridge_required?: boolean;
|
||||
recovery_bridge_replay_ready?: boolean;
|
||||
recovery_bridge_mode?: string | null;
|
||||
@@ -421,6 +491,7 @@ export type StaleNodeRiskProduct = {
|
||||
export type StaleNodeRiskNode = {
|
||||
node_id: string;
|
||||
name: string;
|
||||
area?: string | null;
|
||||
node_key?: string;
|
||||
reported_version?: string | null;
|
||||
health_status: string;
|
||||
@@ -432,6 +503,27 @@ export type StaleNodeRiskNode = {
|
||||
direct_peer_ready_count?: number;
|
||||
direct_peer_target_count?: number;
|
||||
direct_peer_deficit?: number;
|
||||
direct_ready_areas?: string[];
|
||||
external_area_ready_count?: number;
|
||||
required_external_area_count?: number;
|
||||
area_diversity_alert?: boolean;
|
||||
required_independent_ingress_count?: number;
|
||||
independent_ingress_alert?: boolean;
|
||||
full_directory_expected?: boolean;
|
||||
known_peer_directory_count?: number;
|
||||
expected_directory_count?: number;
|
||||
directory_dissemination_alert?: boolean;
|
||||
updater_subscription_alert?: boolean;
|
||||
updater_wake_unsupported?: boolean;
|
||||
updater_runtime_missing?: boolean;
|
||||
standard_updater_line?: boolean;
|
||||
staged_self_update_pending?: boolean;
|
||||
post_update_heartbeat_gap?: boolean;
|
||||
standard_control_dependency?: boolean;
|
||||
standard_control_url?: string | null;
|
||||
registry_runtime_status?: string | null;
|
||||
resolved_service_count?: number;
|
||||
independent_ingress_count?: number;
|
||||
alerts?: string[];
|
||||
recovery_bridge_required?: boolean;
|
||||
recovery_bridge_replay_ready?: boolean;
|
||||
@@ -445,11 +537,22 @@ export type StaleNodeRiskSummary = {
|
||||
stale_nodes: number;
|
||||
blocked_nodes: number;
|
||||
direct_peer_alert_nodes?: number;
|
||||
area_diversity_alert_nodes?: number;
|
||||
independent_ingress_alert_nodes?: number;
|
||||
directory_dissemination_alert_nodes?: number;
|
||||
updater_subscription_alert_nodes?: number;
|
||||
updater_wake_unsupported_nodes?: number;
|
||||
updater_runtime_missing_nodes?: number;
|
||||
standard_updater_line_nodes?: number;
|
||||
staged_self_update_pending_nodes?: number;
|
||||
post_update_heartbeat_gap_nodes?: number;
|
||||
artifact_gap_nodes?: number;
|
||||
standard_control_dependency_nodes?: number;
|
||||
registry_candidate_only_nodes?: number;
|
||||
unknown_profile_nodes?: number;
|
||||
waiting_update_status_nodes?: number;
|
||||
unknown_version_nodes?: number;
|
||||
legacy_recovery_contract_nodes?: number;
|
||||
standard_recovery_contract_nodes?: number;
|
||||
recovery_bridge_required_nodes?: number;
|
||||
recovery_bridge_replay_ready_nodes?: number;
|
||||
waiting_recovery_heartbeat_nodes?: number;
|
||||
@@ -459,7 +562,7 @@ export type StaleNodeRiskReport = {
|
||||
cluster_id: string;
|
||||
generated_at: string;
|
||||
heartbeat_stale_after_seconds?: number;
|
||||
legacy_removal_allowed: boolean;
|
||||
fabric_standard_cleanup_allowed: boolean;
|
||||
bridge_hold_required?: boolean;
|
||||
bridge_hold_node_ids?: string[];
|
||||
bridge_hold_reasons?: string[];
|
||||
@@ -914,7 +1017,7 @@ export type FabricServiceChannelLeaseSummary = {
|
||||
primary_route_id?: string;
|
||||
primary_route_status?: string;
|
||||
data_plane?: FabricServiceChannelDataPlaneContract;
|
||||
force_backend_fallback: boolean;
|
||||
force_degraded_route: boolean;
|
||||
expired: boolean;
|
||||
issued_at: string;
|
||||
expires_at: string;
|
||||
@@ -965,15 +1068,15 @@ export type FabricServiceChannelAccessTelemetryNode = {
|
||||
total_accepted: number;
|
||||
signed_accepted: number;
|
||||
introspection_accepted: number;
|
||||
legacy_unsigned_accepted: number;
|
||||
backend_fallback_count: number;
|
||||
backend_fallback_blocked_count?: number;
|
||||
unsigned_accepted: number;
|
||||
degraded_route_use_count: number;
|
||||
degraded_route_blocked_count?: number;
|
||||
fabric_route_send_failure_count?: number;
|
||||
data_plane_contract_count?: number;
|
||||
last_data_plane_mode?: string;
|
||||
last_working_data_transport?: string;
|
||||
last_steady_state_transport?: string;
|
||||
last_backend_relay_policy?: string;
|
||||
last_degraded_route_policy?: string;
|
||||
last_logical_flow_mode?: string;
|
||||
last_data_plane_violation_status?: string;
|
||||
last_data_plane_violation_reason?: string;
|
||||
@@ -1000,17 +1103,17 @@ export type FabricServiceChannelAccessTelemetryChannel = {
|
||||
selected_exit_node_id?: string;
|
||||
primary_route_id?: string;
|
||||
primary_route_status?: string;
|
||||
force_backend_fallback: boolean;
|
||||
force_degraded_route: boolean;
|
||||
entry_node_total_accepted: number;
|
||||
entry_node_introspection_accepted: number;
|
||||
entry_node_backend_fallback_count: number;
|
||||
entry_node_backend_fallback_blocked_count?: number;
|
||||
entry_node_degraded_route_count: number;
|
||||
entry_node_degraded_route_blocked_count?: number;
|
||||
entry_node_fabric_route_send_failure_count?: number;
|
||||
entry_node_data_plane_contract_count?: number;
|
||||
entry_node_last_data_plane_mode?: string;
|
||||
entry_node_last_working_data_transport?: string;
|
||||
entry_node_last_steady_state_transport?: string;
|
||||
entry_node_last_backend_relay_policy?: string;
|
||||
entry_node_last_degraded_route_policy?: string;
|
||||
entry_node_last_logical_flow_mode?: string;
|
||||
entry_node_last_data_plane_violation_status?: string;
|
||||
entry_node_last_data_plane_violation_reason?: string;
|
||||
@@ -1094,15 +1197,15 @@ export type FabricServiceChannelAccessTelemetry = {
|
||||
total_accepted: number;
|
||||
signed_accepted: number;
|
||||
introspection_accepted: number;
|
||||
legacy_unsigned_accepted: number;
|
||||
backend_fallback_count: number;
|
||||
backend_fallback_blocked_count?: number;
|
||||
unsigned_accepted: number;
|
||||
degraded_route_use_count: number;
|
||||
degraded_route_blocked_count?: number;
|
||||
fabric_route_send_failure_count?: number;
|
||||
data_plane_contract_count?: number;
|
||||
last_data_plane_mode?: string;
|
||||
last_working_data_transport?: string;
|
||||
last_steady_state_transport?: string;
|
||||
last_backend_relay_policy?: string;
|
||||
last_degraded_route_policy?: string;
|
||||
last_logical_flow_mode?: string;
|
||||
last_data_plane_violation_status?: string;
|
||||
last_data_plane_violation_reason?: string;
|
||||
@@ -1250,7 +1353,7 @@ export type FabricServiceChannelPoolPolicy = {
|
||||
route_rebuild: string;
|
||||
entry_failover: string;
|
||||
exit_failover: string;
|
||||
backend_fallback_allowed: boolean;
|
||||
degraded_route_allowed: boolean;
|
||||
sticky_session: boolean;
|
||||
source: string;
|
||||
updated_by_user_id?: string | null;
|
||||
|
||||
Reference in New Issue
Block a user