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/aws.js
CHANGED
|
@@ -87,33 +87,35 @@ export async function awsApi(method, service, host, path, body, credentials, reg
|
|
|
87
87
|
return res.text();
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
// --- JSON API (App Runner, ECR, CloudWatch Logs) ---
|
|
91
|
+
|
|
92
|
+
export async function awsJsonApi(target, body, service, credentials, region, host) {
|
|
93
|
+
if (!host) host = `${service}.${region}.amazonaws.com`;
|
|
94
|
+
|
|
93
95
|
var now = new Date();
|
|
94
96
|
var amzDate = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+/, "");
|
|
95
97
|
var dateStamp = amzDate.slice(0, 8);
|
|
96
98
|
|
|
97
|
-
var
|
|
98
|
-
|
|
99
|
-
Version: "2011-06-15",
|
|
100
|
-
});
|
|
101
|
-
var queryString = queryParams.toString();
|
|
99
|
+
var bodyStr = JSON.stringify(body || {});
|
|
100
|
+
var payloadHash = sha256(bodyStr);
|
|
102
101
|
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
var canonicalHeaders =
|
|
103
|
+
`content-type:application/x-amz-json-1.0\n` +
|
|
104
|
+
`host:${host}\n` +
|
|
105
|
+
`x-amz-date:${amzDate}\n` +
|
|
106
|
+
`x-amz-target:${target}\n`;
|
|
107
|
+
var signedHeaders = "content-type;host;x-amz-date;x-amz-target";
|
|
106
108
|
|
|
107
109
|
var canonicalRequest = [
|
|
108
|
-
"
|
|
110
|
+
"POST",
|
|
109
111
|
"/",
|
|
110
|
-
|
|
112
|
+
"",
|
|
111
113
|
canonicalHeaders,
|
|
112
114
|
signedHeaders,
|
|
113
115
|
payloadHash,
|
|
114
116
|
].join("\n");
|
|
115
117
|
|
|
116
|
-
var credentialScope = `${dateStamp}/
|
|
118
|
+
var credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
|
|
117
119
|
var stringToSign = [
|
|
118
120
|
"AWS4-HMAC-SHA256",
|
|
119
121
|
amzDate,
|
|
@@ -124,8 +126,8 @@ export async function verifyCredentials(credentials, region) {
|
|
|
124
126
|
var signingKey = getSignatureKey(
|
|
125
127
|
credentials.secretAccessKey,
|
|
126
128
|
dateStamp,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
region,
|
|
130
|
+
service
|
|
129
131
|
);
|
|
130
132
|
var signature = createHmac("sha256", signingKey)
|
|
131
133
|
.update(stringToSign)
|
|
@@ -137,38 +139,61 @@ export async function verifyCredentials(credentials, region) {
|
|
|
137
139
|
`SignedHeaders=${signedHeaders}, ` +
|
|
138
140
|
`Signature=${signature}`;
|
|
139
141
|
|
|
140
|
-
var res = await fetch(`https://${host}
|
|
141
|
-
method: "
|
|
142
|
+
var res = await fetch(`https://${host}/`, {
|
|
143
|
+
method: "POST",
|
|
142
144
|
headers: {
|
|
145
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
143
146
|
"X-Amz-Date": amzDate,
|
|
147
|
+
"X-Amz-Target": target,
|
|
144
148
|
Authorization: authHeader,
|
|
145
149
|
},
|
|
150
|
+
body: bodyStr,
|
|
146
151
|
});
|
|
147
152
|
|
|
148
153
|
if (!res.ok) {
|
|
149
154
|
var text = await res.text();
|
|
150
|
-
throw new Error(`
|
|
155
|
+
throw new Error(`AWS ${service} ${target}: ${res.status} ${text}`);
|
|
151
156
|
}
|
|
152
157
|
|
|
153
|
-
return res.
|
|
158
|
+
return res.json();
|
|
154
159
|
}
|
|
155
160
|
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
// --- Query API (RDS, EC2, IAM) ---
|
|
162
|
+
|
|
163
|
+
var QUERY_API_VERSIONS = {
|
|
164
|
+
rds: "2014-10-31",
|
|
165
|
+
ec2: "2016-11-15",
|
|
166
|
+
iam: "2010-05-08",
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export async function awsQueryApi(action, params, service, credentials, region, opts) {
|
|
170
|
+
opts = opts || {};
|
|
171
|
+
var version = opts.version || QUERY_API_VERSIONS[service] || "2012-10-17";
|
|
172
|
+
var host = opts.host || `${service}.${region}.amazonaws.com`;
|
|
173
|
+
|
|
158
174
|
var now = new Date();
|
|
159
175
|
var amzDate = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+/, "");
|
|
160
176
|
var dateStamp = amzDate.slice(0, 8);
|
|
161
177
|
|
|
162
|
-
var
|
|
178
|
+
var formParams = new URLSearchParams();
|
|
179
|
+
formParams.set("Action", action);
|
|
180
|
+
formParams.set("Version", version);
|
|
181
|
+
if (params) {
|
|
182
|
+
for (var [k, v] of Object.entries(params)) {
|
|
183
|
+
formParams.set(k, v);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
var bodyStr = formParams.toString();
|
|
163
187
|
var payloadHash = sha256(bodyStr);
|
|
164
188
|
|
|
165
|
-
var target = "AppRunner.ListServices";
|
|
166
189
|
var canonicalHeaders =
|
|
167
|
-
`content-type:application/x-
|
|
190
|
+
`content-type:application/x-www-form-urlencoded\n` +
|
|
168
191
|
`host:${host}\n` +
|
|
169
|
-
`x-amz-date:${amzDate}\n
|
|
170
|
-
|
|
171
|
-
|
|
192
|
+
`x-amz-date:${amzDate}\n`;
|
|
193
|
+
var signedHeaders = "content-type;host;x-amz-date";
|
|
194
|
+
|
|
195
|
+
// Use us-east-1 for global services like IAM
|
|
196
|
+
var signingRegion = service === "iam" ? "us-east-1" : region;
|
|
172
197
|
|
|
173
198
|
var canonicalRequest = [
|
|
174
199
|
"POST",
|
|
@@ -179,7 +204,7 @@ export async function checkAppRunner(credentials, region) {
|
|
|
179
204
|
payloadHash,
|
|
180
205
|
].join("\n");
|
|
181
206
|
|
|
182
|
-
var credentialScope = `${dateStamp}/${
|
|
207
|
+
var credentialScope = `${dateStamp}/${signingRegion}/${service}/aws4_request`;
|
|
183
208
|
var stringToSign = [
|
|
184
209
|
"AWS4-HMAC-SHA256",
|
|
185
210
|
amzDate,
|
|
@@ -190,8 +215,8 @@ export async function checkAppRunner(credentials, region) {
|
|
|
190
215
|
var signingKey = getSignatureKey(
|
|
191
216
|
credentials.secretAccessKey,
|
|
192
217
|
dateStamp,
|
|
193
|
-
|
|
194
|
-
|
|
218
|
+
signingRegion,
|
|
219
|
+
service
|
|
195
220
|
);
|
|
196
221
|
var signature = createHmac("sha256", signingKey)
|
|
197
222
|
.update(stringToSign)
|
|
@@ -206,9 +231,8 @@ export async function checkAppRunner(credentials, region) {
|
|
|
206
231
|
var res = await fetch(`https://${host}/`, {
|
|
207
232
|
method: "POST",
|
|
208
233
|
headers: {
|
|
209
|
-
"Content-Type": "application/x-
|
|
234
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
210
235
|
"X-Amz-Date": amzDate,
|
|
211
|
-
"X-Amz-Target": target,
|
|
212
236
|
Authorization: authHeader,
|
|
213
237
|
},
|
|
214
238
|
body: bodyStr,
|
|
@@ -216,8 +240,258 @@ export async function checkAppRunner(credentials, region) {
|
|
|
216
240
|
|
|
217
241
|
if (!res.ok) {
|
|
218
242
|
var text = await res.text();
|
|
219
|
-
throw new Error(`
|
|
243
|
+
throw new Error(`AWS ${service} ${action}: ${res.status} ${text}`);
|
|
220
244
|
}
|
|
221
245
|
|
|
222
|
-
return res.
|
|
246
|
+
return res.text();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// --- REST XML API (Route 53) ---
|
|
250
|
+
|
|
251
|
+
export async function awsRestXmlApi(method, path, body, credentials) {
|
|
252
|
+
var host = "route53.amazonaws.com";
|
|
253
|
+
var signingRegion = "us-east-1";
|
|
254
|
+
var service = "route53";
|
|
255
|
+
|
|
256
|
+
var now = new Date();
|
|
257
|
+
var amzDate = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+/, "");
|
|
258
|
+
var dateStamp = amzDate.slice(0, 8);
|
|
259
|
+
|
|
260
|
+
var bodyStr = body || "";
|
|
261
|
+
var payloadHash = sha256(bodyStr);
|
|
262
|
+
|
|
263
|
+
var canonicalHeaders;
|
|
264
|
+
var signedHeaders;
|
|
265
|
+
if (method === "GET") {
|
|
266
|
+
canonicalHeaders =
|
|
267
|
+
`host:${host}\n` +
|
|
268
|
+
`x-amz-date:${amzDate}\n`;
|
|
269
|
+
signedHeaders = "host;x-amz-date";
|
|
270
|
+
} else {
|
|
271
|
+
canonicalHeaders =
|
|
272
|
+
`content-type:application/xml\n` +
|
|
273
|
+
`host:${host}\n` +
|
|
274
|
+
`x-amz-date:${amzDate}\n`;
|
|
275
|
+
signedHeaders = "content-type;host;x-amz-date";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
var canonicalRequest = [
|
|
279
|
+
method,
|
|
280
|
+
path,
|
|
281
|
+
"",
|
|
282
|
+
canonicalHeaders,
|
|
283
|
+
signedHeaders,
|
|
284
|
+
payloadHash,
|
|
285
|
+
].join("\n");
|
|
286
|
+
|
|
287
|
+
var credentialScope = `${dateStamp}/${signingRegion}/${service}/aws4_request`;
|
|
288
|
+
var stringToSign = [
|
|
289
|
+
"AWS4-HMAC-SHA256",
|
|
290
|
+
amzDate,
|
|
291
|
+
credentialScope,
|
|
292
|
+
sha256(canonicalRequest),
|
|
293
|
+
].join("\n");
|
|
294
|
+
|
|
295
|
+
var signingKey = getSignatureKey(
|
|
296
|
+
credentials.secretAccessKey,
|
|
297
|
+
dateStamp,
|
|
298
|
+
signingRegion,
|
|
299
|
+
service
|
|
300
|
+
);
|
|
301
|
+
var signature = createHmac("sha256", signingKey)
|
|
302
|
+
.update(stringToSign)
|
|
303
|
+
.digest("hex");
|
|
304
|
+
|
|
305
|
+
var authHeader =
|
|
306
|
+
`AWS4-HMAC-SHA256 ` +
|
|
307
|
+
`Credential=${credentials.accessKeyId}/${credentialScope}, ` +
|
|
308
|
+
`SignedHeaders=${signedHeaders}, ` +
|
|
309
|
+
`Signature=${signature}`;
|
|
310
|
+
|
|
311
|
+
var headers = {
|
|
312
|
+
"X-Amz-Date": amzDate,
|
|
313
|
+
Authorization: authHeader,
|
|
314
|
+
};
|
|
315
|
+
if (method !== "GET") {
|
|
316
|
+
headers["Content-Type"] = "application/xml";
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
var res = await fetch(`https://${host}${path}`, {
|
|
320
|
+
method,
|
|
321
|
+
headers,
|
|
322
|
+
body: method === "GET" ? undefined : bodyStr,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
if (!res.ok) {
|
|
326
|
+
var text = await res.text();
|
|
327
|
+
throw new Error(`AWS Route 53 ${method} ${path}: ${res.status} ${text}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return res.text();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// --- XML helpers ---
|
|
334
|
+
|
|
335
|
+
export function xmlVal(xml, tag) {
|
|
336
|
+
var match = xml.match(new RegExp(`<${tag}>([^<]*)</${tag}>`));
|
|
337
|
+
return match ? match[1] : null;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function xmlList(xml, tag) {
|
|
341
|
+
var results = [];
|
|
342
|
+
var re = new RegExp(`<${tag}>[\\s\\S]*?</${tag}>`, "g");
|
|
343
|
+
var match;
|
|
344
|
+
while ((match = re.exec(xml)) !== null) {
|
|
345
|
+
results.push(match[0]);
|
|
346
|
+
}
|
|
347
|
+
return results;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function xmlBlock(xml, tag) {
|
|
351
|
+
var match = xml.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
|
|
352
|
+
return match ? match[1] : null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// --- STS / Credential verification ---
|
|
356
|
+
|
|
357
|
+
export async function verifyCredentials(credentials, region) {
|
|
358
|
+
// Use STS GetCallerIdentity to verify credentials
|
|
359
|
+
var host = "sts.amazonaws.com";
|
|
360
|
+
var now = new Date();
|
|
361
|
+
var amzDate = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+/, "");
|
|
362
|
+
var dateStamp = amzDate.slice(0, 8);
|
|
363
|
+
|
|
364
|
+
var queryParams = new URLSearchParams({
|
|
365
|
+
Action: "GetCallerIdentity",
|
|
366
|
+
Version: "2011-06-15",
|
|
367
|
+
});
|
|
368
|
+
var queryString = queryParams.toString();
|
|
369
|
+
|
|
370
|
+
var payloadHash = sha256("");
|
|
371
|
+
var canonicalHeaders = `host:${host}\nx-amz-date:${amzDate}\n`;
|
|
372
|
+
var signedHeaders = "host;x-amz-date";
|
|
373
|
+
|
|
374
|
+
var canonicalRequest = [
|
|
375
|
+
"GET",
|
|
376
|
+
"/",
|
|
377
|
+
queryString,
|
|
378
|
+
canonicalHeaders,
|
|
379
|
+
signedHeaders,
|
|
380
|
+
payloadHash,
|
|
381
|
+
].join("\n");
|
|
382
|
+
|
|
383
|
+
var credentialScope = `${dateStamp}/us-east-1/sts/aws4_request`;
|
|
384
|
+
var stringToSign = [
|
|
385
|
+
"AWS4-HMAC-SHA256",
|
|
386
|
+
amzDate,
|
|
387
|
+
credentialScope,
|
|
388
|
+
sha256(canonicalRequest),
|
|
389
|
+
].join("\n");
|
|
390
|
+
|
|
391
|
+
var signingKey = getSignatureKey(
|
|
392
|
+
credentials.secretAccessKey,
|
|
393
|
+
dateStamp,
|
|
394
|
+
"us-east-1",
|
|
395
|
+
"sts"
|
|
396
|
+
);
|
|
397
|
+
var signature = createHmac("sha256", signingKey)
|
|
398
|
+
.update(stringToSign)
|
|
399
|
+
.digest("hex");
|
|
400
|
+
|
|
401
|
+
var authHeader =
|
|
402
|
+
`AWS4-HMAC-SHA256 ` +
|
|
403
|
+
`Credential=${credentials.accessKeyId}/${credentialScope}, ` +
|
|
404
|
+
`SignedHeaders=${signedHeaders}, ` +
|
|
405
|
+
`Signature=${signature}`;
|
|
406
|
+
|
|
407
|
+
var res = await fetch(`https://${host}/?${queryString}`, {
|
|
408
|
+
method: "GET",
|
|
409
|
+
headers: {
|
|
410
|
+
"X-Amz-Date": amzDate,
|
|
411
|
+
Authorization: authHeader,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
if (!res.ok) {
|
|
416
|
+
var text = await res.text();
|
|
417
|
+
throw new Error(`STS GetCallerIdentity failed: ${res.status} ${text}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return res.text();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// --- Account ID ---
|
|
424
|
+
|
|
425
|
+
export async function getAccountId(credentials, region) {
|
|
426
|
+
var xml = await verifyCredentials(credentials, region);
|
|
427
|
+
return xmlVal(xml, "Account");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// --- IAM: ensure ECR access role for App Runner ---
|
|
431
|
+
|
|
432
|
+
export async function ensureEcrAccessRole(credentials, region) {
|
|
433
|
+
var roleName = "relight-apprunner-ecr";
|
|
434
|
+
var cr = { accessKeyId: credentials.accessKeyId, secretAccessKey: credentials.secretAccessKey };
|
|
435
|
+
|
|
436
|
+
// Check if role exists
|
|
437
|
+
try {
|
|
438
|
+
var xml = await awsQueryApi("GetRole", { RoleName: roleName }, "iam", cr, region, {
|
|
439
|
+
host: "iam.amazonaws.com",
|
|
440
|
+
});
|
|
441
|
+
var arn = xmlVal(xml, "Arn");
|
|
442
|
+
if (arn) return arn;
|
|
443
|
+
} catch {
|
|
444
|
+
// Role doesn't exist, create it
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Trust policy for App Runner build service
|
|
448
|
+
var trustPolicy = JSON.stringify({
|
|
449
|
+
Version: "2012-10-17",
|
|
450
|
+
Statement: [
|
|
451
|
+
{
|
|
452
|
+
Effect: "Allow",
|
|
453
|
+
Principal: { Service: "build.apprunner.amazonaws.com" },
|
|
454
|
+
Action: "sts:AssumeRole",
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
var createXml = await awsQueryApi(
|
|
460
|
+
"CreateRole",
|
|
461
|
+
{
|
|
462
|
+
RoleName: roleName,
|
|
463
|
+
AssumeRolePolicyDocument: trustPolicy,
|
|
464
|
+
Description: "Allows App Runner to pull images from ECR",
|
|
465
|
+
},
|
|
466
|
+
"iam",
|
|
467
|
+
cr,
|
|
468
|
+
region,
|
|
469
|
+
{ host: "iam.amazonaws.com" }
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
var arn = xmlVal(createXml, "Arn");
|
|
473
|
+
|
|
474
|
+
// Attach ECR access policy
|
|
475
|
+
await awsQueryApi(
|
|
476
|
+
"AttachRolePolicy",
|
|
477
|
+
{
|
|
478
|
+
RoleName: roleName,
|
|
479
|
+
PolicyArn: "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess",
|
|
480
|
+
},
|
|
481
|
+
"iam",
|
|
482
|
+
cr,
|
|
483
|
+
region,
|
|
484
|
+
{ host: "iam.amazonaws.com" }
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
// Wait a moment for IAM propagation
|
|
488
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
489
|
+
|
|
490
|
+
return arn;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// --- App Runner check (refactored to use awsJsonApi) ---
|
|
494
|
+
|
|
495
|
+
export async function checkAppRunner(credentials, region) {
|
|
496
|
+
return awsJsonApi("AppRunner.ListServices", {}, "apprunner", credentials, region);
|
|
223
497
|
}
|