relight-cli 0.2.0 → 0.4.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/package.json +1 -1
- package/src/cli.js +94 -49
- package/src/commands/apps.js +13 -10
- package/src/commands/auth.js +3 -63
- package/src/commands/config.js +16 -16
- package/src/commands/cost.js +6 -6
- package/src/commands/db.js +434 -190
- package/src/commands/deploy.js +38 -72
- package/src/commands/doctor.js +30 -6
- package/src/commands/domains.js +41 -24
- package/src/commands/logs.js +4 -4
- package/src/commands/open.js +4 -4
- package/src/commands/ps.js +20 -15
- package/src/commands/scale.js +4 -4
- package/src/commands/service.js +227 -0
- package/src/lib/clouds/gcp.js +97 -1
- package/src/lib/clouds/neon.js +147 -0
- package/src/lib/config.js +169 -11
- package/src/lib/link.js +13 -2
- package/src/lib/providers/aws/db.js +217 -226
- package/src/lib/providers/aws/dns.js +1 -1
- package/src/lib/providers/cf/db.js +33 -131
- package/src/lib/providers/cf/dns.js +3 -3
- package/src/lib/providers/gcp/app.js +82 -7
- package/src/lib/providers/gcp/db.js +169 -254
- package/src/lib/providers/gcp/dns.js +9 -7
- package/src/lib/providers/neon/db.js +306 -0
- package/src/lib/providers/resolve.js +33 -3
package/src/commands/deploy.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { createInterface } from "readline";
|
|
2
|
-
import { randomBytes } from "crypto";
|
|
3
2
|
import { phase, status, success, hint, fatal, fmt, generateAppName } from "../lib/output.js";
|
|
4
3
|
import { readLink, linkApp, resolveAppName } from "../lib/link.js";
|
|
5
|
-
import {
|
|
4
|
+
import { resolveTarget } from "../lib/providers/resolve.js";
|
|
6
5
|
import { dockerBuild, dockerTag, dockerPush, dockerLogin } from "../lib/docker.js";
|
|
7
6
|
|
|
8
7
|
export async function deploy(nameOrPath, path, options) {
|
|
9
|
-
var
|
|
10
|
-
var cfg =
|
|
8
|
+
var target = await resolveTarget(options);
|
|
9
|
+
var cfg = target.cfg;
|
|
11
10
|
|
|
12
11
|
// Smart arg parsing: if first arg looks like a path, shift args
|
|
13
12
|
var name;
|
|
@@ -26,12 +25,12 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
26
25
|
var tag = options.tag || `${Date.now()}`;
|
|
27
26
|
|
|
28
27
|
// Get registry credentials and image tag
|
|
29
|
-
var registry = await
|
|
28
|
+
var registry = await target.provider("registry");
|
|
30
29
|
var remoteTag = await registry.getImageTag(cfg, name, tag);
|
|
31
30
|
var localTag = `relight-${name}:${tag}`;
|
|
32
31
|
|
|
33
32
|
// Load existing config from deployed worker (null on first deploy)
|
|
34
|
-
var appProvider = await
|
|
33
|
+
var appProvider = await target.provider("app");
|
|
35
34
|
var appConfig;
|
|
36
35
|
try {
|
|
37
36
|
appConfig = await appProvider.getAppConfig(cfg, name);
|
|
@@ -41,7 +40,7 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
41
40
|
|
|
42
41
|
var isFirstDeploy = !appConfig;
|
|
43
42
|
|
|
44
|
-
// Get valid regions for this cloud
|
|
43
|
+
// Get valid regions for this cloud/service
|
|
45
44
|
var validRegions = appProvider.getRegions();
|
|
46
45
|
var validCodes = validRegions.map((r) => r.code);
|
|
47
46
|
|
|
@@ -89,12 +88,13 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
var
|
|
91
|
+
var isService = target.kind === "service";
|
|
92
|
+
var defaultRegion = isService ? "self-hosted" : target.type === "gcp" ? "us-central1" : target.type === "aws" ? "us-east-1" : "enam";
|
|
93
93
|
var regions;
|
|
94
94
|
|
|
95
95
|
if (options.regions) {
|
|
96
96
|
regions = options.regions.split(",").map((r) => r.trim());
|
|
97
|
-
} else if ((
|
|
97
|
+
} else if (!isService && (target.type === "gcp" || target.type === "aws") && process.stdin.isTTY) {
|
|
98
98
|
// Interactive region picker for GCP/AWS first deploy
|
|
99
99
|
var { createInterface: createRL } = await import("readline");
|
|
100
100
|
var rl = createRL({ input: process.stdin, output: process.stderr });
|
|
@@ -130,10 +130,10 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
130
130
|
appConfig = {
|
|
131
131
|
name,
|
|
132
132
|
regions,
|
|
133
|
-
instances: options.instances || (
|
|
133
|
+
instances: options.instances || (isService ? 1 : 2),
|
|
134
134
|
port: options.port || 8080,
|
|
135
135
|
sleepAfter: options.sleep || "30s",
|
|
136
|
-
instanceType: options.instanceType || (
|
|
136
|
+
instanceType: options.instanceType || (isService || target.type === "gcp" || target.type === "aws" ? undefined : "lite"),
|
|
137
137
|
vcpu: options.vcpu || undefined,
|
|
138
138
|
memory: options.memory || undefined,
|
|
139
139
|
disk: options.disk || undefined,
|
|
@@ -147,48 +147,7 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
// --- D1 database (--db flag, CF only) ---
|
|
151
150
|
var newSecrets = {};
|
|
152
|
-
if (options.db && !appConfig.dbId) {
|
|
153
|
-
if (cloud === "gcp") {
|
|
154
|
-
process.stderr.write(
|
|
155
|
-
`\n${fmt.dim("Cloud SQL provisioning takes 3-10 minutes. Use 'relight db create' separately after deploy.")}\n\n`
|
|
156
|
-
);
|
|
157
|
-
} else if (cloud === "aws") {
|
|
158
|
-
process.stderr.write(
|
|
159
|
-
`\n${fmt.dim("RDS provisioning takes 5-15 minutes. Use 'relight db create' separately after deploy.")}\n\n`
|
|
160
|
-
);
|
|
161
|
-
} else if (cloud === "cf") {
|
|
162
|
-
var { createD1Database, getWorkersSubdomain } = await import("../lib/clouds/cf.js");
|
|
163
|
-
var dbResult = await createD1Database(cfg.accountId, cfg.apiToken, `relight-${name}`, {
|
|
164
|
-
locationHint: options.dbLocation,
|
|
165
|
-
jurisdiction: options.dbJurisdiction,
|
|
166
|
-
});
|
|
167
|
-
appConfig.dbId = dbResult.uuid;
|
|
168
|
-
appConfig.dbName = `relight-${name}`;
|
|
169
|
-
|
|
170
|
-
if (!appConfig.envKeys) appConfig.envKeys = [];
|
|
171
|
-
if (!appConfig.secretKeys) appConfig.secretKeys = [];
|
|
172
|
-
if (!appConfig.env) appConfig.env = {};
|
|
173
|
-
|
|
174
|
-
var subdomain = await getWorkersSubdomain(cfg.accountId, cfg.apiToken);
|
|
175
|
-
var dbUrl = subdomain
|
|
176
|
-
? `https://relight-${name}.${subdomain}.workers.dev`
|
|
177
|
-
: null;
|
|
178
|
-
|
|
179
|
-
if (dbUrl) {
|
|
180
|
-
appConfig.env["DB_URL"] = dbUrl;
|
|
181
|
-
if (!appConfig.envKeys.includes("DB_URL")) appConfig.envKeys.push("DB_URL");
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
appConfig.env["DB_TOKEN"] = "[hidden]";
|
|
185
|
-
appConfig.secretKeys = appConfig.secretKeys.filter((k) => k !== "DB_TOKEN");
|
|
186
|
-
appConfig.secretKeys.push("DB_TOKEN");
|
|
187
|
-
appConfig.envKeys = appConfig.envKeys.filter((k) => k !== "DB_TOKEN");
|
|
188
|
-
|
|
189
|
-
newSecrets.DB_TOKEN = randomBytes(32).toString("hex");
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
151
|
|
|
193
152
|
// --- Summary & confirmation ---
|
|
194
153
|
var instanceDesc = appConfig.vcpu
|
|
@@ -198,7 +157,11 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
198
157
|
process.stderr.write(`\n${fmt.bold("Deploy summary")}\n`);
|
|
199
158
|
process.stderr.write(`${fmt.dim("-".repeat(40))}\n`);
|
|
200
159
|
process.stderr.write(` ${fmt.bold("App:")} ${fmt.app(name)}${isFirstDeploy ? fmt.dim(" (new)") : ""}\n`);
|
|
201
|
-
|
|
160
|
+
if (target.kind === "service") {
|
|
161
|
+
process.stderr.write(` ${fmt.bold("Service:")} ${fmt.cloud(target.id)} ${fmt.dim(`(${target.type})`)}\n`);
|
|
162
|
+
} else {
|
|
163
|
+
process.stderr.write(` ${fmt.bold("Cloud:")} ${fmt.cloud(target.id)}\n`);
|
|
164
|
+
}
|
|
202
165
|
process.stderr.write(` ${fmt.bold("Path:")} ${dockerPath}\n`);
|
|
203
166
|
process.stderr.write(` ${fmt.bold("Image:")} ${remoteTag}\n`);
|
|
204
167
|
process.stderr.write(` ${fmt.bold("Regions:")} ${appConfig.regions.join(", ")}\n`);
|
|
@@ -225,7 +188,7 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
225
188
|
|
|
226
189
|
// 1. Build Docker image
|
|
227
190
|
var platform = "linux/amd64";
|
|
228
|
-
if (
|
|
191
|
+
if (target.type === "slicervm") {
|
|
229
192
|
// Match VM architecture
|
|
230
193
|
var { listNodes } = await import("../lib/clouds/slicervm.js");
|
|
231
194
|
var nodes = await listNodes(cfg);
|
|
@@ -236,8 +199,8 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
236
199
|
status(`${localTag} for ${platform}`);
|
|
237
200
|
dockerBuild(dockerPath, localTag, { platform });
|
|
238
201
|
|
|
239
|
-
if (
|
|
240
|
-
//
|
|
202
|
+
if (target.kind === "service") {
|
|
203
|
+
// Service: skip registry push - deploy extracts and uploads the image directly
|
|
241
204
|
phase("Deploying");
|
|
242
205
|
await appProvider.deploy(cfg, name, localTag, {
|
|
243
206
|
appConfig,
|
|
@@ -268,21 +231,20 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
268
231
|
var url = await appProvider.getAppUrl(cfg, name);
|
|
269
232
|
|
|
270
233
|
if (options.json) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
);
|
|
234
|
+
var result = {
|
|
235
|
+
name,
|
|
236
|
+
image: remoteTag,
|
|
237
|
+
url,
|
|
238
|
+
regions: appConfig.regions,
|
|
239
|
+
instances: appConfig.instances,
|
|
240
|
+
firstDeploy: isFirstDeploy,
|
|
241
|
+
};
|
|
242
|
+
if (target.kind === "service") {
|
|
243
|
+
result.service = target.id;
|
|
244
|
+
} else {
|
|
245
|
+
result.cloud = target.id;
|
|
246
|
+
}
|
|
247
|
+
console.log(JSON.stringify(result, null, 2));
|
|
286
248
|
} else {
|
|
287
249
|
success(`App ${fmt.app(name)} deployed!`);
|
|
288
250
|
process.stderr.write(` ${fmt.bold("Name:")} ${fmt.app(name)}\n`);
|
|
@@ -294,5 +256,9 @@ export async function deploy(nameOrPath, path, options) {
|
|
|
294
256
|
}
|
|
295
257
|
|
|
296
258
|
// Link this directory to the app
|
|
297
|
-
|
|
259
|
+
if (target.kind === "service") {
|
|
260
|
+
linkApp(name, null, options.dns, null, target.id);
|
|
261
|
+
} else {
|
|
262
|
+
linkApp(name, target.id, options.dns);
|
|
263
|
+
}
|
|
298
264
|
}
|
package/src/commands/doctor.js
CHANGED
|
@@ -4,10 +4,14 @@ import {
|
|
|
4
4
|
tryGetConfig,
|
|
5
5
|
CONFIG_PATH,
|
|
6
6
|
CLOUD_NAMES,
|
|
7
|
+
SERVICE_TYPES,
|
|
8
|
+
getRegisteredServices,
|
|
9
|
+
normalizeServiceConfig,
|
|
7
10
|
} from "../lib/config.js";
|
|
8
11
|
import { verifyToken as cfVerify, getWorkersSubdomain } from "../lib/clouds/cf.js";
|
|
9
|
-
import { mintAccessToken, verifyProject as gcpVerifyProject,
|
|
12
|
+
import { mintAccessToken, verifyProject as gcpVerifyProject, listAllServices as gcpListServices, gcpApi, AR_API, SQLADMIN_API, DNS_API } from "../lib/clouds/gcp.js";
|
|
10
13
|
import { verifyCredentials as awsVerify, checkAppRunner, awsJsonApi, awsQueryApi, awsRestXmlApi } from "../lib/clouds/aws.js";
|
|
14
|
+
import { verifyConnection as slicerVerify } from "../lib/clouds/slicervm.js";
|
|
11
15
|
import kleur from "kleur";
|
|
12
16
|
|
|
13
17
|
var PASS = kleur.green("[ok]");
|
|
@@ -16,7 +20,7 @@ var SKIP = kleur.yellow("[--]");
|
|
|
16
20
|
|
|
17
21
|
export async function doctor() {
|
|
18
22
|
process.stderr.write(`\n${kleur.bold("relight doctor")}\n`);
|
|
19
|
-
process.stderr.write(`${kleur.dim("
|
|
23
|
+
process.stderr.write(`${kleur.dim("-".repeat(50))}\n\n`);
|
|
20
24
|
var allGood = true;
|
|
21
25
|
|
|
22
26
|
// --- General checks ---
|
|
@@ -73,9 +77,29 @@ export async function doctor() {
|
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
80
|
+
// --- Services ---
|
|
81
|
+
|
|
82
|
+
var services = getRegisteredServices();
|
|
83
|
+
if (services.length > 0) {
|
|
84
|
+
process.stderr.write(`\n${kleur.bold(" Services")}\n`);
|
|
85
|
+
|
|
86
|
+
for (var service of services) {
|
|
87
|
+
var typeName = SERVICE_TYPES[service.type]?.name || service.type;
|
|
88
|
+
var endpoint = service.socketPath || service.apiUrl || "unknown";
|
|
89
|
+
|
|
90
|
+
allGood =
|
|
91
|
+
(await asyncCheck(`${service.name} (${typeName} - ${endpoint})`, async () => {
|
|
92
|
+
if (service.type === "slicervm") {
|
|
93
|
+
var cfg = normalizeServiceConfig(service);
|
|
94
|
+
await slicerVerify(cfg);
|
|
95
|
+
}
|
|
96
|
+
})) && allGood;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
76
100
|
// --- Summary ---
|
|
77
101
|
|
|
78
|
-
process.stderr.write(`\n${kleur.dim("
|
|
102
|
+
process.stderr.write(`\n${kleur.dim("-".repeat(50))}\n`);
|
|
79
103
|
if (allGood) {
|
|
80
104
|
process.stderr.write(kleur.green("All checks passed.\n\n"));
|
|
81
105
|
} else {
|
|
@@ -132,7 +156,7 @@ async function checkGCP(cfg) {
|
|
|
132
156
|
|
|
133
157
|
ok =
|
|
134
158
|
(await asyncCheck("Cloud Run API reachable", async () => {
|
|
135
|
-
await
|
|
159
|
+
await gcpListServices(token, cfg.project);
|
|
136
160
|
})) && ok;
|
|
137
161
|
|
|
138
162
|
ok =
|
|
@@ -204,7 +228,7 @@ function check(label, fn) {
|
|
|
204
228
|
return true;
|
|
205
229
|
} catch (e) {
|
|
206
230
|
process.stderr.write(` ${FAIL} ${label}`);
|
|
207
|
-
if (e.message) process.stderr.write(kleur.dim(`
|
|
231
|
+
if (e.message) process.stderr.write(kleur.dim(` - ${e.message}`));
|
|
208
232
|
process.stderr.write("\n");
|
|
209
233
|
return false;
|
|
210
234
|
}
|
|
@@ -217,7 +241,7 @@ async function asyncCheck(label, fn) {
|
|
|
217
241
|
return true;
|
|
218
242
|
} catch (e) {
|
|
219
243
|
process.stderr.write(` ${FAIL} ${label}`);
|
|
220
|
-
if (e.message) process.stderr.write(kleur.dim(`
|
|
244
|
+
if (e.message) process.stderr.write(kleur.dim(` - ${truncate(e.message, 80)}`));
|
|
221
245
|
process.stderr.write("\n");
|
|
222
246
|
return false;
|
|
223
247
|
}
|
package/src/commands/domains.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "readline";
|
|
2
2
|
import { phase, status, success, fatal, hint, fmt } from "../lib/output.js";
|
|
3
3
|
import { resolveAppName, resolveDns, readLink, linkApp } from "../lib/link.js";
|
|
4
|
-
import { resolveCloudId, getCloudCfg, getProvider } from "../lib/providers/resolve.js";
|
|
4
|
+
import { resolveTarget, resolveCloudId, getCloudCfg, getProvider } from "../lib/providers/resolve.js";
|
|
5
5
|
import kleur from "kleur";
|
|
6
6
|
|
|
7
7
|
function prompt(rl, question) {
|
|
@@ -10,9 +10,9 @@ function prompt(rl, question) {
|
|
|
10
10
|
|
|
11
11
|
export async function domainsList(name, options) {
|
|
12
12
|
name = resolveAppName(name);
|
|
13
|
-
var
|
|
14
|
-
var cfg =
|
|
15
|
-
var dnsProvider = await
|
|
13
|
+
var target = await resolveTarget(options);
|
|
14
|
+
var cfg = target.cfg;
|
|
15
|
+
var dnsProvider = await target.provider("dns");
|
|
16
16
|
|
|
17
17
|
var result = await dnsProvider.listDomains(cfg, name);
|
|
18
18
|
|
|
@@ -58,16 +58,16 @@ export async function domainsAdd(args, options) {
|
|
|
58
58
|
domain = null;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
var
|
|
62
|
-
var appCfg =
|
|
63
|
-
var appProvider = await
|
|
61
|
+
var target = await resolveTarget(options);
|
|
62
|
+
var appCfg = target.cfg;
|
|
63
|
+
var appProvider = await target.provider("app");
|
|
64
64
|
|
|
65
65
|
// Cross-cloud DNS: --dns flag or .relight dns field specifies a different cloud for DNS records
|
|
66
66
|
var dnsFlag = options.dns || resolveDns();
|
|
67
|
-
var crossCloud = dnsFlag && resolveCloudId(dnsFlag) !==
|
|
68
|
-
var dnsCloud = crossCloud ? resolveCloudId(dnsFlag) :
|
|
67
|
+
var crossCloud = dnsFlag && (target.kind === "service" || resolveCloudId(dnsFlag) !== target.id);
|
|
68
|
+
var dnsCloud = crossCloud ? resolveCloudId(dnsFlag) : (target.kind === "cloud" ? target.id : null);
|
|
69
69
|
var dnsCfg = crossCloud ? getCloudCfg(dnsCloud) : appCfg;
|
|
70
|
-
var dnsProvider = await getProvider(dnsCloud, "dns");
|
|
70
|
+
var dnsProvider = crossCloud ? await getProvider(dnsCloud, "dns") : await target.provider("dns");
|
|
71
71
|
|
|
72
72
|
var appConfig = await appProvider.getAppConfig(appCfg, name);
|
|
73
73
|
if (!appConfig) {
|
|
@@ -144,18 +144,29 @@ export async function domainsAdd(args, options) {
|
|
|
144
144
|
rl.close();
|
|
145
145
|
|
|
146
146
|
if (crossCloud) {
|
|
147
|
-
// Cross-cloud:
|
|
147
|
+
// Cross-cloud: set up domain mapping on the app cloud first (e.g. Firebase Hosting for GCP)
|
|
148
|
+
var dnsTarget;
|
|
149
|
+
var dnsProxied = true;
|
|
150
|
+
if (appProvider.mapCustomDomain) {
|
|
151
|
+
status(`Setting up hosting for ${domain}...`);
|
|
152
|
+
var mapping = await appProvider.mapCustomDomain(appCfg, name, domain);
|
|
153
|
+
dnsTarget = mapping.dnsTarget;
|
|
154
|
+
if (mapping.proxied === false) dnsProxied = false;
|
|
155
|
+
} else {
|
|
156
|
+
var appUrl = await appProvider.getAppUrl(appCfg, name);
|
|
157
|
+
dnsTarget = new URL(appUrl).hostname;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Create DNS record pointing to the hosting provider
|
|
148
161
|
status(`Creating DNS record for ${domain}...`);
|
|
149
|
-
var appUrl = await appProvider.getAppUrl(appCfg, name);
|
|
150
|
-
var target = new URL(appUrl).hostname;
|
|
151
162
|
try {
|
|
152
|
-
await dnsProvider.addDnsRecord(dnsCfg, domain,
|
|
163
|
+
await dnsProvider.addDnsRecord(dnsCfg, domain, dnsTarget, zone, { proxied: dnsProxied });
|
|
153
164
|
} catch (e) {
|
|
154
165
|
fatal(e.message);
|
|
155
166
|
}
|
|
156
167
|
|
|
157
|
-
// Update app config on the app cloud
|
|
158
|
-
status(`Updating app config
|
|
168
|
+
// Update app config on the app cloud/service
|
|
169
|
+
status(`Updating app config...`);
|
|
159
170
|
if (!appConfig.domains) appConfig.domains = [];
|
|
160
171
|
if (!appConfig.domains.includes(domain)) {
|
|
161
172
|
appConfig.domains.push(domain);
|
|
@@ -165,7 +176,7 @@ export async function domainsAdd(args, options) {
|
|
|
165
176
|
// Persist dns cloud in .relight so future commands don't need --dns
|
|
166
177
|
var linked = readLink();
|
|
167
178
|
if (linked && !linked.dns) {
|
|
168
|
-
linkApp(linked.app, linked.cloud, dnsCloud);
|
|
179
|
+
linkApp(linked.app, linked.cloud, dnsCloud, undefined, linked.compute);
|
|
169
180
|
}
|
|
170
181
|
} else {
|
|
171
182
|
// Same-cloud: existing flow
|
|
@@ -193,22 +204,28 @@ export async function domainsRemove(args, options) {
|
|
|
193
204
|
fatal("Usage: relight domains remove [name] <domain>");
|
|
194
205
|
}
|
|
195
206
|
|
|
196
|
-
var
|
|
197
|
-
var appCfg =
|
|
207
|
+
var target = await resolveTarget(options);
|
|
208
|
+
var appCfg = target.cfg;
|
|
198
209
|
|
|
199
210
|
var dnsFlag = options.dns || resolveDns();
|
|
200
|
-
var crossCloud = dnsFlag && resolveCloudId(dnsFlag) !==
|
|
201
|
-
var dnsCloud = crossCloud ? resolveCloudId(dnsFlag) :
|
|
211
|
+
var crossCloud = dnsFlag && (target.kind === "service" || resolveCloudId(dnsFlag) !== target.id);
|
|
212
|
+
var dnsCloud = crossCloud ? resolveCloudId(dnsFlag) : (target.kind === "cloud" ? target.id : null);
|
|
202
213
|
var dnsCfg = crossCloud ? getCloudCfg(dnsCloud) : appCfg;
|
|
203
|
-
var dnsProvider = await getProvider(dnsCloud, "dns");
|
|
214
|
+
var dnsProvider = crossCloud ? await getProvider(dnsCloud, "dns") : await target.provider("dns");
|
|
204
215
|
|
|
205
216
|
status(`Removing ${domain}...`);
|
|
206
217
|
|
|
207
218
|
if (crossCloud) {
|
|
208
|
-
// Cross-cloud: remove DNS record from dns cloud, update app config on app cloud
|
|
219
|
+
// Cross-cloud: remove DNS record from dns cloud, update app config on app cloud/service
|
|
209
220
|
await dnsProvider.removeDnsRecord(dnsCfg, domain);
|
|
210
221
|
|
|
211
|
-
var appProvider = await
|
|
222
|
+
var appProvider = await target.provider("app");
|
|
223
|
+
|
|
224
|
+
// Remove domain mapping on the app cloud if supported
|
|
225
|
+
if (appProvider.unmapCustomDomain) {
|
|
226
|
+
await appProvider.unmapCustomDomain(appCfg, name, domain);
|
|
227
|
+
}
|
|
228
|
+
|
|
212
229
|
var appConfig = await appProvider.getAppConfig(appCfg, name);
|
|
213
230
|
if (appConfig) {
|
|
214
231
|
appConfig.domains = (appConfig.domains || []).filter((d) => d !== domain);
|
package/src/commands/logs.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { fatal, fmt } from "../lib/output.js";
|
|
2
2
|
import { resolveAppName } from "../lib/link.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveTarget } from "../lib/providers/resolve.js";
|
|
4
4
|
import kleur from "kleur";
|
|
5
5
|
|
|
6
6
|
export async function logs(name, options) {
|
|
7
7
|
name = resolveAppName(name);
|
|
8
|
-
var
|
|
9
|
-
var cfg =
|
|
10
|
-
var appProvider = await
|
|
8
|
+
var target = await resolveTarget(options);
|
|
9
|
+
var cfg = target.cfg;
|
|
10
|
+
var appProvider = await target.provider("app");
|
|
11
11
|
|
|
12
12
|
process.stderr.write(
|
|
13
13
|
`Tailing logs for ${fmt.app(name)}... ${fmt.dim("(ctrl+c to stop)")}\n\n`
|
package/src/commands/open.js
CHANGED
|
@@ -2,13 +2,13 @@ import { execSync } from "child_process";
|
|
|
2
2
|
import { platform } from "os";
|
|
3
3
|
import { fatal, fmt } from "../lib/output.js";
|
|
4
4
|
import { resolveAppName } from "../lib/link.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveTarget } from "../lib/providers/resolve.js";
|
|
6
6
|
|
|
7
7
|
export async function open(name, options) {
|
|
8
8
|
name = resolveAppName(name);
|
|
9
|
-
var
|
|
10
|
-
var cfg =
|
|
11
|
-
var appProvider = await
|
|
9
|
+
var target = await resolveTarget(options);
|
|
10
|
+
var cfg = target.cfg;
|
|
11
|
+
var appProvider = await target.provider("app");
|
|
12
12
|
|
|
13
13
|
var url = await appProvider.getAppUrl(cfg, name);
|
|
14
14
|
|
package/src/commands/ps.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { fatal, fmt, table } from "../lib/output.js";
|
|
2
2
|
import { resolveAppName } from "../lib/link.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveTarget } from "../lib/providers/resolve.js";
|
|
4
4
|
import kleur from "kleur";
|
|
5
5
|
|
|
6
6
|
export async function ps(name, options) {
|
|
7
7
|
name = resolveAppName(name);
|
|
8
|
-
var
|
|
9
|
-
var cfg =
|
|
10
|
-
var appProvider = await
|
|
8
|
+
var target = await resolveTarget(options);
|
|
9
|
+
var cfg = target.cfg;
|
|
10
|
+
var appProvider = await target.provider("app");
|
|
11
11
|
|
|
12
12
|
var appConfig = await appProvider.getAppConfig(cfg, name);
|
|
13
13
|
|
|
@@ -23,22 +23,25 @@ export async function ps(name, options) {
|
|
|
23
23
|
// Fetch live metrics
|
|
24
24
|
var metrics = await appProvider.getContainerStatus(cfg, name);
|
|
25
25
|
|
|
26
|
-
// Aggregate: group by region +
|
|
26
|
+
// Aggregate: group by region + id, keep only active instances
|
|
27
27
|
var containers = [];
|
|
28
28
|
for (var row of metrics) {
|
|
29
29
|
var dim = row.dimensions;
|
|
30
|
-
if (
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if (dim.active === false) continue;
|
|
31
|
+
var id = dim.durableObjectId || dim.hostname || null;
|
|
32
|
+
var region = dim.region || dim.hostname || "-";
|
|
33
|
+
var existing = id ? containers.find(
|
|
34
|
+
(c) => c.region === region && c.id === id
|
|
35
|
+
) : null;
|
|
34
36
|
if (existing) {
|
|
35
37
|
existing.cpuSamples++;
|
|
36
38
|
existing.cpuLoad += row.avg?.cpuLoad || 0;
|
|
37
39
|
existing.memory += row.avg?.memory || 0;
|
|
38
40
|
} else {
|
|
39
41
|
containers.push({
|
|
40
|
-
region
|
|
41
|
-
|
|
42
|
+
region,
|
|
43
|
+
id,
|
|
44
|
+
status: dim.status || null,
|
|
42
45
|
cpuLoad: row.avg?.cpuLoad || 0,
|
|
43
46
|
memory: row.avg?.memory || 0,
|
|
44
47
|
cpuSamples: 1,
|
|
@@ -49,7 +52,7 @@ export async function ps(name, options) {
|
|
|
49
52
|
c.cpuLoad = c.cpuLoad / c.cpuSamples;
|
|
50
53
|
c.memory = c.memory / c.cpuSamples;
|
|
51
54
|
}
|
|
52
|
-
containers.sort((a, b) => a.region.localeCompare(b.region
|
|
55
|
+
containers.sort((a, b) => (a.region || "").localeCompare(b.region || ""));
|
|
53
56
|
|
|
54
57
|
if (options.json) {
|
|
55
58
|
console.log(
|
|
@@ -61,7 +64,8 @@ export async function ps(name, options) {
|
|
|
61
64
|
instances,
|
|
62
65
|
containers: containers.map((c) => ({
|
|
63
66
|
region: c.region,
|
|
64
|
-
id: c.
|
|
67
|
+
id: c.id || null,
|
|
68
|
+
status: c.status || null,
|
|
65
69
|
cpu: +(c.cpuLoad * 100).toFixed(1),
|
|
66
70
|
memoryMiB: +(c.memory / 1024 / 1024).toFixed(0),
|
|
67
71
|
})),
|
|
@@ -102,11 +106,12 @@ export async function ps(name, options) {
|
|
|
102
106
|
console.log(`\n${fmt.bold("Containers:")}`);
|
|
103
107
|
|
|
104
108
|
if (containers.length > 0) {
|
|
105
|
-
var
|
|
109
|
+
var hasIds = containers.some((c) => c.id);
|
|
110
|
+
var headers = hasIds ? ["", "REGION", "ID", "CPU", "MEMORY"] : ["", "REGION", "STATUS", "CPU", "MEMORY"];
|
|
106
111
|
var rows = containers.map((c) => [
|
|
107
112
|
kleur.green("*"),
|
|
108
113
|
c.region,
|
|
109
|
-
c.
|
|
114
|
+
hasIds ? (c.id || "-").slice(0, 8) : (c.status || "running"),
|
|
110
115
|
(c.cpuLoad * 100).toFixed(1) + "%",
|
|
111
116
|
(c.memory / 1024 / 1024).toFixed(0) + " MiB",
|
|
112
117
|
]);
|
package/src/commands/scale.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { success, fatal, fmt } from "../lib/output.js";
|
|
2
2
|
import { resolveAppName } from "../lib/link.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveTarget } from "../lib/providers/resolve.js";
|
|
4
4
|
|
|
5
5
|
export async function scale(name, options) {
|
|
6
6
|
name = resolveAppName(name);
|
|
7
|
-
var
|
|
8
|
-
var cfg =
|
|
9
|
-
var appProvider = await
|
|
7
|
+
var target = await resolveTarget(options);
|
|
8
|
+
var cfg = target.cfg;
|
|
9
|
+
var appProvider = await target.provider("app");
|
|
10
10
|
|
|
11
11
|
var appConfig = await appProvider.getAppConfig(cfg, name);
|
|
12
12
|
|