cloudron 4.13.1 → 4.14.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/bin/cloudron +3 -1
- package/package.json +2 -2
- package/src/actions.js +90 -20
package/bin/cloudron
CHANGED
|
@@ -73,6 +73,7 @@ program.command('configure')
|
|
|
73
73
|
.option('--no-wait', 'Wait for healthcheck to succeed [false]', false)
|
|
74
74
|
.option('-p, --port-bindings [PORT=port,...]', 'Query port bindings')
|
|
75
75
|
.option('-l, --location <location>', 'Location')
|
|
76
|
+
.option('-s, --secondary-domains [DOMAIN=domain,...]', 'Query/Set secondary domains')
|
|
76
77
|
.action(actions.configure);
|
|
77
78
|
|
|
78
79
|
program.command('debug [cmd...]')
|
|
@@ -128,8 +129,9 @@ program.command('install')
|
|
|
128
129
|
.description('Install or update app')
|
|
129
130
|
.option('--image <docker image>', 'Docker image')
|
|
130
131
|
.option('--no-wait', 'Wait for healthcheck to succeed [false]', false)
|
|
131
|
-
.option('-p, --port-bindings [PORT=port,...]', 'Query port bindings')
|
|
132
|
+
.option('-p, --port-bindings [PORT=port,...]', 'Query/Set port bindings')
|
|
132
133
|
.option('-l, --location <domain>', 'Subdomain or full domain')
|
|
134
|
+
.option('-s, --secondary-domains [DOMAIN=domain,...]', 'Query/Set secondary domains')
|
|
133
135
|
.option('--appstore-id <appid[@version]>', 'Use app from the store')
|
|
134
136
|
.option('--no-sso', 'Disable Cloudron SSO [false]', false)
|
|
135
137
|
.option('--debug [cmd]', 'Enable debug mode')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudron",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Cloudron Commandline Tool",
|
|
6
6
|
"main": "main.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"author": "Cloudron Developers <support@cloudron.io>",
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"async": "^3.2.3",
|
|
21
|
-
"cloudron-manifestformat": "^5.
|
|
21
|
+
"cloudron-manifestformat": "^5.15.0",
|
|
22
22
|
"commander": "^6.1.0",
|
|
23
23
|
"debug": "^4.3.3",
|
|
24
24
|
"delay": "^5.0.0",
|
package/src/actions.js
CHANGED
|
@@ -106,6 +106,7 @@ async function selectDomain(location, options) {
|
|
|
106
106
|
const domains = response.body.domains;
|
|
107
107
|
|
|
108
108
|
let domain;
|
|
109
|
+
let subdomain = location;
|
|
109
110
|
let matchingDomain = domains
|
|
110
111
|
.map(function (d) { return d.domain; } )
|
|
111
112
|
.sort(function(a, b) { return a.length < b.length; })
|
|
@@ -113,7 +114,7 @@ async function selectDomain(location, options) {
|
|
|
113
114
|
|
|
114
115
|
if (matchingDomain) {
|
|
115
116
|
domain = matchingDomain;
|
|
116
|
-
|
|
117
|
+
subdomain = location.slice(0, -matchingDomain.length-1);
|
|
117
118
|
} else { // use the admin domain
|
|
118
119
|
domain = domains
|
|
119
120
|
.map(function (d) { return d.domain; } )
|
|
@@ -121,7 +122,7 @@ async function selectDomain(location, options) {
|
|
|
121
122
|
.find(function (d) { return adminFqdn.endsWith(d); });
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
return {
|
|
125
|
+
return { subdomain, domain };
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
async function stopActiveTask(app, options) {
|
|
@@ -197,7 +198,7 @@ async function getApp(options) {
|
|
|
197
198
|
const response = await createRequest('GET', '/api/v1/apps', options);
|
|
198
199
|
if (response.statusCode !== 200) throw new Error(`Failed to get apps: ${requestError(response)}`);
|
|
199
200
|
|
|
200
|
-
const match = response.body.apps.filter(function (m) { return m.location === app || m.fqdn === app; });
|
|
201
|
+
const match = response.body.apps.filter(function (m) { return m.subdomain === app || m.location === app || m.fqdn === app; });
|
|
201
202
|
if (match.length == 0) throw new Error(`App at location ${app} not found`);
|
|
202
203
|
|
|
203
204
|
return match[0];
|
|
@@ -448,12 +449,25 @@ async function list(options) {
|
|
|
448
449
|
console.log(t.toString());
|
|
449
450
|
}
|
|
450
451
|
|
|
452
|
+
async function querySecondaryDomains(app, manifest, options) {
|
|
453
|
+
const secondaryDomains = {};
|
|
454
|
+
|
|
455
|
+
if(!manifest.httpPorts) return secondaryDomains;
|
|
456
|
+
|
|
457
|
+
for (const env in manifest.httpPorts) {
|
|
458
|
+
const defaultDomain = (app && app.secondaryDomains && app.secondaryDomains[env]) ? app.secondaryDomains[env] : (manifest.httpPorts[env].defaultValue || '');
|
|
459
|
+
const input = readlineSync.question(`${manifest.httpPorts[env].description} (default: "${defaultDomain}"): `, {});
|
|
460
|
+
secondaryDomains[env] = await selectDomain(input, options);
|
|
461
|
+
}
|
|
462
|
+
return secondaryDomains;
|
|
463
|
+
}
|
|
464
|
+
|
|
451
465
|
function queryPortBindings(app, manifest) {
|
|
452
|
-
const portBindings = {
|
|
466
|
+
const portBindings = {};
|
|
453
467
|
const allPorts = _.extend({}, manifest.tcpPorts, manifest.udpPorts);
|
|
454
|
-
for (
|
|
455
|
-
|
|
456
|
-
|
|
468
|
+
for (const env in allPorts) {
|
|
469
|
+
const defaultPort = (app && app.portBindings && app.portBindings[env]) ? app.portBindings[env] : (allPorts[env].defaultValue || '');
|
|
470
|
+
const port = readlineSync.question(allPorts[env].description + ' (default ' + env + '=' + defaultPort + '. "x" to disable): ', {});
|
|
457
471
|
if (port === '') {
|
|
458
472
|
portBindings[env] = defaultPort;
|
|
459
473
|
} else if (isNaN(parseInt(port, 10))) {
|
|
@@ -530,6 +544,27 @@ async function install(options) {
|
|
|
530
544
|
|
|
531
545
|
const domainObject = await selectDomain(location, options);
|
|
532
546
|
|
|
547
|
+
// secondary domains
|
|
548
|
+
let secondaryDomains = {};
|
|
549
|
+
if (options.secondaryDomains) {
|
|
550
|
+
// ask the user for port values if the ports are different in the app and the manifest
|
|
551
|
+
if (typeof options.secondaryDomains === 'string') {
|
|
552
|
+
secondaryDomains = {};
|
|
553
|
+
for (const kv of options.secondaryDomains.split(',')) {
|
|
554
|
+
const tmp = kv.split('=');
|
|
555
|
+
secondaryDomains[tmp[0]] = await selectDomain(tmp[1], options);
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
secondaryDomains = await querySecondaryDomains(null /* existing app */, manifest, options);
|
|
559
|
+
}
|
|
560
|
+
} else if (manifest.httpPorts) { // just put in defaults
|
|
561
|
+
for (const env in manifest.httpPorts) {
|
|
562
|
+
secondaryDomains[env] = await selectDomain(manifest.httpPorts[env].defaultValue, options);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
for (const binding in secondaryDomains) console.log(`Secondary domain ${binding}: ${secondaryDomains[binding].subdomain}.${secondaryDomains[binding].domain}`);
|
|
567
|
+
|
|
533
568
|
// port bindings
|
|
534
569
|
let portBindings = {};
|
|
535
570
|
if (options.portBindings) {
|
|
@@ -551,16 +586,16 @@ async function install(options) {
|
|
|
551
586
|
}
|
|
552
587
|
}
|
|
553
588
|
|
|
554
|
-
for (
|
|
555
|
-
console.log('%s: %s', binding, portBindings[binding]);
|
|
556
|
-
}
|
|
589
|
+
for (const binding in portBindings) console.log(`Port ${binding}: ${portBindings[binding]}`);
|
|
557
590
|
|
|
558
591
|
const data = {
|
|
559
592
|
appStoreId: options.appstoreId || '', // note case change
|
|
560
593
|
manifest: options.appstoreId ? null : manifest, // cloudron ignores manifest anyway if appStoreId is set
|
|
561
|
-
location: domainObject.
|
|
594
|
+
location: domainObject.subdomain, // LEGACY
|
|
595
|
+
subdomain: domainObject.subdomain,
|
|
562
596
|
domain: domainObject.domain,
|
|
563
|
-
|
|
597
|
+
secondaryDomains,
|
|
598
|
+
portBindings,
|
|
564
599
|
accessRestriction: null
|
|
565
600
|
};
|
|
566
601
|
|
|
@@ -610,20 +645,46 @@ async function configure(options) {
|
|
|
610
645
|
|
|
611
646
|
const domainObject = await selectDomain(location, options);
|
|
612
647
|
|
|
648
|
+
const secondaryDomains = {};
|
|
649
|
+
app.secondaryDomains.forEach(sd => {
|
|
650
|
+
secondaryDomains[sd.environmentVariable] = {
|
|
651
|
+
subdomain: sd.subdomain,
|
|
652
|
+
domain: sd.domain
|
|
653
|
+
};
|
|
654
|
+
});
|
|
655
|
+
|
|
613
656
|
const data = {
|
|
614
|
-
location: domainObject.
|
|
657
|
+
location: domainObject.subdomain, // LEGACY
|
|
658
|
+
subdomain: domainObject.subdomain,
|
|
615
659
|
domain: domainObject.domain,
|
|
616
|
-
portBindings: app.portBindings
|
|
660
|
+
portBindings: app.portBindings,
|
|
661
|
+
secondaryDomains
|
|
617
662
|
};
|
|
618
663
|
|
|
664
|
+
// secondary domains
|
|
665
|
+
if (options.secondaryDomains) {
|
|
666
|
+
// ask the user for port values if the ports are different in the app and the manifest
|
|
667
|
+
if (typeof options.secondaryDomains === 'string') {
|
|
668
|
+
data.secondaryDomains = {};
|
|
669
|
+
for (const kv of options.secondaryDomains.split(',')) {
|
|
670
|
+
const tmp = kv.split('=');
|
|
671
|
+
data.secondaryDomains[tmp[0]] = await selectDomain(tmp[1], options);
|
|
672
|
+
}
|
|
673
|
+
} else {
|
|
674
|
+
data.secondaryDomains = await querySecondaryDomains(app, app.manifest, options);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
for (const binding in data.secondaryDomains) console.log(`Secondary domain ${binding}: ${data.secondaryDomains[binding].subdomain}.${data.secondaryDomains[binding].domain}`);
|
|
678
|
+
}
|
|
679
|
+
|
|
619
680
|
// port bindings
|
|
620
681
|
if (options.portBindings) {
|
|
621
|
-
|
|
682
|
+
let portBindings = app.portBindings;
|
|
622
683
|
// ask the user for port values if the ports are different in the app and the manifest
|
|
623
684
|
if (typeof options.portBindings === 'string') {
|
|
624
685
|
portBindings = {};
|
|
625
686
|
options.portBindings.split(',').forEach(function (kv) {
|
|
626
|
-
|
|
687
|
+
const tmp = kv.split('=');
|
|
627
688
|
if (isNaN(parseInt(tmp[1], 10))) return; // disable the port
|
|
628
689
|
portBindings[tmp[0]] = parseInt(tmp[1], 10);
|
|
629
690
|
});
|
|
@@ -908,6 +969,7 @@ async function inspect(options) {
|
|
|
908
969
|
for (const app of response.body.apps) {
|
|
909
970
|
const response2 = await createRequest('GET', `/api/v1/apps/${app.id}`, options);
|
|
910
971
|
if (response2.statusCode !== 200) return exit(`Failed to list app: ${requestError(response2)}`);
|
|
972
|
+
response2.body.location = response2.body.subdomain; // LEGACY support
|
|
911
973
|
apps.push(response2.body);
|
|
912
974
|
}
|
|
913
975
|
|
|
@@ -1146,12 +1208,20 @@ async function clone(options) {
|
|
|
1146
1208
|
|
|
1147
1209
|
if (!options.backup) return exit('Use --backup to specify the backup id');
|
|
1148
1210
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1211
|
+
const location = options.location || readlineSync.question('Cloned app location: ', {});
|
|
1212
|
+
const secondaryDomains = await querySecondaryDomains(app, app.manifest, options);
|
|
1213
|
+
const portBindings = queryPortBindings(app, app.manifest);
|
|
1214
|
+
const backupId = options.backup;
|
|
1152
1215
|
|
|
1153
1216
|
const domainObject = await selectDomain(location, options);
|
|
1154
|
-
const data = {
|
|
1217
|
+
const data = {
|
|
1218
|
+
backupId,
|
|
1219
|
+
location: domainObject.subdomain, // LEGACY
|
|
1220
|
+
subdomain: domainObject.subdomain,
|
|
1221
|
+
domain: domainObject.domain,
|
|
1222
|
+
secondaryDomains,
|
|
1223
|
+
portBindings
|
|
1224
|
+
};
|
|
1155
1225
|
const request = createRequest('POST', `/api/v1/apps/${app.id}/clone`, options);
|
|
1156
1226
|
const response = await request.send(data);
|
|
1157
1227
|
if (response.statusCode !== 201) return exit(`Failed to list apps: ${requestError(response)}`);
|