@replicated/portal-components 0.0.2 → 0.0.3

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.
Files changed (216) hide show
  1. package/components/metadata/registry.json +83 -2
  2. package/components/metadata/registry.md +27 -2
  3. package/dist/actions/index.d.mts +566 -3
  4. package/dist/actions/index.d.ts +566 -3
  5. package/dist/actions/index.js +1853 -12
  6. package/dist/actions/index.js.map +1 -1
  7. package/dist/airgap-instances.d.mts +26 -0
  8. package/dist/airgap-instances.d.ts +26 -0
  9. package/dist/airgap-instances.js +354 -0
  10. package/dist/airgap-instances.js.map +1 -0
  11. package/dist/error-page.d.mts +14 -0
  12. package/dist/error-page.d.ts +14 -0
  13. package/dist/error-page.js +153 -0
  14. package/dist/error-page.js.map +1 -0
  15. package/dist/error.d.mts +15 -0
  16. package/dist/error.d.ts +15 -0
  17. package/dist/error.js +144 -0
  18. package/dist/error.js.map +1 -0
  19. package/dist/esm/actions/index.js +1816 -13
  20. package/dist/esm/actions/index.js.map +1 -1
  21. package/dist/esm/airgap-instances.js +352 -0
  22. package/dist/esm/airgap-instances.js.map +1 -0
  23. package/dist/esm/error-page.js +151 -0
  24. package/dist/esm/error-page.js.map +1 -0
  25. package/dist/esm/error.js +142 -0
  26. package/dist/esm/error.js.map +1 -0
  27. package/dist/esm/helm-install-wizard.js +1007 -0
  28. package/dist/esm/helm-install-wizard.js.map +1 -0
  29. package/dist/esm/index.js +2232 -155
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/install-actions.js +746 -0
  32. package/dist/esm/install-actions.js.map +1 -0
  33. package/dist/esm/install-card.js +115 -0
  34. package/dist/esm/install-card.js.map +1 -0
  35. package/dist/esm/install-targets.js +48 -0
  36. package/dist/esm/install-targets.js.map +1 -0
  37. package/dist/esm/instance-card.js +197 -0
  38. package/dist/esm/instance-card.js.map +1 -0
  39. package/dist/esm/join-team.js +218 -0
  40. package/dist/esm/join-team.js.map +1 -0
  41. package/dist/esm/license-card.js +131 -0
  42. package/dist/esm/license-card.js.map +1 -0
  43. package/dist/esm/license-details.js +667 -0
  44. package/dist/esm/license-details.js.map +1 -0
  45. package/dist/esm/linux-install-wizard.js +1083 -0
  46. package/dist/esm/linux-install-wizard.js.map +1 -0
  47. package/dist/esm/login.js +261 -0
  48. package/dist/esm/login.js.map +1 -0
  49. package/dist/esm/online-instance-list.js +287 -0
  50. package/dist/esm/online-instance-list.js.map +1 -0
  51. package/dist/esm/pending-installations.js +235 -0
  52. package/dist/esm/pending-installations.js.map +1 -0
  53. package/dist/esm/release-history-panel.js +100 -0
  54. package/dist/esm/release-history-panel.js.map +1 -0
  55. package/dist/esm/release-notes-card.js +23 -0
  56. package/dist/esm/release-notes-card.js.map +1 -0
  57. package/dist/esm/security-card.js +700 -0
  58. package/dist/esm/security-card.js.map +1 -0
  59. package/dist/esm/support-bundle-collection-card.js +170 -0
  60. package/dist/esm/support-bundle-collection-card.js.map +1 -0
  61. package/dist/esm/support-bundles-card.js +306 -0
  62. package/dist/esm/support-bundles-card.js.map +1 -0
  63. package/dist/esm/support-card.js +305 -0
  64. package/dist/esm/support-card.js.map +1 -0
  65. package/dist/esm/team-selection.js +117 -0
  66. package/dist/esm/team-selection.js.map +1 -0
  67. package/dist/esm/team-settings-card.js +78 -0
  68. package/dist/esm/team-settings-card.js.map +1 -0
  69. package/dist/esm/team-settings.js +136 -0
  70. package/dist/esm/team-settings.js.map +1 -0
  71. package/dist/esm/top-nav-user-menu.js +173 -0
  72. package/dist/esm/top-nav-user-menu.js.map +1 -0
  73. package/dist/esm/top-nav.js +398 -0
  74. package/dist/esm/top-nav.js.map +1 -0
  75. package/dist/esm/update-layout.js +405 -0
  76. package/dist/esm/update-layout.js.map +1 -0
  77. package/dist/esm/updates-card.js +85 -0
  78. package/dist/esm/updates-card.js.map +1 -0
  79. package/dist/esm/upload-support-bundle-modal.js +143 -0
  80. package/dist/esm/upload-support-bundle-modal.js.map +1 -0
  81. package/dist/esm/user-settings-card.js +21 -0
  82. package/dist/esm/user-settings-card.js.map +1 -0
  83. package/dist/esm/user-settings.js +368 -0
  84. package/dist/esm/user-settings.js.map +1 -0
  85. package/dist/esm/utils/index.js +170 -0
  86. package/dist/esm/utils/index.js.map +1 -0
  87. package/dist/helm-install-wizard.d.mts +38 -0
  88. package/dist/helm-install-wizard.d.ts +38 -0
  89. package/dist/helm-install-wizard.js +1011 -0
  90. package/dist/helm-install-wizard.js.map +1 -0
  91. package/dist/index.d.mts +11 -27
  92. package/dist/index.d.ts +11 -27
  93. package/dist/index.js +2258 -154
  94. package/dist/index.js.map +1 -1
  95. package/dist/install-B19AaKF_.d.mts +233 -0
  96. package/dist/install-Bi1qJ8Bu.d.ts +233 -0
  97. package/dist/install-actions.d.mts +141 -0
  98. package/dist/install-actions.d.ts +141 -0
  99. package/dist/install-actions.js +765 -0
  100. package/dist/install-actions.js.map +1 -0
  101. package/dist/install-card.d.mts +15 -0
  102. package/dist/install-card.d.ts +15 -0
  103. package/dist/install-card.js +117 -0
  104. package/dist/install-card.js.map +1 -0
  105. package/dist/install-targets.d.mts +19 -0
  106. package/dist/install-targets.d.ts +19 -0
  107. package/dist/install-targets.js +50 -0
  108. package/dist/install-targets.js.map +1 -0
  109. package/dist/instance-card.d.mts +22 -0
  110. package/dist/instance-card.d.ts +22 -0
  111. package/dist/instance-card.js +199 -0
  112. package/dist/instance-card.js.map +1 -0
  113. package/dist/join-team.d.mts +30 -0
  114. package/dist/join-team.d.ts +30 -0
  115. package/dist/join-team.js +220 -0
  116. package/dist/join-team.js.map +1 -0
  117. package/dist/license-card.d.mts +15 -0
  118. package/dist/license-card.d.ts +15 -0
  119. package/dist/license-card.js +133 -0
  120. package/dist/license-card.js.map +1 -0
  121. package/dist/license-details.d.mts +10 -0
  122. package/dist/license-details.d.ts +10 -0
  123. package/dist/license-details.js +669 -0
  124. package/dist/license-details.js.map +1 -0
  125. package/dist/linux-install-wizard.d.mts +66 -0
  126. package/dist/linux-install-wizard.d.ts +66 -0
  127. package/dist/linux-install-wizard.js +1093 -0
  128. package/dist/linux-install-wizard.js.map +1 -0
  129. package/dist/login.d.mts +37 -0
  130. package/dist/login.d.ts +37 -0
  131. package/dist/login.js +263 -0
  132. package/dist/login.js.map +1 -0
  133. package/dist/online-instance-list.d.mts +22 -0
  134. package/dist/online-instance-list.d.ts +22 -0
  135. package/dist/online-instance-list.js +289 -0
  136. package/dist/online-instance-list.js.map +1 -0
  137. package/dist/pending-installations.d.mts +15 -0
  138. package/dist/pending-installations.d.ts +15 -0
  139. package/dist/pending-installations.js +237 -0
  140. package/dist/pending-installations.js.map +1 -0
  141. package/dist/release-history-panel.d.mts +22 -0
  142. package/dist/release-history-panel.d.ts +22 -0
  143. package/dist/release-history-panel.js +102 -0
  144. package/dist/release-history-panel.js.map +1 -0
  145. package/dist/release-notes-card.d.mts +13 -0
  146. package/dist/release-notes-card.d.ts +13 -0
  147. package/dist/release-notes-card.js +25 -0
  148. package/dist/release-notes-card.js.map +1 -0
  149. package/dist/security-card.d.mts +73 -0
  150. package/dist/security-card.d.ts +73 -0
  151. package/dist/security-card.js +702 -0
  152. package/dist/security-card.js.map +1 -0
  153. package/dist/styles.css +1877 -194
  154. package/dist/support-bundle-collection-card.d.mts +20 -0
  155. package/dist/support-bundle-collection-card.d.ts +20 -0
  156. package/dist/support-bundle-collection-card.js +172 -0
  157. package/dist/support-bundle-collection-card.js.map +1 -0
  158. package/dist/support-bundles-card.d.mts +19 -0
  159. package/dist/support-bundles-card.d.ts +19 -0
  160. package/dist/support-bundles-card.js +308 -0
  161. package/dist/support-bundles-card.js.map +1 -0
  162. package/dist/support-card.d.mts +8 -0
  163. package/dist/support-card.d.ts +8 -0
  164. package/dist/support-card.js +307 -0
  165. package/dist/support-card.js.map +1 -0
  166. package/dist/team-selection.d.mts +23 -0
  167. package/dist/team-selection.d.ts +23 -0
  168. package/dist/team-selection.js +119 -0
  169. package/dist/team-selection.js.map +1 -0
  170. package/dist/team-settings-card-Dq1d9b5c.d.mts +14 -0
  171. package/dist/team-settings-card-Dq1d9b5c.d.ts +14 -0
  172. package/dist/team-settings-card.d.mts +2 -0
  173. package/dist/team-settings-card.d.ts +2 -0
  174. package/dist/team-settings-card.js +80 -0
  175. package/dist/team-settings-card.js.map +1 -0
  176. package/dist/team-settings.d.mts +25 -0
  177. package/dist/team-settings.d.ts +25 -0
  178. package/dist/team-settings.js +138 -0
  179. package/dist/team-settings.js.map +1 -0
  180. package/dist/top-nav-0mb1K_H0.d.mts +32 -0
  181. package/dist/top-nav-0mb1K_H0.d.ts +32 -0
  182. package/dist/top-nav-user-menu.d.mts +18 -0
  183. package/dist/top-nav-user-menu.d.ts +18 -0
  184. package/dist/top-nav-user-menu.js +175 -0
  185. package/dist/top-nav-user-menu.js.map +1 -0
  186. package/dist/top-nav.d.mts +3 -0
  187. package/dist/top-nav.d.ts +3 -0
  188. package/dist/top-nav.js +400 -0
  189. package/dist/top-nav.js.map +1 -0
  190. package/dist/update-layout.d.mts +12 -0
  191. package/dist/update-layout.d.ts +12 -0
  192. package/dist/update-layout.js +407 -0
  193. package/dist/update-layout.js.map +1 -0
  194. package/dist/updates-card-BbubBrVR.d.mts +18 -0
  195. package/dist/updates-card-BbubBrVR.d.ts +18 -0
  196. package/dist/updates-card.d.mts +2 -0
  197. package/dist/updates-card.d.ts +2 -0
  198. package/dist/updates-card.js +87 -0
  199. package/dist/updates-card.js.map +1 -0
  200. package/dist/upload-support-bundle-modal.d.mts +19 -0
  201. package/dist/upload-support-bundle-modal.d.ts +19 -0
  202. package/dist/upload-support-bundle-modal.js +145 -0
  203. package/dist/upload-support-bundle-modal.js.map +1 -0
  204. package/dist/user-settings-card.d.mts +8 -0
  205. package/dist/user-settings-card.d.ts +8 -0
  206. package/dist/user-settings-card.js +23 -0
  207. package/dist/user-settings-card.js.map +1 -0
  208. package/dist/user-settings.d.mts +47 -0
  209. package/dist/user-settings.d.ts +47 -0
  210. package/dist/user-settings.js +370 -0
  211. package/dist/user-settings.js.map +1 -0
  212. package/dist/utils/index.d.mts +70 -0
  213. package/dist/utils/index.d.ts +70 -0
  214. package/dist/utils/index.js +177 -0
  215. package/dist/utils/index.js.map +1 -0
  216. package/package.json +163 -3
@@ -0,0 +1,746 @@
1
+ import { cache } from 'react';
2
+
3
+ /**
4
+ * Enterprise Portal Components
5
+ * This file is generated by tsup. Do not edit manually.
6
+ */
7
+
8
+ // src/utils/api-client.ts
9
+ async function authenticatedFetch(url, options = {}) {
10
+ const { token, ...fetchOptions } = options;
11
+ const headers = new Headers(fetchOptions.headers);
12
+ if (token) {
13
+ headers.set("authorization", `Bearer ${token}`);
14
+ }
15
+ const response = await fetch(url, {
16
+ ...fetchOptions,
17
+ headers
18
+ });
19
+ if (response.status === 401) {
20
+ await handle401();
21
+ }
22
+ if (response.status === 502 || response.status === 503 || response.status === 504) {
23
+ await handleServerError(response.status);
24
+ }
25
+ return response;
26
+ }
27
+ async function handle401() {
28
+ const { redirect } = await import('next/navigation');
29
+ return redirect("/?expired=1");
30
+ }
31
+ async function handleServerError(statusCode) {
32
+ const { redirect } = await import('next/navigation');
33
+ let sourceUrl;
34
+ try {
35
+ const { headers } = await import('next/headers');
36
+ const headersList = await headers();
37
+ const referer = headersList.get("referer");
38
+ const host = headersList.get("host");
39
+ const pathname = headersList.get("x-invoke-path") || headersList.get("x-forwarded-path");
40
+ if (referer) {
41
+ sourceUrl = referer;
42
+ } else if (host && pathname) {
43
+ const protocol = headersList.get("x-forwarded-proto") || "https";
44
+ sourceUrl = `${protocol}://${host}${pathname}`;
45
+ }
46
+ } catch (error) {
47
+ console.debug("[portal-components] Could not determine source URL", error);
48
+ }
49
+ const params = new URLSearchParams({ code: String(statusCode) });
50
+ if (sourceUrl) {
51
+ params.set("source", sourceUrl);
52
+ }
53
+ return redirect(`/error?${params.toString()}`);
54
+ }
55
+ var getApiOrigin = () => {
56
+ return (process.env.REPLICATED_APP_ORIGIN || "https://replicated.app").replace(/\/+$/, "");
57
+ };
58
+ var fetchCustomBrandingImpl = async () => {
59
+ const appSlug = process.env.PORTAL_APP_SLUG;
60
+ if (!appSlug) {
61
+ throw new Error("PORTAL_APP_SLUG is not configured");
62
+ }
63
+ const url = `${getApiOrigin()}/v3/custom-branding?app_slug=${encodeURIComponent(
64
+ appSlug
65
+ )}`;
66
+ if (process.env.NODE_ENV !== "production") {
67
+ console.debug(
68
+ "[portal-components] fetching custom branding via %s",
69
+ url
70
+ );
71
+ }
72
+ const response = await fetch(url, {
73
+ headers: {
74
+ accept: "application/json"
75
+ }
76
+ });
77
+ if (!response.ok) {
78
+ throw new Error(
79
+ `Custom branding request failed (${response.status} ${response.statusText})`
80
+ );
81
+ }
82
+ const payload = await response.json();
83
+ const brandingData = payload?.branding_data;
84
+ if (typeof brandingData !== "string") {
85
+ throw new Error("Custom branding response missing branding_data string");
86
+ }
87
+ return {
88
+ brandingData,
89
+ documentation: payload?.documentation ?? null
90
+ };
91
+ };
92
+ cache(fetchCustomBrandingImpl);
93
+ var decodeJwtPayload = (token) => {
94
+ const parts = token.split(".");
95
+ if (parts.length !== 3) {
96
+ throw new Error("Invalid JWT received");
97
+ }
98
+ const payloadSegment = parts[1];
99
+ if (!payloadSegment) {
100
+ throw new Error("JWT payload segment missing");
101
+ }
102
+ const padded = payloadSegment.padEnd(
103
+ payloadSegment.length + (4 - payloadSegment.length % 4) % 4,
104
+ "="
105
+ );
106
+ const decoded = Buffer.from(padded, "base64").toString("utf-8");
107
+ return JSON.parse(decoded);
108
+ };
109
+ var getCustomerIdFromToken = (token) => {
110
+ const payload = decodeJwtPayload(token);
111
+ const customerId = payload?.customer_id || payload?.customerId;
112
+ if (typeof customerId !== "string" || !customerId.trim()) {
113
+ throw new Error("Unable to determine customer_id from session token");
114
+ }
115
+ return customerId.trim();
116
+ };
117
+
118
+ // src/actions/install.ts
119
+ async function fetchChannelReleases(input, context) {
120
+ const { token, channelId } = input;
121
+ if (!token || typeof token !== "string") {
122
+ throw new Error("fetchChannelReleases requires a session token");
123
+ }
124
+ const customerId = getCustomerIdFromToken(token);
125
+ const origin = getApiOrigin();
126
+ const url = new URL(`${origin}/v3/channel-releases`);
127
+ url.searchParams.set("customer_id", customerId);
128
+ if (channelId) {
129
+ url.searchParams.set("channel_id", channelId);
130
+ }
131
+ if (process.env.NODE_ENV !== "production") {
132
+ console.debug("[portal-components] fetching channel releases via %s", url.toString());
133
+ }
134
+ const response = await authenticatedFetch(url.toString(), {
135
+ method: "GET",
136
+ token,
137
+ headers: {
138
+ accept: "application/json"
139
+ },
140
+ signal: context?.signal
141
+ });
142
+ if (!response.ok) {
143
+ const errorText = await response.text();
144
+ throw new Error(
145
+ `Channel releases request failed (${response.status} ${response.statusText}): ${errorText}`
146
+ );
147
+ }
148
+ const payload = await response.json();
149
+ return {
150
+ channelReleases: payload.channelReleases || []
151
+ };
152
+ }
153
+ async function createInstallOptions(input, context) {
154
+ const {
155
+ token,
156
+ installType,
157
+ instanceName,
158
+ serviceAccountId,
159
+ networkAvailability,
160
+ isMultiNode = false,
161
+ channelId,
162
+ channelReleaseSequence,
163
+ registryAvailability,
164
+ kubernetesDistribution
165
+ } = input;
166
+ if (!token || typeof token !== "string") {
167
+ throw new Error("createInstallOptions requires a session token");
168
+ }
169
+ if (!instanceName?.trim()) {
170
+ throw new Error("Instance name is required");
171
+ }
172
+ if (!serviceAccountId?.trim()) {
173
+ throw new Error("Service account ID is required");
174
+ }
175
+ const customerId = getCustomerIdFromToken(token);
176
+ const origin = getApiOrigin();
177
+ const endpoint = `${origin}/v3/customers/${customerId}/install-options?includeInstructions=true`;
178
+ const body = {
179
+ install_type: installType,
180
+ instance_name: instanceName.trim(),
181
+ service_account_id: serviceAccountId.trim(),
182
+ network_availability: networkAvailability,
183
+ is_multi_node: isMultiNode
184
+ };
185
+ if (channelId) {
186
+ body.channel_id = channelId;
187
+ }
188
+ if (channelReleaseSequence !== void 0) {
189
+ body.channel_release_sequence = channelReleaseSequence;
190
+ }
191
+ if (registryAvailability) {
192
+ body.registry_availability = registryAvailability;
193
+ }
194
+ if (kubernetesDistribution) {
195
+ body.kubernetes_distribution = kubernetesDistribution;
196
+ }
197
+ if (process.env.NODE_ENV !== "production") {
198
+ console.debug("[portal-components] creating install options via %s", endpoint);
199
+ }
200
+ const response = await authenticatedFetch(endpoint, {
201
+ method: "POST",
202
+ token,
203
+ headers: {
204
+ "content-type": "application/json",
205
+ accept: "application/json"
206
+ },
207
+ body: JSON.stringify(body),
208
+ signal: context?.signal
209
+ });
210
+ if (!response.ok) {
211
+ const errorText = await response.text();
212
+ throw new Error(
213
+ `Create install options failed (${response.status} ${response.statusText}): ${errorText}`
214
+ );
215
+ }
216
+ return await response.json();
217
+ }
218
+ async function getInstallOptions(input, context) {
219
+ const {
220
+ token,
221
+ installOptionsId,
222
+ includeInstructions = true,
223
+ privateRegistryHostname,
224
+ proxyUrl
225
+ } = input;
226
+ if (!token || typeof token !== "string") {
227
+ throw new Error("getInstallOptions requires a session token");
228
+ }
229
+ if (!installOptionsId?.trim()) {
230
+ throw new Error("Install options ID is required");
231
+ }
232
+ const customerId = getCustomerIdFromToken(token);
233
+ const origin = getApiOrigin();
234
+ const url = new URL(`${origin}/v3/customers/${customerId}/install-options/${installOptionsId.trim()}`);
235
+ if (includeInstructions) {
236
+ url.searchParams.set("includeInstructions", "true");
237
+ }
238
+ if (privateRegistryHostname) {
239
+ url.searchParams.set("privateRegistryHostname", privateRegistryHostname);
240
+ }
241
+ if (proxyUrl) {
242
+ url.searchParams.set("proxyUrl", proxyUrl);
243
+ }
244
+ if (process.env.NODE_ENV !== "production") {
245
+ console.debug("[portal-components] fetching install options via %s", url.toString());
246
+ }
247
+ const response = await authenticatedFetch(url.toString(), {
248
+ method: "GET",
249
+ token,
250
+ headers: {
251
+ accept: "application/json"
252
+ },
253
+ signal: context?.signal
254
+ });
255
+ if (!response.ok) {
256
+ const errorText = await response.text();
257
+ throw new Error(
258
+ `Get install options failed (${response.status} ${response.statusText}): ${errorText}`
259
+ );
260
+ }
261
+ return await response.json();
262
+ }
263
+ async function updateInstallOptions(input, context) {
264
+ const {
265
+ token,
266
+ installOptionsId,
267
+ installType,
268
+ channelId,
269
+ channelReleaseSequence,
270
+ networkAvailability,
271
+ registryAvailability,
272
+ kubernetesDistribution,
273
+ isMultiNode,
274
+ serviceAccountId,
275
+ adminConsoleUrl,
276
+ status,
277
+ includeInstructions = true,
278
+ privateRegistryHostname,
279
+ proxyUrl
280
+ } = input;
281
+ if (!token || typeof token !== "string") {
282
+ throw new Error("updateInstallOptions requires a session token");
283
+ }
284
+ if (!installOptionsId?.trim()) {
285
+ throw new Error("Install options ID is required");
286
+ }
287
+ const customerId = getCustomerIdFromToken(token);
288
+ const origin = getApiOrigin();
289
+ const url = new URL(`${origin}/v3/customers/${customerId}/install-options/${installOptionsId.trim()}`);
290
+ if (includeInstructions) {
291
+ url.searchParams.set("includeInstructions", "true");
292
+ }
293
+ if (privateRegistryHostname) {
294
+ url.searchParams.set("privateRegistryHostname", privateRegistryHostname);
295
+ }
296
+ if (proxyUrl) {
297
+ url.searchParams.set("proxyUrl", proxyUrl);
298
+ }
299
+ const body = {};
300
+ if (installType !== void 0) {
301
+ body.install_type = installType;
302
+ }
303
+ if (channelId !== void 0) {
304
+ body.channel_id = channelId;
305
+ }
306
+ if (channelReleaseSequence !== void 0) {
307
+ body.channel_release_sequence = channelReleaseSequence;
308
+ }
309
+ if (networkAvailability !== void 0) {
310
+ body.network_availability = networkAvailability;
311
+ }
312
+ if (registryAvailability !== void 0) {
313
+ body.registry_availability = registryAvailability;
314
+ }
315
+ if (kubernetesDistribution !== void 0) {
316
+ body.kubernetes_distribution = kubernetesDistribution;
317
+ }
318
+ if (isMultiNode !== void 0) {
319
+ body.is_multi_node = isMultiNode;
320
+ }
321
+ if (serviceAccountId !== void 0) {
322
+ body.service_account_id = serviceAccountId;
323
+ }
324
+ if (adminConsoleUrl !== void 0) {
325
+ body.admin_console_url = adminConsoleUrl;
326
+ }
327
+ if (status !== void 0) {
328
+ body.status = status;
329
+ }
330
+ if (process.env.NODE_ENV !== "production") {
331
+ console.debug("[portal-components] updating install options via %s", url.toString());
332
+ }
333
+ const response = await authenticatedFetch(url.toString(), {
334
+ method: "PATCH",
335
+ token,
336
+ headers: {
337
+ "content-type": "application/json",
338
+ accept: "application/json"
339
+ },
340
+ body: JSON.stringify(body),
341
+ signal: context?.signal
342
+ });
343
+ if (!response.ok) {
344
+ const errorText = await response.text();
345
+ throw new Error(
346
+ `Update install options failed (${response.status} ${response.statusText}): ${errorText}`
347
+ );
348
+ }
349
+ return await response.json();
350
+ }
351
+ function filterEmbeddedClusterReleases(releases) {
352
+ return releases.filter(
353
+ (release) => release.installationTypes?.embeddedCluster?.version
354
+ );
355
+ }
356
+ function filterHelmReleases(releases) {
357
+ return releases.filter(
358
+ (release) => release.installationTypes?.helm?.version
359
+ );
360
+ }
361
+ async function getUpdateInstructions(input, context) {
362
+ const {
363
+ token,
364
+ installOptionsId,
365
+ instanceId,
366
+ targetChannelId,
367
+ targetChannelSequence,
368
+ privateRegistryHostname
369
+ } = input;
370
+ if (!token || typeof token !== "string") {
371
+ throw new Error("getUpdateInstructions requires a session token");
372
+ }
373
+ if (!targetChannelId?.trim()) {
374
+ throw new Error("Target channel ID is required");
375
+ }
376
+ if (targetChannelSequence === void 0 || targetChannelSequence === null) {
377
+ throw new Error("Target channel sequence is required");
378
+ }
379
+ const identifier = installOptionsId || instanceId;
380
+ if (!identifier) {
381
+ throw new Error("Either installOptionsId or instanceId is required");
382
+ }
383
+ const customerId = getCustomerIdFromToken(token);
384
+ const origin = getApiOrigin();
385
+ const queryParams = new URLSearchParams();
386
+ queryParams.set("targetChannelId", targetChannelId.trim());
387
+ queryParams.set("targetChannelSequence", targetChannelSequence.toString());
388
+ if (privateRegistryHostname?.trim()) {
389
+ queryParams.set("privateRegistryHostname", privateRegistryHostname.trim());
390
+ }
391
+ if (!installOptionsId && instanceId) {
392
+ queryParams.set("instance_id", instanceId);
393
+ }
394
+ const pathId = installOptionsId || "placeholder";
395
+ const url = `${origin}/v3/customers/${customerId}/install-options/${pathId}/update-instructions?${queryParams.toString()}`;
396
+ if (process.env.NODE_ENV !== "production") {
397
+ console.debug("[portal-components] fetching update instructions via %s", url);
398
+ }
399
+ const response = await authenticatedFetch(url, {
400
+ method: "GET",
401
+ token,
402
+ headers: {
403
+ accept: "application/json"
404
+ },
405
+ signal: context?.signal
406
+ });
407
+ if (!response.ok) {
408
+ const errorText = await response.text();
409
+ let errorMessage = "Failed to fetch update instructions";
410
+ try {
411
+ const errorData = JSON.parse(errorText);
412
+ errorMessage = errorData.error || errorData.message || errorMessage;
413
+ } catch {
414
+ }
415
+ throw new Error(errorMessage);
416
+ }
417
+ const data = await response.json();
418
+ return {
419
+ instructions: {
420
+ format: data.format || "steps",
421
+ install_type: data.install_type,
422
+ title: data.title,
423
+ steps: data.steps || []
424
+ }
425
+ };
426
+ }
427
+ async function fetchPendingInstallations(input, context) {
428
+ const { token } = input;
429
+ if (typeof token !== "string" || token.trim().length === 0) {
430
+ throw new Error("fetchPendingInstallations requires a non-empty token");
431
+ }
432
+ let customerId;
433
+ try {
434
+ customerId = getCustomerIdFromToken(token);
435
+ } catch {
436
+ return { installations: [] };
437
+ }
438
+ const origin = getApiOrigin();
439
+ const queryParams = new URLSearchParams();
440
+ queryParams.set("status", "in_progress");
441
+ queryParams.set("page_size", "5");
442
+ queryParams.set("order_by", "created_at");
443
+ queryParams.set("order_direction", "desc");
444
+ const endpoint = `${origin}/v3/customers/${customerId}/install-options?${queryParams.toString()}`;
445
+ const response = await authenticatedFetch(endpoint, {
446
+ method: "GET",
447
+ token,
448
+ headers: {
449
+ accept: "application/json"
450
+ },
451
+ signal: context?.signal
452
+ });
453
+ if (!response.ok) {
454
+ throw new Error(
455
+ `Pending installations request failed (${response.status} ${response.statusText})`
456
+ );
457
+ }
458
+ const payload = await response.json();
459
+ console.log("[portal-components] fetchPendingInstallations raw response:", JSON.stringify(payload, null, 2));
460
+ const installArray = payload?.install_options || [];
461
+ const installations = installArray.map((item) => ({
462
+ id: String(item.id || ""),
463
+ name: String(item.instance_name || "Unknown"),
464
+ method: item.install_type === "helm" ? "helm" : "linux",
465
+ startedBy: String(item.service_account_email_address || "Unknown"),
466
+ startedAt: String(item.started_at || (/* @__PURE__ */ new Date()).toISOString())
467
+ }));
468
+ console.log("[portal-components] fetchPendingInstallations parsed installations:", installations.length);
469
+ return {
470
+ installations
471
+ };
472
+ }
473
+
474
+ // src/actions/instances.ts
475
+ var ACTIVE_INSTANCE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
476
+ function isInstanceActive(instance) {
477
+ if (!instance.lastCheckin) {
478
+ return false;
479
+ }
480
+ const lastCheckinTime = new Date(instance.lastCheckin).getTime();
481
+ const threshold = Date.now() - ACTIVE_INSTANCE_THRESHOLD_MS;
482
+ return lastCheckinTime > threshold;
483
+ }
484
+ function filterActiveInactiveInstances(instances) {
485
+ const activeInstances = instances.filter(isInstanceActive);
486
+ const inactiveInstances = instances.filter((instance) => !isInstanceActive(instance));
487
+ return { activeInstances, inactiveInstances };
488
+ }
489
+ function getInstallType(instance, installOptions) {
490
+ const instanceHasNoInstallTypes = !instance.kotsInstallId && !instance.kurlInstallId && !instance.embeddedClusterId;
491
+ const isManuallyCreatedAirgapInstance = instance.isAirgap && instanceHasNoInstallTypes;
492
+ if (instance.embeddedClusterId) {
493
+ return "embedded cluster";
494
+ }
495
+ if (instanceHasNoInstallTypes && instance.k8sDistribution) {
496
+ return "helm";
497
+ }
498
+ if (isManuallyCreatedAirgapInstance && !instance.k8sDistribution) {
499
+ if (installOptions) {
500
+ const matchingInstallOption = installOptions.find(
501
+ (option) => option.instance_id === instance.id
502
+ );
503
+ if (matchingInstallOption) {
504
+ if (matchingInstallOption.install_type === "helm") {
505
+ return "helm";
506
+ }
507
+ if (matchingInstallOption.install_type === "linux") {
508
+ return "embedded cluster";
509
+ }
510
+ }
511
+ }
512
+ return "embedded cluster";
513
+ }
514
+ return null;
515
+ }
516
+ function calculateUpdatesForInstance(instance, channelReleases) {
517
+ if (!channelReleases || !Array.isArray(channelReleases)) {
518
+ return 0;
519
+ }
520
+ const instanceChannelSequence = instance.channelSequence ?? 0;
521
+ const matchingReleases = channelReleases.filter(
522
+ (release) => release.channelId === instance.channelId
523
+ );
524
+ return matchingReleases.filter(
525
+ (release) => release.channelSequence > instanceChannelSequence
526
+ ).length;
527
+ }
528
+ function getInstanceName(instance) {
529
+ const nameTag = instance.tags?.find((tag) => tag.key === "name");
530
+ return nameTag?.value || instance.id.slice(0, 7);
531
+ }
532
+ async function fetchInstances2(input, context) {
533
+ const { token } = input;
534
+ if (!token || typeof token !== "string") {
535
+ throw new Error("fetchInstances requires a session token");
536
+ }
537
+ const customerId = getCustomerIdFromToken(token);
538
+ const origin = getApiOrigin();
539
+ const url = new URL(`${origin}/v3/instances`);
540
+ url.searchParams.set("customer_id", customerId);
541
+ if (process.env.NODE_ENV !== "production") {
542
+ console.debug("[portal-components] fetching instances via %s", url.toString());
543
+ }
544
+ const response = await authenticatedFetch(url.toString(), {
545
+ method: "GET",
546
+ token,
547
+ headers: {
548
+ accept: "application/json"
549
+ },
550
+ signal: context?.signal
551
+ });
552
+ if (!response.ok) {
553
+ const errorText = await response.text();
554
+ throw new Error(
555
+ `Instances request failed (${response.status} ${response.statusText}): ${errorText}`
556
+ );
557
+ }
558
+ const payload = await response.json();
559
+ const allInstances = payload.instances || [];
560
+ const online = allInstances.filter((instance) => !instance.isAirgap);
561
+ const airgap = allInstances.filter((instance) => instance.isAirgap);
562
+ return {
563
+ instances: allInstances,
564
+ online,
565
+ airgap
566
+ };
567
+ }
568
+ async function createAirgapInstance(input, context) {
569
+ const {
570
+ token,
571
+ serviceAccountId,
572
+ instanceName,
573
+ channelId,
574
+ channelSequence,
575
+ k8sVersion,
576
+ k8sDistribution,
577
+ appStatus = "missing"
578
+ } = input;
579
+ if (!token || typeof token !== "string") {
580
+ throw new Error("createAirgapInstance requires a session token");
581
+ }
582
+ if (!serviceAccountId?.trim()) {
583
+ throw new Error("Service account ID is required");
584
+ }
585
+ if (!channelId?.trim()) {
586
+ throw new Error("Channel ID is required");
587
+ }
588
+ const customerId = getCustomerIdFromToken(token);
589
+ const origin = getApiOrigin();
590
+ const url = new URL(`${origin}/v3/instance/airgap`);
591
+ url.searchParams.set("customer_id", customerId);
592
+ const body = {
593
+ service_account_id: serviceAccountId.trim(),
594
+ channel_id: channelId.trim(),
595
+ channel_sequence: channelSequence,
596
+ app_status: appStatus
597
+ };
598
+ if (instanceName?.trim()) {
599
+ body.instance_name = instanceName.trim();
600
+ }
601
+ if (k8sVersion?.trim()) {
602
+ body.k8s_version = k8sVersion.trim();
603
+ }
604
+ if (k8sDistribution?.trim()) {
605
+ body.k8s_distribution = k8sDistribution.trim();
606
+ }
607
+ if (process.env.NODE_ENV !== "production") {
608
+ console.debug("[portal-components] creating airgap instance via %s", url.toString());
609
+ }
610
+ const response = await authenticatedFetch(url.toString(), {
611
+ method: "POST",
612
+ token,
613
+ headers: {
614
+ "content-type": "application/json",
615
+ accept: "application/json"
616
+ },
617
+ body: JSON.stringify(body),
618
+ signal: context?.signal
619
+ });
620
+ if (!response.ok) {
621
+ const errorText = await response.text();
622
+ let errorMessage = "Failed to create airgap instance";
623
+ try {
624
+ const errorData = JSON.parse(errorText);
625
+ errorMessage = errorData.error || errorData.message || errorMessage;
626
+ } catch {
627
+ }
628
+ throw new Error(errorMessage);
629
+ }
630
+ const data = await response.json();
631
+ return {
632
+ instanceId: data.instance_id,
633
+ licenseId: data.license_id,
634
+ createdAt: data.created_at,
635
+ lastActive: data.last_active,
636
+ appFirstReadyAt: data.app_first_ready_at,
637
+ appStatus: data.app_status,
638
+ isAirgap: data.is_airgap,
639
+ channelId: data.channel_id,
640
+ channelSequence: data.channel_sequence,
641
+ k8sVersion: data.k8s_version,
642
+ k8sDistribution: data.k8s_distribution
643
+ };
644
+ }
645
+ async function updateAirgapInstance(input, context) {
646
+ const {
647
+ token,
648
+ instanceId,
649
+ serviceAccountId,
650
+ channelId,
651
+ channelSequence
652
+ } = input;
653
+ if (!token || typeof token !== "string") {
654
+ throw new Error("updateAirgapInstance requires a session token");
655
+ }
656
+ if (!instanceId?.trim()) {
657
+ throw new Error("Instance ID is required");
658
+ }
659
+ if (!channelId?.trim()) {
660
+ throw new Error("Channel ID is required");
661
+ }
662
+ const customerId = getCustomerIdFromToken(token);
663
+ const origin = getApiOrigin();
664
+ const url = new URL(`${origin}/v3/instance/airgap/${instanceId.trim()}`);
665
+ url.searchParams.set("customer_id", customerId);
666
+ const body = {
667
+ channel_id: channelId.trim(),
668
+ channel_sequence: channelSequence
669
+ };
670
+ if (serviceAccountId?.trim()) {
671
+ body.service_account_id = serviceAccountId.trim();
672
+ }
673
+ if (process.env.NODE_ENV !== "production") {
674
+ console.debug("[portal-components] updating airgap instance via %s", url.toString());
675
+ }
676
+ const response = await authenticatedFetch(url.toString(), {
677
+ method: "PATCH",
678
+ token,
679
+ headers: {
680
+ "content-type": "application/json",
681
+ accept: "application/json"
682
+ },
683
+ body: JSON.stringify(body),
684
+ signal: context?.signal
685
+ });
686
+ if (!response.ok) {
687
+ const errorText = await response.text();
688
+ let errorMessage = "Failed to update airgap instance";
689
+ try {
690
+ const errorData = JSON.parse(errorText);
691
+ errorMessage = errorData.error || errorData.message || errorMessage;
692
+ } catch {
693
+ }
694
+ throw new Error(errorMessage);
695
+ }
696
+ return { success: true };
697
+ }
698
+ async function fetchInstallOptionsByInstanceIds(input, context) {
699
+ const { token, instanceIds } = input;
700
+ if (!token || typeof token !== "string") {
701
+ throw new Error("fetchInstallOptionsByInstanceIds requires a session token");
702
+ }
703
+ if (!instanceIds || instanceIds.length === 0) {
704
+ return { installOptions: [] };
705
+ }
706
+ const customerId = getCustomerIdFromToken(token);
707
+ const origin = getApiOrigin();
708
+ const chunks = [];
709
+ for (let i = 0; i < instanceIds.length; i += 50) {
710
+ chunks.push(instanceIds.slice(i, i + 50));
711
+ }
712
+ const allInstallOptions = [];
713
+ for (const chunk of chunks) {
714
+ if (chunk.length === 0) continue;
715
+ const queryParams = chunk.map((id) => `instance_id=${encodeURIComponent(id)}`).join("&");
716
+ const url = `${origin}/v3/customers/${customerId}/install-options?${queryParams}`;
717
+ if (process.env.NODE_ENV !== "production") {
718
+ console.debug("[portal-components] fetching install options via %s", url);
719
+ }
720
+ const response = await authenticatedFetch(url, {
721
+ method: "GET",
722
+ token,
723
+ headers: {
724
+ accept: "application/json"
725
+ },
726
+ signal: context?.signal
727
+ });
728
+ if (response.status === 404) {
729
+ continue;
730
+ }
731
+ if (!response.ok) {
732
+ const errorText = await response.text();
733
+ throw new Error(
734
+ `Install options request failed (${response.status} ${response.statusText}): ${errorText}`
735
+ );
736
+ }
737
+ const payload = await response.json();
738
+ const options = payload?.install_options || [];
739
+ allInstallOptions.push(...options);
740
+ }
741
+ return { installOptions: allInstallOptions };
742
+ }
743
+
744
+ export { ACTIVE_INSTANCE_THRESHOLD_MS, calculateUpdatesForInstance, createAirgapInstance, createInstallOptions, fetchChannelReleases, fetchInstallOptionsByInstanceIds, fetchInstances2 as fetchInstances, fetchPendingInstallations, filterActiveInactiveInstances, filterEmbeddedClusterReleases, filterHelmReleases, getInstallOptions, getInstallType, getInstanceName, getUpdateInstructions, isInstanceActive, updateAirgapInstance, updateInstallOptions };
745
+ //# sourceMappingURL=install-actions.js.map
746
+ //# sourceMappingURL=install-actions.js.map