@veltrixsecops/app-sdk 3.2.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/byol/index.cjs +3802 -0
- package/dist/byol/index.cjs.map +1 -0
- package/dist/byol/index.d.cts +578 -0
- package/dist/byol/index.d.ts +578 -0
- package/dist/byol/index.js +2587 -0
- package/dist/byol/index.js.map +1 -0
- package/dist/chunk-3WK4EVQ3.js +842 -0
- package/dist/chunk-3WK4EVQ3.js.map +1 -0
- package/dist/{chunk-EOOEHZGC.js → chunk-HIL6RIDG.js} +107 -3
- package/dist/chunk-HIL6RIDG.js.map +1 -0
- package/dist/{chunk-VWFTOFTI.js → chunk-Y6XBNZGR.js} +8 -2
- package/dist/chunk-Y6XBNZGR.js.map +1 -0
- package/dist/client/index.cjs +112 -2
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +118 -6
- package/dist/client/index.d.ts +118 -6
- package/dist/client/index.js +13 -1
- package/dist/connections/index.cjs +1787 -0
- package/dist/connections/index.cjs.map +1 -0
- package/dist/connections/index.d.cts +78 -0
- package/dist/connections/index.d.ts +78 -0
- package/dist/connections/index.js +754 -0
- package/dist/connections/index.js.map +1 -0
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/opentofu/index.cjs +138 -0
- package/dist/opentofu/index.cjs.map +1 -0
- package/dist/opentofu/index.d.cts +131 -0
- package/dist/opentofu/index.d.ts +131 -0
- package/dist/opentofu/index.js +109 -0
- package/dist/opentofu/index.js.map +1 -0
- package/dist/pipeline/index.cjs +7 -0
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.d.cts +9 -2
- package/dist/pipeline/index.d.ts +9 -2
- package/dist/pipeline/index.js +3 -1
- package/dist/pipeline-beeT8dje.d.cts +408 -0
- package/dist/pipeline-beeT8dje.d.ts +408 -0
- package/dist/ui/index.cjs +136 -0
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +109 -1
- package/dist/ui/index.d.ts +109 -1
- package/dist/ui/index.js +35 -677
- package/dist/ui/index.js.map +1 -1
- package/dist/{use-app-context-OQlcmq7t.d.cts → use-app-context-Byv_fam0.d.cts} +206 -4
- package/dist/{use-app-context-OQlcmq7t.d.ts → use-app-context-Byv_fam0.d.ts} +206 -4
- package/opentofu/README.md +114 -0
- package/opentofu/modules/aws/main.tf +1008 -0
- package/opentofu/modules/aws/outputs.tf +90 -0
- package/opentofu/modules/aws/variables.tf +437 -0
- package/opentofu/modules/aws/versions.tf +23 -0
- package/opentofu/modules/azure/main.tf +920 -0
- package/opentofu/modules/azure/outputs.tf +72 -0
- package/opentofu/modules/azure/variables.tf +498 -0
- package/opentofu/modules/azure/versions.tf +27 -0
- package/opentofu/modules/gcp/main.tf +645 -0
- package/opentofu/modules/gcp/outputs.tf +73 -0
- package/opentofu/modules/gcp/variables.tf +428 -0
- package/opentofu/modules/gcp/versions.tf +27 -0
- package/opentofu/modules/hetzner/main.tf +432 -0
- package/opentofu/modules/hetzner/outputs.tf +72 -0
- package/opentofu/modules/hetzner/variables.tf +423 -0
- package/opentofu/modules/hetzner/versions.tf +26 -0
- package/package.json +33 -1
- package/dist/chunk-EOOEHZGC.js.map +0 -1
- package/dist/chunk-VWFTOFTI.js.map +0 -1
- package/dist/pipeline-A-gSsPRR.d.cts +0 -189
- package/dist/pipeline-A-gSsPRR.d.ts +0 -189
|
@@ -0,0 +1,3802 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/client/inventory.ts
|
|
34
|
+
async function inventoryError(res) {
|
|
35
|
+
const text = await res.text().catch(() => "");
|
|
36
|
+
if (text) {
|
|
37
|
+
try {
|
|
38
|
+
const body = JSON.parse(text);
|
|
39
|
+
const message = body?.error ?? body?.message;
|
|
40
|
+
if (message) return new Error(message);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
return new Error(text);
|
|
44
|
+
}
|
|
45
|
+
return new Error(`HTTP ${res.status}`);
|
|
46
|
+
}
|
|
47
|
+
function toInventoryItem(raw) {
|
|
48
|
+
return {
|
|
49
|
+
id: String(raw.id),
|
|
50
|
+
hostname: raw.hostname ?? "",
|
|
51
|
+
port: raw.port ?? void 0,
|
|
52
|
+
webPort: raw.webPort ?? null,
|
|
53
|
+
sshUser: raw.sshUser ?? null,
|
|
54
|
+
splunkHome: raw.splunkHome ?? null,
|
|
55
|
+
type: Array.isArray(raw.type) ? raw.type : void 0,
|
|
56
|
+
domains: Array.isArray(raw.domains) ? raw.domains : [],
|
|
57
|
+
ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],
|
|
58
|
+
tags: Array.isArray(raw.tags) ? raw.tags.map((tag) => ({ id: String(tag.id), name: String(tag.name) })) : [],
|
|
59
|
+
connectivityProviderId: raw.connectivityProviderId ?? null,
|
|
60
|
+
credentialId: raw.credentialId ?? null
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function resolveTool(name) {
|
|
64
|
+
const res = await authFetch(`/api/tools?search=${encodeURIComponent(name)}&limit=100`);
|
|
65
|
+
if (!res.ok) throw await inventoryError(res);
|
|
66
|
+
const body = await res.json();
|
|
67
|
+
const tools = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
68
|
+
return tools.find((tool) => tool.name === name) ?? null;
|
|
69
|
+
}
|
|
70
|
+
async function listInventory() {
|
|
71
|
+
const res = await authFetch(INVENTORY_API);
|
|
72
|
+
if (!res.ok) throw await inventoryError(res);
|
|
73
|
+
const data = await res.json();
|
|
74
|
+
return Array.isArray(data) ? data.map(toInventoryItem) : [];
|
|
75
|
+
}
|
|
76
|
+
async function addInventoryItem(input) {
|
|
77
|
+
const res = await authFetch(INVENTORY_API, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "Content-Type": "application/json" },
|
|
80
|
+
body: JSON.stringify(input)
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) throw await inventoryError(res);
|
|
83
|
+
return toInventoryItem(await res.json());
|
|
84
|
+
}
|
|
85
|
+
async function updateInventoryItem(id, input) {
|
|
86
|
+
const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {
|
|
87
|
+
method: "PUT",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify(input)
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) throw await inventoryError(res);
|
|
92
|
+
return toInventoryItem(await res.json());
|
|
93
|
+
}
|
|
94
|
+
async function removeInventoryItem(id) {
|
|
95
|
+
const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {
|
|
96
|
+
method: "DELETE"
|
|
97
|
+
});
|
|
98
|
+
if (!res.ok && res.status !== 204) throw await inventoryError(res);
|
|
99
|
+
}
|
|
100
|
+
var INVENTORY_API;
|
|
101
|
+
var init_inventory = __esm({
|
|
102
|
+
"src/client/inventory.ts"() {
|
|
103
|
+
"use strict";
|
|
104
|
+
init_client();
|
|
105
|
+
INVENTORY_API = "/api/components";
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// src/client/connectivity.ts
|
|
110
|
+
async function providerError(res) {
|
|
111
|
+
const text = await res.text().catch(() => "");
|
|
112
|
+
if (text) {
|
|
113
|
+
try {
|
|
114
|
+
const body = JSON.parse(text);
|
|
115
|
+
const message = body?.error ?? body?.message;
|
|
116
|
+
if (message) return new Error(message);
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
return new Error(text);
|
|
120
|
+
}
|
|
121
|
+
return new Error(`HTTP ${res.status}`);
|
|
122
|
+
}
|
|
123
|
+
async function listConnectivityProviders() {
|
|
124
|
+
const res = await authFetch(CONNECTIVITY_PROVIDERS_API);
|
|
125
|
+
if (!res.ok) throw await providerError(res);
|
|
126
|
+
const body = await res.json();
|
|
127
|
+
const providers = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
128
|
+
return providers.map((provider) => ({
|
|
129
|
+
id: String(provider.id),
|
|
130
|
+
name: provider.name ?? "",
|
|
131
|
+
providerType: provider.providerType ?? void 0,
|
|
132
|
+
status: provider.status ?? void 0
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
var CONNECTIVITY_PROVIDERS_API;
|
|
136
|
+
var init_connectivity = __esm({
|
|
137
|
+
"src/client/connectivity.ts"() {
|
|
138
|
+
"use strict";
|
|
139
|
+
init_client();
|
|
140
|
+
CONNECTIVITY_PROVIDERS_API = "/api/connectivity-providers";
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// src/client/environments.ts
|
|
145
|
+
async function environmentError(res) {
|
|
146
|
+
const text = await res.text().catch(() => "");
|
|
147
|
+
if (text) {
|
|
148
|
+
try {
|
|
149
|
+
const body = JSON.parse(text);
|
|
150
|
+
const message = body?.error ?? body?.message;
|
|
151
|
+
if (message) return new Error(message);
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
return new Error(text);
|
|
155
|
+
}
|
|
156
|
+
return new Error(`HTTP ${res.status}`);
|
|
157
|
+
}
|
|
158
|
+
async function listEnvironments() {
|
|
159
|
+
const res = await authFetch(ENVIRONMENTS_API);
|
|
160
|
+
if (!res.ok) throw await environmentError(res);
|
|
161
|
+
const body = await res.json();
|
|
162
|
+
const rows = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
163
|
+
return rows.map((env) => ({
|
|
164
|
+
id: String(env.id),
|
|
165
|
+
name: env.name ?? "",
|
|
166
|
+
environmentTypes: Array.isArray(env.environmentTypes) ? env.environmentTypes : []
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
var ENVIRONMENTS_API;
|
|
170
|
+
var init_environments = __esm({
|
|
171
|
+
"src/client/environments.ts"() {
|
|
172
|
+
"use strict";
|
|
173
|
+
init_client();
|
|
174
|
+
ENVIRONMENTS_API = "/api/environments";
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// src/client/credentials.ts
|
|
179
|
+
async function credentialError(res) {
|
|
180
|
+
const text = await res.text().catch(() => "");
|
|
181
|
+
if (text) {
|
|
182
|
+
try {
|
|
183
|
+
const body = JSON.parse(text);
|
|
184
|
+
const message = body?.error ?? body?.message;
|
|
185
|
+
if (message) return new Error(message);
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
return new Error(text);
|
|
189
|
+
}
|
|
190
|
+
return new Error(`HTTP ${res.status}`);
|
|
191
|
+
}
|
|
192
|
+
function toCredentialSummary(raw) {
|
|
193
|
+
const hasSecret = Boolean(
|
|
194
|
+
raw.hasApiToken || raw.hasPassword || raw.apiToken && raw.apiToken.length > 0 || raw.password && raw.password.length > 0
|
|
195
|
+
);
|
|
196
|
+
return {
|
|
197
|
+
id: String(raw.id),
|
|
198
|
+
name: raw.name ?? "",
|
|
199
|
+
username: raw.username ?? "",
|
|
200
|
+
type: raw.type ?? null,
|
|
201
|
+
endpoint: raw.endpoint ?? null,
|
|
202
|
+
environmentType: raw.environmentType ?? null,
|
|
203
|
+
toolId: raw.toolId ?? "",
|
|
204
|
+
hasSecret,
|
|
205
|
+
tags: Array.isArray(raw.tags) ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) })) : []
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
async function listCredentials(toolId) {
|
|
209
|
+
const res = await authFetch(`/api/tools/${encodeURIComponent(toolId)}/credentials`);
|
|
210
|
+
if (!res.ok) throw await credentialError(res);
|
|
211
|
+
const data = await res.json();
|
|
212
|
+
const rows = Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : [];
|
|
213
|
+
return rows.map(toCredentialSummary);
|
|
214
|
+
}
|
|
215
|
+
async function createCredential(input) {
|
|
216
|
+
const res = await authFetch(CREDENTIALS_API, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers: { "Content-Type": "application/json" },
|
|
219
|
+
body: JSON.stringify({
|
|
220
|
+
name: input.name,
|
|
221
|
+
username: input.username,
|
|
222
|
+
password: input.password ?? "",
|
|
223
|
+
apiToken: input.apiToken,
|
|
224
|
+
type: input.type,
|
|
225
|
+
endpoint: input.endpoint,
|
|
226
|
+
environmentType: input.environmentType ?? null,
|
|
227
|
+
toolId: input.toolId,
|
|
228
|
+
tagIds: input.tagIds ?? []
|
|
229
|
+
})
|
|
230
|
+
});
|
|
231
|
+
if (!res.ok) throw await credentialError(res);
|
|
232
|
+
const body = await res.json();
|
|
233
|
+
return { id: String(body.id) };
|
|
234
|
+
}
|
|
235
|
+
async function updateCredential(id, input) {
|
|
236
|
+
const body = {};
|
|
237
|
+
if (input.name !== void 0) body.name = input.name;
|
|
238
|
+
if (input.username !== void 0) body.username = input.username;
|
|
239
|
+
if (input.password !== void 0) body.password = input.password;
|
|
240
|
+
if (input.apiToken !== void 0) body.apiToken = input.apiToken;
|
|
241
|
+
if (input.type !== void 0) body.type = input.type;
|
|
242
|
+
if (input.endpoint !== void 0) body.endpoint = input.endpoint;
|
|
243
|
+
if (input.environmentType !== void 0) body.environmentType = input.environmentType;
|
|
244
|
+
if (input.tagIds !== void 0) body.tagIds = input.tagIds;
|
|
245
|
+
const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {
|
|
246
|
+
method: "PUT",
|
|
247
|
+
headers: { "Content-Type": "application/json" },
|
|
248
|
+
body: JSON.stringify(body)
|
|
249
|
+
});
|
|
250
|
+
if (!res.ok) throw await credentialError(res);
|
|
251
|
+
const result = await res.json().catch(() => ({}));
|
|
252
|
+
return { id: result.id ? String(result.id) : id };
|
|
253
|
+
}
|
|
254
|
+
async function removeCredential(id) {
|
|
255
|
+
const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {
|
|
256
|
+
method: "DELETE"
|
|
257
|
+
});
|
|
258
|
+
if (!res.ok && res.status !== 204) throw await credentialError(res);
|
|
259
|
+
}
|
|
260
|
+
async function testConnection(appId, credentialId, opts = {}) {
|
|
261
|
+
const endpoint = typeof opts.endpoint === "string" ? opts.endpoint.trim() : "";
|
|
262
|
+
const res = await authFetch(
|
|
263
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/test`,
|
|
264
|
+
endpoint ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ endpoint }) } : { method: "POST" }
|
|
265
|
+
);
|
|
266
|
+
if (!res.ok) {
|
|
267
|
+
const err = await credentialError(res);
|
|
268
|
+
return { ok: false, message: err.message };
|
|
269
|
+
}
|
|
270
|
+
return await res.json();
|
|
271
|
+
}
|
|
272
|
+
async function runOperation(appId, operationId, opts = {}) {
|
|
273
|
+
const res = await authFetch(
|
|
274
|
+
`/api/apps/${encodeURIComponent(appId)}/operations/${encodeURIComponent(operationId)}`,
|
|
275
|
+
{
|
|
276
|
+
method: "POST",
|
|
277
|
+
headers: { "Content-Type": "application/json" },
|
|
278
|
+
body: JSON.stringify({ credentialId: opts.credentialId, params: opts.params ?? {} })
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
const err = await credentialError(res);
|
|
283
|
+
return { ok: false, message: err.message };
|
|
284
|
+
}
|
|
285
|
+
return await res.json();
|
|
286
|
+
}
|
|
287
|
+
var CREDENTIALS_API;
|
|
288
|
+
var init_credentials = __esm({
|
|
289
|
+
"src/client/credentials.ts"() {
|
|
290
|
+
"use strict";
|
|
291
|
+
init_client();
|
|
292
|
+
CREDENTIALS_API = "/api/credentials";
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// src/client/onboarding.ts
|
|
297
|
+
async function onboardingError(res) {
|
|
298
|
+
const text = await res.text().catch(() => "");
|
|
299
|
+
if (text) {
|
|
300
|
+
try {
|
|
301
|
+
const body = JSON.parse(text);
|
|
302
|
+
const message = body?.error ?? body?.message;
|
|
303
|
+
if (message) return new Error(message);
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
return new Error(text);
|
|
307
|
+
}
|
|
308
|
+
return new Error(`HTTP ${res.status}`);
|
|
309
|
+
}
|
|
310
|
+
async function fetchAppOnboarding(appId) {
|
|
311
|
+
try {
|
|
312
|
+
const res = await authFetch("/api/apps/enabled", { method: "GET" });
|
|
313
|
+
if (!res.ok) return null;
|
|
314
|
+
const data = await res.json();
|
|
315
|
+
const list = Array.isArray(data) ? data : data?.data ?? [];
|
|
316
|
+
const app = list.find(
|
|
317
|
+
(a) => a.appId === appId || a.id === appId
|
|
318
|
+
);
|
|
319
|
+
return app?.connection?.onboarding ?? null;
|
|
320
|
+
} catch {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function startOnboarding(appId, input) {
|
|
325
|
+
const res = await authFetch(
|
|
326
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/onboard/start`,
|
|
327
|
+
{
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers: { "Content-Type": "application/json" },
|
|
330
|
+
body: JSON.stringify({
|
|
331
|
+
environmentId: input.environmentId,
|
|
332
|
+
connectionName: input.connectionName,
|
|
333
|
+
settings: input.settings ?? {}
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
if (!res.ok) throw await onboardingError(res);
|
|
338
|
+
return await res.json();
|
|
339
|
+
}
|
|
340
|
+
async function getOnboardingStatus(appId, credentialId, opts = {}) {
|
|
341
|
+
const qs = opts.verify ? "?verify=1" : "";
|
|
342
|
+
const res = await authFetch(
|
|
343
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/status${qs}`
|
|
344
|
+
);
|
|
345
|
+
if (!res.ok) throw await onboardingError(res);
|
|
346
|
+
return await res.json();
|
|
347
|
+
}
|
|
348
|
+
async function revokeOnboarding(appId, credentialId) {
|
|
349
|
+
const res = await authFetch(
|
|
350
|
+
`/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/revoke`,
|
|
351
|
+
{ method: "POST" }
|
|
352
|
+
);
|
|
353
|
+
if (!res.ok) throw await onboardingError(res);
|
|
354
|
+
return await res.json();
|
|
355
|
+
}
|
|
356
|
+
var init_onboarding = __esm({
|
|
357
|
+
"src/client/onboarding.ts"() {
|
|
358
|
+
"use strict";
|
|
359
|
+
init_client();
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// src/client/index.ts
|
|
364
|
+
var client_exports = {};
|
|
365
|
+
__export(client_exports, {
|
|
366
|
+
HOST_RUNTIME_GLOBAL: () => HOST_RUNTIME_GLOBAL,
|
|
367
|
+
addInventoryItem: () => addInventoryItem,
|
|
368
|
+
authFetch: () => authFetch,
|
|
369
|
+
createCredential: () => createCredential,
|
|
370
|
+
fetchAppOnboarding: () => fetchAppOnboarding,
|
|
371
|
+
getHostRuntime: () => getHostRuntime,
|
|
372
|
+
getOnboardingStatus: () => getOnboardingStatus,
|
|
373
|
+
listConnectivityProviders: () => listConnectivityProviders,
|
|
374
|
+
listCredentials: () => listCredentials,
|
|
375
|
+
listEnvironments: () => listEnvironments,
|
|
376
|
+
listInventory: () => listInventory,
|
|
377
|
+
removeCredential: () => removeCredential,
|
|
378
|
+
removeInventoryItem: () => removeInventoryItem,
|
|
379
|
+
requireHostRuntime: () => requireHostRuntime,
|
|
380
|
+
resolveTool: () => resolveTool,
|
|
381
|
+
revokeOnboarding: () => revokeOnboarding,
|
|
382
|
+
runOperation: () => runOperation,
|
|
383
|
+
startOnboarding: () => startOnboarding,
|
|
384
|
+
testConnection: () => testConnection,
|
|
385
|
+
updateCredential: () => updateCredential,
|
|
386
|
+
updateInventoryItem: () => updateInventoryItem
|
|
387
|
+
});
|
|
388
|
+
function getHostRuntime() {
|
|
389
|
+
const runtime = globalThis[HOST_RUNTIME_GLOBAL];
|
|
390
|
+
return runtime ?? null;
|
|
391
|
+
}
|
|
392
|
+
function requireHostRuntime() {
|
|
393
|
+
const runtime = getHostRuntime();
|
|
394
|
+
if (!runtime) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
`Veltrix host runtime not found \u2014 app client bundles only run inside the Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return runtime;
|
|
400
|
+
}
|
|
401
|
+
function authFetch(input, init) {
|
|
402
|
+
const runtime = getHostRuntime();
|
|
403
|
+
if (runtime) return runtime.authFetch(input, init);
|
|
404
|
+
return fetch(input, init);
|
|
405
|
+
}
|
|
406
|
+
var HOST_RUNTIME_GLOBAL;
|
|
407
|
+
var init_client = __esm({
|
|
408
|
+
"src/client/index.ts"() {
|
|
409
|
+
"use strict";
|
|
410
|
+
init_inventory();
|
|
411
|
+
init_connectivity();
|
|
412
|
+
init_environments();
|
|
413
|
+
init_credentials();
|
|
414
|
+
init_onboarding();
|
|
415
|
+
HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// src/byol/index.tsx
|
|
420
|
+
var byol_exports = {};
|
|
421
|
+
__export(byol_exports, {
|
|
422
|
+
BYOL_TAG_KEYS: () => BYOL_TAG_KEYS,
|
|
423
|
+
ByolInfrastructureDetail: () => ByolInfrastructureDetail,
|
|
424
|
+
ByolInfrastructureManager: () => ByolInfrastructureManager,
|
|
425
|
+
ByolPlanModal: () => ByolPlanModal,
|
|
426
|
+
CONTROL_PLANE_LAYOUT_OPTIONS: () => CONTROL_PLANE_LAYOUT_OPTIONS,
|
|
427
|
+
DEPLOYMENT_STEPS: () => DEPLOYMENT_STEPS,
|
|
428
|
+
DestroyPlanModal: () => DestroyPlanModal,
|
|
429
|
+
MANAGED_BY: () => MANAGED_BY,
|
|
430
|
+
MIN_HEAVY_FORWARDERS: () => MIN_HEAVY_FORWARDERS,
|
|
431
|
+
PLACEMENT_GRANULARITY_OPTIONS: () => PLACEMENT_GRANULARITY_OPTIONS,
|
|
432
|
+
SINGLE_SITE_PLACEMENT: () => SINGLE_SITE_PLACEMENT,
|
|
433
|
+
TIER_LABELS: () => TIER_LABELS,
|
|
434
|
+
TIER_ORDER: () => TIER_ORDER,
|
|
435
|
+
allocateNodesBySite: () => allocateNodesBySite,
|
|
436
|
+
buildByolPlan: () => buildByolPlan,
|
|
437
|
+
buildByolResourcePlan: () => buildByolResourcePlan,
|
|
438
|
+
buildByolTags: () => buildByolTags,
|
|
439
|
+
default: () => ByolInfrastructureManager_default,
|
|
440
|
+
diffPlan: () => diffPlan,
|
|
441
|
+
effectivePlacement: () => effectivePlacement,
|
|
442
|
+
isNotStarted: () => isNotStarted,
|
|
443
|
+
isRunning: () => isRunning,
|
|
444
|
+
planHasChanges: () => planHasChanges,
|
|
445
|
+
resourceStatusLabel: () => resourceStatusLabel,
|
|
446
|
+
resourceStatusVariant: () => resourceStatusVariant,
|
|
447
|
+
statusLabel: () => statusLabel,
|
|
448
|
+
statusVariant: () => statusVariant,
|
|
449
|
+
stepStatusVariant: () => stepStatusVariant,
|
|
450
|
+
validatePlacement: () => validatePlacement
|
|
451
|
+
});
|
|
452
|
+
module.exports = __toCommonJS(byol_exports);
|
|
453
|
+
|
|
454
|
+
// src/byol/ByolInfrastructureManager.tsx
|
|
455
|
+
var import_react4 = require("react");
|
|
456
|
+
init_client();
|
|
457
|
+
|
|
458
|
+
// src/ui/index.tsx
|
|
459
|
+
var React = __toESM(require("react"), 1);
|
|
460
|
+
init_client();
|
|
461
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
462
|
+
function getHostUi(name) {
|
|
463
|
+
return getHostRuntime()?.ui?.[name];
|
|
464
|
+
}
|
|
465
|
+
var fallbackNote = { fontFamily: "inherit" };
|
|
466
|
+
var Button = React.forwardRef((props, ref) => {
|
|
467
|
+
const HostButton = getHostUi("Button");
|
|
468
|
+
if (HostButton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostButton, { ref, ...props });
|
|
469
|
+
const {
|
|
470
|
+
variant: _variant,
|
|
471
|
+
size: _size,
|
|
472
|
+
isLoading,
|
|
473
|
+
loadingText,
|
|
474
|
+
fullWidth,
|
|
475
|
+
leftIcon,
|
|
476
|
+
rightIcon,
|
|
477
|
+
children,
|
|
478
|
+
disabled,
|
|
479
|
+
type,
|
|
480
|
+
style,
|
|
481
|
+
...rest
|
|
482
|
+
} = props;
|
|
483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
484
|
+
"button",
|
|
485
|
+
{
|
|
486
|
+
ref,
|
|
487
|
+
type: type ?? "button",
|
|
488
|
+
disabled: disabled || isLoading,
|
|
489
|
+
"aria-busy": isLoading || void 0,
|
|
490
|
+
style: {
|
|
491
|
+
...fallbackNote,
|
|
492
|
+
display: "inline-flex",
|
|
493
|
+
alignItems: "center",
|
|
494
|
+
gap: 6,
|
|
495
|
+
padding: "6px 14px",
|
|
496
|
+
borderRadius: 6,
|
|
497
|
+
border: "1px solid currentColor",
|
|
498
|
+
background: "transparent",
|
|
499
|
+
cursor: disabled || isLoading ? "not-allowed" : "pointer",
|
|
500
|
+
width: fullWidth ? "100%" : void 0,
|
|
501
|
+
opacity: disabled || isLoading ? 0.6 : 1,
|
|
502
|
+
justifyContent: "center",
|
|
503
|
+
...style
|
|
504
|
+
},
|
|
505
|
+
...rest,
|
|
506
|
+
children: [
|
|
507
|
+
leftIcon,
|
|
508
|
+
isLoading ? loadingText ?? children : children,
|
|
509
|
+
rightIcon
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
});
|
|
514
|
+
Button.displayName = "Button";
|
|
515
|
+
var BADGE_FALLBACK_COLORS = {
|
|
516
|
+
default: "#6b7280",
|
|
517
|
+
primary: "#4f46e5",
|
|
518
|
+
secondary: "#6b7280",
|
|
519
|
+
success: "#16a34a",
|
|
520
|
+
danger: "#dc2626",
|
|
521
|
+
warning: "#d97706",
|
|
522
|
+
info: "#0284c7"
|
|
523
|
+
};
|
|
524
|
+
var Badge = ({ variant = "default", size = "md", rounded, dot, children, style, ...rest }) => {
|
|
525
|
+
const HostBadge = getHostUi("Badge");
|
|
526
|
+
if (HostBadge) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostBadge, { variant, size, rounded, dot, style, ...rest, children });
|
|
527
|
+
const color = BADGE_FALLBACK_COLORS[variant];
|
|
528
|
+
const fontSize = size === "sm" ? 11 : size === "lg" ? 14 : 12;
|
|
529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
530
|
+
"span",
|
|
531
|
+
{
|
|
532
|
+
style: {
|
|
533
|
+
...fallbackNote,
|
|
534
|
+
display: "inline-flex",
|
|
535
|
+
alignItems: "center",
|
|
536
|
+
gap: 4,
|
|
537
|
+
padding: size === "sm" ? "1px 6px" : size === "lg" ? "3px 10px" : "2px 8px",
|
|
538
|
+
borderRadius: rounded ? 999 : 4,
|
|
539
|
+
border: `1px solid ${color}`,
|
|
540
|
+
color,
|
|
541
|
+
fontSize,
|
|
542
|
+
fontWeight: 500,
|
|
543
|
+
...style
|
|
544
|
+
},
|
|
545
|
+
...rest,
|
|
546
|
+
children: [
|
|
547
|
+
dot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", style: { width: 6, height: 6, borderRadius: "50%", background: color } }),
|
|
548
|
+
children
|
|
549
|
+
]
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
};
|
|
553
|
+
Badge.displayName = "Badge";
|
|
554
|
+
var Card = ({ children, style, ...rest }) => {
|
|
555
|
+
const HostCard = getHostUi("Card");
|
|
556
|
+
if (HostCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCard, { style, ...rest, children });
|
|
557
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, border: "1px solid #d1d5db", borderRadius: 8, overflow: "hidden", ...style }, ...rest, children });
|
|
558
|
+
};
|
|
559
|
+
Card.displayName = "Card";
|
|
560
|
+
var CardHeader = ({ actions, children, style, ...rest }) => {
|
|
561
|
+
const HostCardHeader = getHostUi("CardHeader");
|
|
562
|
+
if (HostCardHeader) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardHeader, { actions, style, ...rest, children });
|
|
563
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
564
|
+
"div",
|
|
565
|
+
{
|
|
566
|
+
style: {
|
|
567
|
+
...fallbackNote,
|
|
568
|
+
padding: "12px 16px",
|
|
569
|
+
borderBottom: "1px solid #d1d5db",
|
|
570
|
+
display: "flex",
|
|
571
|
+
alignItems: "center",
|
|
572
|
+
justifyContent: "space-between",
|
|
573
|
+
gap: 12,
|
|
574
|
+
...style
|
|
575
|
+
},
|
|
576
|
+
...rest,
|
|
577
|
+
children: [
|
|
578
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0 }, children }),
|
|
579
|
+
actions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: actions })
|
|
580
|
+
]
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
};
|
|
584
|
+
CardHeader.displayName = "CardHeader";
|
|
585
|
+
var CardBody = ({ children, style, ...rest }) => {
|
|
586
|
+
const HostCardBody = getHostUi("CardBody");
|
|
587
|
+
if (HostCardBody) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardBody, { style, ...rest, children });
|
|
588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, padding: "12px 16px", ...style }, ...rest, children });
|
|
589
|
+
};
|
|
590
|
+
CardBody.displayName = "CardBody";
|
|
591
|
+
var CardFooter = ({ children, style, ...rest }) => {
|
|
592
|
+
const HostCardFooter = getHostUi("CardFooter");
|
|
593
|
+
if (HostCardFooter) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardFooter, { style, ...rest, children });
|
|
594
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, padding: "12px 16px", borderTop: "1px solid #d1d5db", ...style }, ...rest, children });
|
|
595
|
+
};
|
|
596
|
+
CardFooter.displayName = "CardFooter";
|
|
597
|
+
var Input = React.forwardRef((props, ref) => {
|
|
598
|
+
const HostInput = getHostUi("Input");
|
|
599
|
+
if (HostInput) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostInput, { ref, ...props });
|
|
600
|
+
const { label, error, helperText, leftIcon, rightIcon, fullWidth = true, id, style, ...rest } = props;
|
|
601
|
+
const inputId = id ?? (label ? `vx-input-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
|
|
602
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
|
|
603
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
|
|
604
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
605
|
+
leftIcon,
|
|
606
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
607
|
+
"input",
|
|
608
|
+
{
|
|
609
|
+
ref,
|
|
610
|
+
id: inputId,
|
|
611
|
+
"aria-invalid": !!error || void 0,
|
|
612
|
+
style: { flex: 1, padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af", ...style },
|
|
613
|
+
...rest
|
|
614
|
+
}
|
|
615
|
+
),
|
|
616
|
+
rightIcon
|
|
617
|
+
] }),
|
|
618
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
|
|
619
|
+
helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
|
|
620
|
+
] });
|
|
621
|
+
});
|
|
622
|
+
Input.displayName = "Input";
|
|
623
|
+
var Textarea = React.forwardRef((props, ref) => {
|
|
624
|
+
const HostTextarea = getHostUi("Textarea");
|
|
625
|
+
if (HostTextarea) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTextarea, { ref, ...props });
|
|
626
|
+
const { label, error, helperText, fullWidth = true, id, rows = 4, style, ...rest } = props;
|
|
627
|
+
const textareaId = id ?? (label ? `vx-textarea-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
|
|
628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
|
|
629
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: textareaId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
|
|
630
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
631
|
+
"textarea",
|
|
632
|
+
{
|
|
633
|
+
ref,
|
|
634
|
+
id: textareaId,
|
|
635
|
+
rows,
|
|
636
|
+
"aria-invalid": !!error || void 0,
|
|
637
|
+
style: { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af", ...style },
|
|
638
|
+
...rest
|
|
639
|
+
}
|
|
640
|
+
),
|
|
641
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
|
|
642
|
+
helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
|
|
643
|
+
] });
|
|
644
|
+
});
|
|
645
|
+
Textarea.displayName = "Textarea";
|
|
646
|
+
var Checkbox = React.forwardRef((props, ref) => {
|
|
647
|
+
const HostCheckbox = getHostUi("Checkbox");
|
|
648
|
+
if (HostCheckbox) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCheckbox, { ref, ...props });
|
|
649
|
+
const { label, error, helperText, id, disabled, ...rest } = props;
|
|
650
|
+
const generatedId = React.useId();
|
|
651
|
+
const inputId = id ?? generatedId;
|
|
652
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fallbackNote, children: [
|
|
653
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref, id: inputId, type: "checkbox", disabled, "aria-invalid": !!error || void 0, ...rest }),
|
|
655
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: { fontSize: 13, cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.6 : 1 }, children: label })
|
|
656
|
+
] }),
|
|
657
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
|
|
658
|
+
helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
|
|
659
|
+
] });
|
|
660
|
+
});
|
|
661
|
+
Checkbox.displayName = "Checkbox";
|
|
662
|
+
var Select = (props) => {
|
|
663
|
+
const HostSelect = getHostUi("Select");
|
|
664
|
+
if (HostSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSelect, { ...props });
|
|
665
|
+
const { options, value, onChange, placeholder = "Select\u2026", label, error, helperText, disabled, fullWidth = true, id, name, className } = props;
|
|
666
|
+
const selectId = id ?? (label ? `vx-select-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
|
|
667
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
|
|
668
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: selectId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
|
|
669
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
670
|
+
"select",
|
|
671
|
+
{
|
|
672
|
+
id: selectId,
|
|
673
|
+
name,
|
|
674
|
+
"aria-label": props["aria-label"],
|
|
675
|
+
"aria-invalid": !!error || void 0,
|
|
676
|
+
disabled,
|
|
677
|
+
value: value ?? "",
|
|
678
|
+
onChange: (event) => onChange?.(event.target.value),
|
|
679
|
+
style: { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
|
|
680
|
+
children: [
|
|
681
|
+
placeholder && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", disabled: true, children: placeholder }),
|
|
682
|
+
options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
|
|
683
|
+
]
|
|
684
|
+
}
|
|
685
|
+
),
|
|
686
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
|
|
687
|
+
helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
|
|
688
|
+
] });
|
|
689
|
+
};
|
|
690
|
+
Select.displayName = "Select";
|
|
691
|
+
var SearchBox = (props) => {
|
|
692
|
+
const HostSearchBox = getHostUi("SearchBox");
|
|
693
|
+
if (HostSearchBox) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSearchBox, { ...props });
|
|
694
|
+
const { value, onChange, placeholder, disabled, className, "aria-label": ariaLabel } = props;
|
|
695
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
696
|
+
"input",
|
|
697
|
+
{
|
|
698
|
+
type: "search",
|
|
699
|
+
value,
|
|
700
|
+
onChange: (event) => onChange(event.target.value),
|
|
701
|
+
placeholder,
|
|
702
|
+
disabled,
|
|
703
|
+
"aria-label": ariaLabel ?? placeholder ?? "Search",
|
|
704
|
+
className,
|
|
705
|
+
style: { ...fallbackNote, width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" }
|
|
706
|
+
}
|
|
707
|
+
);
|
|
708
|
+
};
|
|
709
|
+
SearchBox.displayName = "SearchBox";
|
|
710
|
+
var Pagination = (props) => {
|
|
711
|
+
const HostPagination = getHostUi("Pagination");
|
|
712
|
+
if (HostPagination) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostPagination, { ...props });
|
|
713
|
+
const { page, pageSize, totalItems, onPageChange, disabled, className } = props;
|
|
714
|
+
const pageCount = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
715
|
+
const canGoPrev = !disabled && page > 1;
|
|
716
|
+
const canGoNext = !disabled && page < pageCount;
|
|
717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { "aria-label": "Pagination", className, style: { ...fallbackNote, display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
718
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: !canGoPrev, style: { padding: "4px 10px" }, children: "Prev" }),
|
|
719
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
720
|
+
"page ",
|
|
721
|
+
page,
|
|
722
|
+
" of ",
|
|
723
|
+
pageCount
|
|
724
|
+
] }),
|
|
725
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: !canGoNext, style: { padding: "4px 10px" }, children: "Next" })
|
|
726
|
+
] });
|
|
727
|
+
};
|
|
728
|
+
Pagination.displayName = "Pagination";
|
|
729
|
+
var FilterBar = (props) => {
|
|
730
|
+
const HostFilterBar = getHostUi("FilterBar");
|
|
731
|
+
if (HostFilterBar) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFilterBar, { ...props });
|
|
732
|
+
const { filters, search, className } = props;
|
|
733
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [
|
|
734
|
+
search && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
735
|
+
"input",
|
|
736
|
+
{
|
|
737
|
+
type: "search",
|
|
738
|
+
"aria-label": search.placeholder ?? "Search",
|
|
739
|
+
placeholder: search.placeholder,
|
|
740
|
+
value: search.value,
|
|
741
|
+
onChange: (event) => search.onChange(event.target.value),
|
|
742
|
+
style: { padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" }
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
filters.map((filter) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", flexDirection: "column", fontSize: 12 }, children: [
|
|
746
|
+
filter.label,
|
|
747
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
748
|
+
"select",
|
|
749
|
+
{
|
|
750
|
+
"aria-label": filter.label,
|
|
751
|
+
value: filter.value ?? "",
|
|
752
|
+
onChange: (event) => filter.onChange(event.target.value === "" ? null : event.target.value),
|
|
753
|
+
style: { padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
|
|
754
|
+
children: [
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: filter.label }),
|
|
756
|
+
filter.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
)
|
|
760
|
+
] }, filter.key))
|
|
761
|
+
] });
|
|
762
|
+
};
|
|
763
|
+
FilterBar.displayName = "FilterBar";
|
|
764
|
+
var SortSelect = (props) => {
|
|
765
|
+
const HostSortSelect = getHostUi("SortSelect");
|
|
766
|
+
if (HostSortSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSortSelect, { ...props });
|
|
767
|
+
const { options, value, direction, onChange, disabled, className } = props;
|
|
768
|
+
const directionLabel = direction === "asc" ? "Sort ascending" : "Sort descending";
|
|
769
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
770
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
771
|
+
"select",
|
|
772
|
+
{
|
|
773
|
+
"aria-label": "Sort by",
|
|
774
|
+
value,
|
|
775
|
+
disabled,
|
|
776
|
+
onChange: (event) => onChange(event.target.value, direction),
|
|
777
|
+
style: { padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
|
|
778
|
+
children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, children: option.label }, option.value))
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
782
|
+
"button",
|
|
783
|
+
{
|
|
784
|
+
type: "button",
|
|
785
|
+
disabled,
|
|
786
|
+
onClick: () => onChange(value, direction === "asc" ? "desc" : "asc"),
|
|
787
|
+
"aria-label": directionLabel,
|
|
788
|
+
style: { padding: "4px 10px" },
|
|
789
|
+
children: direction === "asc" ? "\u2191" : "\u2193"
|
|
790
|
+
}
|
|
791
|
+
)
|
|
792
|
+
] });
|
|
793
|
+
};
|
|
794
|
+
SortSelect.displayName = "SortSelect";
|
|
795
|
+
var FormField = (props) => {
|
|
796
|
+
const HostFormField = getHostUi("FormField");
|
|
797
|
+
if (HostFormField) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFormField, { ...props });
|
|
798
|
+
const { label, htmlFor, error, hint, required, className, children } = props;
|
|
799
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: fallbackNote, children: [
|
|
800
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: [
|
|
801
|
+
label,
|
|
802
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", style: { color: "#dc2626", marginLeft: 2 }, children: "*" })
|
|
803
|
+
] }),
|
|
804
|
+
children,
|
|
805
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
|
|
806
|
+
hint && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: hint })
|
|
807
|
+
] });
|
|
808
|
+
};
|
|
809
|
+
FormField.displayName = "FormField";
|
|
810
|
+
var Tabs = (props) => {
|
|
811
|
+
const HostTabs = getHostUi("Tabs");
|
|
812
|
+
if (HostTabs) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTabs, { ...props });
|
|
813
|
+
const { tabs, defaultActiveIndex = 0, activeIndex, onTabChange, children, className } = props;
|
|
814
|
+
const [internalIndex, setInternalIndex] = React.useState(defaultActiveIndex);
|
|
815
|
+
const isControlled = activeIndex !== void 0;
|
|
816
|
+
const currentIndex = isControlled ? activeIndex : internalIndex;
|
|
817
|
+
const activeTab = tabs[currentIndex];
|
|
818
|
+
const selectTab = (index) => {
|
|
819
|
+
if (tabs[index]?.disabled) return;
|
|
820
|
+
if (!isControlled) setInternalIndex(index);
|
|
821
|
+
onTabChange?.(index);
|
|
822
|
+
};
|
|
823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: fallbackNote, children: [
|
|
824
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "tablist", style: { display: "flex", gap: 4, borderBottom: "1px solid #d1d5db" }, children: tabs.map((tab, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
825
|
+
"button",
|
|
826
|
+
{
|
|
827
|
+
type: "button",
|
|
828
|
+
role: "tab",
|
|
829
|
+
"aria-selected": index === currentIndex,
|
|
830
|
+
disabled: tab.disabled,
|
|
831
|
+
onClick: () => selectTab(index),
|
|
832
|
+
style: {
|
|
833
|
+
padding: "6px 12px",
|
|
834
|
+
border: "none",
|
|
835
|
+
borderBottom: index === currentIndex ? "2px solid currentColor" : "2px solid transparent",
|
|
836
|
+
background: "transparent",
|
|
837
|
+
cursor: tab.disabled ? "not-allowed" : "pointer",
|
|
838
|
+
fontWeight: index === currentIndex ? 600 : 400,
|
|
839
|
+
opacity: tab.disabled ? 0.5 : 1
|
|
840
|
+
},
|
|
841
|
+
children: tab.label
|
|
842
|
+
},
|
|
843
|
+
tab.key ?? index
|
|
844
|
+
)) }),
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "tabpanel", style: { padding: 12 }, children: children || activeTab?.content })
|
|
846
|
+
] });
|
|
847
|
+
};
|
|
848
|
+
Tabs.displayName = "Tabs";
|
|
849
|
+
var EmptyState = (props) => {
|
|
850
|
+
const HostEmptyState = getHostUi("EmptyState");
|
|
851
|
+
if (HostEmptyState) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostEmptyState, { ...props });
|
|
852
|
+
const { icon, title, description, action, className } = props;
|
|
853
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, textAlign: "center", padding: "32px 16px" }, children: [
|
|
854
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 12 }, children: icon }),
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontWeight: 600, marginBottom: description ? 4 : 0 }, children: title }),
|
|
856
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 13, color: "#6b7280", marginBottom: action ? 12 : 0 }, children: description }),
|
|
857
|
+
action
|
|
858
|
+
] });
|
|
859
|
+
};
|
|
860
|
+
EmptyState.displayName = "EmptyState";
|
|
861
|
+
var skeletonRadius = { text: 4, circular: "50%", rectangular: 8 };
|
|
862
|
+
var Skeleton = (props) => {
|
|
863
|
+
const HostSkeleton = getHostUi("Skeleton");
|
|
864
|
+
if (HostSkeleton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeleton, { ...props });
|
|
865
|
+
const { variant = "text", width, height, style, ...rest } = props;
|
|
866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
867
|
+
"div",
|
|
868
|
+
{
|
|
869
|
+
"aria-hidden": "true",
|
|
870
|
+
style: {
|
|
871
|
+
background: "#e5e7eb",
|
|
872
|
+
borderRadius: skeletonRadius[variant],
|
|
873
|
+
width: width ?? (variant === "text" ? "100%" : void 0),
|
|
874
|
+
height: height ?? (variant === "text" ? 14 : void 0),
|
|
875
|
+
...style
|
|
876
|
+
},
|
|
877
|
+
...rest
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
};
|
|
881
|
+
Skeleton.displayName = "Skeleton";
|
|
882
|
+
var SkeletonText = (props) => {
|
|
883
|
+
const HostSkeletonText = getHostUi("SkeletonText");
|
|
884
|
+
if (HostSkeletonText) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeletonText, { ...props });
|
|
885
|
+
const { lines = 3, width = "100%", lastLineWidth = "80%", className } = props;
|
|
886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: { display: "flex", flexDirection: "column", gap: 8 }, children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "text", width: index === lines - 1 ? lastLineWidth : width }, index)) });
|
|
887
|
+
};
|
|
888
|
+
SkeletonText.displayName = "SkeletonText";
|
|
889
|
+
var SkeletonCard = (props) => {
|
|
890
|
+
const HostSkeletonCard = getHostUi("SkeletonCard");
|
|
891
|
+
if (HostSkeletonCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeletonCard, { ...props });
|
|
892
|
+
const { hasAvatar, hasActions, className } = props;
|
|
893
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { border: "1px solid #e5e7eb", borderRadius: 8, padding: 16 }, children: [
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 12 }, children: [
|
|
895
|
+
hasAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "circular", width: 40, height: 40 }),
|
|
896
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1 }, children: [
|
|
897
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "text", width: "60%" }),
|
|
898
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonText, { lines: 2 }) })
|
|
899
|
+
] })
|
|
900
|
+
] }),
|
|
901
|
+
hasActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 12 }, children: [
|
|
902
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "rectangular", width: 72, height: 30 }),
|
|
903
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "rectangular", width: 72, height: 30 })
|
|
904
|
+
] })
|
|
905
|
+
] });
|
|
906
|
+
};
|
|
907
|
+
SkeletonCard.displayName = "SkeletonCard";
|
|
908
|
+
var Tooltip = (props) => {
|
|
909
|
+
const HostTooltip = getHostUi("Tooltip");
|
|
910
|
+
if (HostTooltip) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTooltip, { ...props });
|
|
911
|
+
const { content, disabled, className, children } = props;
|
|
912
|
+
const titleText = typeof content === "string" ? content : void 0;
|
|
913
|
+
if (disabled || !titleText) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className, title: titleText, style: { display: "inline-flex" }, children });
|
|
915
|
+
};
|
|
916
|
+
Tooltip.displayName = "Tooltip";
|
|
917
|
+
var spinnerDiameter = { sm: 16, md: 24, lg: 32 };
|
|
918
|
+
var Spinner = (props) => {
|
|
919
|
+
const HostSpinner = getHostUi("Spinner");
|
|
920
|
+
if (HostSpinner) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSpinner, { ...props });
|
|
921
|
+
const { size = "md", className, label } = props;
|
|
922
|
+
const diameter = spinnerDiameter[size];
|
|
923
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, role: "status", "aria-live": "polite", style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
|
|
924
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
925
|
+
"div",
|
|
926
|
+
{
|
|
927
|
+
"aria-hidden": "true",
|
|
928
|
+
style: {
|
|
929
|
+
width: diameter,
|
|
930
|
+
height: diameter,
|
|
931
|
+
borderRadius: "50%",
|
|
932
|
+
border: "2px solid currentColor",
|
|
933
|
+
borderTopColor: "transparent",
|
|
934
|
+
animation: "vx-sdk-spin 0.8s linear infinite"
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
),
|
|
938
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 8, fontSize: 12, color: "#6b7280" }, children: label }),
|
|
939
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: label || "Loading" }),
|
|
940
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: "@keyframes vx-sdk-spin { to { transform: rotate(360deg) } }" })
|
|
941
|
+
] });
|
|
942
|
+
};
|
|
943
|
+
Spinner.displayName = "Spinner";
|
|
944
|
+
function FallbackDataTable(props) {
|
|
945
|
+
const { columns, data, rowKey, isLoading, emptyState, onRowClick, rowActions, className } = props;
|
|
946
|
+
const showEmpty = !isLoading && data.length === 0;
|
|
947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: fallbackNote, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse" }, children: [
|
|
948
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
949
|
+
columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: { textAlign: column.align ?? "left", padding: "8px 10px", borderBottom: "1px solid #d1d5db" }, children: column.header }, column.key)),
|
|
950
|
+
rowActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: { padding: "8px 10px", borderBottom: "1px solid #d1d5db" } })
|
|
951
|
+
] }) }),
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [
|
|
953
|
+
isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: Math.max(columns.length + (rowActions ? 1 : 0), 1), style: { padding: "12px 10px" }, children: "Loading\u2026" }) }),
|
|
954
|
+
showEmpty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: Math.max(columns.length + (rowActions ? 1 : 0), 1), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { title: emptyState?.title ?? "No data", description: emptyState?.description, icon: emptyState?.icon, action: emptyState?.action }) }) }),
|
|
955
|
+
!isLoading && !showEmpty && data.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
956
|
+
"tr",
|
|
957
|
+
{
|
|
958
|
+
onClick: onRowClick ? () => onRowClick(row) : void 0,
|
|
959
|
+
style: { cursor: onRowClick ? "pointer" : void 0, borderBottom: "1px solid #e5e7eb" },
|
|
960
|
+
children: [
|
|
961
|
+
columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { textAlign: column.align ?? "left", padding: "8px 10px" }, children: column.render ? column.render(row) : String(row[column.key] ?? "") }, column.key)),
|
|
962
|
+
rowActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { padding: "8px 10px" }, onClick: (event) => event.stopPropagation(), children: rowActions(row) })
|
|
963
|
+
]
|
|
964
|
+
},
|
|
965
|
+
rowKey(row)
|
|
966
|
+
))
|
|
967
|
+
] })
|
|
968
|
+
] }) });
|
|
969
|
+
}
|
|
970
|
+
function DataTable(props) {
|
|
971
|
+
const HostDataTable = getHostUi("DataTable");
|
|
972
|
+
if (HostDataTable) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostDataTable, { ...props });
|
|
973
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FallbackDataTable, { ...props });
|
|
974
|
+
}
|
|
975
|
+
var DELTA_FALLBACK_COLOR = {
|
|
976
|
+
up: "#16a34a",
|
|
977
|
+
down: "#dc2626",
|
|
978
|
+
neutral: "#6b7280"
|
|
979
|
+
};
|
|
980
|
+
var StatsCard = (props) => {
|
|
981
|
+
const HostStatsCard = getHostUi("StatsCard");
|
|
982
|
+
if (HostStatsCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostStatsCard, { ...props });
|
|
983
|
+
const { label, value, icon, delta, isLoading, onClick, className } = props;
|
|
984
|
+
const isClickable = Boolean(onClick);
|
|
985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
986
|
+
"div",
|
|
987
|
+
{
|
|
988
|
+
role: isClickable ? "button" : void 0,
|
|
989
|
+
tabIndex: isClickable ? 0 : void 0,
|
|
990
|
+
onClick,
|
|
991
|
+
onKeyDown: isClickable ? (event) => {
|
|
992
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
993
|
+
event.preventDefault();
|
|
994
|
+
onClick?.();
|
|
995
|
+
}
|
|
996
|
+
} : void 0,
|
|
997
|
+
className,
|
|
998
|
+
style: {
|
|
999
|
+
...fallbackNote,
|
|
1000
|
+
border: "1px solid #d1d5db",
|
|
1001
|
+
borderRadius: 8,
|
|
1002
|
+
padding: 16,
|
|
1003
|
+
display: "flex",
|
|
1004
|
+
alignItems: "flex-start",
|
|
1005
|
+
justifyContent: "space-between",
|
|
1006
|
+
gap: 12,
|
|
1007
|
+
cursor: isClickable ? "pointer" : void 0
|
|
1008
|
+
},
|
|
1009
|
+
children: [
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
1011
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 13, color: "#6b7280", margin: 0 }, children: label }),
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 28, fontWeight: 700, margin: "4px 0 0" }, children: isLoading ? "\u2026" : value }),
|
|
1013
|
+
delta && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { fontSize: 13, color: DELTA_FALLBACK_COLOR[delta.direction], margin: "4px 0 0" }, children: [
|
|
1014
|
+
delta.value,
|
|
1015
|
+
" ",
|
|
1016
|
+
delta.label
|
|
1017
|
+
] })
|
|
1018
|
+
] }),
|
|
1019
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": "true", children: icon })
|
|
1020
|
+
]
|
|
1021
|
+
}
|
|
1022
|
+
);
|
|
1023
|
+
};
|
|
1024
|
+
StatsCard.displayName = "StatsCard";
|
|
1025
|
+
var FORM_DIALOG_MAX_WIDTH = { sm: 420, md: 520, lg: 680 };
|
|
1026
|
+
var FormDialog = (props) => {
|
|
1027
|
+
const HostFormDialog = getHostUi("FormDialog");
|
|
1028
|
+
if (HostFormDialog) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFormDialog, { ...props });
|
|
1029
|
+
const {
|
|
1030
|
+
isOpen,
|
|
1031
|
+
onClose,
|
|
1032
|
+
title,
|
|
1033
|
+
description,
|
|
1034
|
+
children,
|
|
1035
|
+
onSubmit,
|
|
1036
|
+
submitText = "Save",
|
|
1037
|
+
cancelText = "Cancel",
|
|
1038
|
+
isSubmitting = false,
|
|
1039
|
+
error = null,
|
|
1040
|
+
size = "md",
|
|
1041
|
+
disableBackdropClose = false,
|
|
1042
|
+
submitDisabled = false
|
|
1043
|
+
} = props;
|
|
1044
|
+
React.useEffect(() => {
|
|
1045
|
+
if (!isOpen) return;
|
|
1046
|
+
const handleKeyDown = (event) => {
|
|
1047
|
+
if (event.key === "Escape" && !isSubmitting) onClose();
|
|
1048
|
+
};
|
|
1049
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1050
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1051
|
+
}, [isOpen, isSubmitting, onClose]);
|
|
1052
|
+
if (!isOpen) return null;
|
|
1053
|
+
const requestClose = () => {
|
|
1054
|
+
if (!isSubmitting) onClose();
|
|
1055
|
+
};
|
|
1056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1057
|
+
"div",
|
|
1058
|
+
{
|
|
1059
|
+
style: { position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 50 },
|
|
1060
|
+
onClick: disableBackdropClose ? void 0 : requestClose,
|
|
1061
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1062
|
+
"div",
|
|
1063
|
+
{
|
|
1064
|
+
role: "dialog",
|
|
1065
|
+
"aria-modal": "true",
|
|
1066
|
+
"aria-label": title,
|
|
1067
|
+
style: { background: "white", color: "black", borderRadius: 8, width: "100%", maxWidth: FORM_DIALOG_MAX_WIDTH[size], maxHeight: "80vh", overflow: "auto", ...fallbackNote },
|
|
1068
|
+
onClick: (event) => event.stopPropagation(),
|
|
1069
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1070
|
+
"form",
|
|
1071
|
+
{
|
|
1072
|
+
onSubmit: (event) => {
|
|
1073
|
+
event.preventDefault();
|
|
1074
|
+
void onSubmit();
|
|
1075
|
+
},
|
|
1076
|
+
children: [
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderBottom: "1px solid #d1d5db" }, children: [
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title }),
|
|
1079
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: "4px 0 0", fontSize: 13, color: "#6b7280" }, children: description })
|
|
1080
|
+
] }),
|
|
1081
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, display: "flex", flexDirection: "column", gap: 12 }, children: [
|
|
1082
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { margin: 0, fontSize: 13, color: "#dc2626" }, children: error }),
|
|
1083
|
+
children
|
|
1084
|
+
] }),
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderTop: "1px solid #d1d5db", display: "flex", justifyContent: "flex-end", gap: 8 }, children: [
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { type: "button", variant: "secondary", onClick: requestClose, disabled: isSubmitting, children: cancelText }),
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { type: "submit", variant: "primary", isLoading: isSubmitting, disabled: submitDisabled, children: submitText })
|
|
1088
|
+
] })
|
|
1089
|
+
]
|
|
1090
|
+
}
|
|
1091
|
+
)
|
|
1092
|
+
}
|
|
1093
|
+
)
|
|
1094
|
+
}
|
|
1095
|
+
);
|
|
1096
|
+
};
|
|
1097
|
+
FormDialog.displayName = "FormDialog";
|
|
1098
|
+
var MODAL_MAX_WIDTH = { sm: 420, md: 520, lg: 680, xl: 896 };
|
|
1099
|
+
var Modal = (props) => {
|
|
1100
|
+
const HostModal = getHostUi("Modal");
|
|
1101
|
+
if (HostModal) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostModal, { ...props });
|
|
1102
|
+
const {
|
|
1103
|
+
isOpen,
|
|
1104
|
+
onClose,
|
|
1105
|
+
title,
|
|
1106
|
+
subtitle,
|
|
1107
|
+
children,
|
|
1108
|
+
footer,
|
|
1109
|
+
size = "md",
|
|
1110
|
+
disableBackdropClose = false,
|
|
1111
|
+
disableEscapeClose = false,
|
|
1112
|
+
hideCloseButton = false,
|
|
1113
|
+
closeLabel = "Close",
|
|
1114
|
+
className
|
|
1115
|
+
} = props;
|
|
1116
|
+
React.useEffect(() => {
|
|
1117
|
+
if (!isOpen) return;
|
|
1118
|
+
const handleKeyDown = (event) => {
|
|
1119
|
+
if (event.key === "Escape" && !disableEscapeClose) onClose();
|
|
1120
|
+
};
|
|
1121
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1122
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1123
|
+
}, [isOpen, disableEscapeClose, onClose]);
|
|
1124
|
+
if (!isOpen) return null;
|
|
1125
|
+
const hasHeader = title != null || subtitle != null || !hideCloseButton;
|
|
1126
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1127
|
+
"div",
|
|
1128
|
+
{
|
|
1129
|
+
style: { position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 50 },
|
|
1130
|
+
onClick: disableBackdropClose ? void 0 : onClose,
|
|
1131
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1132
|
+
"div",
|
|
1133
|
+
{
|
|
1134
|
+
role: "dialog",
|
|
1135
|
+
"aria-modal": "true",
|
|
1136
|
+
style: { background: "white", color: "black", borderRadius: 8, width: "100%", maxWidth: MODAL_MAX_WIDTH[size], maxHeight: "80vh", overflow: "auto", ...fallbackNote },
|
|
1137
|
+
className,
|
|
1138
|
+
onClick: (event) => event.stopPropagation(),
|
|
1139
|
+
children: [
|
|
1140
|
+
hasHeader && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderBottom: "1px solid #d1d5db", display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16 }, children: [
|
|
1141
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0 }, children: [
|
|
1142
|
+
title != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title }),
|
|
1143
|
+
subtitle != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: "4px 0 0", fontSize: 13, color: "#6b7280" }, children: subtitle })
|
|
1144
|
+
] }),
|
|
1145
|
+
!hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: onClose, "aria-label": closeLabel, style: { background: "none", border: "none", cursor: "pointer", fontSize: 18, lineHeight: 1 }, children: "\xD7" })
|
|
1146
|
+
] }),
|
|
1147
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, display: "flex", flexDirection: "column", gap: 12 }, children }),
|
|
1148
|
+
footer != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, borderTop: "1px solid #d1d5db", display: "flex", justifyContent: "flex-end", gap: 8 }, children: footer })
|
|
1149
|
+
]
|
|
1150
|
+
}
|
|
1151
|
+
)
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
1154
|
+
};
|
|
1155
|
+
Modal.displayName = "Modal";
|
|
1156
|
+
var ALERT_FALLBACK_COLORS = {
|
|
1157
|
+
info: { bg: "#eff6ff", fg: "#1e40af", border: "#93c5fd" },
|
|
1158
|
+
success: { bg: "#ecfdf5", fg: "#065f46", border: "#6ee7b7" },
|
|
1159
|
+
warning: { bg: "#fffbeb", fg: "#92400e", border: "#fcd34d" },
|
|
1160
|
+
danger: { bg: "#fef2f2", fg: "#991b1b", border: "#fca5a5" }
|
|
1161
|
+
};
|
|
1162
|
+
var Alert = (props) => {
|
|
1163
|
+
const HostAlert = getHostUi("Alert");
|
|
1164
|
+
if (HostAlert) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostAlert, { ...props });
|
|
1165
|
+
const { variant = "info", title, children, icon, onDismiss, action, className } = props;
|
|
1166
|
+
const c = ALERT_FALLBACK_COLORS[variant];
|
|
1167
|
+
const role = variant === "danger" || variant === "warning" ? "alert" : "status";
|
|
1168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1169
|
+
"div",
|
|
1170
|
+
{
|
|
1171
|
+
role,
|
|
1172
|
+
className,
|
|
1173
|
+
style: {
|
|
1174
|
+
display: "flex",
|
|
1175
|
+
alignItems: "flex-start",
|
|
1176
|
+
gap: 10,
|
|
1177
|
+
border: `1px solid ${c.border}`,
|
|
1178
|
+
background: c.bg,
|
|
1179
|
+
color: c.fg,
|
|
1180
|
+
borderRadius: 8,
|
|
1181
|
+
padding: "12px 16px",
|
|
1182
|
+
...fallbackNote
|
|
1183
|
+
},
|
|
1184
|
+
children: [
|
|
1185
|
+
icon !== false && icon != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flexShrink: 0 }, children: icon }),
|
|
1186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
1187
|
+
title != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, fontSize: 14, fontWeight: 600 }, children: title }),
|
|
1188
|
+
children != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 14, marginTop: title != null ? 2 : 0 }, children })
|
|
1189
|
+
] }),
|
|
1190
|
+
action != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flexShrink: 0 }, children: action }),
|
|
1191
|
+
onDismiss && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1192
|
+
"button",
|
|
1193
|
+
{
|
|
1194
|
+
type: "button",
|
|
1195
|
+
onClick: onDismiss,
|
|
1196
|
+
"aria-label": "Dismiss",
|
|
1197
|
+
style: { flexShrink: 0, background: "none", border: "none", cursor: "pointer", color: "inherit", opacity: 0.7 },
|
|
1198
|
+
children: "\xD7"
|
|
1199
|
+
}
|
|
1200
|
+
)
|
|
1201
|
+
]
|
|
1202
|
+
}
|
|
1203
|
+
);
|
|
1204
|
+
};
|
|
1205
|
+
Alert.displayName = "Alert";
|
|
1206
|
+
var MultiSelect = (props) => {
|
|
1207
|
+
const HostMultiSelect = getHostUi("MultiSelect");
|
|
1208
|
+
if (HostMultiSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostMultiSelect, { ...props });
|
|
1209
|
+
const { options, value = [], onChange, label, disabled, fullWidth = true, name } = props;
|
|
1210
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { ...fallbackNote, display: "block", width: fullWidth ? "100%" : void 0 }, children: [
|
|
1211
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { display: "block", marginBottom: 4, fontSize: 14, fontWeight: 500 }, children: label }),
|
|
1212
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1213
|
+
"select",
|
|
1214
|
+
{
|
|
1215
|
+
multiple: true,
|
|
1216
|
+
name,
|
|
1217
|
+
disabled,
|
|
1218
|
+
value,
|
|
1219
|
+
"aria-label": props["aria-label"],
|
|
1220
|
+
onChange: (e) => onChange?.(Array.from(e.target.selectedOptions, (o) => o.value)),
|
|
1221
|
+
style: { width: "100%", minHeight: 96, padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
|
|
1222
|
+
children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
|
|
1223
|
+
}
|
|
1224
|
+
)
|
|
1225
|
+
] });
|
|
1226
|
+
};
|
|
1227
|
+
MultiSelect.displayName = "MultiSelect";
|
|
1228
|
+
var fallbackConfirmationContext = {
|
|
1229
|
+
confirm: (options) => {
|
|
1230
|
+
console.warn(
|
|
1231
|
+
`[@veltrixsecops/app-sdk/ui] ConfirmationDialog is only available inside the Veltrix platform \u2014 "${options.title}" auto-resolved to "not confirmed" (fail closed).`
|
|
1232
|
+
);
|
|
1233
|
+
return Promise.resolve(false);
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
function useConfirmDialog() {
|
|
1237
|
+
const hostUseConfirmDialog = getHostUi("useConfirmDialog");
|
|
1238
|
+
if (hostUseConfirmDialog) return hostUseConfirmDialog();
|
|
1239
|
+
return fallbackConfirmationContext;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// src/byol/types.ts
|
|
1243
|
+
var SINGLE_SITE_PLACEMENT = { mode: "single" };
|
|
1244
|
+
var MIN_HEAVY_FORWARDERS = 1;
|
|
1245
|
+
var SELF_HOSTED = "self-hosted";
|
|
1246
|
+
var SELF_HOSTED_LABEL = "Self-Hosted";
|
|
1247
|
+
var DEFAULT_DEPLOYMENT_TYPES = [
|
|
1248
|
+
{ value: "single", label: "Single instance" },
|
|
1249
|
+
{ value: "distributed", label: "Distributed" }
|
|
1250
|
+
];
|
|
1251
|
+
var NETWORK_MODE_OPTIONS = [
|
|
1252
|
+
{ value: "dedicated", label: "Dedicated \u2014 your cloud (BYOC)" },
|
|
1253
|
+
{ value: "existing", label: "Existing network \u2014 your cloud (BYOC)" },
|
|
1254
|
+
{ value: "shared", label: "Veltrix-hosted (shared)" }
|
|
1255
|
+
];
|
|
1256
|
+
var DNS_MODE_OPTIONS = [
|
|
1257
|
+
{ value: "managed", label: "Managed" },
|
|
1258
|
+
{ value: "delegated", label: "Delegated" },
|
|
1259
|
+
{ value: "private-only", label: "Private only" }
|
|
1260
|
+
];
|
|
1261
|
+
var BYOC_NETWORK_MODES = /* @__PURE__ */ new Set(["dedicated", "existing"]);
|
|
1262
|
+
var CONTROL_PLANE_LAYOUT_OPTIONS = [
|
|
1263
|
+
{ value: "dedicated", label: "Dedicated", description: "5 servers \u2014 one management role each (highest isolation)." },
|
|
1264
|
+
{ value: "consolidated", label: "Consolidated", description: "~3 servers \u2014 cluster manager and SH deployer isolated, rest combined." },
|
|
1265
|
+
{ value: "single", label: "Single node", description: "1 server running every management role (lowest cost, non-HA)." }
|
|
1266
|
+
];
|
|
1267
|
+
var PLACEMENT_GRANULARITY_OPTIONS = [
|
|
1268
|
+
{ value: "az", label: "Availability zones (same region)" },
|
|
1269
|
+
{ value: "region", label: "Regions (multi-region)" }
|
|
1270
|
+
];
|
|
1271
|
+
function editFormState(row) {
|
|
1272
|
+
const providerId = row.cloudProviderId ? row.cloudProviderId : row.hosting_type === SELF_HOSTED_LABEL ? SELF_HOSTED : "";
|
|
1273
|
+
return {
|
|
1274
|
+
name: row.name ?? "",
|
|
1275
|
+
deploymentType: row.deploymentType ?? "single",
|
|
1276
|
+
environmentType: row.environmentType ?? "",
|
|
1277
|
+
providerId,
|
|
1278
|
+
region: row.region ?? "",
|
|
1279
|
+
indexerCount: String(row.indexerCount ?? 1),
|
|
1280
|
+
searchHeadCount: String(row.searchHeadCount ?? 1),
|
|
1281
|
+
networkMode: row.networkMode ?? "shared",
|
|
1282
|
+
dnsMode: row.dnsMode ?? "managed",
|
|
1283
|
+
cloudAccountConnectionId: row.cloudAccountConnectionId ?? "",
|
|
1284
|
+
controlPlaneLayout: row.controlPlaneLayout ?? "dedicated",
|
|
1285
|
+
heavyForwarderCount: String(row.heavyForwarderCount ?? 1),
|
|
1286
|
+
instanceType: row.instanceType ?? "",
|
|
1287
|
+
indexerPlacement: row.indexerPlacement ?? { mode: "single" },
|
|
1288
|
+
searchHeadPlacement: row.searchHeadPlacement ?? { mode: "single" },
|
|
1289
|
+
versionId: row.versionId ?? ""
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
var BLANK_FORM = {
|
|
1293
|
+
name: "",
|
|
1294
|
+
deploymentType: "single",
|
|
1295
|
+
environmentType: "",
|
|
1296
|
+
providerId: "",
|
|
1297
|
+
region: "",
|
|
1298
|
+
indexerCount: "1",
|
|
1299
|
+
searchHeadCount: "1",
|
|
1300
|
+
// New cloud infra defaults to 'dedicated' — OpenTofu creates its own VPC and the
|
|
1301
|
+
// deployment depends on nothing pre-existing. 'shared' attaches to a Veltrix-managed
|
|
1302
|
+
// base network that must be provisioned by a separate platform tofu stack; until that
|
|
1303
|
+
// stack exists, defaulting to 'shared' produces "no matching VPC" at apply time.
|
|
1304
|
+
networkMode: "dedicated",
|
|
1305
|
+
dnsMode: "managed",
|
|
1306
|
+
cloudAccountConnectionId: "",
|
|
1307
|
+
controlPlaneLayout: "dedicated",
|
|
1308
|
+
heavyForwarderCount: "1",
|
|
1309
|
+
instanceType: "",
|
|
1310
|
+
indexerPlacement: { mode: "single" },
|
|
1311
|
+
searchHeadPlacement: { mode: "single" },
|
|
1312
|
+
versionId: ""
|
|
1313
|
+
};
|
|
1314
|
+
var INSTANCE_TYPE_EXAMPLES = {
|
|
1315
|
+
aws: "t2.medium",
|
|
1316
|
+
azure: "Standard_B2s",
|
|
1317
|
+
gcp: "e2-medium",
|
|
1318
|
+
hetzner: "cx22"
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
// src/byol/status.ts
|
|
1322
|
+
var VARIANTS = {
|
|
1323
|
+
not_started: "default",
|
|
1324
|
+
provisioning: "info",
|
|
1325
|
+
running: "success",
|
|
1326
|
+
active: "success",
|
|
1327
|
+
degraded: "warning",
|
|
1328
|
+
stopped: "warning",
|
|
1329
|
+
failed: "danger",
|
|
1330
|
+
error: "danger",
|
|
1331
|
+
destroying: "info"
|
|
1332
|
+
};
|
|
1333
|
+
var LABELS = {
|
|
1334
|
+
not_started: "Not Started",
|
|
1335
|
+
provisioning: "Provisioning",
|
|
1336
|
+
running: "Running",
|
|
1337
|
+
active: "Running",
|
|
1338
|
+
degraded: "Degraded",
|
|
1339
|
+
stopped: "Stopped",
|
|
1340
|
+
failed: "Failed",
|
|
1341
|
+
error: "Failed",
|
|
1342
|
+
destroying: "Destroying"
|
|
1343
|
+
};
|
|
1344
|
+
function statusVariant(status) {
|
|
1345
|
+
return status && VARIANTS[status] || "default";
|
|
1346
|
+
}
|
|
1347
|
+
function statusLabel(status) {
|
|
1348
|
+
if (!status) return "Unknown";
|
|
1349
|
+
return LABELS[status] || status.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1350
|
+
}
|
|
1351
|
+
function isRunning(status) {
|
|
1352
|
+
return status === "running" || status === "active";
|
|
1353
|
+
}
|
|
1354
|
+
function isNotStarted(status) {
|
|
1355
|
+
return !status || status === "not_started";
|
|
1356
|
+
}
|
|
1357
|
+
var RESOURCE_VARIANTS = {
|
|
1358
|
+
ready: "success",
|
|
1359
|
+
provisioning: "info",
|
|
1360
|
+
attention: "warning",
|
|
1361
|
+
failed: "danger",
|
|
1362
|
+
not_started: "default"
|
|
1363
|
+
};
|
|
1364
|
+
var RESOURCE_LABELS = {
|
|
1365
|
+
ready: "Ready",
|
|
1366
|
+
provisioning: "Provisioning",
|
|
1367
|
+
attention: "Attention",
|
|
1368
|
+
failed: "Failed",
|
|
1369
|
+
not_started: "Not started"
|
|
1370
|
+
};
|
|
1371
|
+
function resourceStatusVariant(status) {
|
|
1372
|
+
return status && RESOURCE_VARIANTS[status] || "default";
|
|
1373
|
+
}
|
|
1374
|
+
function resourceStatusLabel(status) {
|
|
1375
|
+
if (!status) return "Not started";
|
|
1376
|
+
return RESOURCE_LABELS[status] || status.replace(/_/g, " ");
|
|
1377
|
+
}
|
|
1378
|
+
var STEP_VARIANTS = {
|
|
1379
|
+
done: "success",
|
|
1380
|
+
running: "info",
|
|
1381
|
+
failed: "danger",
|
|
1382
|
+
pending: "default"
|
|
1383
|
+
};
|
|
1384
|
+
function stepStatusVariant(status) {
|
|
1385
|
+
return status && STEP_VARIANTS[status] || "default";
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// src/byol/detail/shared.tsx
|
|
1389
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1390
|
+
var rgbToken = (name, fallback) => `rgb(var(${name}, ${fallback}))`;
|
|
1391
|
+
var tokens = {
|
|
1392
|
+
border: rgbToken("--color-border", "229 231 235"),
|
|
1393
|
+
borderStrong: rgbToken("--color-border-strong", "209 213 219"),
|
|
1394
|
+
surface: rgbToken("--color-surface-raised", "255 255 255"),
|
|
1395
|
+
// cards / panels / active nav
|
|
1396
|
+
surface2: rgbToken("--color-surface", "249 250 251"),
|
|
1397
|
+
// page surface — sidebar / insets
|
|
1398
|
+
text: rgbToken("--color-content-primary", "17 24 39"),
|
|
1399
|
+
muted: rgbToken("--color-content-secondary", "75 85 99"),
|
|
1400
|
+
faint: rgbToken("--color-content-tertiary", "156 163 175"),
|
|
1401
|
+
primary: "var(--veltrix-app-primary, #FF6600)",
|
|
1402
|
+
danger: rgbToken("--color-danger", "220 38 38"),
|
|
1403
|
+
success: rgbToken("--color-success", "22 163 74"),
|
|
1404
|
+
info: rgbToken("--color-info", "14 116 144"),
|
|
1405
|
+
warning: rgbToken("--color-warning", "161 98 7")
|
|
1406
|
+
};
|
|
1407
|
+
var StatusPill = ({ status }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { variant: statusVariant(status), children: statusLabel(status) });
|
|
1408
|
+
var ResourcePill = ({ status }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { variant: resourceStatusVariant(status), size: "sm", children: resourceStatusLabel(status) });
|
|
1409
|
+
var Meta = ({ label, children }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: 11, textTransform: "uppercase", letterSpacing: ".05em", color: tokens.faint }, children: label }),
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontWeight: 600, color: tokens.text, marginTop: 2 }, children })
|
|
1412
|
+
] });
|
|
1413
|
+
function barColor(status) {
|
|
1414
|
+
switch (status) {
|
|
1415
|
+
case "ready":
|
|
1416
|
+
return tokens.success;
|
|
1417
|
+
case "provisioning":
|
|
1418
|
+
return tokens.info;
|
|
1419
|
+
case "attention":
|
|
1420
|
+
return tokens.warning;
|
|
1421
|
+
case "failed":
|
|
1422
|
+
return tokens.danger;
|
|
1423
|
+
default:
|
|
1424
|
+
return tokens.borderStrong;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
var ProgressMeter = ({ resources }) => {
|
|
1428
|
+
const total = resources.length;
|
|
1429
|
+
const ready = resources.filter((r) => r.status === "ready").length;
|
|
1430
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1431
|
+
for (const r of resources) counts.set(r.status, (counts.get(r.status) ?? 0) + 1);
|
|
1432
|
+
const order = ["ready", "provisioning", "attention", "failed", "not_started"];
|
|
1433
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { width: 300, maxWidth: "48vw" }, children: [
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", fontSize: 12, color: tokens.muted, marginBottom: 6 }, children: [
|
|
1435
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Deployment progress" }),
|
|
1436
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontVariantNumeric: "tabular-nums" }, children: total === 0 ? "Not deployed" : `${ready} / ${total} resources ready` })
|
|
1437
|
+
] }),
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { height: 8, borderRadius: 999, background: tokens.surface2, overflow: "hidden", display: "flex" }, children: total === 0 ? null : order.map((s2) => {
|
|
1439
|
+
const n = counts.get(s2) ?? 0;
|
|
1440
|
+
if (!n) return null;
|
|
1441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: `${n / total * 100}%`, background: barColor(s2), height: "100%" } }, s2);
|
|
1442
|
+
}) })
|
|
1443
|
+
] });
|
|
1444
|
+
};
|
|
1445
|
+
var Panel = ({
|
|
1446
|
+
title,
|
|
1447
|
+
actions,
|
|
1448
|
+
children
|
|
1449
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { border: `1px solid ${tokens.border}`, borderRadius: 12, background: tokens.surface, overflow: "hidden" }, children: [
|
|
1450
|
+
(title || actions) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1451
|
+
"div",
|
|
1452
|
+
{
|
|
1453
|
+
style: {
|
|
1454
|
+
display: "flex",
|
|
1455
|
+
alignItems: "center",
|
|
1456
|
+
justifyContent: "space-between",
|
|
1457
|
+
gap: 12,
|
|
1458
|
+
padding: "12px 16px",
|
|
1459
|
+
borderBottom: `1px solid ${tokens.border}`
|
|
1460
|
+
},
|
|
1461
|
+
children: [
|
|
1462
|
+
title ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { margin: 0, fontSize: 14, fontWeight: 600, color: tokens.text }, children: title }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", {}),
|
|
1463
|
+
actions
|
|
1464
|
+
]
|
|
1465
|
+
}
|
|
1466
|
+
),
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { padding: 16 }, children })
|
|
1468
|
+
] });
|
|
1469
|
+
|
|
1470
|
+
// src/byol/api.ts
|
|
1471
|
+
init_client();
|
|
1472
|
+
async function errorText(res) {
|
|
1473
|
+
return res.json().then((b) => b?.error || `HTTP ${res.status}`).catch(() => `HTTP ${res.status}`);
|
|
1474
|
+
}
|
|
1475
|
+
function formatDate(value) {
|
|
1476
|
+
if (!value) return "\u2014";
|
|
1477
|
+
const date = new Date(value);
|
|
1478
|
+
return Number.isNaN(date.getTime()) ? "\u2014" : date.toLocaleDateString();
|
|
1479
|
+
}
|
|
1480
|
+
function formatDateTime(value) {
|
|
1481
|
+
if (!value) return "\u2014";
|
|
1482
|
+
const date = new Date(value);
|
|
1483
|
+
return Number.isNaN(date.getTime()) ? "\u2014" : date.toLocaleString();
|
|
1484
|
+
}
|
|
1485
|
+
async function json(res) {
|
|
1486
|
+
if (!res.ok) throw new Error(await errorText(res));
|
|
1487
|
+
return res.json();
|
|
1488
|
+
}
|
|
1489
|
+
function getInfra(apiBase, id) {
|
|
1490
|
+
return authFetch(`${apiBase}/${id}`).then((r) => json(r));
|
|
1491
|
+
}
|
|
1492
|
+
function getResources(apiBase, id) {
|
|
1493
|
+
return authFetch(`${apiBase}/${id}/resources`).then((r) => json(r));
|
|
1494
|
+
}
|
|
1495
|
+
function getDeployments(apiBase, id) {
|
|
1496
|
+
return authFetch(`${apiBase}/${id}/deployments`).then((r) => json(r));
|
|
1497
|
+
}
|
|
1498
|
+
function getPlan(apiBase, id) {
|
|
1499
|
+
return authFetch(`${apiBase}/${id}/plan`).then((r) => json(r));
|
|
1500
|
+
}
|
|
1501
|
+
function deployInfra(apiBase, id) {
|
|
1502
|
+
return authFetch(`${apiBase}/${id}/deploy`, { method: "POST" }).then(
|
|
1503
|
+
(r) => json(r)
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
function destroyInfra(apiBase, id) {
|
|
1507
|
+
return authFetch(`${apiBase}/${id}/destroy`, { method: "POST" }).then(
|
|
1508
|
+
(r) => json(r)
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
function lifecycleInfra(apiBase, id, action) {
|
|
1512
|
+
return authFetch(`${apiBase}/${id}/${action}`, { method: "POST" }).then((r) => json(r));
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// src/byol/ByolInfrastructureDetail.tsx
|
|
1516
|
+
var import_react3 = require("react");
|
|
1517
|
+
|
|
1518
|
+
// src/byol/placement.ts
|
|
1519
|
+
function allocateNodesBySite(totalNodes, sites) {
|
|
1520
|
+
const n = Math.max(0, Math.floor(totalNodes));
|
|
1521
|
+
const list = sites ?? [];
|
|
1522
|
+
if (list.length === 0) return [];
|
|
1523
|
+
const base = list.map(() => 0);
|
|
1524
|
+
let assigned = 0;
|
|
1525
|
+
for (let i = 0; i < list.length && assigned < n; i++) {
|
|
1526
|
+
base[i] = 1;
|
|
1527
|
+
assigned++;
|
|
1528
|
+
}
|
|
1529
|
+
const remaining = n - assigned;
|
|
1530
|
+
if (remaining > 0) {
|
|
1531
|
+
const totalPercent = list.reduce((sum, s2) => sum + Math.max(0, s2.percent), 0) || 1;
|
|
1532
|
+
const ideal = list.map((s2) => Math.max(0, s2.percent) / totalPercent * remaining);
|
|
1533
|
+
const floors = ideal.map((v) => Math.floor(v));
|
|
1534
|
+
let leftover = remaining - floors.reduce((a, b) => a + b, 0);
|
|
1535
|
+
const order = list.map((_, i) => ({ i, frac: ideal[i] - floors[i] })).sort((a, b) => b.frac - a.frac || a.i - b.i);
|
|
1536
|
+
for (let k = 0; k < order.length && leftover > 0; k++) {
|
|
1537
|
+
floors[order[k].i]++;
|
|
1538
|
+
leftover--;
|
|
1539
|
+
}
|
|
1540
|
+
for (let i = 0; i < list.length; i++) base[i] += floors[i];
|
|
1541
|
+
}
|
|
1542
|
+
return list.map((s2, i) => ({ site: s2.site, count: base[i], percent: s2.percent }));
|
|
1543
|
+
}
|
|
1544
|
+
function validatePlacement(placement, totalNodes) {
|
|
1545
|
+
if (!placement || placement.mode === "single") return null;
|
|
1546
|
+
const sites = placement.sites ?? [];
|
|
1547
|
+
if (sites.length < 2) return "Multi-site placement needs at least two sites.";
|
|
1548
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1549
|
+
for (const s2 of sites) {
|
|
1550
|
+
if (!s2.site || !s2.site.trim()) return "Every site must be selected.";
|
|
1551
|
+
if (seen.has(s2.site)) return `Site "${s2.site}" is listed more than once.`;
|
|
1552
|
+
seen.add(s2.site);
|
|
1553
|
+
if (!(s2.percent > 0)) return `Site "${s2.site}" must have a percent greater than zero.`;
|
|
1554
|
+
}
|
|
1555
|
+
const sumPercent = sites.reduce((sum, s2) => sum + s2.percent, 0);
|
|
1556
|
+
if (Math.round(sumPercent) !== 100) return `Site percentages must total 100 (currently ${Math.round(sumPercent)}).`;
|
|
1557
|
+
if (sites.length > Math.floor(totalNodes)) {
|
|
1558
|
+
return `Too many sites (${sites.length}) for ${totalNodes} node${totalNodes === 1 ? "" : "s"} \u2014 each site needs at least one node.`;
|
|
1559
|
+
}
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
function effectivePlacement(placement, eligible) {
|
|
1563
|
+
if (!eligible || !placement || placement.mode !== "multi-site") return { mode: "single" };
|
|
1564
|
+
if (!placement.sites || placement.sites.length < 2) return { mode: "single" };
|
|
1565
|
+
return placement;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// src/byol/topology.ts
|
|
1569
|
+
var CONTROL_PLANE_ROLE_META = {
|
|
1570
|
+
"license-manager": { kind: "license-manager", name: "License Manager", role: "Serves BYOL license pool" },
|
|
1571
|
+
"cluster-manager": { kind: "cluster-manager", name: "Cluster Manager", role: "Indexer cluster coordinator" },
|
|
1572
|
+
"sh-deployer": { kind: "sh-deployer", name: "SH Deployer", role: "Search head cluster deployer" },
|
|
1573
|
+
"deployment-server": { kind: "deployment-server", name: "Deployment Server", role: "Forwarder app distribution" },
|
|
1574
|
+
"monitoring-console": { kind: "monitoring-console", name: "Monitoring Console", role: "Fleet health & DMC" }
|
|
1575
|
+
};
|
|
1576
|
+
function controlPlaneGroups(layout) {
|
|
1577
|
+
switch (layout) {
|
|
1578
|
+
case "single":
|
|
1579
|
+
return [["license-manager", "cluster-manager", "sh-deployer", "deployment-server", "monitoring-console"]];
|
|
1580
|
+
case "consolidated":
|
|
1581
|
+
return [["cluster-manager"], ["sh-deployer"], ["license-manager", "deployment-server", "monitoring-console"]];
|
|
1582
|
+
case "dedicated":
|
|
1583
|
+
default:
|
|
1584
|
+
return [["license-manager"], ["cluster-manager"], ["sh-deployer"], ["deployment-server"], ["monitoring-console"]];
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
function buildControlPlane(layout, region) {
|
|
1588
|
+
return controlPlaneGroups(layout).map((roles) => {
|
|
1589
|
+
if (roles.length === 1) {
|
|
1590
|
+
const meta = CONTROL_PLANE_ROLE_META[roles[0]];
|
|
1591
|
+
return { planKey: `control-plane/${roles[0]}`, tier: "control-plane", kind: meta.kind, name: meta.name, role: meta.role, region, roles: [roles[0]] };
|
|
1592
|
+
}
|
|
1593
|
+
const label = roles.map((r) => CONTROL_PLANE_ROLE_META[r].name).join(" \xB7 ");
|
|
1594
|
+
return {
|
|
1595
|
+
planKey: "control-plane/management",
|
|
1596
|
+
tier: "control-plane",
|
|
1597
|
+
kind: "management-node",
|
|
1598
|
+
name: roles.length >= 5 ? "Management node (all roles)" : "Management node",
|
|
1599
|
+
role: label,
|
|
1600
|
+
region,
|
|
1601
|
+
roles: [...roles]
|
|
1602
|
+
};
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
function assignNodeSites(count, placement, primaryRegion, legacyRegions) {
|
|
1606
|
+
const eff = effectivePlacement(placement ?? void 0, true);
|
|
1607
|
+
if (eff.mode === "multi-site" && eff.sites && eff.sites.length >= 2) {
|
|
1608
|
+
const granularity = eff.granularity ?? "az";
|
|
1609
|
+
const out2 = [];
|
|
1610
|
+
for (const alloc of allocateNodesBySite(count, eff.sites)) {
|
|
1611
|
+
for (let k = 0; k < alloc.count; k++) {
|
|
1612
|
+
out2.push(granularity === "az" ? { region: primaryRegion, zone: alloc.site } : { region: alloc.site, zone: null });
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return out2;
|
|
1616
|
+
}
|
|
1617
|
+
const out = [];
|
|
1618
|
+
for (let i = 0; i < count; i++) out.push({ region: pickRegion(legacyRegions, i, primaryRegion), zone: null });
|
|
1619
|
+
return out;
|
|
1620
|
+
}
|
|
1621
|
+
var TIER_LABELS = {
|
|
1622
|
+
foundation: "Foundation",
|
|
1623
|
+
"control-plane": "Control plane",
|
|
1624
|
+
data: "Data tier \u2014 indexer cluster",
|
|
1625
|
+
search: "Search tier \u2014 search head cluster",
|
|
1626
|
+
ingest: "Ingest & access"
|
|
1627
|
+
};
|
|
1628
|
+
var TIER_ORDER = ["foundation", "control-plane", "data", "search", "ingest"];
|
|
1629
|
+
var DISTRIBUTED = "distributed";
|
|
1630
|
+
function pickRegion(regions, index, fallback) {
|
|
1631
|
+
if (regions && regions.length > 0) return regions[index % regions.length];
|
|
1632
|
+
return fallback;
|
|
1633
|
+
}
|
|
1634
|
+
function buildByolResourcePlan(input) {
|
|
1635
|
+
const distributed = (input.deploymentType ?? "single") === DISTRIBUTED;
|
|
1636
|
+
const primaryRegion = input.region ?? null;
|
|
1637
|
+
const isCloud = input.isCloud ?? false;
|
|
1638
|
+
const items = [];
|
|
1639
|
+
items.push({ planKey: "foundation/network", tier: "foundation", kind: "network", name: "Network", role: "VPC \xB7 subnets \xB7 security groups", region: primaryRegion });
|
|
1640
|
+
if (distributed && isCloud) {
|
|
1641
|
+
items.push({ planKey: "foundation/load-balancer", tier: "foundation", kind: "load-balancer", name: "Load balancer", role: "Search + HEC ingress", region: primaryRegion });
|
|
1642
|
+
items.push({ planKey: "foundation/dns", tier: "foundation", kind: "dns", name: "DNS", role: "Public + private records", region: "global" });
|
|
1643
|
+
}
|
|
1644
|
+
items.push({ planKey: "foundation/tls", tier: "foundation", kind: "tls", name: "TLS certificates", role: "Web + inter-node (S2S)", region: null });
|
|
1645
|
+
items.push({ planKey: "foundation/storage", tier: "foundation", kind: "storage", name: "Storage", role: distributed ? "SmartStore + hot/warm volumes" : "Index volumes", region: primaryRegion });
|
|
1646
|
+
items.push({ planKey: "foundation/secrets", tier: "foundation", kind: "secrets", name: "Secrets", role: "Admin password \xB7 pass4SymmKey", region: null });
|
|
1647
|
+
items.push({ planKey: "foundation/license-file", tier: "foundation", kind: "license-file", name: "BYOL license file", role: "Uploaded & validated", region: null });
|
|
1648
|
+
if (!distributed) {
|
|
1649
|
+
items.push({ planKey: "data/standalone", tier: "data", kind: "standalone", name: "Splunk instance", role: "All-in-one (indexer + search head + web)", region: primaryRegion });
|
|
1650
|
+
items.push({ planKey: "ingest/hec", tier: "ingest", kind: "hec", name: "HTTP Event Collector", role: "Token endpoint", region: primaryRegion });
|
|
1651
|
+
return items.map((it, i) => stampOrder(it, i));
|
|
1652
|
+
}
|
|
1653
|
+
items.push(...buildControlPlane(input.controlPlaneLayout ?? "dedicated", primaryRegion));
|
|
1654
|
+
const indexerCount = Math.max(1, input.indexerCount ?? 1);
|
|
1655
|
+
const indexerSites = assignNodeSites(indexerCount, input.indexerPlacement, primaryRegion, input.indexerRegions);
|
|
1656
|
+
for (let i = 0; i < indexerCount; i++) {
|
|
1657
|
+
items.push({
|
|
1658
|
+
planKey: `data/indexer-${i + 1}`,
|
|
1659
|
+
tier: "data",
|
|
1660
|
+
kind: "indexer",
|
|
1661
|
+
name: `Indexer peer ${i + 1}`,
|
|
1662
|
+
role: "Cluster peer node",
|
|
1663
|
+
region: indexerSites[i].region,
|
|
1664
|
+
zone: indexerSites[i].zone
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
const searchHeadCount = Math.max(1, input.searchHeadCount ?? 1);
|
|
1668
|
+
const searchHeadSites = assignNodeSites(searchHeadCount, input.searchHeadPlacement, primaryRegion, input.searchHeadRegions);
|
|
1669
|
+
for (let i = 0; i < searchHeadCount; i++) {
|
|
1670
|
+
items.push({
|
|
1671
|
+
planKey: `search/search-head-${i + 1}`,
|
|
1672
|
+
tier: "search",
|
|
1673
|
+
kind: "search-head",
|
|
1674
|
+
name: `Search head ${i + 1}`,
|
|
1675
|
+
role: i === 0 ? "SHC captain candidate" : "SHC member",
|
|
1676
|
+
region: searchHeadSites[i].region,
|
|
1677
|
+
zone: searchHeadSites[i].zone
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
items.push({ planKey: "ingest/hec", tier: "ingest", kind: "hec", name: "HTTP Event Collector", role: "Token endpoint via LB", region: primaryRegion });
|
|
1681
|
+
const heavyForwarderCount = Math.max(1, input.heavyForwarderCount ?? 1);
|
|
1682
|
+
for (let i = 0; i < heavyForwarderCount; i++) {
|
|
1683
|
+
items.push({
|
|
1684
|
+
planKey: `ingest/heavy-forwarder-${i + 1}`,
|
|
1685
|
+
tier: "ingest",
|
|
1686
|
+
kind: "heavy-forwarder",
|
|
1687
|
+
name: `Heavy Forwarder ${i + 1}`,
|
|
1688
|
+
role: "Ingest routing / props",
|
|
1689
|
+
region: primaryRegion
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
return items.map((it, i) => stampOrder(it, i));
|
|
1693
|
+
}
|
|
1694
|
+
function stampOrder(item, index) {
|
|
1695
|
+
return { ...item, sortOrder: index };
|
|
1696
|
+
}
|
|
1697
|
+
var DEPLOYMENT_STEPS = [
|
|
1698
|
+
{ key: "plan", title: "Plan created", detail: "Resources planned from topology; desired state recorded." },
|
|
1699
|
+
{ key: "foundation", title: "Provision foundation", detail: "Network, load balancer, DNS, TLS, storage and secrets." },
|
|
1700
|
+
{ key: "license", title: "License Manager online \xB7 license applied", detail: "BYOL license validated and pool published." },
|
|
1701
|
+
{ key: "cluster", title: "Cluster Manager online", detail: "Indexer cluster policy (RF/SF) published." },
|
|
1702
|
+
{ key: "indexers", title: "Indexer peers joining cluster", detail: "Indexer peers boot and register with the cluster manager." },
|
|
1703
|
+
{ key: "search-heads", title: "Search head cluster forming", detail: "Deployer pushes the SHC bundle; captain election." },
|
|
1704
|
+
{ key: "services", title: "Deployment server & monitoring console", detail: "Ancillary control-plane services come online." },
|
|
1705
|
+
{ key: "post-config", title: "Post-deploy configuration", detail: "Apply indexes, HEC tokens and Splunk apps." },
|
|
1706
|
+
{ key: "health", title: "End-to-end health check", detail: "Verify search, ingest and replication end to end." }
|
|
1707
|
+
];
|
|
1708
|
+
|
|
1709
|
+
// src/byol/detail/OverviewTab.tsx
|
|
1710
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1711
|
+
var OverviewTab = ({ infra, resources }) => {
|
|
1712
|
+
const distributed = infra.deploymentType === "distributed";
|
|
1713
|
+
const regionCount = new Set(
|
|
1714
|
+
resources.map((r) => r.region).filter((r) => Boolean(r) && r !== "global")
|
|
1715
|
+
).size;
|
|
1716
|
+
const readyCount = resources.filter((r) => r.status === "ready").length;
|
|
1717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 20 }, children: [
|
|
1718
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 12 }, children: [
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatsCard, { label: "Indexer peers", value: infra.indexerCount ?? "\u2014" }),
|
|
1720
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatsCard, { label: "Search heads", value: infra.searchHeadCount ?? "\u2014" }),
|
|
1721
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatsCard, { label: "Regions", value: regionCount || (infra.region ? 1 : "\u2014") }),
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1723
|
+
StatsCard,
|
|
1724
|
+
{
|
|
1725
|
+
label: resources.length ? "Resources ready" : "Resources planned",
|
|
1726
|
+
value: resources.length ? `${readyCount} / ${resources.length}` : "\u2014"
|
|
1727
|
+
}
|
|
1728
|
+
)
|
|
1729
|
+
] }),
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Panel, { title: "What gets deployed", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { style: { margin: 0, fontSize: 13, lineHeight: 1.6, color: tokens.muted }, children: [
|
|
1731
|
+
distributed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1732
|
+
"A distributed BYOL environment provisions a full Splunk topology: a foundation tier (network, load balancing, DNS, TLS, storage/SmartStore, secrets and your license file), a control plane (license manager, cluster manager, deployment server, monitoring console and search-head deployer), an",
|
|
1733
|
+
" ",
|
|
1734
|
+
infra.indexerCount,
|
|
1735
|
+
"-peer indexer cluster, a ",
|
|
1736
|
+
infra.searchHeadCount,
|
|
1737
|
+
"-member search-head cluster, and the ingest path (HEC + heavy forwarders)."
|
|
1738
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: "A single-instance BYOL environment provisions the foundation (network, TLS, storage, secrets and your license file) plus one all-in-one Splunk node (indexer + search head + web) and an HTTP Event Collector endpoint." }),
|
|
1739
|
+
" ",
|
|
1740
|
+
"See the ",
|
|
1741
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "Resources" }),
|
|
1742
|
+
" tab for the live plan and ",
|
|
1743
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "Activity" }),
|
|
1744
|
+
" for the run log."
|
|
1745
|
+
] }) })
|
|
1746
|
+
] });
|
|
1747
|
+
};
|
|
1748
|
+
|
|
1749
|
+
// src/byol/detail/ResourcesTab.tsx
|
|
1750
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1751
|
+
var STRIPE = {
|
|
1752
|
+
ready: tokens.success,
|
|
1753
|
+
provisioning: tokens.info,
|
|
1754
|
+
attention: tokens.warning,
|
|
1755
|
+
failed: tokens.danger,
|
|
1756
|
+
not_started: tokens.borderStrong
|
|
1757
|
+
};
|
|
1758
|
+
var LEGEND = [
|
|
1759
|
+
{ status: "ready", label: "Ready" },
|
|
1760
|
+
{ status: "provisioning", label: "Provisioning" },
|
|
1761
|
+
{ status: "attention", label: "Attention" },
|
|
1762
|
+
{ status: "failed", label: "Failed" },
|
|
1763
|
+
{ status: "not_started", label: "Not started" }
|
|
1764
|
+
];
|
|
1765
|
+
var ResourceCard = ({ resource }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1766
|
+
"div",
|
|
1767
|
+
{
|
|
1768
|
+
style: {
|
|
1769
|
+
position: "relative",
|
|
1770
|
+
border: `1px solid ${tokens.border}`,
|
|
1771
|
+
borderRadius: 8,
|
|
1772
|
+
background: tokens.surface,
|
|
1773
|
+
padding: "12px 13px",
|
|
1774
|
+
display: "flex",
|
|
1775
|
+
flexDirection: "column",
|
|
1776
|
+
gap: 8,
|
|
1777
|
+
overflow: "hidden"
|
|
1778
|
+
},
|
|
1779
|
+
children: [
|
|
1780
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { position: "absolute", left: 0, top: 0, bottom: 0, width: 3, background: STRIPE[resource.status] ?? STRIPE.not_started } }),
|
|
1781
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }, children: [
|
|
1782
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontWeight: 600, fontSize: 13, color: tokens.text }, children: resource.name }),
|
|
1783
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ResourcePill, { status: resource.status })
|
|
1784
|
+
] }),
|
|
1785
|
+
resource.role ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 11, color: tokens.faint }, children: resource.role }) : null,
|
|
1786
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, fontSize: 11, color: tokens.muted }, children: [
|
|
1787
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: resource.zone ? `${resource.region ?? ""} \xB7 ${resource.zone}` : resource.region || "\u2014" }),
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontFamily: "var(--font-mono, ui-monospace, monospace)", color: tokens.faint }, children: resource.externalRef || "\u2014" })
|
|
1789
|
+
] })
|
|
1790
|
+
]
|
|
1791
|
+
}
|
|
1792
|
+
);
|
|
1793
|
+
var ResourcesTab = ({ resources, derived }) => {
|
|
1794
|
+
if (resources.length === 0) {
|
|
1795
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1796
|
+
EmptyState,
|
|
1797
|
+
{
|
|
1798
|
+
title: "No resources planned",
|
|
1799
|
+
description: "This infrastructure has no derivable topology yet. Check the deployment type and node counts in Settings."
|
|
1800
|
+
}
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1803
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 18 }, children: [
|
|
1804
|
+
derived ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1805
|
+
"div",
|
|
1806
|
+
{
|
|
1807
|
+
style: {
|
|
1808
|
+
fontSize: 13,
|
|
1809
|
+
color: tokens.muted,
|
|
1810
|
+
background: tokens.surface2,
|
|
1811
|
+
border: `1px solid ${tokens.border}`,
|
|
1812
|
+
borderRadius: 8,
|
|
1813
|
+
padding: "10px 14px"
|
|
1814
|
+
},
|
|
1815
|
+
children: [
|
|
1816
|
+
"This is the plan derived from the topology \u2014 the resources that ",
|
|
1817
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "will" }),
|
|
1818
|
+
" be provisioned. Deploy the environment to begin creating them."
|
|
1819
|
+
]
|
|
1820
|
+
}
|
|
1821
|
+
) : null,
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 14, fontSize: 12, color: tokens.muted }, children: LEGEND.map((l) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { width: 8, height: 8, borderRadius: "50%", background: STRIPE[l.status] } }),
|
|
1824
|
+
l.label
|
|
1825
|
+
] }, l.status)) }),
|
|
1826
|
+
TIER_ORDER.map((tier) => {
|
|
1827
|
+
const inTier = resources.filter((r) => r.tier === tier);
|
|
1828
|
+
if (inTier.length === 0) return null;
|
|
1829
|
+
const ready = inTier.filter((r) => r.status === "ready").length;
|
|
1830
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
1831
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "baseline", gap: 10, marginBottom: 9 }, children: [
|
|
1832
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1833
|
+
"h3",
|
|
1834
|
+
{
|
|
1835
|
+
style: {
|
|
1836
|
+
margin: 0,
|
|
1837
|
+
fontSize: 12,
|
|
1838
|
+
letterSpacing: ".08em",
|
|
1839
|
+
textTransform: "uppercase",
|
|
1840
|
+
color: tokens.primary
|
|
1841
|
+
},
|
|
1842
|
+
children: TIER_LABELS[tier]
|
|
1843
|
+
}
|
|
1844
|
+
),
|
|
1845
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { fontSize: 12, color: tokens.faint, fontVariantNumeric: "tabular-nums" }, children: [
|
|
1846
|
+
inTier.length,
|
|
1847
|
+
" ",
|
|
1848
|
+
inTier.length === 1 ? "resource" : "resources",
|
|
1849
|
+
!derived ? ` \xB7 ${ready} ready` : ""
|
|
1850
|
+
] })
|
|
1851
|
+
] }),
|
|
1852
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(232px, 1fr))", gap: 10 }, children: inTier.map((r) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ResourceCard, { resource: r }, r.planKey)) })
|
|
1853
|
+
] }, tier);
|
|
1854
|
+
})
|
|
1855
|
+
] });
|
|
1856
|
+
};
|
|
1857
|
+
|
|
1858
|
+
// src/byol/detail/ActivityTab.tsx
|
|
1859
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1860
|
+
var NODE_COLOR = {
|
|
1861
|
+
done: tokens.success,
|
|
1862
|
+
running: tokens.info,
|
|
1863
|
+
failed: tokens.danger,
|
|
1864
|
+
pending: tokens.borderStrong
|
|
1865
|
+
};
|
|
1866
|
+
var StepNode = ({ step, last }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative", paddingBottom: last ? 0 : 20 }, children: [
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1868
|
+
"span",
|
|
1869
|
+
{
|
|
1870
|
+
style: {
|
|
1871
|
+
position: "absolute",
|
|
1872
|
+
left: -24,
|
|
1873
|
+
top: 2,
|
|
1874
|
+
width: 16,
|
|
1875
|
+
height: 16,
|
|
1876
|
+
borderRadius: "50%",
|
|
1877
|
+
background: NODE_COLOR[step.status] ?? NODE_COLOR.pending,
|
|
1878
|
+
border: `3px solid ${tokens.surface}`,
|
|
1879
|
+
boxShadow: `0 0 0 1px ${tokens.border}`
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
),
|
|
1883
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontWeight: 600, fontSize: 14, color: tokens.text }, children: step.title }),
|
|
1885
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Badge, { variant: stepStatusVariant(step.status), size: "sm", children: step.status }),
|
|
1886
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 12, color: tokens.faint }, children: step.completedAt ? formatDateTime(step.completedAt) : step.startedAt ? formatDateTime(step.startedAt) : "" })
|
|
1887
|
+
] }),
|
|
1888
|
+
step.detail ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 13, color: tokens.muted, marginTop: 3 }, children: step.detail }) : null,
|
|
1889
|
+
step.logs ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1890
|
+
"pre",
|
|
1891
|
+
{
|
|
1892
|
+
style: {
|
|
1893
|
+
marginTop: 8,
|
|
1894
|
+
background: tokens.surface2,
|
|
1895
|
+
border: `1px solid ${tokens.border}`,
|
|
1896
|
+
borderRadius: 8,
|
|
1897
|
+
padding: "9px 12px",
|
|
1898
|
+
fontFamily: "var(--font-mono, ui-monospace, monospace)",
|
|
1899
|
+
fontSize: 11.5,
|
|
1900
|
+
color: tokens.muted,
|
|
1901
|
+
whiteSpace: "pre-wrap",
|
|
1902
|
+
overflowX: "auto"
|
|
1903
|
+
},
|
|
1904
|
+
children: step.logs
|
|
1905
|
+
}
|
|
1906
|
+
) : null
|
|
1907
|
+
] });
|
|
1908
|
+
var RunTimeline = ({ run }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
1909
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 14, flexWrap: "wrap" }, children: [
|
|
1910
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h3", { style: { margin: 0, fontSize: 14, fontWeight: 600, color: tokens.text, textTransform: "capitalize" }, children: [
|
|
1911
|
+
run.action,
|
|
1912
|
+
" run"
|
|
1913
|
+
] }),
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Badge, { variant: run.status === "succeeded" ? "success" : run.status === "failed" ? "danger" : "info", size: "sm", children: run.status }),
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontSize: 12, color: tokens.faint }, children: [
|
|
1916
|
+
"started ",
|
|
1917
|
+
formatDateTime(run.startedAt)
|
|
1918
|
+
] })
|
|
1919
|
+
] }),
|
|
1920
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative", paddingLeft: 26 }, children: [
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { position: "absolute", left: 8, top: 4, bottom: 4, width: 2, background: tokens.border } }),
|
|
1922
|
+
run.steps.map((s2, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StepNode, { step: s2, last: i === run.steps.length - 1 }, s2.id || s2.key))
|
|
1923
|
+
] })
|
|
1924
|
+
] });
|
|
1925
|
+
var ActivityTab = ({ deployments }) => {
|
|
1926
|
+
if (deployments.length === 0) {
|
|
1927
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1928
|
+
EmptyState,
|
|
1929
|
+
{
|
|
1930
|
+
title: "No deployment activity yet",
|
|
1931
|
+
description: "When you deploy this environment, each provisioning step will stream here."
|
|
1932
|
+
}
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1935
|
+
const [latest, ...previous] = deployments;
|
|
1936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 28 }, children: [
|
|
1937
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RunTimeline, { run: latest }),
|
|
1938
|
+
previous.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h4", { style: { margin: "0 0 10px", fontSize: 12, letterSpacing: ".06em", textTransform: "uppercase", color: tokens.faint }, children: "Earlier runs" }),
|
|
1940
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: previous.map((run) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1941
|
+
"div",
|
|
1942
|
+
{
|
|
1943
|
+
style: {
|
|
1944
|
+
display: "flex",
|
|
1945
|
+
alignItems: "center",
|
|
1946
|
+
justifyContent: "space-between",
|
|
1947
|
+
gap: 10,
|
|
1948
|
+
padding: "10px 14px",
|
|
1949
|
+
border: `1px solid ${tokens.border}`,
|
|
1950
|
+
borderRadius: 8,
|
|
1951
|
+
background: tokens.surface,
|
|
1952
|
+
fontSize: 13
|
|
1953
|
+
},
|
|
1954
|
+
children: [
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { textTransform: "capitalize", color: tokens.text }, children: run.action }),
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: tokens.faint }, children: formatDateTime(run.startedAt) }),
|
|
1957
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Badge, { variant: run.status === "succeeded" ? "success" : run.status === "failed" ? "danger" : "info", size: "sm", children: run.status })
|
|
1958
|
+
]
|
|
1959
|
+
},
|
|
1960
|
+
run.id
|
|
1961
|
+
)) })
|
|
1962
|
+
] }) : null
|
|
1963
|
+
] });
|
|
1964
|
+
};
|
|
1965
|
+
|
|
1966
|
+
// src/byol/detail/AccessTab.tsx
|
|
1967
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1968
|
+
var mono = "var(--font-mono, ui-monospace, monospace)";
|
|
1969
|
+
function asHost(ref) {
|
|
1970
|
+
if (!ref) return null;
|
|
1971
|
+
const v = ref.trim();
|
|
1972
|
+
if (/^https?:\/\//i.test(v)) {
|
|
1973
|
+
try {
|
|
1974
|
+
return new URL(v).host;
|
|
1975
|
+
} catch {
|
|
1976
|
+
return null;
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
const HOSTNAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/i;
|
|
1980
|
+
return HOSTNAME.test(v) ? v : null;
|
|
1981
|
+
}
|
|
1982
|
+
var Row = ({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1983
|
+
"div",
|
|
1984
|
+
{
|
|
1985
|
+
style: {
|
|
1986
|
+
display: "grid",
|
|
1987
|
+
gridTemplateColumns: "180px 1fr",
|
|
1988
|
+
gap: 16,
|
|
1989
|
+
alignItems: "center",
|
|
1990
|
+
padding: "11px 0",
|
|
1991
|
+
borderBottom: `1px solid ${tokens.border}`,
|
|
1992
|
+
fontSize: 13
|
|
1993
|
+
},
|
|
1994
|
+
children: [
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: tokens.muted }, children: label }),
|
|
1996
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontFamily: mono, fontSize: 12.5, color: tokens.text, overflowWrap: "anywhere" }, children: value })
|
|
1997
|
+
]
|
|
1998
|
+
}
|
|
1999
|
+
);
|
|
2000
|
+
var AccessTab = ({ infra, resources }) => {
|
|
2001
|
+
if (!isRunning(infra.status)) {
|
|
2002
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2003
|
+
EmptyState,
|
|
2004
|
+
{
|
|
2005
|
+
title: "Endpoints appear once the environment is running",
|
|
2006
|
+
description: "Deploy the environment; when the search tier is up, its Splunk Web, management and HEC endpoints show here."
|
|
2007
|
+
}
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
const refOf = (planKey) => resources.find((r) => r.planKey === planKey)?.externalRef ?? null;
|
|
2011
|
+
const dnsHost = asHost(refOf("foundation/dns"));
|
|
2012
|
+
const albHost = asHost(refOf("ingest/hec"));
|
|
2013
|
+
const webHost = dnsHost ?? albHost;
|
|
2014
|
+
const hecHost = albHost ?? dnsHost;
|
|
2015
|
+
const indexerTargets = resources.filter((r) => r.kind === "indexer").map((r) => asHost(r.externalRef)).filter((h) => Boolean(h)).map((h) => `${h}:9997`);
|
|
2016
|
+
const endpoints = [];
|
|
2017
|
+
if (webHost) {
|
|
2018
|
+
endpoints.push({ label: "Splunk Web (search)", value: `https://${webHost}` });
|
|
2019
|
+
endpoints.push({ label: "Management API", value: `https://${webHost}:8089` });
|
|
2020
|
+
}
|
|
2021
|
+
if (hecHost) {
|
|
2022
|
+
endpoints.push({ label: "HTTP Event Collector", value: `https://${hecHost}:8088/services/collector` });
|
|
2023
|
+
}
|
|
2024
|
+
if (endpoints.length === 0) {
|
|
2025
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Panel, { title: "Endpoints", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: { margin: 0, fontSize: 13, lineHeight: 1.6, color: tokens.muted }, children: [
|
|
2026
|
+
"The environment is running, but the provisioner has not reported a reachable endpoint yet. Splunk Web, the management API and the HTTP Event Collector URL appear here as soon as the load balancer and DNS name are published. Check the ",
|
|
2027
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Resources" }),
|
|
2028
|
+
" tab for each component\u2019s external reference."
|
|
2029
|
+
] }) });
|
|
2030
|
+
}
|
|
2031
|
+
const tcpoutGroup = infra.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "veltrix_splunk";
|
|
2032
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2033
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Panel, { title: "Endpoints", children: endpoints.map((e) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Row, { label: e.label, value: e.value }, e.label)) }),
|
|
2034
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Panel, { title: "Point a forwarder at this environment", children: indexerTargets.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2035
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: { margin: "0 0 12px", fontSize: 13, color: tokens.muted }, children: [
|
|
2036
|
+
"Add this to a universal forwarder\u2019s ",
|
|
2037
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontFamily: mono }, children: "outputs.conf" }),
|
|
2038
|
+
":"
|
|
2039
|
+
] }),
|
|
2040
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2041
|
+
"pre",
|
|
2042
|
+
{
|
|
2043
|
+
style: {
|
|
2044
|
+
margin: 0,
|
|
2045
|
+
background: tokens.surface2,
|
|
2046
|
+
border: `1px solid ${tokens.border}`,
|
|
2047
|
+
borderRadius: 8,
|
|
2048
|
+
padding: "13px 15px",
|
|
2049
|
+
fontFamily: mono,
|
|
2050
|
+
fontSize: 12,
|
|
2051
|
+
color: tokens.text,
|
|
2052
|
+
overflowX: "auto"
|
|
2053
|
+
},
|
|
2054
|
+
children: `[tcpout]
|
|
2055
|
+
defaultGroup = ${tcpoutGroup}
|
|
2056
|
+
|
|
2057
|
+
[tcpout:${tcpoutGroup}]
|
|
2058
|
+
server = ${indexerTargets.join(", ")}
|
|
2059
|
+
sslVerifyServerCert = true`
|
|
2060
|
+
}
|
|
2061
|
+
)
|
|
2062
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { margin: 0, fontSize: 13, color: tokens.muted }, children: "Forwarder targets appear here once the indexer peers report their addresses." }) })
|
|
2063
|
+
] });
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
// src/byol/detail/ConfigurationTab.tsx
|
|
2067
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2068
|
+
var ConfigurationTab = ({ links, configBase }) => {
|
|
2069
|
+
if (!links || links.length === 0) {
|
|
2070
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2071
|
+
EmptyState,
|
|
2072
|
+
{
|
|
2073
|
+
title: "No configuration surfaces linked",
|
|
2074
|
+
description: "This app has not surfaced any configuration links for its BYOL environments."
|
|
2075
|
+
}
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
const resolve = (link) => {
|
|
2079
|
+
if (configBase && link.configTypeId) return `${configBase}/${link.configTypeId}`;
|
|
2080
|
+
return link.href;
|
|
2081
|
+
};
|
|
2082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 14 }, children: [
|
|
2083
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: { margin: 0, fontSize: 13, color: tokens.muted }, children: "Once the environment is running, configure it as code. These open the app\u2019s configuration surfaces." }),
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))", gap: 12 }, children: links.map((link) => {
|
|
2085
|
+
const url = resolve(link);
|
|
2086
|
+
const inner = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2087
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("h4", { style: { margin: "0 0 4px", fontSize: 13, display: "flex", justifyContent: "space-between", color: tokens.text }, children: [
|
|
2088
|
+
link.title,
|
|
2089
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: tokens.faint }, "aria-hidden": true, children: "\u2197" })
|
|
2090
|
+
] }),
|
|
2091
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: { margin: 0, fontSize: 12, color: tokens.muted }, children: link.description })
|
|
2092
|
+
] });
|
|
2093
|
+
const cardStyle = {
|
|
2094
|
+
border: `1px solid ${tokens.border}`,
|
|
2095
|
+
borderRadius: 8,
|
|
2096
|
+
padding: 14,
|
|
2097
|
+
background: tokens.surface,
|
|
2098
|
+
textDecoration: "none",
|
|
2099
|
+
color: "inherit",
|
|
2100
|
+
display: "block",
|
|
2101
|
+
cursor: url ? "pointer" : "default",
|
|
2102
|
+
opacity: url ? 1 : 0.7
|
|
2103
|
+
};
|
|
2104
|
+
return url ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("a", { href: url, style: cardStyle, children: inner }, link.key) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: cardStyle, title: "This link is unavailable here", children: inner }, link.key);
|
|
2105
|
+
}) })
|
|
2106
|
+
] });
|
|
2107
|
+
};
|
|
2108
|
+
|
|
2109
|
+
// src/byol/detail/SettingsTab.tsx
|
|
2110
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2111
|
+
var Row2 = ({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2112
|
+
"div",
|
|
2113
|
+
{
|
|
2114
|
+
style: {
|
|
2115
|
+
display: "grid",
|
|
2116
|
+
gridTemplateColumns: "200px 1fr",
|
|
2117
|
+
gap: 16,
|
|
2118
|
+
padding: "11px 0",
|
|
2119
|
+
borderBottom: `1px solid ${tokens.border}`,
|
|
2120
|
+
fontSize: 13
|
|
2121
|
+
},
|
|
2122
|
+
children: [
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { color: tokens.muted }, children: label }),
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { color: tokens.text, fontWeight: 600 }, children: value })
|
|
2125
|
+
]
|
|
2126
|
+
}
|
|
2127
|
+
);
|
|
2128
|
+
var SettingsTab = ({ infra, busy, onEdit, onDestroy, onDelete }) => {
|
|
2129
|
+
const provider = infra.hosting_type || (infra.cloudProviderId ? "Cloud" : SELF_HOSTED_LABEL);
|
|
2130
|
+
const neverDeployed = isNotStarted(infra.status);
|
|
2131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2132
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2133
|
+
Panel,
|
|
2134
|
+
{
|
|
2135
|
+
title: "Topology",
|
|
2136
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { variant: "ghost", size: "sm", onClick: onEdit, disabled: busy, children: "Edit" }),
|
|
2137
|
+
children: [
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Row2, { label: "Name", value: infra.name }),
|
|
2139
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Row2, { label: "Deployment type", value: infra.deploymentType ?? "\u2014" }),
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Row2, { label: "Indexers / Search heads", value: `${infra.indexerCount ?? "\u2014"} / ${infra.searchHeadCount ?? "\u2014"}` }),
|
|
2141
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Row2, { label: "Provider", value: provider }),
|
|
2142
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Row2, { label: "Region", value: infra.region || "\u2014" }),
|
|
2143
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Row2, { label: "Environment", value: infra.environmentType || "\u2014" })
|
|
2144
|
+
]
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { border: `1px solid ${tokens.danger}`, borderRadius: 12, padding: "16px 18px" }, children: [
|
|
2148
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { style: { margin: "0 0 4px", fontSize: 14, color: tokens.danger }, children: "Danger zone" }),
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: { margin: "0 0 12px", fontSize: 13, color: tokens.muted }, children: neverDeployed ? "This environment has not been deployed. Deleting removes the record and its plan." : "Destroy tears down every resource in the plan \u2014 instances, storage, network. This cannot be undone." }),
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
|
|
2151
|
+
!neverDeployed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { variant: "ghost", size: "sm", onClick: onDestroy, disabled: busy, children: "Destroy infrastructure" }) : null,
|
|
2152
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { variant: "danger", size: "sm", onClick: onDelete, disabled: busy, children: "Delete record" })
|
|
2153
|
+
] })
|
|
2154
|
+
] })
|
|
2155
|
+
] });
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
// src/byol/detail/ByolPlanModal.tsx
|
|
2159
|
+
var import_react = __toESM(require("react"), 1);
|
|
2160
|
+
|
|
2161
|
+
// src/byol/diffPlan.ts
|
|
2162
|
+
var nz = (v) => v == null ? null : v;
|
|
2163
|
+
function differs(current, desired) {
|
|
2164
|
+
return current.tier !== desired.tier || current.kind !== desired.kind || current.name !== desired.name || nz(current.role) !== nz(desired.role) || nz(current.region) !== nz(desired.region);
|
|
2165
|
+
}
|
|
2166
|
+
function needsReplan(status) {
|
|
2167
|
+
return status === "failed" || status === "attention";
|
|
2168
|
+
}
|
|
2169
|
+
function diffPlan(current, desired) {
|
|
2170
|
+
const currentByKey = new Map(current.map((c) => [c.planKey, c]));
|
|
2171
|
+
const desiredKeys = new Set(desired.map((d) => d.planKey));
|
|
2172
|
+
const add = [];
|
|
2173
|
+
const change = [];
|
|
2174
|
+
const noop = [];
|
|
2175
|
+
for (const item of desired) {
|
|
2176
|
+
const existing = currentByKey.get(item.planKey);
|
|
2177
|
+
if (!existing) add.push(item);
|
|
2178
|
+
else if (differs(existing, item) || needsReplan(existing.status)) change.push(item);
|
|
2179
|
+
else noop.push(item);
|
|
2180
|
+
}
|
|
2181
|
+
const destroy = current.filter((c) => !desiredKeys.has(c.planKey));
|
|
2182
|
+
return { add, change, destroy, noop };
|
|
2183
|
+
}
|
|
2184
|
+
function tierRank(tier) {
|
|
2185
|
+
const idx = TIER_ORDER.indexOf(tier);
|
|
2186
|
+
return idx === -1 ? TIER_ORDER.length : idx;
|
|
2187
|
+
}
|
|
2188
|
+
function buildByolPlan(current, desired) {
|
|
2189
|
+
const diff = diffPlan(current, desired);
|
|
2190
|
+
const summary = {
|
|
2191
|
+
add: diff.add.length,
|
|
2192
|
+
change: diff.change.length,
|
|
2193
|
+
destroy: diff.destroy.length,
|
|
2194
|
+
noop: diff.noop.length
|
|
2195
|
+
};
|
|
2196
|
+
const action = /* @__PURE__ */ new Map();
|
|
2197
|
+
for (const d of diff.add) action.set(d.planKey, "add");
|
|
2198
|
+
for (const d of diff.change) action.set(d.planKey, "change");
|
|
2199
|
+
for (const d of diff.noop) action.set(d.planKey, "noop");
|
|
2200
|
+
const lines = [];
|
|
2201
|
+
desired.forEach((d, i) => {
|
|
2202
|
+
lines.push({
|
|
2203
|
+
tier: d.tier,
|
|
2204
|
+
rank: d.sortOrder ?? i,
|
|
2205
|
+
planKey: d.planKey,
|
|
2206
|
+
action: action.get(d.planKey) ?? "noop",
|
|
2207
|
+
name: d.name,
|
|
2208
|
+
role: nz(d.role),
|
|
2209
|
+
region: nz(d.region),
|
|
2210
|
+
kind: d.kind
|
|
2211
|
+
});
|
|
2212
|
+
});
|
|
2213
|
+
const tail = desired.length;
|
|
2214
|
+
diff.destroy.forEach((c, i) => {
|
|
2215
|
+
lines.push({
|
|
2216
|
+
tier: c.tier,
|
|
2217
|
+
rank: tail + i,
|
|
2218
|
+
planKey: c.planKey,
|
|
2219
|
+
action: "destroy",
|
|
2220
|
+
name: c.name,
|
|
2221
|
+
role: nz(c.role),
|
|
2222
|
+
region: nz(c.region),
|
|
2223
|
+
kind: c.kind
|
|
2224
|
+
});
|
|
2225
|
+
});
|
|
2226
|
+
lines.sort((a, b) => tierRank(a.tier) - tierRank(b.tier) || a.rank - b.rank);
|
|
2227
|
+
const groups = [];
|
|
2228
|
+
const byTier = /* @__PURE__ */ new Map();
|
|
2229
|
+
for (const line of lines) {
|
|
2230
|
+
let group = byTier.get(line.tier);
|
|
2231
|
+
if (!group) {
|
|
2232
|
+
group = { tier: line.tier, items: [] };
|
|
2233
|
+
byTier.set(line.tier, group);
|
|
2234
|
+
groups.push(group);
|
|
2235
|
+
}
|
|
2236
|
+
group.items.push({
|
|
2237
|
+
planKey: line.planKey,
|
|
2238
|
+
action: line.action,
|
|
2239
|
+
name: line.name,
|
|
2240
|
+
role: line.role,
|
|
2241
|
+
region: line.region,
|
|
2242
|
+
kind: line.kind
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
return { summary, groups };
|
|
2246
|
+
}
|
|
2247
|
+
function planHasChanges(summary) {
|
|
2248
|
+
return summary.add + summary.change + summary.destroy > 0;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// src/byol/detail/planLines.tsx
|
|
2252
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2253
|
+
var ACTION_META = {
|
|
2254
|
+
add: { glyph: "+", color: tokens.success, label: "add" },
|
|
2255
|
+
change: { glyph: "~", color: tokens.warning, label: "change" },
|
|
2256
|
+
destroy: { glyph: "\u2212", color: tokens.danger, label: "destroy" },
|
|
2257
|
+
noop: { glyph: "\xB7", color: tokens.faint, label: "no change" }
|
|
2258
|
+
};
|
|
2259
|
+
var tierLabel = (tier) => TIER_LABELS[tier] ?? tier.replace(/-/g, " ");
|
|
2260
|
+
var PlanLine = ({ item }) => {
|
|
2261
|
+
const meta = ACTION_META[item.action];
|
|
2262
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2263
|
+
"div",
|
|
2264
|
+
{
|
|
2265
|
+
style: {
|
|
2266
|
+
position: "relative",
|
|
2267
|
+
border: `1px solid ${tokens.border}`,
|
|
2268
|
+
borderRadius: 8,
|
|
2269
|
+
background: tokens.surface,
|
|
2270
|
+
padding: "10px 12px 10px 14px",
|
|
2271
|
+
display: "flex",
|
|
2272
|
+
alignItems: "center",
|
|
2273
|
+
gap: 10,
|
|
2274
|
+
overflow: "hidden"
|
|
2275
|
+
},
|
|
2276
|
+
children: [
|
|
2277
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { position: "absolute", left: 0, top: 0, bottom: 0, width: 3, background: meta.color } }),
|
|
2278
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2279
|
+
"span",
|
|
2280
|
+
{
|
|
2281
|
+
"aria-hidden": true,
|
|
2282
|
+
title: meta.label,
|
|
2283
|
+
style: {
|
|
2284
|
+
flex: "none",
|
|
2285
|
+
width: 20,
|
|
2286
|
+
height: 20,
|
|
2287
|
+
borderRadius: 5,
|
|
2288
|
+
display: "inline-flex",
|
|
2289
|
+
alignItems: "center",
|
|
2290
|
+
justifyContent: "center",
|
|
2291
|
+
fontWeight: 700,
|
|
2292
|
+
fontSize: 14,
|
|
2293
|
+
lineHeight: 1,
|
|
2294
|
+
color: meta.color,
|
|
2295
|
+
background: tokens.surface2
|
|
2296
|
+
},
|
|
2297
|
+
children: meta.glyph
|
|
2298
|
+
}
|
|
2299
|
+
),
|
|
2300
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
2301
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 600, fontSize: 13, color: tokens.text, textDecoration: item.action === "destroy" ? "line-through" : "none" }, children: item.name }),
|
|
2302
|
+
item.role ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: 11, color: tokens.faint }, children: item.role }) : null
|
|
2303
|
+
] }),
|
|
2304
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { fontSize: 11, color: tokens.muted, flex: "none" }, children: item.region || "\u2014" })
|
|
2305
|
+
]
|
|
2306
|
+
}
|
|
2307
|
+
);
|
|
2308
|
+
};
|
|
2309
|
+
var PlanGroup = ({ tier, items }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2311
|
+
"h3",
|
|
2312
|
+
{
|
|
2313
|
+
style: {
|
|
2314
|
+
margin: "0 0 8px",
|
|
2315
|
+
fontSize: 12,
|
|
2316
|
+
letterSpacing: ".08em",
|
|
2317
|
+
textTransform: "uppercase",
|
|
2318
|
+
color: tokens.primary
|
|
2319
|
+
},
|
|
2320
|
+
children: tierLabel(tier)
|
|
2321
|
+
}
|
|
2322
|
+
),
|
|
2323
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PlanLine, { item }, item.planKey)) })
|
|
2324
|
+
] });
|
|
2325
|
+
|
|
2326
|
+
// src/byol/detail/ByolPlanModal.tsx
|
|
2327
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2328
|
+
var InfoPanel = ({ title, children }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { border: `1px solid ${tokens.border}`, borderRadius: 8, background: tokens.surface2, overflow: "hidden" }, children: [
|
|
2329
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2330
|
+
"div",
|
|
2331
|
+
{
|
|
2332
|
+
style: {
|
|
2333
|
+
padding: "8px 12px",
|
|
2334
|
+
borderBottom: `1px solid ${tokens.border}`,
|
|
2335
|
+
fontSize: 11,
|
|
2336
|
+
letterSpacing: ".08em",
|
|
2337
|
+
textTransform: "uppercase",
|
|
2338
|
+
color: tokens.primary,
|
|
2339
|
+
fontWeight: 700
|
|
2340
|
+
},
|
|
2341
|
+
children: title
|
|
2342
|
+
}
|
|
2343
|
+
),
|
|
2344
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "10px 12px" }, children })
|
|
2345
|
+
] });
|
|
2346
|
+
var NetworkPanel = ({ plan }) => {
|
|
2347
|
+
if (!plan.network) {
|
|
2348
|
+
if (!plan.networkUnavailable) return null;
|
|
2349
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InfoPanel, { title: "Network", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontSize: 13, color: tokens.muted }, children: "Subnet allocation preview is temporarily unavailable \u2014 the CIDR is reserved when you apply." }) });
|
|
2350
|
+
}
|
|
2351
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InfoPanel, { title: "Network", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { fontSize: 13, color: tokens.text }, children: [
|
|
2352
|
+
"Subnet",
|
|
2353
|
+
" ",
|
|
2354
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { style: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: tokens.primary }, children: plan.network.subnetCidr }),
|
|
2355
|
+
" ",
|
|
2356
|
+
"will be allocated in",
|
|
2357
|
+
" ",
|
|
2358
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { style: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: tokens.text }, children: plan.network.networkRef }),
|
|
2359
|
+
"."
|
|
2360
|
+
] }) });
|
|
2361
|
+
};
|
|
2362
|
+
var TagsPanel = ({ plan }) => {
|
|
2363
|
+
const entries = plan.tags ? Object.entries(plan.tags) : [];
|
|
2364
|
+
if (entries.length === 0) return null;
|
|
2365
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InfoPanel, { title: "Tags applied to every resource", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: "grid", gridTemplateColumns: "auto 1fr", gap: "4px 12px", fontSize: 12 }, children: entries.map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react.default.Fragment, { children: [
|
|
2366
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2367
|
+
"span",
|
|
2368
|
+
{
|
|
2369
|
+
style: {
|
|
2370
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
2371
|
+
color: tokens.muted,
|
|
2372
|
+
whiteSpace: "nowrap"
|
|
2373
|
+
},
|
|
2374
|
+
children: key
|
|
2375
|
+
}
|
|
2376
|
+
),
|
|
2377
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { color: tokens.text, wordBreak: "break-all" }, children: value })
|
|
2378
|
+
] }, key)) }) });
|
|
2379
|
+
};
|
|
2380
|
+
var SummaryChips = ({ plan }) => {
|
|
2381
|
+
const { add, change, destroy } = plan.summary;
|
|
2382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [
|
|
2383
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Badge, { variant: "success", size: "sm", children: [
|
|
2384
|
+
"+",
|
|
2385
|
+
add,
|
|
2386
|
+
" to add"
|
|
2387
|
+
] }),
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Badge, { variant: "warning", size: "sm", children: [
|
|
2389
|
+
"~",
|
|
2390
|
+
change,
|
|
2391
|
+
" to change"
|
|
2392
|
+
] }),
|
|
2393
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Badge, { variant: "danger", size: "sm", children: [
|
|
2394
|
+
"\u2212",
|
|
2395
|
+
destroy,
|
|
2396
|
+
" to destroy"
|
|
2397
|
+
] }),
|
|
2398
|
+
plan.summary.noop > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { style: { fontSize: 12, color: tokens.faint }, children: [
|
|
2399
|
+
plan.summary.noop,
|
|
2400
|
+
" unchanged"
|
|
2401
|
+
] }) : null
|
|
2402
|
+
] });
|
|
2403
|
+
};
|
|
2404
|
+
var PlanBody = ({ plan, loading, error, allowApplyWithoutChanges }) => {
|
|
2405
|
+
if (loading) {
|
|
2406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "24px 0", color: tokens.muted, fontSize: 13 }, children: [
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { size: "sm" }),
|
|
2408
|
+
" Computing plan\u2026"
|
|
2409
|
+
] });
|
|
2410
|
+
}
|
|
2411
|
+
if (error) {
|
|
2412
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "12px 0", color: tokens.danger, fontSize: 13 }, children: error });
|
|
2413
|
+
}
|
|
2414
|
+
if (!plan) return null;
|
|
2415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2416
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SummaryChips, { plan }),
|
|
2417
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NetworkPanel, { plan }),
|
|
2418
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TagsPanel, { plan }),
|
|
2419
|
+
!planHasChanges(plan.summary) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2420
|
+
"div",
|
|
2421
|
+
{
|
|
2422
|
+
style: {
|
|
2423
|
+
fontSize: 13,
|
|
2424
|
+
color: tokens.muted,
|
|
2425
|
+
background: tokens.surface2,
|
|
2426
|
+
border: `1px solid ${tokens.border}`,
|
|
2427
|
+
borderRadius: 8,
|
|
2428
|
+
padding: "10px 14px"
|
|
2429
|
+
},
|
|
2430
|
+
children: allowApplyWithoutChanges ? "No plan changes \u2014 the previous run did not complete. Apply to re-run the deployment with the same plan." : "No changes. Infrastructure is up to date."
|
|
2431
|
+
}
|
|
2432
|
+
) : plan.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PlanGroup, { tier: group.tier, items: group.items }, group.tier))
|
|
2433
|
+
] });
|
|
2434
|
+
};
|
|
2435
|
+
var ByolPlanModal = ({
|
|
2436
|
+
isOpen,
|
|
2437
|
+
onClose,
|
|
2438
|
+
plan,
|
|
2439
|
+
loading,
|
|
2440
|
+
error,
|
|
2441
|
+
applying,
|
|
2442
|
+
onApply,
|
|
2443
|
+
infraName,
|
|
2444
|
+
allowApplyWithoutChanges
|
|
2445
|
+
}) => {
|
|
2446
|
+
const noChanges = !plan || !planHasChanges(plan.summary);
|
|
2447
|
+
const applyDisabled = loading || applying || noChanges && !allowApplyWithoutChanges;
|
|
2448
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2449
|
+
Modal,
|
|
2450
|
+
{
|
|
2451
|
+
isOpen,
|
|
2452
|
+
onClose,
|
|
2453
|
+
size: "lg",
|
|
2454
|
+
title: "Review deployment plan",
|
|
2455
|
+
subtitle: infraName ? `Changes that will be applied to ${infraName}.` : "Changes that will be applied to this environment.",
|
|
2456
|
+
disableBackdropClose: applying,
|
|
2457
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2458
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { variant: "ghost", size: "sm", onClick: onClose, disabled: applying, children: "Cancel" }),
|
|
2459
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { variant: "primary", size: "sm", onClick: onApply, isLoading: applying, disabled: applyDisabled, children: "Apply" })
|
|
2460
|
+
] }),
|
|
2461
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PlanBody, { plan, loading, error, allowApplyWithoutChanges })
|
|
2462
|
+
}
|
|
2463
|
+
);
|
|
2464
|
+
};
|
|
2465
|
+
|
|
2466
|
+
// src/byol/detail/DestroyPlanModal.tsx
|
|
2467
|
+
var import_react2 = require("react");
|
|
2468
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2469
|
+
var DestroyPlanModal = ({
|
|
2470
|
+
isOpen,
|
|
2471
|
+
onClose,
|
|
2472
|
+
resources,
|
|
2473
|
+
loading,
|
|
2474
|
+
error,
|
|
2475
|
+
destroying,
|
|
2476
|
+
onConfirm,
|
|
2477
|
+
infraName
|
|
2478
|
+
}) => {
|
|
2479
|
+
const plan = (0, import_react2.useMemo)(() => resources ? buildByolPlan(resources, []) : null, [resources]);
|
|
2480
|
+
const destroyCount = plan?.summary.destroy ?? 0;
|
|
2481
|
+
const confirmDisabled = loading || destroying;
|
|
2482
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2483
|
+
Modal,
|
|
2484
|
+
{
|
|
2485
|
+
isOpen,
|
|
2486
|
+
onClose,
|
|
2487
|
+
size: "lg",
|
|
2488
|
+
title: "Destroy infrastructure",
|
|
2489
|
+
subtitle: infraName ? `Every resource for "${infraName}" will be torn down.` : "Every resource for this environment will be torn down.",
|
|
2490
|
+
disableBackdropClose: destroying,
|
|
2491
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { variant: "ghost", size: "sm", onClick: onClose, disabled: destroying, children: "Cancel" }),
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Button, { variant: "danger", size: "sm", onClick: onConfirm, isLoading: destroying, disabled: confirmDisabled, children: "Destroy" })
|
|
2494
|
+
] }),
|
|
2495
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
2496
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Alert, { variant: "danger", title: "This cannot be undone", children: "Destroying removes every instance, storage volume, and network resource provisioned for this environment." }),
|
|
2497
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 13, color: tokens.danger }, children: error }) : null,
|
|
2498
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "8px 0", color: tokens.muted, fontSize: 13 }, children: [
|
|
2499
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Spinner, { size: "sm" }),
|
|
2500
|
+
" Loading current resources\u2026"
|
|
2501
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Badge, { variant: "danger", size: "sm", children: [
|
|
2503
|
+
"\u2212",
|
|
2504
|
+
destroyCount,
|
|
2505
|
+
" to destroy"
|
|
2506
|
+
] }),
|
|
2507
|
+
plan && plan.groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: plan.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PlanGroup, { tier: group.tier, items: group.items }, group.tier)) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2508
|
+
"div",
|
|
2509
|
+
{
|
|
2510
|
+
style: {
|
|
2511
|
+
fontSize: 13,
|
|
2512
|
+
color: tokens.muted,
|
|
2513
|
+
background: tokens.surface2,
|
|
2514
|
+
border: `1px solid ${tokens.border}`,
|
|
2515
|
+
borderRadius: 8,
|
|
2516
|
+
padding: "10px 14px"
|
|
2517
|
+
},
|
|
2518
|
+
children: "No resource inventory is available for this environment \u2014 it will still be destroyed."
|
|
2519
|
+
}
|
|
2520
|
+
)
|
|
2521
|
+
] })
|
|
2522
|
+
] })
|
|
2523
|
+
}
|
|
2524
|
+
);
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
// src/byol/detail/icons.tsx
|
|
2528
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2529
|
+
var Svg = ({ size = 16, style, children }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2530
|
+
"svg",
|
|
2531
|
+
{
|
|
2532
|
+
width: size,
|
|
2533
|
+
height: size,
|
|
2534
|
+
viewBox: "0 0 24 24",
|
|
2535
|
+
fill: "none",
|
|
2536
|
+
stroke: "currentColor",
|
|
2537
|
+
strokeWidth: 2,
|
|
2538
|
+
strokeLinecap: "round",
|
|
2539
|
+
strokeLinejoin: "round",
|
|
2540
|
+
"aria-hidden": true,
|
|
2541
|
+
focusable: false,
|
|
2542
|
+
style: { display: "block", flex: "none", ...style },
|
|
2543
|
+
children
|
|
2544
|
+
}
|
|
2545
|
+
);
|
|
2546
|
+
var OverviewIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Svg, { ...p, children: [
|
|
2547
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: "3", y: "3", width: "7", height: "9", rx: "1" }),
|
|
2548
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: "14", y: "3", width: "7", height: "5", rx: "1" }),
|
|
2549
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: "14", y: "12", width: "7", height: "9", rx: "1" }),
|
|
2550
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: "3", y: "16", width: "7", height: "5", rx: "1" })
|
|
2551
|
+
] });
|
|
2552
|
+
var ResourcesIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Svg, { ...p, children: [
|
|
2553
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: "2", y: "2", width: "20", height: "8", rx: "2" }),
|
|
2554
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { x: "2", y: "14", width: "20", height: "8", rx: "2" }),
|
|
2555
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "6", y1: "6", x2: "6.01", y2: "6" }),
|
|
2556
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "6", y1: "18", x2: "6.01", y2: "18" })
|
|
2557
|
+
] });
|
|
2558
|
+
var ActivityIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Svg, { ...p, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M22 12h-4l-3 9L9 3l-3 9H2" }) });
|
|
2559
|
+
var AccessIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Svg, { ...p, children: [
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "7.5", cy: "15.5", r: "5.5" }),
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m21 2-9.6 9.6" }),
|
|
2562
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m15.5 7.5 3 3L22 7l-3-3" })
|
|
2563
|
+
] });
|
|
2564
|
+
var ConfigurationIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Svg, { ...p, children: [
|
|
2565
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "21", y1: "4", x2: "14", y2: "4" }),
|
|
2566
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "10", y1: "4", x2: "3", y2: "4" }),
|
|
2567
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "21", y1: "12", x2: "12", y2: "12" }),
|
|
2568
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "8", y1: "12", x2: "3", y2: "12" }),
|
|
2569
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "21", y1: "20", x2: "16", y2: "20" }),
|
|
2570
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "12", y1: "20", x2: "3", y2: "20" }),
|
|
2571
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "14", y1: "2", x2: "14", y2: "6" }),
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "8", y1: "10", x2: "8", y2: "14" }),
|
|
2573
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "16", y1: "18", x2: "16", y2: "22" })
|
|
2574
|
+
] });
|
|
2575
|
+
var SettingsIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Svg, { ...p, children: [
|
|
2576
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2Z" }),
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "12", cy: "12", r: "3" })
|
|
2578
|
+
] });
|
|
2579
|
+
|
|
2580
|
+
// src/byol/ByolInfrastructureDetail.tsx
|
|
2581
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2582
|
+
var PROVISIONING_POLL_MS = 4e3;
|
|
2583
|
+
var SECTIONS = [
|
|
2584
|
+
{ key: "overview", label: "Overview", Icon: OverviewIcon },
|
|
2585
|
+
{ key: "resources", label: "Resources", Icon: ResourcesIcon },
|
|
2586
|
+
{ key: "activity", label: "Activity", Icon: ActivityIcon },
|
|
2587
|
+
{ key: "access", label: "Access", Icon: AccessIcon },
|
|
2588
|
+
{ key: "config", label: "Configuration", Icon: ConfigurationIcon },
|
|
2589
|
+
{ key: "settings", label: "Settings", Icon: SettingsIcon }
|
|
2590
|
+
];
|
|
2591
|
+
function derivePlan(infra) {
|
|
2592
|
+
const plan = buildByolResourcePlan({
|
|
2593
|
+
deploymentType: infra.deploymentType,
|
|
2594
|
+
indexerCount: infra.indexerCount,
|
|
2595
|
+
searchHeadCount: infra.searchHeadCount,
|
|
2596
|
+
hostingType: infra.hosting_type,
|
|
2597
|
+
isCloud: Boolean(infra.cloudProviderId),
|
|
2598
|
+
region: infra.region ?? null,
|
|
2599
|
+
indexerRegions: (infra.indexerRegions ?? []).map((r) => r.region),
|
|
2600
|
+
searchHeadRegions: (infra.searchHeadRegions ?? []).map((r) => r.region)
|
|
2601
|
+
});
|
|
2602
|
+
return plan.map((p) => ({
|
|
2603
|
+
id: p.planKey,
|
|
2604
|
+
infrastructureId: infra.id,
|
|
2605
|
+
tier: p.tier,
|
|
2606
|
+
kind: p.kind,
|
|
2607
|
+
name: p.name,
|
|
2608
|
+
role: p.role,
|
|
2609
|
+
region: p.region,
|
|
2610
|
+
status: "not_started",
|
|
2611
|
+
externalRef: null,
|
|
2612
|
+
message: null,
|
|
2613
|
+
planKey: p.planKey,
|
|
2614
|
+
sortOrder: p.sortOrder
|
|
2615
|
+
}));
|
|
2616
|
+
}
|
|
2617
|
+
function readSectionFromUrl() {
|
|
2618
|
+
if (typeof window === "undefined") return null;
|
|
2619
|
+
const s2 = new URLSearchParams(window.location.search).get("section");
|
|
2620
|
+
return SECTIONS.some((x) => x.key === s2) ? s2 : null;
|
|
2621
|
+
}
|
|
2622
|
+
var ByolInfrastructureDetail = ({
|
|
2623
|
+
apiBase,
|
|
2624
|
+
initialInfra,
|
|
2625
|
+
configBase,
|
|
2626
|
+
configLinks,
|
|
2627
|
+
onBack,
|
|
2628
|
+
onEdit,
|
|
2629
|
+
onDeleted,
|
|
2630
|
+
onChanged,
|
|
2631
|
+
reloadSignal = 0
|
|
2632
|
+
}) => {
|
|
2633
|
+
const [infra, setInfra] = (0, import_react3.useState)(initialInfra);
|
|
2634
|
+
const [resources, setResources] = (0, import_react3.useState)([]);
|
|
2635
|
+
const [deployments, setDeployments] = (0, import_react3.useState)([]);
|
|
2636
|
+
const [section, setSection] = (0, import_react3.useState)(readSectionFromUrl() ?? "overview");
|
|
2637
|
+
const [collapsed, setCollapsed] = (0, import_react3.useState)(false);
|
|
2638
|
+
const [loading, setLoading] = (0, import_react3.useState)(true);
|
|
2639
|
+
const [busy, setBusy] = (0, import_react3.useState)(false);
|
|
2640
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
2641
|
+
const [planOpen, setPlanOpen] = (0, import_react3.useState)(false);
|
|
2642
|
+
const [plan, setPlan] = (0, import_react3.useState)(null);
|
|
2643
|
+
const [planLoading, setPlanLoading] = (0, import_react3.useState)(false);
|
|
2644
|
+
const [planError, setPlanError] = (0, import_react3.useState)(null);
|
|
2645
|
+
const [applying, setApplying] = (0, import_react3.useState)(false);
|
|
2646
|
+
const [destroyOpen, setDestroyOpen] = (0, import_react3.useState)(false);
|
|
2647
|
+
const [destroyResources, setDestroyResources] = (0, import_react3.useState)(null);
|
|
2648
|
+
const [destroyLoading, setDestroyLoading] = (0, import_react3.useState)(false);
|
|
2649
|
+
const [destroyError, setDestroyError] = (0, import_react3.useState)(null);
|
|
2650
|
+
const [destroying, setDestroying] = (0, import_react3.useState)(false);
|
|
2651
|
+
const { confirm } = useConfirmDialog();
|
|
2652
|
+
const id = initialInfra.id;
|
|
2653
|
+
const initialRef = (0, import_react3.useRef)(initialInfra);
|
|
2654
|
+
initialRef.current = initialInfra;
|
|
2655
|
+
const load = (0, import_react3.useCallback)(async () => {
|
|
2656
|
+
setError(null);
|
|
2657
|
+
try {
|
|
2658
|
+
const [freshInfra, res, deps] = await Promise.all([
|
|
2659
|
+
getInfra(apiBase, id).catch(() => initialRef.current),
|
|
2660
|
+
getResources(apiBase, id).catch(() => []),
|
|
2661
|
+
getDeployments(apiBase, id).catch(() => [])
|
|
2662
|
+
]);
|
|
2663
|
+
setInfra(freshInfra);
|
|
2664
|
+
setResources(res);
|
|
2665
|
+
setDeployments(deps);
|
|
2666
|
+
} catch (e) {
|
|
2667
|
+
setError(e.message);
|
|
2668
|
+
} finally {
|
|
2669
|
+
setLoading(false);
|
|
2670
|
+
}
|
|
2671
|
+
}, [apiBase, id]);
|
|
2672
|
+
(0, import_react3.useEffect)(() => {
|
|
2673
|
+
void load();
|
|
2674
|
+
}, [load, reloadSignal]);
|
|
2675
|
+
(0, import_react3.useEffect)(() => {
|
|
2676
|
+
if (typeof window === "undefined") return;
|
|
2677
|
+
const params = new URLSearchParams(window.location.search);
|
|
2678
|
+
params.set("infra", id);
|
|
2679
|
+
params.set("section", section);
|
|
2680
|
+
window.history.replaceState(null, "", `${window.location.pathname}?${params.toString()}`);
|
|
2681
|
+
}, [id, section]);
|
|
2682
|
+
const refresh = (0, import_react3.useCallback)(async () => {
|
|
2683
|
+
await load();
|
|
2684
|
+
onChanged();
|
|
2685
|
+
}, [load, onChanged]);
|
|
2686
|
+
const transient = infra.status === "provisioning" || infra.status === "destroying";
|
|
2687
|
+
(0, import_react3.useEffect)(() => {
|
|
2688
|
+
if (!transient) return;
|
|
2689
|
+
let active = true;
|
|
2690
|
+
const timer = setInterval(() => {
|
|
2691
|
+
if (active) void refresh();
|
|
2692
|
+
}, PROVISIONING_POLL_MS);
|
|
2693
|
+
return () => {
|
|
2694
|
+
active = false;
|
|
2695
|
+
clearInterval(timer);
|
|
2696
|
+
};
|
|
2697
|
+
}, [transient, refresh]);
|
|
2698
|
+
const runAction = (0, import_react3.useCallback)(
|
|
2699
|
+
async (fn) => {
|
|
2700
|
+
setBusy(true);
|
|
2701
|
+
setError(null);
|
|
2702
|
+
try {
|
|
2703
|
+
await fn();
|
|
2704
|
+
await refresh();
|
|
2705
|
+
} catch (e) {
|
|
2706
|
+
setError(e.message);
|
|
2707
|
+
} finally {
|
|
2708
|
+
setBusy(false);
|
|
2709
|
+
}
|
|
2710
|
+
},
|
|
2711
|
+
[refresh]
|
|
2712
|
+
);
|
|
2713
|
+
const openPlan = (0, import_react3.useCallback)(() => {
|
|
2714
|
+
setPlanOpen(true);
|
|
2715
|
+
setPlan(null);
|
|
2716
|
+
setPlanError(null);
|
|
2717
|
+
setPlanLoading(true);
|
|
2718
|
+
getPlan(apiBase, id).then((p) => setPlan(p)).catch((e) => setPlanError(e.message)).finally(() => setPlanLoading(false));
|
|
2719
|
+
}, [apiBase, id]);
|
|
2720
|
+
const closePlan = (0, import_react3.useCallback)(() => {
|
|
2721
|
+
if (applying) return;
|
|
2722
|
+
setPlanOpen(false);
|
|
2723
|
+
}, [applying]);
|
|
2724
|
+
const onApply = (0, import_react3.useCallback)(async () => {
|
|
2725
|
+
setApplying(true);
|
|
2726
|
+
setPlanError(null);
|
|
2727
|
+
try {
|
|
2728
|
+
await deployInfra(apiBase, id);
|
|
2729
|
+
setPlanOpen(false);
|
|
2730
|
+
setSection("activity");
|
|
2731
|
+
await refresh();
|
|
2732
|
+
} catch (e) {
|
|
2733
|
+
setPlanError(e.message);
|
|
2734
|
+
} finally {
|
|
2735
|
+
setApplying(false);
|
|
2736
|
+
}
|
|
2737
|
+
}, [apiBase, id, refresh]);
|
|
2738
|
+
const openDestroy = (0, import_react3.useCallback)(() => {
|
|
2739
|
+
setDestroyOpen(true);
|
|
2740
|
+
setDestroyError(null);
|
|
2741
|
+
setDestroyResources(null);
|
|
2742
|
+
setDestroyLoading(true);
|
|
2743
|
+
getResources(apiBase, id).then((res) => setDestroyResources(res)).catch(() => setDestroyResources(resources)).finally(() => setDestroyLoading(false));
|
|
2744
|
+
}, [apiBase, id, resources]);
|
|
2745
|
+
const closeDestroy = (0, import_react3.useCallback)(() => {
|
|
2746
|
+
if (destroying) return;
|
|
2747
|
+
setDestroyOpen(false);
|
|
2748
|
+
}, [destroying]);
|
|
2749
|
+
const confirmDestroy = (0, import_react3.useCallback)(async () => {
|
|
2750
|
+
setDestroying(true);
|
|
2751
|
+
setDestroyError(null);
|
|
2752
|
+
try {
|
|
2753
|
+
await destroyInfra(apiBase, id);
|
|
2754
|
+
setDestroyOpen(false);
|
|
2755
|
+
setSection("activity");
|
|
2756
|
+
await refresh();
|
|
2757
|
+
} catch (e) {
|
|
2758
|
+
setDestroyError(e.message);
|
|
2759
|
+
} finally {
|
|
2760
|
+
setDestroying(false);
|
|
2761
|
+
}
|
|
2762
|
+
}, [apiBase, id, refresh]);
|
|
2763
|
+
const onLifecycle = (action) => runAction(() => lifecycleInfra(apiBase, id, action));
|
|
2764
|
+
const onDelete = async () => {
|
|
2765
|
+
const ok = await confirm({
|
|
2766
|
+
title: "Delete infrastructure record",
|
|
2767
|
+
message: `Delete "${infra.name}"? This cannot be undone.`,
|
|
2768
|
+
confirmText: "Delete",
|
|
2769
|
+
cancelText: "Cancel",
|
|
2770
|
+
variant: "danger"
|
|
2771
|
+
});
|
|
2772
|
+
if (!ok) return;
|
|
2773
|
+
setBusy(true);
|
|
2774
|
+
setError(null);
|
|
2775
|
+
try {
|
|
2776
|
+
const res = await Promise.resolve().then(() => (init_client(), client_exports)).then(({ authFetch: authFetch2 }) => authFetch2(`${apiBase}/${id}`, { method: "DELETE" }));
|
|
2777
|
+
if (!res.ok && res.status !== 204) throw new Error(await errorText(res));
|
|
2778
|
+
onDeleted();
|
|
2779
|
+
} catch (e) {
|
|
2780
|
+
setError(e.message);
|
|
2781
|
+
setBusy(false);
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
const status = infra.status;
|
|
2785
|
+
const notStarted = isNotStarted(status);
|
|
2786
|
+
const failed = status === "failed" || status === "error";
|
|
2787
|
+
const provisioning = status === "provisioning" || status === "destroying";
|
|
2788
|
+
const running = isRunning(status);
|
|
2789
|
+
const deprovisioned = status === "deprovisioned" || status === "decommissioned";
|
|
2790
|
+
const latestRunAction = deployments[0]?.action;
|
|
2791
|
+
const failedDestroy = failed && latestRunAction === "destroy";
|
|
2792
|
+
const derived = resources.length === 0;
|
|
2793
|
+
const displayResources = (0, import_react3.useMemo)(
|
|
2794
|
+
() => derived ? derivePlan(infra) : resources,
|
|
2795
|
+
[derived, infra, resources]
|
|
2796
|
+
);
|
|
2797
|
+
const provider = infra.hosting_type || (infra.cloudProviderId ? "Cloud" : SELF_HOSTED_LABEL);
|
|
2798
|
+
const primaryAction = (() => {
|
|
2799
|
+
if (notStarted) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "primary", size: "sm", onClick: openPlan, disabled: busy, children: "Deploy environment" });
|
|
2800
|
+
if (deprovisioned) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "primary", size: "sm", onClick: openPlan, disabled: busy, children: "Re-provision" });
|
|
2801
|
+
if (failedDestroy) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "danger", size: "sm", onClick: openDestroy, disabled: busy, children: "Retry Destroy" });
|
|
2802
|
+
if (failed) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "primary", size: "sm", onClick: openPlan, disabled: busy, children: "Retry deployment" });
|
|
2803
|
+
if (provisioning) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "primary", size: "sm", onClick: () => setSection("activity"), children: "View progress" });
|
|
2804
|
+
return null;
|
|
2805
|
+
})();
|
|
2806
|
+
const content = (() => {
|
|
2807
|
+
switch (section) {
|
|
2808
|
+
case "overview":
|
|
2809
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(OverviewTab, { infra, resources: displayResources });
|
|
2810
|
+
case "resources":
|
|
2811
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ResourcesTab, { resources: displayResources, derived });
|
|
2812
|
+
case "activity":
|
|
2813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ActivityTab, { deployments });
|
|
2814
|
+
case "access":
|
|
2815
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AccessTab, { infra, resources });
|
|
2816
|
+
case "config":
|
|
2817
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ConfigurationTab, { links: configLinks ?? [], configBase });
|
|
2818
|
+
case "settings":
|
|
2819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2820
|
+
SettingsTab,
|
|
2821
|
+
{
|
|
2822
|
+
infra,
|
|
2823
|
+
busy,
|
|
2824
|
+
onEdit: () => onEdit(infra),
|
|
2825
|
+
onDestroy: openDestroy,
|
|
2826
|
+
onDelete
|
|
2827
|
+
}
|
|
2828
|
+
);
|
|
2829
|
+
}
|
|
2830
|
+
})();
|
|
2831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
2832
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2833
|
+
"button",
|
|
2834
|
+
{
|
|
2835
|
+
onClick: onBack,
|
|
2836
|
+
style: {
|
|
2837
|
+
display: "inline-flex",
|
|
2838
|
+
alignItems: "center",
|
|
2839
|
+
gap: 7,
|
|
2840
|
+
color: tokens.muted,
|
|
2841
|
+
fontSize: 13,
|
|
2842
|
+
background: "none",
|
|
2843
|
+
border: "none",
|
|
2844
|
+
cursor: "pointer",
|
|
2845
|
+
padding: 0,
|
|
2846
|
+
marginBottom: 12
|
|
2847
|
+
},
|
|
2848
|
+
children: "\u2039 Back to infrastructure"
|
|
2849
|
+
}
|
|
2850
|
+
),
|
|
2851
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { border: `1px solid ${tokens.border}`, borderRadius: 12, background: tokens.surface, overflow: "hidden" }, children: [
|
|
2852
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "18px 24px", alignItems: "flex-start", justifyContent: "space-between", padding: 20 }, children: [
|
|
2853
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { minWidth: 260, flex: 1 }, children: [
|
|
2854
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
|
|
2855
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { style: { margin: 0, fontSize: 21, letterSpacing: "-.01em", color: tokens.text }, children: infra.name }),
|
|
2856
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusPill, { status }),
|
|
2857
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Spinner, { size: "sm" }) : null
|
|
2858
|
+
] }),
|
|
2859
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "12px 20px", marginTop: 12 }, children: [
|
|
2860
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Meta, { label: "Environment", children: infra.environmentType || "\u2014" }),
|
|
2861
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Meta, { label: "Deployment", children: infra.deploymentType ?? "\u2014" }),
|
|
2862
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Meta, { label: "Provider", children: provider }),
|
|
2863
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Meta, { label: "Region", children: infra.region || "\u2014" }),
|
|
2864
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Meta, { label: "Updated", children: formatDate(infra.updatedAt) })
|
|
2865
|
+
] }),
|
|
2866
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { marginTop: 14 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ProgressMeter, { resources: displayResources }) })
|
|
2867
|
+
] }),
|
|
2868
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 10 }, children: [
|
|
2869
|
+
primaryAction,
|
|
2870
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap", justifyContent: "flex-end" }, children: [
|
|
2871
|
+
running ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2872
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => onLifecycle("stop"), disabled: busy, children: "Stop" }),
|
|
2873
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => onLifecycle("restart"), disabled: busy, children: "Restart" })
|
|
2874
|
+
] }) : null,
|
|
2875
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => onEdit(infra), disabled: busy, children: "Edit topology" }),
|
|
2876
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => void refresh(), disabled: busy, children: "Refresh" })
|
|
2877
|
+
] })
|
|
2878
|
+
] })
|
|
2879
|
+
] }),
|
|
2880
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { padding: "0 20px 12px", color: tokens.danger, fontSize: 13 }, children: error }) : null,
|
|
2881
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", borderTop: `1px solid ${tokens.border}`, alignItems: "stretch" }, children: [
|
|
2882
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2883
|
+
"aside",
|
|
2884
|
+
{
|
|
2885
|
+
style: {
|
|
2886
|
+
flex: `0 0 ${collapsed ? 56 : 220}px`,
|
|
2887
|
+
width: collapsed ? 56 : 220,
|
|
2888
|
+
borderRight: `1px solid ${tokens.border}`,
|
|
2889
|
+
background: tokens.surface2,
|
|
2890
|
+
padding: "14px 12px",
|
|
2891
|
+
display: "flex",
|
|
2892
|
+
flexDirection: "column",
|
|
2893
|
+
gap: 2,
|
|
2894
|
+
transition: "flex-basis .12s, width .12s"
|
|
2895
|
+
},
|
|
2896
|
+
children: [
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: collapsed ? "center" : "space-between", padding: "2px 6px 10px" }, children: [
|
|
2898
|
+
collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 11, letterSpacing: ".1em", textTransform: "uppercase", color: tokens.faint, fontWeight: 700 }, children: "This infrastructure" }),
|
|
2899
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2900
|
+
"button",
|
|
2901
|
+
{
|
|
2902
|
+
onClick: () => setCollapsed((c) => !c),
|
|
2903
|
+
title: collapsed ? "Expand" : "Collapse",
|
|
2904
|
+
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
2905
|
+
style: {
|
|
2906
|
+
border: `1px solid ${tokens.border}`,
|
|
2907
|
+
background: tokens.surface,
|
|
2908
|
+
color: tokens.muted,
|
|
2909
|
+
borderRadius: 6,
|
|
2910
|
+
width: 24,
|
|
2911
|
+
height: 24,
|
|
2912
|
+
cursor: "pointer",
|
|
2913
|
+
lineHeight: 1
|
|
2914
|
+
},
|
|
2915
|
+
children: collapsed ? "\xBB" : "\xAB"
|
|
2916
|
+
}
|
|
2917
|
+
)
|
|
2918
|
+
] }),
|
|
2919
|
+
SECTIONS.map((s2) => {
|
|
2920
|
+
const active = s2.key === section;
|
|
2921
|
+
const { Icon } = s2;
|
|
2922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2923
|
+
"button",
|
|
2924
|
+
{
|
|
2925
|
+
onClick: () => setSection(s2.key),
|
|
2926
|
+
title: s2.label,
|
|
2927
|
+
style: {
|
|
2928
|
+
position: "relative",
|
|
2929
|
+
display: "flex",
|
|
2930
|
+
alignItems: "center",
|
|
2931
|
+
justifyContent: collapsed ? "center" : "flex-start",
|
|
2932
|
+
gap: 11,
|
|
2933
|
+
padding: collapsed ? "9px 0" : "9px 10px",
|
|
2934
|
+
borderRadius: 8,
|
|
2935
|
+
border: "none",
|
|
2936
|
+
background: active ? tokens.surface : "transparent",
|
|
2937
|
+
color: active ? tokens.primary : tokens.muted,
|
|
2938
|
+
fontWeight: 600,
|
|
2939
|
+
fontSize: 13,
|
|
2940
|
+
cursor: "pointer",
|
|
2941
|
+
textAlign: "left",
|
|
2942
|
+
width: "100%"
|
|
2943
|
+
},
|
|
2944
|
+
children: [
|
|
2945
|
+
active ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { position: "absolute", left: 0, top: 8, bottom: 8, width: 3, borderRadius: "0 3px 3px 0", background: tokens.primary } }) : null,
|
|
2946
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { width: 18, display: "inline-flex", alignItems: "center", justifyContent: "center", flex: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 16 }) }),
|
|
2947
|
+
collapsed ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { flex: 1 }, children: s2.label }),
|
|
2948
|
+
!collapsed && s2.key === "resources" && displayResources.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 11, fontWeight: 600, color: tokens.faint }, children: displayResources.length }) : null,
|
|
2949
|
+
!collapsed && s2.key === "activity" && provisioning ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { width: 7, height: 7, borderRadius: "50%", background: tokens.info } }) : null
|
|
2950
|
+
]
|
|
2951
|
+
},
|
|
2952
|
+
s2.key
|
|
2953
|
+
);
|
|
2954
|
+
})
|
|
2955
|
+
]
|
|
2956
|
+
}
|
|
2957
|
+
),
|
|
2958
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flex: 1, minWidth: 0, padding: "22px 24px" }, children: content })
|
|
2959
|
+
] })
|
|
2960
|
+
] }),
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2962
|
+
ByolPlanModal,
|
|
2963
|
+
{
|
|
2964
|
+
isOpen: planOpen,
|
|
2965
|
+
onClose: closePlan,
|
|
2966
|
+
plan,
|
|
2967
|
+
loading: planLoading,
|
|
2968
|
+
error: planError,
|
|
2969
|
+
applying,
|
|
2970
|
+
onApply,
|
|
2971
|
+
infraName: infra.name,
|
|
2972
|
+
allowApplyWithoutChanges: infra.status === "failed" || infra.status === "error" || infra.status === "not_started"
|
|
2973
|
+
}
|
|
2974
|
+
),
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2976
|
+
DestroyPlanModal,
|
|
2977
|
+
{
|
|
2978
|
+
isOpen: destroyOpen,
|
|
2979
|
+
onClose: closeDestroy,
|
|
2980
|
+
resources: destroyResources,
|
|
2981
|
+
loading: destroyLoading,
|
|
2982
|
+
error: destroyError,
|
|
2983
|
+
destroying,
|
|
2984
|
+
onConfirm: confirmDestroy,
|
|
2985
|
+
infraName: infra.name
|
|
2986
|
+
}
|
|
2987
|
+
)
|
|
2988
|
+
] });
|
|
2989
|
+
};
|
|
2990
|
+
|
|
2991
|
+
// src/byol/ClusterPlacementField.tsx
|
|
2992
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2993
|
+
var MODE_OPTIONS = [
|
|
2994
|
+
{ value: "single", label: "Single site \u2014 all nodes in the main region" },
|
|
2995
|
+
{ value: "multi-site", label: "Multi-site \u2014 spread nodes by percent" }
|
|
2996
|
+
];
|
|
2997
|
+
var AWS_AZ_LETTERS = ["a", "b", "c", "d", "e", "f"];
|
|
2998
|
+
var GCP_AZ_LETTERS = ["a", "b", "c", "d", "f"];
|
|
2999
|
+
var AZURE_ZONES = ["1", "2", "3"];
|
|
3000
|
+
function azOptionsFor(providerCode, region) {
|
|
3001
|
+
const code = (providerCode || "aws").toLowerCase();
|
|
3002
|
+
const base = region || "region";
|
|
3003
|
+
if (code === "azure") return AZURE_ZONES.map((z) => ({ value: z, label: `Zone ${z}` }));
|
|
3004
|
+
if (code === "gcp") return GCP_AZ_LETTERS.map((l) => ({ value: `${base}-${l}`, label: `${base}-${l}` }));
|
|
3005
|
+
return AWS_AZ_LETTERS.map((l) => ({ value: `${base}${l}`, label: `${base}${l}` }));
|
|
3006
|
+
}
|
|
3007
|
+
function evenPercents(count) {
|
|
3008
|
+
if (count <= 0) return [];
|
|
3009
|
+
const base = Math.floor(100 / count);
|
|
3010
|
+
const remainder = 100 - base * count;
|
|
3011
|
+
return Array.from({ length: count }, (_, i) => base + (i < remainder ? 1 : 0));
|
|
3012
|
+
}
|
|
3013
|
+
function defaultSites(granularity, primaryRegion, providerCode, regionOptions) {
|
|
3014
|
+
const [p0, p1] = evenPercents(2);
|
|
3015
|
+
if (granularity === "az") {
|
|
3016
|
+
const az = azOptionsFor(providerCode, primaryRegion);
|
|
3017
|
+
return [
|
|
3018
|
+
{ site: az[0]?.value ?? `${primaryRegion}a`, percent: p0 },
|
|
3019
|
+
{ site: az[1]?.value ?? `${primaryRegion}b`, percent: p1 }
|
|
3020
|
+
];
|
|
3021
|
+
}
|
|
3022
|
+
const regions = regionOptions.map((o) => o.value).filter(Boolean);
|
|
3023
|
+
const first = primaryRegion || regions[0] || "";
|
|
3024
|
+
const second = regions.find((r) => r !== first) ?? "";
|
|
3025
|
+
return [
|
|
3026
|
+
{ site: first, percent: p0 },
|
|
3027
|
+
{ site: second, percent: p1 }
|
|
3028
|
+
];
|
|
3029
|
+
}
|
|
3030
|
+
var ClusterPlacementField = ({
|
|
3031
|
+
label,
|
|
3032
|
+
placement,
|
|
3033
|
+
nodeCount,
|
|
3034
|
+
primaryRegion,
|
|
3035
|
+
providerCode,
|
|
3036
|
+
regionOptions,
|
|
3037
|
+
onChange
|
|
3038
|
+
}) => {
|
|
3039
|
+
const multi = placement.mode === "multi-site";
|
|
3040
|
+
const granularity = placement.granularity ?? "az";
|
|
3041
|
+
const sites = placement.sites ?? [];
|
|
3042
|
+
const siteOptions = granularity === "az" ? azOptionsFor(providerCode, primaryRegion) : regionOptions;
|
|
3043
|
+
const emit = (next) => onChange({ mode: "multi-site", granularity, sites: next });
|
|
3044
|
+
const setMode = (mode) => {
|
|
3045
|
+
if (mode !== "multi-site") return onChange({ mode: "single" });
|
|
3046
|
+
onChange({
|
|
3047
|
+
mode: "multi-site",
|
|
3048
|
+
granularity,
|
|
3049
|
+
sites: sites.length >= 2 ? sites : defaultSites(granularity, primaryRegion, providerCode, regionOptions)
|
|
3050
|
+
});
|
|
3051
|
+
};
|
|
3052
|
+
const setGranularity = (value) => {
|
|
3053
|
+
const g = value;
|
|
3054
|
+
onChange({ mode: "multi-site", granularity: g, sites: defaultSites(g, primaryRegion, providerCode, regionOptions) });
|
|
3055
|
+
};
|
|
3056
|
+
const updateSite = (i, patch) => emit(sites.map((s2, idx) => idx === i ? { ...s2, ...patch } : s2));
|
|
3057
|
+
const addSite = () => {
|
|
3058
|
+
const used = new Set(sites.map((s2) => s2.site));
|
|
3059
|
+
const nextSite = siteOptions.map((o) => o.value).find((v) => !used.has(v)) ?? "";
|
|
3060
|
+
emit([...sites, { site: nextSite, percent: 0 }]);
|
|
3061
|
+
};
|
|
3062
|
+
const removeSite = (i) => emit(sites.filter((_, idx) => idx !== i));
|
|
3063
|
+
const distributeEvenly = () => {
|
|
3064
|
+
const percents = evenPercents(sites.length);
|
|
3065
|
+
emit(sites.map((s2, i) => ({ ...s2, percent: percents[i] })));
|
|
3066
|
+
};
|
|
3067
|
+
const allocation = multi ? allocateNodesBySite(nodeCount, sites) : [];
|
|
3068
|
+
const validationError = multi ? validatePlacement(placement, nodeCount) : null;
|
|
3069
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
3070
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Select, { label, value: placement.mode, onChange: setMode, options: MODE_OPTIONS }),
|
|
3071
|
+
multi ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3072
|
+
"div",
|
|
3073
|
+
{
|
|
3074
|
+
style: {
|
|
3075
|
+
display: "flex",
|
|
3076
|
+
flexDirection: "column",
|
|
3077
|
+
gap: 10,
|
|
3078
|
+
padding: 12,
|
|
3079
|
+
border: `1px solid ${tokens.border}`,
|
|
3080
|
+
borderRadius: 8,
|
|
3081
|
+
background: tokens.surface2
|
|
3082
|
+
},
|
|
3083
|
+
children: [
|
|
3084
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3085
|
+
Select,
|
|
3086
|
+
{
|
|
3087
|
+
label: "Spread across",
|
|
3088
|
+
value: granularity,
|
|
3089
|
+
onChange: setGranularity,
|
|
3090
|
+
options: [...PLACEMENT_GRANULARITY_OPTIONS]
|
|
3091
|
+
}
|
|
3092
|
+
),
|
|
3093
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: sites.map((site, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 92px auto auto", gap: 8, alignItems: "end" }, children: [
|
|
3094
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3095
|
+
Select,
|
|
3096
|
+
{
|
|
3097
|
+
label: i === 0 ? granularity === "az" ? "Zone" : "Region" : void 0,
|
|
3098
|
+
value: site.site,
|
|
3099
|
+
onChange: (value) => updateSite(i, { site: value }),
|
|
3100
|
+
options: siteOptions,
|
|
3101
|
+
placeholder: "Select\u2026"
|
|
3102
|
+
}
|
|
3103
|
+
),
|
|
3104
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3105
|
+
Input,
|
|
3106
|
+
{
|
|
3107
|
+
label: i === 0 ? "Share %" : void 0,
|
|
3108
|
+
type: "number",
|
|
3109
|
+
min: 0,
|
|
3110
|
+
max: 100,
|
|
3111
|
+
value: String(site.percent),
|
|
3112
|
+
onChange: (e) => updateSite(i, { percent: Number(e.target.value) || 0 }),
|
|
3113
|
+
fullWidth: true
|
|
3114
|
+
}
|
|
3115
|
+
),
|
|
3116
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { style: { fontSize: 12, color: tokens.muted, paddingBottom: 10, whiteSpace: "nowrap" }, children: [
|
|
3117
|
+
"\u2192 ",
|
|
3118
|
+
allocation[i]?.count ?? 0,
|
|
3119
|
+
" node",
|
|
3120
|
+
(allocation[i]?.count ?? 0) === 1 ? "" : "s"
|
|
3121
|
+
] }),
|
|
3122
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3123
|
+
Button,
|
|
3124
|
+
{
|
|
3125
|
+
variant: "ghost",
|
|
3126
|
+
size: "sm",
|
|
3127
|
+
onClick: () => removeSite(i),
|
|
3128
|
+
disabled: sites.length <= 2,
|
|
3129
|
+
"aria-label": `Remove site ${i + 1}`,
|
|
3130
|
+
style: { marginBottom: 4 },
|
|
3131
|
+
children: "\u2715"
|
|
3132
|
+
}
|
|
3133
|
+
)
|
|
3134
|
+
] }, i)) }),
|
|
3135
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
3136
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button, { variant: "secondary", size: "sm", onClick: addSite, disabled: sites.length >= siteOptions.length, children: "Add site" }),
|
|
3137
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Button, { variant: "ghost", size: "sm", onClick: distributeEvenly, children: "Distribute evenly" })
|
|
3138
|
+
] }),
|
|
3139
|
+
validationError ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { style: { margin: 0, fontSize: 12, color: tokens.danger }, children: validationError }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { style: { margin: 0, fontSize: 12, color: tokens.muted }, children: [
|
|
3140
|
+
sites.length,
|
|
3141
|
+
" sites \xB7 ",
|
|
3142
|
+
allocation.reduce((n, a) => n + a.count, 0),
|
|
3143
|
+
" of ",
|
|
3144
|
+
nodeCount,
|
|
3145
|
+
" nodes placed."
|
|
3146
|
+
] })
|
|
3147
|
+
]
|
|
3148
|
+
}
|
|
3149
|
+
) : null
|
|
3150
|
+
] });
|
|
3151
|
+
};
|
|
3152
|
+
|
|
3153
|
+
// src/byol/ByolInfrastructureManager.tsx
|
|
3154
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3155
|
+
var ByolInfrastructureManager = ({
|
|
3156
|
+
apiBase,
|
|
3157
|
+
title = "BYOL Infrastructure",
|
|
3158
|
+
deploymentTypes = DEFAULT_DEPLOYMENT_TYPES,
|
|
3159
|
+
configBase,
|
|
3160
|
+
configLinks,
|
|
3161
|
+
versionOptions = [],
|
|
3162
|
+
defaultVersionId
|
|
3163
|
+
}) => {
|
|
3164
|
+
const [infrastructure, setInfrastructure] = (0, import_react4.useState)([]);
|
|
3165
|
+
const [tags, setTags] = (0, import_react4.useState)([]);
|
|
3166
|
+
const [cloudProviders, setCloudProviders] = (0, import_react4.useState)([]);
|
|
3167
|
+
const [regions, setRegions] = (0, import_react4.useState)([]);
|
|
3168
|
+
const [cloudAccounts, setCloudAccounts] = (0, import_react4.useState)([]);
|
|
3169
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
3170
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
|
|
3171
|
+
const [selectedId, setSelectedId] = (0, import_react4.useState)(() => {
|
|
3172
|
+
if (typeof window === "undefined") return null;
|
|
3173
|
+
return new URLSearchParams(window.location.search).get("infra");
|
|
3174
|
+
});
|
|
3175
|
+
const [reloadSignal, setReloadSignal] = (0, import_react4.useState)(0);
|
|
3176
|
+
const [search, setSearch] = (0, import_react4.useState)("");
|
|
3177
|
+
const [environmentFilter, setEnvironmentFilter] = (0, import_react4.useState)(null);
|
|
3178
|
+
const [deploymentFilter, setDeploymentFilter] = (0, import_react4.useState)(null);
|
|
3179
|
+
const [statusFilter, setStatusFilter] = (0, import_react4.useState)(null);
|
|
3180
|
+
const [sortField, setSortField] = (0, import_react4.useState)("name");
|
|
3181
|
+
const [sortDir, setSortDir] = (0, import_react4.useState)("asc");
|
|
3182
|
+
const [page, setPage] = (0, import_react4.useState)(1);
|
|
3183
|
+
const [pageSize, setPageSize] = (0, import_react4.useState)(10);
|
|
3184
|
+
const [dialogOpen, setDialogOpen] = (0, import_react4.useState)(false);
|
|
3185
|
+
const [editing, setEditing] = (0, import_react4.useState)(null);
|
|
3186
|
+
const [form, setForm] = (0, import_react4.useState)(BLANK_FORM);
|
|
3187
|
+
const [submitting, setSubmitting] = (0, import_react4.useState)(false);
|
|
3188
|
+
const [formError, setFormError] = (0, import_react4.useState)(null);
|
|
3189
|
+
const load = (0, import_react4.useCallback)(() => {
|
|
3190
|
+
setIsLoading(true);
|
|
3191
|
+
setError(null);
|
|
3192
|
+
return authFetch(apiBase).then((res) => res.ok ? res.json() : Promise.reject(new Error(`HTTP ${res.status}`))).then((data) => setInfrastructure(Array.isArray(data) ? data : [])).catch((e) => setError(e.message)).finally(() => setIsLoading(false));
|
|
3193
|
+
}, [apiBase]);
|
|
3194
|
+
(0, import_react4.useEffect)(() => {
|
|
3195
|
+
void load();
|
|
3196
|
+
}, [load]);
|
|
3197
|
+
(0, import_react4.useEffect)(() => {
|
|
3198
|
+
authFetch("/api/tags").then((res) => res.ok ? res.json() : []).then((data) => setTags(Array.isArray(data) ? data : [])).catch(() => setTags([]));
|
|
3199
|
+
authFetch("/api/cloud-providers").then((res) => res.ok ? res.json() : []).then(
|
|
3200
|
+
(data) => setCloudProviders(Array.isArray(data) ? data.filter((c) => c.isActive !== false) : [])
|
|
3201
|
+
).catch(() => setCloudProviders([]));
|
|
3202
|
+
authFetch("/api/cloud-accounts").then((res) => res.ok ? res.json() : []).then((data) => setCloudAccounts(Array.isArray(data) ? data : [])).catch(() => setCloudAccounts([]));
|
|
3203
|
+
}, []);
|
|
3204
|
+
const isCloud = form.providerId !== "" && form.providerId !== SELF_HOSTED;
|
|
3205
|
+
const showRegion = isCloud && form.deploymentType === "distributed";
|
|
3206
|
+
const isByoc = BYOC_NETWORK_MODES.has(form.networkMode);
|
|
3207
|
+
const showCloudAccount = isCloud;
|
|
3208
|
+
const cloudAccountRequired = isByoc && isCloud;
|
|
3209
|
+
(0, import_react4.useEffect)(() => {
|
|
3210
|
+
if (!isCloud) {
|
|
3211
|
+
setRegions([]);
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
let cancelled = false;
|
|
3215
|
+
authFetch(`/api/cloud-providers/${form.providerId}/regions`).then((res) => res.ok ? res.json() : []).then((data) => {
|
|
3216
|
+
if (!cancelled) setRegions(Array.isArray(data) ? data.filter((r) => r.isActive !== false) : []);
|
|
3217
|
+
}).catch(() => {
|
|
3218
|
+
if (!cancelled) setRegions([]);
|
|
3219
|
+
});
|
|
3220
|
+
return () => {
|
|
3221
|
+
cancelled = true;
|
|
3222
|
+
};
|
|
3223
|
+
}, [isCloud, form.providerId]);
|
|
3224
|
+
const openCreate = () => {
|
|
3225
|
+
setEditing(null);
|
|
3226
|
+
setForm({ ...BLANK_FORM, versionId: defaultVersionId ?? "" });
|
|
3227
|
+
setFormError(null);
|
|
3228
|
+
setDialogOpen(true);
|
|
3229
|
+
};
|
|
3230
|
+
const openEdit = (row) => {
|
|
3231
|
+
setEditing(row);
|
|
3232
|
+
setForm(editFormState(row));
|
|
3233
|
+
setFormError(null);
|
|
3234
|
+
setDialogOpen(true);
|
|
3235
|
+
};
|
|
3236
|
+
const closeDialog = (0, import_react4.useCallback)(() => {
|
|
3237
|
+
if (submitting) return;
|
|
3238
|
+
setDialogOpen(false);
|
|
3239
|
+
}, [submitting]);
|
|
3240
|
+
const setField = (key, value) => setForm((prev) => ({ ...prev, [key]: value }));
|
|
3241
|
+
const handleProviderChange = (value) => setForm((prev) => ({ ...prev, providerId: value, cloudAccountConnectionId: "" }));
|
|
3242
|
+
const handleSubmit = async () => {
|
|
3243
|
+
if (!form.name.trim()) {
|
|
3244
|
+
setFormError("Name is required");
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
const indexerCount = Number(form.indexerCount);
|
|
3248
|
+
const searchHeadCount = Number(form.searchHeadCount);
|
|
3249
|
+
if (form.deploymentType === "distributed") {
|
|
3250
|
+
if (indexerCount < 3) {
|
|
3251
|
+
setFormError("Distributed deployments require at least 3 indexers");
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
if (searchHeadCount < 2) {
|
|
3255
|
+
setFormError("Distributed deployments require at least 2 search heads");
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
if (showRegion) {
|
|
3259
|
+
const indexerErr = validatePlacement(form.indexerPlacement, indexerCount);
|
|
3260
|
+
if (indexerErr) {
|
|
3261
|
+
setFormError(`Indexer placement: ${indexerErr}`);
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
const searchErr = validatePlacement(form.searchHeadPlacement, searchHeadCount);
|
|
3265
|
+
if (searchErr) {
|
|
3266
|
+
setFormError(`Search head placement: ${searchErr}`);
|
|
3267
|
+
return;
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
if (cloudAccountRequired && !form.cloudAccountConnectionId) {
|
|
3272
|
+
setFormError("Select a verified cloud account for a BYOC deployment target");
|
|
3273
|
+
return;
|
|
3274
|
+
}
|
|
3275
|
+
const selfHosted = form.providerId === SELF_HOSTED;
|
|
3276
|
+
const selectedCloud = cloudProviders.find((c) => c.id === form.providerId);
|
|
3277
|
+
const distributed = form.deploymentType === "distributed";
|
|
3278
|
+
const normalizePlacement = (p) => distributed && showRegion && p?.mode === "multi-site" ? p : { mode: "single" };
|
|
3279
|
+
setSubmitting(true);
|
|
3280
|
+
setFormError(null);
|
|
3281
|
+
const payload = {
|
|
3282
|
+
name: form.name.trim(),
|
|
3283
|
+
deploymentType: form.deploymentType,
|
|
3284
|
+
environmentType: form.environmentType,
|
|
3285
|
+
hosting_type: selfHosted ? SELF_HOSTED_LABEL : selectedCloud?.name ?? "",
|
|
3286
|
+
cloudProviderId: selfHosted ? void 0 : form.providerId || void 0,
|
|
3287
|
+
region: showRegion ? form.region : "",
|
|
3288
|
+
indexerCount,
|
|
3289
|
+
searchHeadCount,
|
|
3290
|
+
networkMode: form.networkMode,
|
|
3291
|
+
dnsMode: form.dnsMode,
|
|
3292
|
+
cloudAccountConnectionId: showCloudAccount ? form.cloudAccountConnectionId : void 0,
|
|
3293
|
+
// Topology authoring — only meaningful for distributed deployments.
|
|
3294
|
+
controlPlaneLayout: distributed ? form.controlPlaneLayout : "dedicated",
|
|
3295
|
+
heavyForwarderCount: distributed ? Math.max(1, Number(form.heavyForwarderCount) || 1) : 1,
|
|
3296
|
+
// Compute size override; empty → the cloud default (t2.medium-class). Only
|
|
3297
|
+
// meaningful for a cloud deployment.
|
|
3298
|
+
instanceType: !selfHosted ? form.instanceType.trim() || void 0 : void 0,
|
|
3299
|
+
indexerPlacement: normalizePlacement(form.indexerPlacement),
|
|
3300
|
+
searchHeadPlacement: normalizePlacement(form.searchHeadPlacement),
|
|
3301
|
+
versionId: form.versionId || void 0
|
|
3302
|
+
};
|
|
3303
|
+
try {
|
|
3304
|
+
const res = await authFetch(editing ? `${apiBase}/${editing.id}` : apiBase, {
|
|
3305
|
+
method: editing ? "PUT" : "POST",
|
|
3306
|
+
headers: { "Content-Type": "application/json" },
|
|
3307
|
+
body: JSON.stringify(payload)
|
|
3308
|
+
});
|
|
3309
|
+
if (!res.ok) throw new Error(await errorText(res));
|
|
3310
|
+
setDialogOpen(false);
|
|
3311
|
+
await load();
|
|
3312
|
+
if (editing) setReloadSignal((n) => n + 1);
|
|
3313
|
+
} catch (e) {
|
|
3314
|
+
setFormError(e.message);
|
|
3315
|
+
} finally {
|
|
3316
|
+
setSubmitting(false);
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
const openDetail = (id) => {
|
|
3320
|
+
setSelectedId(id);
|
|
3321
|
+
if (typeof window !== "undefined" && id === null) {
|
|
3322
|
+
const params = new URLSearchParams(window.location.search);
|
|
3323
|
+
params.delete("infra");
|
|
3324
|
+
params.delete("section");
|
|
3325
|
+
const qs = params.toString();
|
|
3326
|
+
window.history.replaceState(null, "", qs ? `${window.location.pathname}?${qs}` : window.location.pathname);
|
|
3327
|
+
}
|
|
3328
|
+
};
|
|
3329
|
+
const columns = [
|
|
3330
|
+
{ key: "name", header: "Name", render: (row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: row.name }) },
|
|
3331
|
+
{ key: "deploymentType", header: "Deployment", render: (row) => row.deploymentType ?? "\u2014" },
|
|
3332
|
+
{ key: "environmentType", header: "Environment", render: (row) => row.environmentType ?? "\u2014" },
|
|
3333
|
+
{ key: "hosting_type", header: "Provider", render: (row) => row.hosting_type ?? "\u2014" },
|
|
3334
|
+
{ key: "region", header: "Region", render: (row) => row.region || "\u2014" },
|
|
3335
|
+
{ key: "indexerCount", header: "Indexers", align: "right", render: (row) => row.indexerCount ?? "\u2014" },
|
|
3336
|
+
{ key: "searchHeadCount", header: "Search heads", align: "right", render: (row) => row.searchHeadCount ?? "\u2014" },
|
|
3337
|
+
{ key: "status", header: "Status", render: (row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StatusPill, { status: row.status }) },
|
|
3338
|
+
{ key: "updatedAt", header: "Updated", render: (row) => formatDate(row.updatedAt) },
|
|
3339
|
+
{ key: "chevron", header: "", align: "right", width: "32px", render: () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": true, style: { color: tokens.faint }, children: "\u203A" }) }
|
|
3340
|
+
];
|
|
3341
|
+
const environmentOptions = (0, import_react4.useMemo)(() => tags.map((t) => ({ value: t.name, label: t.name })), [tags]);
|
|
3342
|
+
const providerOptions = (0, import_react4.useMemo)(
|
|
3343
|
+
() => [
|
|
3344
|
+
{ value: SELF_HOSTED, label: SELF_HOSTED_LABEL },
|
|
3345
|
+
...cloudProviders.map((c) => ({ value: c.id, label: c.name }))
|
|
3346
|
+
],
|
|
3347
|
+
[cloudProviders]
|
|
3348
|
+
);
|
|
3349
|
+
const regionOptions = (0, import_react4.useMemo)(
|
|
3350
|
+
() => regions.map((r) => ({ value: r.code, label: `${r.name} (${r.code})` })),
|
|
3351
|
+
[regions]
|
|
3352
|
+
);
|
|
3353
|
+
const selectedProvider = (0, import_react4.useMemo)(
|
|
3354
|
+
() => cloudProviders.find((c) => c.id === form.providerId),
|
|
3355
|
+
[cloudProviders, form.providerId]
|
|
3356
|
+
);
|
|
3357
|
+
const verifiedCloudAccounts = (0, import_react4.useMemo)(
|
|
3358
|
+
() => cloudAccounts.filter(
|
|
3359
|
+
(a) => a.status === "VERIFIED" && (!selectedProvider?.code || a.provider === selectedProvider.code)
|
|
3360
|
+
),
|
|
3361
|
+
[cloudAccounts, selectedProvider]
|
|
3362
|
+
);
|
|
3363
|
+
const cloudAccountOptions = (0, import_react4.useMemo)(
|
|
3364
|
+
() => verifiedCloudAccounts.map((a) => ({
|
|
3365
|
+
value: a.id,
|
|
3366
|
+
label: `${a.name} (${a.provider}${a.scope === "platform" ? " \xB7 Veltrix-managed" : ""})`
|
|
3367
|
+
})),
|
|
3368
|
+
[verifiedCloudAccounts]
|
|
3369
|
+
);
|
|
3370
|
+
const statusFilterOptions = (0, import_react4.useMemo)(() => {
|
|
3371
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3372
|
+
infrastructure.forEach((i) => seen.add(i.status ?? "unknown"));
|
|
3373
|
+
return Array.from(seen, (value) => ({ value, label: value }));
|
|
3374
|
+
}, [infrastructure]);
|
|
3375
|
+
const sortOptions = [
|
|
3376
|
+
{ value: "name", label: "Name" },
|
|
3377
|
+
{ value: "indexerCount", label: "Indexers" },
|
|
3378
|
+
{ value: "searchHeadCount", label: "Search heads" },
|
|
3379
|
+
{ value: "status", label: "Status" },
|
|
3380
|
+
{ value: "updatedAt", label: "Updated" }
|
|
3381
|
+
];
|
|
3382
|
+
const filters = [
|
|
3383
|
+
{ key: "environment", label: "Environment", options: environmentOptions, value: environmentFilter, onChange: setEnvironmentFilter, alwaysVisible: true },
|
|
3384
|
+
{ key: "deployment", label: "Deployment", options: deploymentTypes, value: deploymentFilter, onChange: setDeploymentFilter },
|
|
3385
|
+
{ key: "status", label: "Status", options: statusFilterOptions, value: statusFilter, onChange: setStatusFilter }
|
|
3386
|
+
];
|
|
3387
|
+
const filteredSorted = (0, import_react4.useMemo)(() => {
|
|
3388
|
+
const term = search.trim().toLowerCase();
|
|
3389
|
+
const rows = infrastructure.filter((row) => {
|
|
3390
|
+
if (term) {
|
|
3391
|
+
const haystack = `${row.name ?? ""} ${row.hosting_type ?? ""} ${row.region ?? ""}`.toLowerCase();
|
|
3392
|
+
if (!haystack.includes(term)) return false;
|
|
3393
|
+
}
|
|
3394
|
+
if (environmentFilter && row.environmentType !== environmentFilter) return false;
|
|
3395
|
+
if (deploymentFilter && row.deploymentType !== deploymentFilter) return false;
|
|
3396
|
+
if (statusFilter && (row.status ?? "unknown") !== statusFilter) return false;
|
|
3397
|
+
return true;
|
|
3398
|
+
});
|
|
3399
|
+
const dir = sortDir === "asc" ? 1 : -1;
|
|
3400
|
+
return [...rows].sort((a, b) => {
|
|
3401
|
+
switch (sortField) {
|
|
3402
|
+
case "indexerCount":
|
|
3403
|
+
return ((a.indexerCount ?? 0) - (b.indexerCount ?? 0)) * dir;
|
|
3404
|
+
case "searchHeadCount":
|
|
3405
|
+
return ((a.searchHeadCount ?? 0) - (b.searchHeadCount ?? 0)) * dir;
|
|
3406
|
+
case "status":
|
|
3407
|
+
return (a.status ?? "").localeCompare(b.status ?? "") * dir;
|
|
3408
|
+
case "updatedAt":
|
|
3409
|
+
return (new Date(a.updatedAt ?? 0).getTime() - new Date(b.updatedAt ?? 0).getTime()) * dir;
|
|
3410
|
+
case "name":
|
|
3411
|
+
default:
|
|
3412
|
+
return (a.name ?? "").localeCompare(b.name ?? "") * dir;
|
|
3413
|
+
}
|
|
3414
|
+
});
|
|
3415
|
+
}, [infrastructure, search, environmentFilter, deploymentFilter, statusFilter, sortField, sortDir]);
|
|
3416
|
+
const pageRows = (0, import_react4.useMemo)(
|
|
3417
|
+
() => filteredSorted.slice((page - 1) * pageSize, page * pageSize),
|
|
3418
|
+
[filteredSorted, page, pageSize]
|
|
3419
|
+
);
|
|
3420
|
+
(0, import_react4.useEffect)(() => {
|
|
3421
|
+
setPage(1);
|
|
3422
|
+
}, [search, environmentFilter, deploymentFilter, statusFilter, sortField, sortDir]);
|
|
3423
|
+
const selected = selectedId ? infrastructure.find((i) => i.id === selectedId) ?? null : null;
|
|
3424
|
+
if (selectedId && selected) {
|
|
3425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
3426
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3427
|
+
ByolInfrastructureDetail,
|
|
3428
|
+
{
|
|
3429
|
+
apiBase,
|
|
3430
|
+
initialInfra: selected,
|
|
3431
|
+
configBase,
|
|
3432
|
+
configLinks,
|
|
3433
|
+
reloadSignal,
|
|
3434
|
+
onBack: () => openDetail(null),
|
|
3435
|
+
onEdit: openEdit,
|
|
3436
|
+
onDeleted: () => {
|
|
3437
|
+
openDetail(null);
|
|
3438
|
+
void load();
|
|
3439
|
+
},
|
|
3440
|
+
onChanged: () => void load()
|
|
3441
|
+
}
|
|
3442
|
+
),
|
|
3443
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3444
|
+
FormDialog,
|
|
3445
|
+
{
|
|
3446
|
+
isOpen: dialogOpen,
|
|
3447
|
+
onClose: closeDialog,
|
|
3448
|
+
title: editing ? `Edit "${editing.name}"` : "New BYOL infrastructure",
|
|
3449
|
+
description: "Define the deployment topology for a Bring-Your-Own-License Splunk environment.",
|
|
3450
|
+
onSubmit: handleSubmit,
|
|
3451
|
+
submitText: editing ? "Save changes" : "Create infrastructure",
|
|
3452
|
+
isSubmitting: submitting,
|
|
3453
|
+
submitDisabled: !form.name.trim() || cloudAccountRequired && !form.cloudAccountConnectionId,
|
|
3454
|
+
error: formError,
|
|
3455
|
+
size: "md",
|
|
3456
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3457
|
+
FormBody,
|
|
3458
|
+
{
|
|
3459
|
+
form,
|
|
3460
|
+
setField,
|
|
3461
|
+
onProviderChange: handleProviderChange,
|
|
3462
|
+
deploymentTypes,
|
|
3463
|
+
environmentOptions,
|
|
3464
|
+
providerOptions,
|
|
3465
|
+
regionOptions,
|
|
3466
|
+
showRegion,
|
|
3467
|
+
showCloudAccount,
|
|
3468
|
+
cloudAccountRequired,
|
|
3469
|
+
cloudAccountOptions,
|
|
3470
|
+
selectedProviderName: selectedProvider?.name,
|
|
3471
|
+
providerCode: selectedProvider?.code,
|
|
3472
|
+
versionOptions
|
|
3473
|
+
}
|
|
3474
|
+
)
|
|
3475
|
+
}
|
|
3476
|
+
)
|
|
3477
|
+
] });
|
|
3478
|
+
}
|
|
3479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Card, { variant: "bordered", children: [
|
|
3480
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3481
|
+
CardHeader,
|
|
3482
|
+
{
|
|
3483
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
3484
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "secondary", size: "sm", onClick: () => void load(), isLoading, children: "Refresh" }),
|
|
3485
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Button, { variant: "primary", size: "sm", onClick: openCreate, children: "New infrastructure" })
|
|
3486
|
+
] }),
|
|
3487
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title })
|
|
3488
|
+
}
|
|
3489
|
+
),
|
|
3490
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(CardBody, { children: [
|
|
3491
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { style: { color: tokens.danger }, children: [
|
|
3492
|
+
"Failed to load BYOL infrastructure: ",
|
|
3493
|
+
error
|
|
3494
|
+
] }) : null,
|
|
3495
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center", justifyContent: "space-between", marginBottom: 12 }, children: [
|
|
3496
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3497
|
+
FilterBar,
|
|
3498
|
+
{
|
|
3499
|
+
search: { value: search, onChange: setSearch, placeholder: "Search infrastructure\u2026" },
|
|
3500
|
+
filters
|
|
3501
|
+
}
|
|
3502
|
+
),
|
|
3503
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3504
|
+
SortSelect,
|
|
3505
|
+
{
|
|
3506
|
+
options: sortOptions,
|
|
3507
|
+
value: sortField,
|
|
3508
|
+
direction: sortDir,
|
|
3509
|
+
onChange: (field, dir) => {
|
|
3510
|
+
setSortField(field);
|
|
3511
|
+
setSortDir(dir);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
)
|
|
3515
|
+
] }),
|
|
3516
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3517
|
+
DataTable,
|
|
3518
|
+
{
|
|
3519
|
+
columns,
|
|
3520
|
+
data: pageRows,
|
|
3521
|
+
rowKey: (row) => row.id,
|
|
3522
|
+
isLoading,
|
|
3523
|
+
onRowClick: (row) => openDetail(row.id),
|
|
3524
|
+
emptyState: {
|
|
3525
|
+
title: "No BYOL infrastructure yet",
|
|
3526
|
+
description: "Create a BYOL deployment to manage its topology, resources and lifecycle here."
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
),
|
|
3530
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3531
|
+
Pagination,
|
|
3532
|
+
{
|
|
3533
|
+
page,
|
|
3534
|
+
pageSize,
|
|
3535
|
+
totalItems: filteredSorted.length,
|
|
3536
|
+
onPageChange: setPage,
|
|
3537
|
+
onPageSizeChange: setPageSize,
|
|
3538
|
+
pageSizeOptions: [10, 25, 50]
|
|
3539
|
+
}
|
|
3540
|
+
) })
|
|
3541
|
+
] }),
|
|
3542
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3543
|
+
FormDialog,
|
|
3544
|
+
{
|
|
3545
|
+
isOpen: dialogOpen,
|
|
3546
|
+
onClose: closeDialog,
|
|
3547
|
+
title: editing ? `Edit "${editing.name}"` : "New BYOL infrastructure",
|
|
3548
|
+
description: "Define the deployment topology for a Bring-Your-Own-License Splunk environment.",
|
|
3549
|
+
onSubmit: handleSubmit,
|
|
3550
|
+
submitText: editing ? "Save changes" : "Create infrastructure",
|
|
3551
|
+
isSubmitting: submitting,
|
|
3552
|
+
submitDisabled: !form.name.trim() || cloudAccountRequired && !form.cloudAccountConnectionId,
|
|
3553
|
+
error: formError,
|
|
3554
|
+
size: "md",
|
|
3555
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3556
|
+
FormBody,
|
|
3557
|
+
{
|
|
3558
|
+
form,
|
|
3559
|
+
setField,
|
|
3560
|
+
onProviderChange: handleProviderChange,
|
|
3561
|
+
deploymentTypes,
|
|
3562
|
+
environmentOptions,
|
|
3563
|
+
providerOptions,
|
|
3564
|
+
regionOptions,
|
|
3565
|
+
showRegion,
|
|
3566
|
+
showCloudAccount,
|
|
3567
|
+
cloudAccountRequired,
|
|
3568
|
+
cloudAccountOptions,
|
|
3569
|
+
selectedProviderName: selectedProvider?.name,
|
|
3570
|
+
versionOptions
|
|
3571
|
+
}
|
|
3572
|
+
)
|
|
3573
|
+
}
|
|
3574
|
+
)
|
|
3575
|
+
] });
|
|
3576
|
+
};
|
|
3577
|
+
var FormSection = ({
|
|
3578
|
+
title,
|
|
3579
|
+
description,
|
|
3580
|
+
children
|
|
3581
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
3582
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: { margin: 0, fontSize: 13, fontWeight: 600, color: tokens.text }, children: title }),
|
|
3583
|
+
description ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { style: { margin: "2px 0 0", fontSize: 12, color: tokens.muted }, children: description }) : null,
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { marginTop: 10, display: "flex", flexDirection: "column", gap: 12 }, children })
|
|
3585
|
+
] });
|
|
3586
|
+
var FormBody = ({
|
|
3587
|
+
form,
|
|
3588
|
+
setField,
|
|
3589
|
+
onProviderChange,
|
|
3590
|
+
deploymentTypes,
|
|
3591
|
+
environmentOptions,
|
|
3592
|
+
providerOptions,
|
|
3593
|
+
regionOptions,
|
|
3594
|
+
showRegion,
|
|
3595
|
+
showCloudAccount,
|
|
3596
|
+
cloudAccountRequired,
|
|
3597
|
+
cloudAccountOptions,
|
|
3598
|
+
selectedProviderName,
|
|
3599
|
+
providerCode,
|
|
3600
|
+
versionOptions
|
|
3601
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
3602
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Input, { label: "Name", value: form.name, onChange: (e) => setField("name", e.target.value), placeholder: "e.g. Production cluster", fullWidth: true, autoFocus: true }),
|
|
3603
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
3604
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Select, { label: "Deployment type", value: form.deploymentType, onChange: (value) => setField("deploymentType", value), options: deploymentTypes }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Select, { label: "Environment", value: form.environmentType, onChange: (value) => setField("environmentType", value), options: environmentOptions })
|
|
3606
|
+
] }),
|
|
3607
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "grid", gridTemplateColumns: showRegion ? "1fr 1fr" : "1fr", gap: 12 }, children: [
|
|
3608
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Select, { label: "Provider", value: form.providerId, onChange: onProviderChange, options: providerOptions }),
|
|
3609
|
+
showRegion ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Select, { label: "Region", value: form.region, onChange: (value) => setField("region", value), options: regionOptions }) : null
|
|
3610
|
+
] }),
|
|
3611
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(FormSection, { title: "Deployment target", children: [
|
|
3612
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
3613
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3614
|
+
Select,
|
|
3615
|
+
{
|
|
3616
|
+
label: "Network",
|
|
3617
|
+
value: form.networkMode,
|
|
3618
|
+
onChange: (value) => setField("networkMode", value),
|
|
3619
|
+
options: NETWORK_MODE_OPTIONS
|
|
3620
|
+
}
|
|
3621
|
+
),
|
|
3622
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Select, { label: "DNS", value: form.dnsMode, onChange: (value) => setField("dnsMode", value), options: DNS_MODE_OPTIONS })
|
|
3623
|
+
] }),
|
|
3624
|
+
showCloudAccount ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
3625
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3626
|
+
Select,
|
|
3627
|
+
{
|
|
3628
|
+
label: cloudAccountRequired ? "Cloud account *" : "Cloud account",
|
|
3629
|
+
value: form.cloudAccountConnectionId,
|
|
3630
|
+
onChange: (value) => setField("cloudAccountConnectionId", value),
|
|
3631
|
+
options: cloudAccountOptions,
|
|
3632
|
+
placeholder: cloudAccountOptions.length ? "Select a verified cloud account\u2026" : cloudAccountRequired ? "No verified cloud accounts" : "None \u2014 use the default provisioning identity",
|
|
3633
|
+
disabled: cloudAccountOptions.length === 0,
|
|
3634
|
+
helperText: cloudAccountRequired ? "Required for a dedicated or existing-network (BYOC) deployment." : "Optional \u2014 the Veltrix-managed account this hosted deployment provisions through."
|
|
3635
|
+
}
|
|
3636
|
+
),
|
|
3637
|
+
cloudAccountRequired && cloudAccountOptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Alert, { variant: "warning", title: "No verified cloud account available", children: selectedProviderName ? `No verified ${selectedProviderName} cloud account found. Register and verify a cloud account first in Settings \u2192 Cloud Accounts.` : "Register and verify a cloud account first in Settings \u2192 Cloud Accounts." }) : null
|
|
3638
|
+
] }) : null
|
|
3639
|
+
] }),
|
|
3640
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3641
|
+
FormSection,
|
|
3642
|
+
{
|
|
3643
|
+
title: "Topology",
|
|
3644
|
+
description: form.deploymentType === "distributed" ? "Distributed deployments need at least 3 indexers and 2 search heads." : "Number of indexer and search-head nodes to provision.",
|
|
3645
|
+
children: [
|
|
3646
|
+
versionOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3647
|
+
Select,
|
|
3648
|
+
{
|
|
3649
|
+
label: "Splunk version",
|
|
3650
|
+
value: form.versionId,
|
|
3651
|
+
onChange: (value) => setField("versionId", value),
|
|
3652
|
+
options: versionOptions,
|
|
3653
|
+
placeholder: "Use the app's default version",
|
|
3654
|
+
helperText: "The Splunk release installed on every node. Leave unset to use the app's default installer."
|
|
3655
|
+
}
|
|
3656
|
+
) : null,
|
|
3657
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
3658
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Input, { label: "Indexers", type: "number", min: 1, value: form.indexerCount, onChange: (e) => setField("indexerCount", e.target.value), fullWidth: true }),
|
|
3659
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Input, { label: "Search heads", type: "number", min: 1, value: form.searchHeadCount, onChange: (e) => setField("searchHeadCount", e.target.value), fullWidth: true })
|
|
3660
|
+
] }),
|
|
3661
|
+
form.providerId && form.providerId !== SELF_HOSTED ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3662
|
+
Input,
|
|
3663
|
+
{
|
|
3664
|
+
label: "Compute size (instance type)",
|
|
3665
|
+
value: form.instanceType,
|
|
3666
|
+
onChange: (e) => setField("instanceType", e.target.value),
|
|
3667
|
+
placeholder: INSTANCE_TYPE_EXAMPLES.aws,
|
|
3668
|
+
fullWidth: true,
|
|
3669
|
+
helperText: `Leave blank for the cloud default (~2 vCPU / 4 GB). Examples: AWS ${INSTANCE_TYPE_EXAMPLES.aws}, Azure ${INSTANCE_TYPE_EXAMPLES.azure}, GCP ${INSTANCE_TYPE_EXAMPLES.gcp}, Hetzner ${INSTANCE_TYPE_EXAMPLES.hetzner}. Applies to every node; you can change it here later.`
|
|
3670
|
+
}
|
|
3671
|
+
) : null,
|
|
3672
|
+
showRegion ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
3673
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3674
|
+
ClusterPlacementField,
|
|
3675
|
+
{
|
|
3676
|
+
label: "Indexer cluster placement",
|
|
3677
|
+
placement: form.indexerPlacement,
|
|
3678
|
+
nodeCount: Math.max(1, Number(form.indexerCount) || 1),
|
|
3679
|
+
primaryRegion: form.region,
|
|
3680
|
+
providerCode,
|
|
3681
|
+
regionOptions,
|
|
3682
|
+
onChange: (p) => setField("indexerPlacement", p)
|
|
3683
|
+
}
|
|
3684
|
+
),
|
|
3685
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3686
|
+
ClusterPlacementField,
|
|
3687
|
+
{
|
|
3688
|
+
label: "Search head cluster placement",
|
|
3689
|
+
placement: form.searchHeadPlacement,
|
|
3690
|
+
nodeCount: Math.max(1, Number(form.searchHeadCount) || 1),
|
|
3691
|
+
primaryRegion: form.region,
|
|
3692
|
+
providerCode,
|
|
3693
|
+
regionOptions,
|
|
3694
|
+
onChange: (p) => setField("searchHeadPlacement", p)
|
|
3695
|
+
}
|
|
3696
|
+
)
|
|
3697
|
+
] }) : null
|
|
3698
|
+
]
|
|
3699
|
+
}
|
|
3700
|
+
),
|
|
3701
|
+
form.deploymentType === "distributed" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
3702
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3703
|
+
FormSection,
|
|
3704
|
+
{
|
|
3705
|
+
title: "Control plane",
|
|
3706
|
+
description: "How many instances the five management roles run on \u2014 fewer instances cut cost, more give isolation and HA.",
|
|
3707
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3708
|
+
Select,
|
|
3709
|
+
{
|
|
3710
|
+
label: "Consolidation",
|
|
3711
|
+
value: form.controlPlaneLayout,
|
|
3712
|
+
onChange: (value) => setField("controlPlaneLayout", value),
|
|
3713
|
+
options: CONTROL_PLANE_LAYOUT_OPTIONS.map((o) => ({ value: o.value, label: `${o.label} \u2014 ${o.description}` }))
|
|
3714
|
+
}
|
|
3715
|
+
)
|
|
3716
|
+
}
|
|
3717
|
+
),
|
|
3718
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3719
|
+
FormSection,
|
|
3720
|
+
{
|
|
3721
|
+
title: "Ingest",
|
|
3722
|
+
description: "Heavy forwarders for ingest routing. One is provisioned by default; add more for higher throughput.",
|
|
3723
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3724
|
+
Input,
|
|
3725
|
+
{
|
|
3726
|
+
label: "Heavy forwarders",
|
|
3727
|
+
type: "number",
|
|
3728
|
+
min: 1,
|
|
3729
|
+
value: form.heavyForwarderCount,
|
|
3730
|
+
onChange: (e) => setField("heavyForwarderCount", e.target.value),
|
|
3731
|
+
fullWidth: true
|
|
3732
|
+
}
|
|
3733
|
+
)
|
|
3734
|
+
}
|
|
3735
|
+
)
|
|
3736
|
+
] }) : null
|
|
3737
|
+
] });
|
|
3738
|
+
var ByolInfrastructureManager_default = ByolInfrastructureManager;
|
|
3739
|
+
|
|
3740
|
+
// src/byol/tags.ts
|
|
3741
|
+
var BYOL_TAG_KEYS = [
|
|
3742
|
+
"Veltrix:Customer",
|
|
3743
|
+
"Veltrix:Environment",
|
|
3744
|
+
"Veltrix:EnvName",
|
|
3745
|
+
"Veltrix:EnvType",
|
|
3746
|
+
"Veltrix:App",
|
|
3747
|
+
"Veltrix:ManagedBy",
|
|
3748
|
+
"CostCenter",
|
|
3749
|
+
"Owner"
|
|
3750
|
+
];
|
|
3751
|
+
var MANAGED_BY = "Veltrix";
|
|
3752
|
+
function s(value) {
|
|
3753
|
+
return String(value ?? "").trim();
|
|
3754
|
+
}
|
|
3755
|
+
function buildByolTags(input) {
|
|
3756
|
+
const customerId = s(input.customerId);
|
|
3757
|
+
const customerLabel = s(input.customerShortName) || customerId;
|
|
3758
|
+
const costCenter = s(input.costCenter) || customerLabel;
|
|
3759
|
+
const owner = s(input.owner) || customerLabel;
|
|
3760
|
+
return {
|
|
3761
|
+
"Veltrix:Customer": customerLabel,
|
|
3762
|
+
"Veltrix:Environment": s(input.infrastructureId),
|
|
3763
|
+
"Veltrix:EnvName": s(input.name),
|
|
3764
|
+
"Veltrix:EnvType": s(input.environmentType),
|
|
3765
|
+
"Veltrix:App": s(input.appId),
|
|
3766
|
+
"Veltrix:ManagedBy": MANAGED_BY,
|
|
3767
|
+
CostCenter: costCenter,
|
|
3768
|
+
Owner: owner
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3772
|
+
0 && (module.exports = {
|
|
3773
|
+
BYOL_TAG_KEYS,
|
|
3774
|
+
ByolInfrastructureDetail,
|
|
3775
|
+
ByolInfrastructureManager,
|
|
3776
|
+
ByolPlanModal,
|
|
3777
|
+
CONTROL_PLANE_LAYOUT_OPTIONS,
|
|
3778
|
+
DEPLOYMENT_STEPS,
|
|
3779
|
+
DestroyPlanModal,
|
|
3780
|
+
MANAGED_BY,
|
|
3781
|
+
MIN_HEAVY_FORWARDERS,
|
|
3782
|
+
PLACEMENT_GRANULARITY_OPTIONS,
|
|
3783
|
+
SINGLE_SITE_PLACEMENT,
|
|
3784
|
+
TIER_LABELS,
|
|
3785
|
+
TIER_ORDER,
|
|
3786
|
+
allocateNodesBySite,
|
|
3787
|
+
buildByolPlan,
|
|
3788
|
+
buildByolResourcePlan,
|
|
3789
|
+
buildByolTags,
|
|
3790
|
+
diffPlan,
|
|
3791
|
+
effectivePlacement,
|
|
3792
|
+
isNotStarted,
|
|
3793
|
+
isRunning,
|
|
3794
|
+
planHasChanges,
|
|
3795
|
+
resourceStatusLabel,
|
|
3796
|
+
resourceStatusVariant,
|
|
3797
|
+
statusLabel,
|
|
3798
|
+
statusVariant,
|
|
3799
|
+
stepStatusVariant,
|
|
3800
|
+
validatePlacement
|
|
3801
|
+
});
|
|
3802
|
+
//# sourceMappingURL=index.cjs.map
|