Implement dns overwrite and pre-flight checks for multi domain clone
This commit is contained in:
@@ -1546,7 +1546,7 @@
|
|||||||
"title": "Clone {{ app }}",
|
"title": "Clone {{ app }}",
|
||||||
"description": "Using backup from <b>{{ creationTime }}</b> and version <b>v{{ packageVersion }}</b>",
|
"description": "Using backup from <b>{{ creationTime }}</b> and version <b>v{{ packageVersion }}</b>",
|
||||||
"location": "Location",
|
"location": "Location",
|
||||||
"cloneAction": "Clone"
|
"cloneAction": "Clone {{ dnsOverwrite ? 'and overwrite DNS' : '' }}"
|
||||||
},
|
},
|
||||||
"states": {
|
"states": {
|
||||||
"running": "Running",
|
"running": "Running",
|
||||||
|
|||||||
+5
-4
@@ -441,9 +441,9 @@
|
|||||||
<p ng-bind-html="'app.cloneDialog.description' | tr:{ creationTime: (clone.backup.creationTime | prettyDate), packageVersion: clone.backup.packageVersion }"></p>
|
<p ng-bind-html="'app.cloneDialog.description' | tr:{ creationTime: (clone.backup.creationTime | prettyDate), packageVersion: clone.backup.packageVersion }"></p>
|
||||||
<form role="form" ng-submit="clone.submit()" autocomplete="off">
|
<form role="form" ng-submit="clone.submit()" autocomplete="off">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="form-group" ng-class="{ 'has-error': clone.error.location }">
|
<div class="form-group" ng-class="{ 'has-error': clone.error.location.fqdn === clone.subdomain + '.' + clone.domain.domain }">
|
||||||
<label class="control-label" for="cloneLocationInput">{{ 'app.cloneDialog.location' | tr }}</label>
|
<label class="control-label" for="cloneLocationInput">{{ 'app.cloneDialog.location' | tr }}</label>
|
||||||
<div ng-show="clone.error.location"><small>{{ clone.error.location }}</small></div>
|
<div ng-show="clone.error.location.fqdn === clone.subdomain + '.' + clone.domain.domain"><small>{{ clone.error.location.message }}</small></div>
|
||||||
<div class="input-group form-inline">
|
<div class="input-group form-inline">
|
||||||
<input type="text" class="form-control" ng-model="clone.subdomain" id="cloneLocationInput" name="location" placeholder="{{ 'appstore.installDialog.locationPlaceholder' | tr }}" autofocus>
|
<input type="text" class="form-control" ng-model="clone.subdomain" id="cloneLocationInput" name="location" placeholder="{{ 'appstore.installDialog.locationPlaceholder' | tr }}" autofocus>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
@@ -463,7 +463,7 @@
|
|||||||
<div class="has-error text-center" ng-show="clone.error.secondaryDomain">{{ clone.error.secondaryDomain }}</div>
|
<div class="has-error text-center" ng-show="clone.error.secondaryDomain">{{ clone.error.secondaryDomain }}</div>
|
||||||
<div ng-repeat="(env, info) in app.manifest.httpPorts">
|
<div ng-repeat="(env, info) in app.manifest.httpPorts">
|
||||||
<ng-form name="secondaryDomainInfo_form">
|
<ng-form name="secondaryDomainInfo_form">
|
||||||
<div class="form-group" ng-class="{ 'has-error': (!secondaryDomainInfo_form.itemName{{$index}}.$dirty && clone.error.secondaryDomain) || (secondaryDomainInfo_form.itemName{{$index}}.$dirty && secondaryDomainInfo_form.itemName{{$index}}.$invalid) }">
|
<div class="form-group" ng-class="{ 'has-error': (!secondaryDomainInfo_form.itemName{{$index}}.$dirty && clone.error.secondaryDomain) || (secondaryDomainInfo_form.itemName{{$index}}.$dirty && secondaryDomainInfo_form.itemName{{$index}}.$invalid) || (clone.error.location.fqdn === clone.secondaryDomains[env].subdomain + '.' + clone.secondaryDomains[env].domain.domain) }">
|
||||||
<label class="control-label" for="secondaryDomainInput{{env}}">
|
<label class="control-label" for="secondaryDomainInput{{env}}">
|
||||||
{{ info.title }}
|
{{ info.title }}
|
||||||
<sup>
|
<sup>
|
||||||
@@ -471,6 +471,7 @@
|
|||||||
</sup>
|
</sup>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<div ng-show="clone.error.location.fqdn === clone.secondaryDomains[env].subdomain + '.' + clone.secondaryDomains[env].domain.domain"><small>{{ clone.error.location.message }}</small></div>
|
||||||
<div class="input-group form-inline">
|
<div class="input-group form-inline">
|
||||||
<input type="text" class="form-control" ng-model="clone.secondaryDomains[env].subdomain" name="location{{$index}}" placeholder="{{ 'app.location.locationPlaceholder' | tr }}" autofocus>
|
<input type="text" class="form-control" ng-model="clone.secondaryDomains[env].subdomain" name="location{{$index}}" placeholder="{{ 'app.location.locationPlaceholder' | tr }}" autofocus>
|
||||||
|
|
||||||
@@ -513,7 +514,7 @@
|
|||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
|
||||||
<button type="button" class="btn btn-success" ng-click="clone.submit()"><i class="far fa-clone" ng-hide="clone.busy"></i><i class="fa fa-circle-notch fa-spin" ng-show="clone.busy"></i> {{ 'app.cloneDialog.cloneAction' | tr }}</button>
|
<button type="button" class="btn btn-success" ng-click="clone.submit()"><i class="far fa-clone" ng-hide="clone.busy"></i><i class="fa fa-circle-notch fa-spin" ng-show="clone.busy"></i> {{ 'app.cloneDialog.cloneAction' | tr:{ dnsOverwrite: clone.needsOverwrite } }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+39
-16
@@ -1520,6 +1520,8 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
|
|||||||
subdomain: '',
|
subdomain: '',
|
||||||
domain: null,
|
domain: null,
|
||||||
secondaryDomains: {},
|
secondaryDomains: {},
|
||||||
|
needsOverwrite: false,
|
||||||
|
overwriteDns: false,
|
||||||
portBindings: {},
|
portBindings: {},
|
||||||
portBindingsInfo: {},
|
portBindingsInfo: {},
|
||||||
portBindingsEnabled: {},
|
portBindingsEnabled: {},
|
||||||
@@ -1531,6 +1533,9 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
|
|||||||
$scope.clone.backup = backup;
|
$scope.clone.backup = backup;
|
||||||
$scope.clone.domain = $scope.domains.find(function (d) { return app.domain === d.domain; }); // pre-select the app's domain
|
$scope.clone.domain = $scope.domains.find(function (d) { return app.domain === d.domain; }); // pre-select the app's domain
|
||||||
|
|
||||||
|
$scope.clone.needsOverwrite = false;
|
||||||
|
$scope.clone.overwriteDns = false;
|
||||||
|
|
||||||
$scope.clone.secondaryDomains = {};
|
$scope.clone.secondaryDomains = {};
|
||||||
app.secondaryDomains.forEach(function (sd) {
|
app.secondaryDomains.forEach(function (sd) {
|
||||||
$scope.clone.secondaryDomains[sd.environmentVariable] = {
|
$scope.clone.secondaryDomains[sd.environmentVariable] = {
|
||||||
@@ -1573,28 +1578,46 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
|
|||||||
domain: $scope.clone.domain.domain,
|
domain: $scope.clone.domain.domain,
|
||||||
secondaryDomains: secondaryDomains,
|
secondaryDomains: secondaryDomains,
|
||||||
portBindings: finalPortBindings,
|
portBindings: finalPortBindings,
|
||||||
backupId: $scope.clone.backup.id
|
backupId: $scope.clone.backup.id,
|
||||||
|
overwriteDns: $scope.clone.overwriteDns
|
||||||
};
|
};
|
||||||
|
|
||||||
Client.checkDNSRecords(data.domain, data.subdomain, function (error, result) {
|
var allDomains = [{ domain: data.domain, subdomain: data.subdomain }].concat(Object.keys(secondaryDomains).map(function (k) {
|
||||||
if (error) {
|
return {
|
||||||
Client.error(error);
|
domain: secondaryDomains[k].domain,
|
||||||
$scope.clone.busy = false;
|
subdomain: secondaryDomains[k].subdomain
|
||||||
return;
|
};
|
||||||
}
|
}));
|
||||||
|
async.eachSeries(allDomains, function (domain, callback) {
|
||||||
|
if ($scope.clone.overwriteDns) return callback();
|
||||||
|
|
||||||
|
Client.checkDNSRecords(domain.domain, domain.subdomain, function (error, result) {
|
||||||
|
if (error) return callback(error);
|
||||||
|
|
||||||
|
var fqdn = domain.subdomain + '.' + domain.domain;
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
if (result.error.reason === ERROR.ACCESS_DENIED) {
|
if (result.error.reason === ERROR.ACCESS_DENIED) return callback({ type: 'provider', fqdn: fqdn, message: 'DNS credentials for ' + domain.domain + ' are invalid. Update it in Domains & Certs view' });
|
||||||
$scope.clone.error.location = 'DNS credentials for ' + data.domain + ' are invalid. Update it in Domains & Certs view';
|
return callback({ type: 'provider', fqdn: fqdn, message: result.error.message });
|
||||||
} else {
|
|
||||||
$scope.clone.error.location = result.error.message;
|
|
||||||
}
|
|
||||||
$scope.clone.needsOverwrite = true;
|
|
||||||
$scope.clone.busy = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (result.needsOverwrite) {
|
if (result.needsOverwrite) {
|
||||||
$scope.clone.error.location = 'DNS Record already exists. Confirm that the domain is not in use for services external to Cloudron';
|
|
||||||
$scope.clone.needsOverwrite = true;
|
$scope.clone.needsOverwrite = true;
|
||||||
|
$scope.clone.overwriteDns = true;
|
||||||
|
return callback({ type: 'exists', fqdn: fqdn, message: 'DNS Record already exists. Confirm that the domain is not in use for services external to Cloudron' });
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}, function (error) {
|
||||||
|
if (error) {
|
||||||
|
if (error.type) {
|
||||||
|
$scope.clone.error.location = error;
|
||||||
|
$scope.clone.busy = false;
|
||||||
|
} else {
|
||||||
|
Client.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.clone.error.location = error;
|
||||||
$scope.clone.busy = false;
|
$scope.clone.busy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user