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 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.13.1",
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.14.0",
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
- location = location.slice(0, -matchingDomain.length-1);
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 { location, domain };
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 (var env in allPorts) {
455
- var defaultPort = (app && app.portBindings && app.portBindings[env]) ? app.portBindings[env] : (allPorts[env].defaultValue || '');
456
- var port = readlineSync.question(allPorts[env].description + ' (default ' + env + '=' + defaultPort + '. "x" to disable): ', {});
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 (let binding in portBindings) {
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.location,
594
+ location: domainObject.subdomain, // LEGACY
595
+ subdomain: domainObject.subdomain,
562
596
  domain: domainObject.domain,
563
- portBindings: portBindings,
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.location,
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
- var portBindings = app.portBindings;
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
- var tmp = kv.split('=');
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
- let location = options.location || readlineSync.question('Cloned app location: ', {});
1150
- let portBindings = queryPortBindings(app, app.manifest);
1151
- let backupId = options.backup;
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 = { backupId, domain: domainObject.domain, location: domainObject.location, portBindings: portBindings };
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)}`);