alchemy 0.80.1 → 0.81.1
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/bin/alchemy.js +1660 -1382
- package/bin/services/execute-alchemy.ts +18 -6
- package/lib/alchemy.js +1 -1
- package/lib/alchemy.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/hyperdrive.d.ts +23 -5
- package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
- package/lib/cloudflare/hyperdrive.js.map +1 -1
- package/lib/cloudflare/index.d.ts +3 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +3 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +7 -0
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/queue.d.ts +0 -3
- package/lib/cloudflare/queue.d.ts.map +1 -1
- package/lib/cloudflare/queue.js +53 -31
- package/lib/cloudflare/queue.js.map +1 -1
- package/lib/cloudflare/redirect-rule.d.ts +12 -2
- package/lib/cloudflare/redirect-rule.d.ts.map +1 -1
- package/lib/cloudflare/redirect-rule.js +73 -98
- package/lib/cloudflare/redirect-rule.js.map +1 -1
- package/lib/cloudflare/tunnel-route.d.ts +172 -0
- package/lib/cloudflare/tunnel-route.d.ts.map +1 -0
- package/lib/cloudflare/tunnel-route.js +251 -0
- package/lib/cloudflare/tunnel-route.js.map +1 -0
- package/lib/cloudflare/warp-default-profile.d.ts +133 -0
- package/lib/cloudflare/warp-default-profile.d.ts.map +1 -0
- package/lib/cloudflare/warp-default-profile.js +138 -0
- package/lib/cloudflare/warp-default-profile.js.map +1 -0
- package/lib/cloudflare/warp-device-profile.d.ts +272 -0
- package/lib/cloudflare/warp-device-profile.d.ts.map +1 -0
- package/lib/cloudflare/warp-device-profile.js +291 -0
- package/lib/cloudflare/warp-device-profile.js.map +1 -0
- package/lib/scope.d.ts +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +1 -1
- package/lib/scope.js.map +1 -1
- package/package.json +1 -1
- package/src/alchemy.ts +1 -1
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/hyperdrive.ts +27 -5
- package/src/cloudflare/index.ts +3 -0
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +11 -0
- package/src/cloudflare/queue.ts +97 -56
- package/src/cloudflare/redirect-rule.ts +129 -150
- package/src/cloudflare/tunnel-route.ts +495 -0
- package/src/cloudflare/warp-default-profile.ts +320 -0
- package/src/cloudflare/warp-device-profile.ts +616 -0
- package/src/scope.ts +2 -2
- package/workers/tunnel-proxy.js +1 -1
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import type { Context } from "../context.ts";
|
|
2
|
+
import { Resource, ResourceKind } from "../resource.ts";
|
|
3
|
+
import { logger } from "../util/logger.ts";
|
|
4
|
+
import { handleApiError } from "./api-error.ts";
|
|
5
|
+
import type {
|
|
6
|
+
CloudflareApiListResponse,
|
|
7
|
+
CloudflareApiResponse,
|
|
8
|
+
} from "./api-response.ts";
|
|
9
|
+
import {
|
|
10
|
+
createCloudflareApi,
|
|
11
|
+
type CloudflareApi,
|
|
12
|
+
type CloudflareApiOptions,
|
|
13
|
+
} from "./api.ts";
|
|
14
|
+
import type { Tunnel } from "./tunnel.ts";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Route data as returned by Cloudflare API
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
interface CloudflareRoute {
|
|
21
|
+
id: string;
|
|
22
|
+
network: string;
|
|
23
|
+
tunnel_id: string;
|
|
24
|
+
comment?: string;
|
|
25
|
+
virtual_network_id?: string;
|
|
26
|
+
created_at: string;
|
|
27
|
+
deleted_at: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Properties for creating or updating a Cloudflare Tunnel Route
|
|
32
|
+
*/
|
|
33
|
+
export interface TunnelRouteProps extends CloudflareApiOptions {
|
|
34
|
+
/**
|
|
35
|
+
* The private IPv4 or IPv6 range connected by the route, in CIDR notation
|
|
36
|
+
* (e.g., "172.16.0.0/16" or "2001:db8::/32")
|
|
37
|
+
*
|
|
38
|
+
* Note: The network CIDR is immutable and cannot be changed after creation.
|
|
39
|
+
* When updating a route, any network change will trigger a replacement.
|
|
40
|
+
*/
|
|
41
|
+
network: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The tunnel to route traffic through
|
|
45
|
+
* Can be a Tunnel resource or a tunnel ID (UUID)
|
|
46
|
+
*/
|
|
47
|
+
tunnel: string | Tunnel;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optional remark describing the route
|
|
51
|
+
* @maxLength 100
|
|
52
|
+
*/
|
|
53
|
+
comment?: string;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* UUID of the virtual network
|
|
57
|
+
* If not provided, the route will be added to the default virtual network
|
|
58
|
+
*/
|
|
59
|
+
virtualNetworkId?: string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether to adopt an existing route with the same network and tunnel if it exists
|
|
63
|
+
* If true and a route with the same network and tunnel exists, it will be adopted rather than creating a new one
|
|
64
|
+
*
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
adopt?: boolean;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether to delete the route when removed from Alchemy
|
|
71
|
+
* If set to false, the route will remain but the resource will be removed from state
|
|
72
|
+
*
|
|
73
|
+
* @default true
|
|
74
|
+
*/
|
|
75
|
+
delete?: boolean;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Internal route ID for lifecycle management
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
routeId?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isTunnelRoute(resource: any): resource is TunnelRoute {
|
|
85
|
+
return resource?.[ResourceKind] === "cloudflare::TunnelRoute";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Output returned after TunnelRoute creation/update
|
|
90
|
+
*/
|
|
91
|
+
export interface TunnelRoute
|
|
92
|
+
extends Omit<TunnelRouteProps, "delete" | "tunnel" | "routeId"> {
|
|
93
|
+
/**
|
|
94
|
+
* The ID of the route
|
|
95
|
+
*/
|
|
96
|
+
id: string;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The private IPv4 or IPv6 range connected by the route, in CIDR notation
|
|
100
|
+
*/
|
|
101
|
+
network: string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The UUID of the tunnel this route uses
|
|
105
|
+
*/
|
|
106
|
+
tunnelId: string;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Optional remark describing the route
|
|
110
|
+
*/
|
|
111
|
+
comment?: string;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* UUID of the virtual network
|
|
115
|
+
*/
|
|
116
|
+
virtualNetworkId?: string;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Time at which the route was created
|
|
120
|
+
*/
|
|
121
|
+
createdAt: string;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Time at which the route was deleted (null if active)
|
|
125
|
+
*/
|
|
126
|
+
deletedAt: string | null;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resource type identifier for binding
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
type: "cloudflare::TunnelRoute";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates and manages a Cloudflare Tunnel Route, which routes private network traffic
|
|
137
|
+
* through a Cloudflare Tunnel. This resource handles the route lifecycle (create, update, delete).
|
|
138
|
+
*
|
|
139
|
+
* @remarks
|
|
140
|
+
* Tunnel Routes enable private network access by routing CIDR ranges through Cloudflare Tunnels.
|
|
141
|
+
* This is commonly used for Zero Trust network access scenarios where you need to connect
|
|
142
|
+
* private networks to Cloudflare's edge.
|
|
143
|
+
*
|
|
144
|
+
* @see https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/private-net/
|
|
145
|
+
* @see https://developers.cloudflare.com/api/operations/zero-trust-networks-routes-create-a-tunnel-route
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* // Create a basic tunnel route for a private network
|
|
149
|
+
* const tunnel = await Tunnel("my-tunnel", {
|
|
150
|
+
* name: "my-tunnel"
|
|
151
|
+
* });
|
|
152
|
+
*
|
|
153
|
+
* const route = await TunnelRoute("private-network", {
|
|
154
|
+
* network: "172.16.0.0/16",
|
|
155
|
+
* tunnel: tunnel
|
|
156
|
+
* });
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* // Create a route with a comment and virtual network
|
|
160
|
+
* const route = await TunnelRoute("vpc-route", {
|
|
161
|
+
* network: "10.0.0.0/8",
|
|
162
|
+
* tunnel: "f70ff985-a4ef-4643-bbbc-4a0ed4fc8415",
|
|
163
|
+
* comment: "Main VPC network route",
|
|
164
|
+
* virtualNetworkId: "f70ff985-a4ef-4643-bbbc-4a0ed4fc8415"
|
|
165
|
+
* });
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* // Adopt an existing route if it already exists
|
|
169
|
+
* const route = await TunnelRoute("existing-route", {
|
|
170
|
+
* network: "192.168.1.0/24",
|
|
171
|
+
* tunnel: tunnel,
|
|
172
|
+
* adopt: true,
|
|
173
|
+
* comment: "Updated comment"
|
|
174
|
+
* });
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* // Create a route without deleting it when removed from Alchemy
|
|
178
|
+
* const route = await TunnelRoute("persistent-route", {
|
|
179
|
+
* network: "10.1.0.0/16",
|
|
180
|
+
* tunnel: tunnel,
|
|
181
|
+
* delete: false
|
|
182
|
+
* });
|
|
183
|
+
*/
|
|
184
|
+
export const TunnelRoute = Resource(
|
|
185
|
+
"cloudflare::TunnelRoute",
|
|
186
|
+
async function (
|
|
187
|
+
this: Context<TunnelRoute>,
|
|
188
|
+
id: string,
|
|
189
|
+
props: TunnelRouteProps,
|
|
190
|
+
): Promise<TunnelRoute> {
|
|
191
|
+
// Create Cloudflare API client with automatic account discovery
|
|
192
|
+
const api = await createCloudflareApi(props);
|
|
193
|
+
|
|
194
|
+
// Resolve tunnel ID from either string or Tunnel resource
|
|
195
|
+
const tunnelId =
|
|
196
|
+
typeof props.tunnel === "string" ? props.tunnel : props.tunnel.tunnelId;
|
|
197
|
+
|
|
198
|
+
const routeId = props.routeId || this.output?.id;
|
|
199
|
+
const adopt = props.adopt ?? this.scope.adopt;
|
|
200
|
+
|
|
201
|
+
if (this.phase === "delete") {
|
|
202
|
+
// For delete operations, check if the route ID exists in the output
|
|
203
|
+
if (routeId && props.delete !== false) {
|
|
204
|
+
await deleteRoute(api, routeId);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Return destroyed state
|
|
208
|
+
return this.destroy();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check if network is being changed - network is immutable
|
|
212
|
+
if (
|
|
213
|
+
this.phase === "update" &&
|
|
214
|
+
this.output?.network &&
|
|
215
|
+
this.output.network !== props.network
|
|
216
|
+
) {
|
|
217
|
+
logger.log(
|
|
218
|
+
`Network changed from '${this.output.network}' to '${props.network}', replacing route`,
|
|
219
|
+
);
|
|
220
|
+
this.replace(true);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check if tunnel is being changed - tunnel is immutable
|
|
224
|
+
if (
|
|
225
|
+
this.phase === "update" &&
|
|
226
|
+
this.output?.tunnelId &&
|
|
227
|
+
this.output.tunnelId !== tunnelId
|
|
228
|
+
) {
|
|
229
|
+
logger.log(
|
|
230
|
+
`Tunnel changed from '${this.output.tunnelId}' to '${tunnelId}', replacing route`,
|
|
231
|
+
);
|
|
232
|
+
this.replace(true);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let routeData: CloudflareRoute;
|
|
236
|
+
|
|
237
|
+
if (this.phase === "update" && routeId) {
|
|
238
|
+
// Update existing route (only comment and virtualNetworkId can be updated)
|
|
239
|
+
routeData = await updateRoute(api, routeId, {
|
|
240
|
+
comment: props.comment,
|
|
241
|
+
virtualNetworkId: props.virtualNetworkId,
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
// Create new route
|
|
245
|
+
try {
|
|
246
|
+
routeData = await createRoute(api, {
|
|
247
|
+
network: props.network,
|
|
248
|
+
tunnelId,
|
|
249
|
+
comment: props.comment,
|
|
250
|
+
virtualNetworkId: props.virtualNetworkId,
|
|
251
|
+
});
|
|
252
|
+
} catch (error) {
|
|
253
|
+
// Check if this is a "route already exists" error and adopt is enabled
|
|
254
|
+
if (
|
|
255
|
+
adopt &&
|
|
256
|
+
error instanceof Error &&
|
|
257
|
+
(error.message.includes("already exists") ||
|
|
258
|
+
error.message.includes("duplicate") ||
|
|
259
|
+
(error as any).status === 409)
|
|
260
|
+
) {
|
|
261
|
+
logger.log(
|
|
262
|
+
`Route for network '${props.network}' and tunnel '${tunnelId}' already exists, adopting it`,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// Find the existing route by network and tunnel
|
|
266
|
+
const existingRoute = await findRouteByNetworkAndTunnel(
|
|
267
|
+
api,
|
|
268
|
+
props.network,
|
|
269
|
+
tunnelId,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
if (!existingRoute) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`Failed to find existing route for network '${props.network}' and tunnel '${tunnelId}' for adoption`,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
routeData = existingRoute;
|
|
279
|
+
|
|
280
|
+
// Update comment/virtualNetworkId if provided
|
|
281
|
+
if (
|
|
282
|
+
props.comment !== undefined ||
|
|
283
|
+
props.virtualNetworkId !== undefined
|
|
284
|
+
) {
|
|
285
|
+
routeData = await updateRoute(api, existingRoute.id, {
|
|
286
|
+
comment: props.comment,
|
|
287
|
+
virtualNetworkId: props.virtualNetworkId,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
// Re-throw the error if adopt is false or it's not an "already exists" error
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Transform API response to our interface
|
|
298
|
+
return {
|
|
299
|
+
id: routeData.id,
|
|
300
|
+
network: routeData.network,
|
|
301
|
+
tunnelId: routeData.tunnel_id,
|
|
302
|
+
comment: routeData.comment,
|
|
303
|
+
virtualNetworkId: routeData.virtual_network_id,
|
|
304
|
+
createdAt: routeData.created_at,
|
|
305
|
+
deletedAt: routeData.deleted_at,
|
|
306
|
+
type: "cloudflare::TunnelRoute",
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get tunnel route details
|
|
313
|
+
* @internal
|
|
314
|
+
*/
|
|
315
|
+
export async function getTunnelRoute(
|
|
316
|
+
api: CloudflareApi,
|
|
317
|
+
routeId: string,
|
|
318
|
+
): Promise<CloudflareRoute> {
|
|
319
|
+
const response = await api.get(
|
|
320
|
+
`/accounts/${api.accountId}/teamnet/routes/${routeId}`,
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
if (!response.ok) {
|
|
324
|
+
await handleApiError(response, "get", "route", routeId);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const data =
|
|
328
|
+
(await response.json()) as CloudflareApiResponse<CloudflareRoute>;
|
|
329
|
+
return data.result;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* List all tunnel routes with pagination support
|
|
334
|
+
* @internal
|
|
335
|
+
*/
|
|
336
|
+
export async function listTunnelRoutes(
|
|
337
|
+
api: CloudflareApi,
|
|
338
|
+
options?: {
|
|
339
|
+
/** Maximum number of routes to return */
|
|
340
|
+
limit?: number;
|
|
341
|
+
},
|
|
342
|
+
): Promise<CloudflareRoute[]> {
|
|
343
|
+
const routes: CloudflareRoute[] = [];
|
|
344
|
+
let page = 1;
|
|
345
|
+
const perPage = 100; // Maximum allowed by API
|
|
346
|
+
let hasMorePages = true;
|
|
347
|
+
const limit = options?.limit;
|
|
348
|
+
|
|
349
|
+
while (hasMorePages) {
|
|
350
|
+
const params = new URLSearchParams({
|
|
351
|
+
page: page.toString(),
|
|
352
|
+
per_page: perPage.toString(),
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const response = await api.get(
|
|
356
|
+
`/accounts/${api.accountId}/teamnet/routes?${params.toString()}`,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
if (!response.ok) {
|
|
360
|
+
await handleApiError(response, "list", "route", "all");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const data =
|
|
364
|
+
(await response.json()) as CloudflareApiListResponse<CloudflareRoute>;
|
|
365
|
+
|
|
366
|
+
routes.push(...data.result);
|
|
367
|
+
const resultInfo = data.result_info;
|
|
368
|
+
|
|
369
|
+
// Check if we've reached the limit
|
|
370
|
+
if (limit && routes.length >= limit) {
|
|
371
|
+
return routes.slice(0, limit);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check if we've seen all pages
|
|
375
|
+
hasMorePages =
|
|
376
|
+
resultInfo.page * resultInfo.per_page < resultInfo.total_count;
|
|
377
|
+
page++;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return routes;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Find a route by network and tunnel ID
|
|
385
|
+
* @internal
|
|
386
|
+
*/
|
|
387
|
+
export async function findRouteByNetworkAndTunnel(
|
|
388
|
+
api: CloudflareApi,
|
|
389
|
+
network: string,
|
|
390
|
+
tunnelId: string,
|
|
391
|
+
): Promise<CloudflareRoute | null> {
|
|
392
|
+
const routes = await listTunnelRoutes(api);
|
|
393
|
+
|
|
394
|
+
// Look for a route with matching network and tunnel_id
|
|
395
|
+
const match = routes.find(
|
|
396
|
+
(route) => route.network === network && route.tunnel_id === tunnelId,
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
return match || null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Delete a route
|
|
404
|
+
* @internal
|
|
405
|
+
*/
|
|
406
|
+
async function deleteRoute(api: CloudflareApi, routeId: string): Promise<void> {
|
|
407
|
+
const response = await api.delete(
|
|
408
|
+
`/accounts/${api.accountId}/teamnet/routes/${routeId}`,
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
if (!response.ok && response.status !== 404) {
|
|
412
|
+
await handleApiError(response, "delete", "route", routeId);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Create a new route
|
|
418
|
+
* @internal
|
|
419
|
+
*/
|
|
420
|
+
async function createRoute(
|
|
421
|
+
api: CloudflareApi,
|
|
422
|
+
props: {
|
|
423
|
+
network: string;
|
|
424
|
+
tunnelId: string;
|
|
425
|
+
comment?: string;
|
|
426
|
+
virtualNetworkId?: string;
|
|
427
|
+
},
|
|
428
|
+
): Promise<CloudflareRoute> {
|
|
429
|
+
const payload: Record<string, any> = {
|
|
430
|
+
network: props.network,
|
|
431
|
+
tunnel_id: props.tunnelId,
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
if (props.comment !== undefined) {
|
|
435
|
+
payload.comment = props.comment;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (props.virtualNetworkId !== undefined) {
|
|
439
|
+
payload.virtual_network_id = props.virtualNetworkId;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const response = await api.post(
|
|
443
|
+
`/accounts/${api.accountId}/teamnet/routes`,
|
|
444
|
+
payload,
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
if (!response.ok) {
|
|
448
|
+
await handleApiError(
|
|
449
|
+
response,
|
|
450
|
+
"create",
|
|
451
|
+
"route",
|
|
452
|
+
`${props.network} -> ${props.tunnelId}`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const data =
|
|
457
|
+
(await response.json()) as CloudflareApiResponse<CloudflareRoute>;
|
|
458
|
+
return data.result;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Update route configuration
|
|
463
|
+
* @internal
|
|
464
|
+
*/
|
|
465
|
+
async function updateRoute(
|
|
466
|
+
api: CloudflareApi,
|
|
467
|
+
routeId: string,
|
|
468
|
+
props: {
|
|
469
|
+
comment?: string;
|
|
470
|
+
virtualNetworkId?: string;
|
|
471
|
+
},
|
|
472
|
+
): Promise<CloudflareRoute> {
|
|
473
|
+
const payload: Record<string, any> = {};
|
|
474
|
+
|
|
475
|
+
if (props.comment !== undefined) {
|
|
476
|
+
payload.comment = props.comment;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (props.virtualNetworkId !== undefined) {
|
|
480
|
+
payload.virtual_network_id = props.virtualNetworkId;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const response = await api.patch(
|
|
484
|
+
`/accounts/${api.accountId}/teamnet/routes/${routeId}`,
|
|
485
|
+
payload,
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
if (!response.ok) {
|
|
489
|
+
await handleApiError(response, "update", "route", routeId);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const data =
|
|
493
|
+
(await response.json()) as CloudflareApiResponse<CloudflareRoute>;
|
|
494
|
+
return data.result;
|
|
495
|
+
}
|