relight-cli 0.1.0 → 0.3.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 +77 -34
- package/package.json +12 -4
- package/src/cli.js +350 -1
- package/src/commands/apps.js +128 -0
- package/src/commands/auth.js +13 -4
- package/src/commands/config.js +282 -0
- package/src/commands/cost.js +593 -0
- package/src/commands/db.js +775 -0
- package/src/commands/deploy.js +264 -0
- package/src/commands/doctor.js +69 -13
- package/src/commands/domains.js +223 -0
- package/src/commands/logs.js +111 -0
- package/src/commands/open.js +42 -0
- package/src/commands/ps.js +121 -0
- package/src/commands/scale.js +132 -0
- package/src/commands/service.js +227 -0
- package/src/lib/clouds/aws.js +309 -35
- package/src/lib/clouds/cf.js +401 -2
- package/src/lib/clouds/gcp.js +255 -4
- package/src/lib/clouds/neon.js +147 -0
- package/src/lib/clouds/slicervm.js +139 -0
- package/src/lib/config.js +200 -2
- package/src/lib/docker.js +34 -0
- package/src/lib/link.js +31 -5
- package/src/lib/providers/aws/app.js +481 -0
- package/src/lib/providers/aws/db.js +504 -0
- package/src/lib/providers/aws/dns.js +232 -0
- package/src/lib/providers/aws/registry.js +59 -0
- package/src/lib/providers/cf/app.js +596 -0
- package/src/lib/providers/cf/bundle.js +70 -0
- package/src/lib/providers/cf/db.js +181 -0
- package/src/lib/providers/cf/dns.js +148 -0
- package/src/lib/providers/cf/registry.js +17 -0
- package/src/lib/providers/gcp/app.js +429 -0
- package/src/lib/providers/gcp/db.js +372 -0
- package/src/lib/providers/gcp/dns.js +166 -0
- package/src/lib/providers/gcp/registry.js +30 -0
- package/src/lib/providers/neon/db.js +306 -0
- package/src/lib/providers/resolve.js +79 -0
- package/src/lib/providers/slicervm/app.js +396 -0
- package/src/lib/providers/slicervm/db.js +33 -0
- package/src/lib/providers/slicervm/dns.js +58 -0
- package/src/lib/providers/slicervm/registry.js +7 -0
- package/worker-template/package.json +10 -0
- package/worker-template/src/index.js +260 -0
package/src/lib/clouds/cf.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
var CF_API = "https://api.cloudflare.com/client/v4";
|
|
2
|
+
var CF_REGISTRY = "registry.cloudflare.com";
|
|
3
|
+
|
|
4
|
+
export { CF_REGISTRY };
|
|
2
5
|
|
|
3
6
|
export var TOKEN_URL =
|
|
4
7
|
"https://dash.cloudflare.com/profile/api-tokens?" +
|
|
@@ -13,12 +16,16 @@ export var TOKEN_URL =
|
|
|
13
16
|
) +
|
|
14
17
|
"&name=relight-cli";
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
// --- CF API base ---
|
|
20
|
+
|
|
21
|
+
export async function cfApi(method, path, body, apiToken, contentType) {
|
|
17
22
|
var headers = {
|
|
18
23
|
Authorization: `Bearer ${apiToken}`,
|
|
19
24
|
};
|
|
20
25
|
|
|
21
|
-
if (
|
|
26
|
+
if (contentType) {
|
|
27
|
+
headers["Content-Type"] = contentType;
|
|
28
|
+
} else if (body && typeof body === "object") {
|
|
22
29
|
headers["Content-Type"] = "application/json";
|
|
23
30
|
body = JSON.stringify(body);
|
|
24
31
|
}
|
|
@@ -41,6 +48,32 @@ export async function cfApi(method, path, body, apiToken) {
|
|
|
41
48
|
return res.text();
|
|
42
49
|
}
|
|
43
50
|
|
|
51
|
+
// --- CF GraphQL Analytics API ---
|
|
52
|
+
|
|
53
|
+
export async function cfGraphQL(apiToken, query, variables) {
|
|
54
|
+
var res = await fetch("https://api.cloudflare.com/client/v4/graphql", {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: {
|
|
57
|
+
Authorization: `Bearer ${apiToken}`,
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
},
|
|
60
|
+
body: JSON.stringify({ query, variables }),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
var text = await res.text();
|
|
65
|
+
throw new Error(`CF GraphQL: ${res.status} ${text}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
var json = await res.json();
|
|
69
|
+
if (json.errors && json.errors.length > 0) {
|
|
70
|
+
throw new Error(`CF GraphQL: ${json.errors.map((e) => e.message).join(", ")}`);
|
|
71
|
+
}
|
|
72
|
+
return json.data;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// --- Auth verification ---
|
|
76
|
+
|
|
44
77
|
export async function verifyToken(apiToken) {
|
|
45
78
|
return cfApi("GET", "/user/tokens/verify", null, apiToken);
|
|
46
79
|
}
|
|
@@ -50,6 +83,299 @@ export async function listAccounts(apiToken) {
|
|
|
50
83
|
return res.result || [];
|
|
51
84
|
}
|
|
52
85
|
|
|
86
|
+
// --- Registry ---
|
|
87
|
+
|
|
88
|
+
export async function getRegistryCredentials(accountId, apiToken) {
|
|
89
|
+
var res = await cfApi(
|
|
90
|
+
"POST",
|
|
91
|
+
`/accounts/${accountId}/containers/registries/${CF_REGISTRY}/credentials`,
|
|
92
|
+
{ permissions: ["push", "pull"], expiration_minutes: 15 },
|
|
93
|
+
apiToken
|
|
94
|
+
);
|
|
95
|
+
return res.result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// --- Worker scripts ---
|
|
99
|
+
|
|
100
|
+
export async function uploadWorker(accountId, apiToken, scriptName, code, metadata) {
|
|
101
|
+
var form = new FormData();
|
|
102
|
+
form.append(
|
|
103
|
+
"metadata",
|
|
104
|
+
new Blob([JSON.stringify(metadata)], { type: "application/json" })
|
|
105
|
+
);
|
|
106
|
+
form.append(
|
|
107
|
+
"index.js",
|
|
108
|
+
new Blob([code], { type: "application/javascript+module" }),
|
|
109
|
+
"index.js"
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
var res = await fetch(
|
|
113
|
+
`${CF_API}/accounts/${accountId}/workers/scripts/${scriptName}`,
|
|
114
|
+
{
|
|
115
|
+
method: "PUT",
|
|
116
|
+
headers: { Authorization: `Bearer ${apiToken}` },
|
|
117
|
+
body: form,
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
var text = await res.text();
|
|
123
|
+
throw new Error(`Worker upload failed: ${res.status} ${text}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return res.json();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function deleteWorker(accountId, apiToken, scriptName) {
|
|
130
|
+
return cfApi(
|
|
131
|
+
"DELETE",
|
|
132
|
+
`/accounts/${accountId}/workers/scripts/${scriptName}`,
|
|
133
|
+
null,
|
|
134
|
+
apiToken
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function patchWorkerSettings(accountId, apiToken, scriptName, settings) {
|
|
139
|
+
var form = new FormData();
|
|
140
|
+
form.append(
|
|
141
|
+
"settings",
|
|
142
|
+
new Blob([JSON.stringify(settings)], { type: "application/json" })
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
var res = await fetch(
|
|
146
|
+
`${CF_API}/accounts/${accountId}/workers/scripts/${scriptName}/settings`,
|
|
147
|
+
{
|
|
148
|
+
method: "PATCH",
|
|
149
|
+
headers: { Authorization: `Bearer ${apiToken}` },
|
|
150
|
+
body: form,
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
var text = await res.text();
|
|
156
|
+
throw new Error(`Settings update failed: ${res.status} ${text}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return res.json();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function listWorkerScripts(accountId, apiToken) {
|
|
163
|
+
var res = await cfApi(
|
|
164
|
+
"GET",
|
|
165
|
+
`/accounts/${accountId}/workers/scripts`,
|
|
166
|
+
null,
|
|
167
|
+
apiToken
|
|
168
|
+
);
|
|
169
|
+
return res.result || [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function getWorkerSettings(accountId, apiToken, scriptName) {
|
|
173
|
+
var res = await cfApi(
|
|
174
|
+
"GET",
|
|
175
|
+
`/accounts/${accountId}/workers/scripts/${scriptName}/settings`,
|
|
176
|
+
null,
|
|
177
|
+
apiToken
|
|
178
|
+
);
|
|
179
|
+
return res.result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// --- Container applications ---
|
|
183
|
+
|
|
184
|
+
export async function getDONamespaceId(accountId, apiToken, scriptName, className) {
|
|
185
|
+
var res = await cfApi(
|
|
186
|
+
"GET",
|
|
187
|
+
`/accounts/${accountId}/workers/durable_objects/namespaces`,
|
|
188
|
+
null,
|
|
189
|
+
apiToken
|
|
190
|
+
);
|
|
191
|
+
var namespaces = res.result || [];
|
|
192
|
+
var ns = namespaces.find(
|
|
193
|
+
(n) => n.script === scriptName && n.class === className
|
|
194
|
+
);
|
|
195
|
+
return ns ? ns.id : null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function listContainerApps(accountId, apiToken) {
|
|
199
|
+
var res = await cfApi(
|
|
200
|
+
"GET",
|
|
201
|
+
`/accounts/${accountId}/containers/applications`,
|
|
202
|
+
null,
|
|
203
|
+
apiToken
|
|
204
|
+
);
|
|
205
|
+
return res.result || [];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export async function findContainerApp(accountId, apiToken, name) {
|
|
209
|
+
var apps = await listContainerApps(accountId, apiToken);
|
|
210
|
+
return apps.find((a) => a.name === name) || null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function createContainerApp(accountId, apiToken, app) {
|
|
214
|
+
var res = await cfApi(
|
|
215
|
+
"POST",
|
|
216
|
+
`/accounts/${accountId}/containers/applications`,
|
|
217
|
+
app,
|
|
218
|
+
apiToken
|
|
219
|
+
);
|
|
220
|
+
return res.result;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function deleteContainerApp(accountId, apiToken, appId) {
|
|
224
|
+
return cfApi(
|
|
225
|
+
"DELETE",
|
|
226
|
+
`/accounts/${accountId}/containers/applications/${appId}`,
|
|
227
|
+
null,
|
|
228
|
+
apiToken
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function modifyContainerApp(accountId, apiToken, appId, changes) {
|
|
233
|
+
var res = await cfApi(
|
|
234
|
+
"PATCH",
|
|
235
|
+
`/accounts/${accountId}/containers/applications/${appId}`,
|
|
236
|
+
changes,
|
|
237
|
+
apiToken
|
|
238
|
+
);
|
|
239
|
+
return res.result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export async function createRollout(accountId, apiToken, appId, rollout) {
|
|
243
|
+
var res = await cfApi(
|
|
244
|
+
"POST",
|
|
245
|
+
`/accounts/${accountId}/containers/applications/${appId}/rollouts`,
|
|
246
|
+
rollout,
|
|
247
|
+
apiToken
|
|
248
|
+
);
|
|
249
|
+
return res.result;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// --- Tail/logs ---
|
|
253
|
+
|
|
254
|
+
export async function createTail(accountId, apiToken, scriptName) {
|
|
255
|
+
var res = await cfApi(
|
|
256
|
+
"POST",
|
|
257
|
+
`/accounts/${accountId}/workers/scripts/${scriptName}/tails`,
|
|
258
|
+
{},
|
|
259
|
+
apiToken
|
|
260
|
+
);
|
|
261
|
+
return res.result;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export async function deleteTail(accountId, apiToken, scriptName, tailId) {
|
|
265
|
+
return cfApi(
|
|
266
|
+
"DELETE",
|
|
267
|
+
`/accounts/${accountId}/workers/scripts/${scriptName}/tails/${tailId}`,
|
|
268
|
+
null,
|
|
269
|
+
apiToken
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// --- Zones ---
|
|
274
|
+
|
|
275
|
+
export async function listZones(accountId, apiToken) {
|
|
276
|
+
var all = [];
|
|
277
|
+
var page = 1;
|
|
278
|
+
while (true) {
|
|
279
|
+
var res = await cfApi(
|
|
280
|
+
"GET",
|
|
281
|
+
`/zones?account.id=${accountId}&per_page=50&status=active&page=${page}`,
|
|
282
|
+
null,
|
|
283
|
+
apiToken
|
|
284
|
+
);
|
|
285
|
+
var zones = res.result || [];
|
|
286
|
+
all.push(...zones);
|
|
287
|
+
if (zones.length < 50) break;
|
|
288
|
+
page++;
|
|
289
|
+
}
|
|
290
|
+
return all;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function findZoneForHostname(zones, hostname) {
|
|
294
|
+
var match = null;
|
|
295
|
+
for (var zone of zones) {
|
|
296
|
+
if (hostname === zone.name || hostname.endsWith("." + zone.name)) {
|
|
297
|
+
if (!match || zone.name.length > match.name.length) {
|
|
298
|
+
match = zone;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return match;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// --- DNS records ---
|
|
306
|
+
|
|
307
|
+
export async function listDnsRecords(accountId, apiToken, zoneId, params) {
|
|
308
|
+
var qs = new URLSearchParams(params || {}).toString();
|
|
309
|
+
var path = `/zones/${zoneId}/dns_records${qs ? "?" + qs : ""}`;
|
|
310
|
+
var res = await cfApi("GET", path, null, apiToken);
|
|
311
|
+
return res.result || [];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export async function createDnsRecord(accountId, apiToken, zoneId, record) {
|
|
315
|
+
return cfApi(
|
|
316
|
+
"POST",
|
|
317
|
+
`/zones/${zoneId}/dns_records`,
|
|
318
|
+
record,
|
|
319
|
+
apiToken
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export async function deleteDnsRecord(accountId, apiToken, zoneId, recordId) {
|
|
324
|
+
return cfApi(
|
|
325
|
+
"DELETE",
|
|
326
|
+
`/zones/${zoneId}/dns_records/${recordId}`,
|
|
327
|
+
null,
|
|
328
|
+
apiToken
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// --- Workers custom domains ---
|
|
333
|
+
|
|
334
|
+
export async function addWorkerDomain(accountId, apiToken, scriptName, hostname, zoneId) {
|
|
335
|
+
return cfApi(
|
|
336
|
+
"PUT",
|
|
337
|
+
`/accounts/${accountId}/workers/domains`,
|
|
338
|
+
{
|
|
339
|
+
hostname,
|
|
340
|
+
zone_id: zoneId,
|
|
341
|
+
service: scriptName,
|
|
342
|
+
environment: "production",
|
|
343
|
+
},
|
|
344
|
+
apiToken
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export async function removeWorkerDomain(accountId, apiToken, hostname) {
|
|
349
|
+
var res = await cfApi(
|
|
350
|
+
"GET",
|
|
351
|
+
`/accounts/${accountId}/workers/domains`,
|
|
352
|
+
null,
|
|
353
|
+
apiToken
|
|
354
|
+
);
|
|
355
|
+
var domains = res.result || [];
|
|
356
|
+
var domain = domains.find((d) => d.hostname === hostname);
|
|
357
|
+
if (!domain) return;
|
|
358
|
+
|
|
359
|
+
return cfApi(
|
|
360
|
+
"DELETE",
|
|
361
|
+
`/accounts/${accountId}/workers/domains/${domain.id}`,
|
|
362
|
+
null,
|
|
363
|
+
apiToken
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export async function listWorkerDomainsForService(accountId, apiToken, scriptName) {
|
|
368
|
+
var res = await cfApi(
|
|
369
|
+
"GET",
|
|
370
|
+
`/accounts/${accountId}/workers/domains`,
|
|
371
|
+
null,
|
|
372
|
+
apiToken
|
|
373
|
+
);
|
|
374
|
+
return (res.result || []).filter((d) => d.service === scriptName);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// --- Workers subdomain ---
|
|
378
|
+
|
|
53
379
|
export async function getWorkersSubdomain(accountId, apiToken) {
|
|
54
380
|
try {
|
|
55
381
|
var res = await cfApi(
|
|
@@ -63,3 +389,76 @@ export async function getWorkersSubdomain(accountId, apiToken) {
|
|
|
63
389
|
return null;
|
|
64
390
|
}
|
|
65
391
|
}
|
|
392
|
+
|
|
393
|
+
export async function enableWorkerSubdomain(accountId, apiToken, scriptName) {
|
|
394
|
+
return cfApi(
|
|
395
|
+
"POST",
|
|
396
|
+
`/accounts/${accountId}/workers/services/${scriptName}/environments/production/subdomain`,
|
|
397
|
+
{ enabled: true },
|
|
398
|
+
apiToken
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// --- D1 databases ---
|
|
403
|
+
|
|
404
|
+
export async function createD1Database(accountId, apiToken, name, { locationHint, jurisdiction } = {}) {
|
|
405
|
+
var body = { name };
|
|
406
|
+
if (jurisdiction) body.jurisdiction = jurisdiction;
|
|
407
|
+
else if (locationHint) body.primary_location_hint = locationHint;
|
|
408
|
+
var res = await cfApi(
|
|
409
|
+
"POST",
|
|
410
|
+
`/accounts/${accountId}/d1/database`,
|
|
411
|
+
body,
|
|
412
|
+
apiToken
|
|
413
|
+
);
|
|
414
|
+
return res.result;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export async function deleteD1Database(accountId, apiToken, dbId) {
|
|
418
|
+
return cfApi(
|
|
419
|
+
"DELETE",
|
|
420
|
+
`/accounts/${accountId}/d1/database/${dbId}`,
|
|
421
|
+
null,
|
|
422
|
+
apiToken
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export async function getD1Database(accountId, apiToken, dbId) {
|
|
427
|
+
var res = await cfApi(
|
|
428
|
+
"GET",
|
|
429
|
+
`/accounts/${accountId}/d1/database/${dbId}`,
|
|
430
|
+
null,
|
|
431
|
+
apiToken
|
|
432
|
+
);
|
|
433
|
+
return res.result;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export async function queryD1(accountId, apiToken, dbId, sql, params) {
|
|
437
|
+
var body = { sql };
|
|
438
|
+
if (params) body.params = params;
|
|
439
|
+
var res = await cfApi(
|
|
440
|
+
"POST",
|
|
441
|
+
`/accounts/${accountId}/d1/database/${dbId}/query`,
|
|
442
|
+
body,
|
|
443
|
+
apiToken
|
|
444
|
+
);
|
|
445
|
+
return res.result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export async function exportD1(accountId, apiToken, dbId, body) {
|
|
449
|
+
return cfApi(
|
|
450
|
+
"POST",
|
|
451
|
+
`/accounts/${accountId}/d1/database/${dbId}/export`,
|
|
452
|
+
body,
|
|
453
|
+
apiToken
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export async function importD1(accountId, apiToken, dbId, body) {
|
|
458
|
+
return cfApi(
|
|
459
|
+
"POST",
|
|
460
|
+
`/accounts/${accountId}/d1/database/${dbId}/import`,
|
|
461
|
+
body,
|
|
462
|
+
apiToken
|
|
463
|
+
);
|
|
464
|
+
}
|