Add pre-flight check for domain collision

This commit is contained in:
Johannes Zellner
2019-09-20 01:55:45 +02:00
parent 9672f7e3da
commit 4fd1e55ae8
3 changed files with 83 additions and 15 deletions

View File

@@ -1721,6 +1721,15 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout
});
};
Client.prototype.getDNSRecords = function (domain, subdomain, type, callback) {
get('/api/v1/domains/' + domain + '/dns?subdomain=' + subdomain + '&type=' + type, null, function (error, data, status) {
if (error) return callback(error);
if (status !== 200) return callback(new ClientError(status, data));
callback(null, data.values);
});
};
Client.prototype.addMailDomain = function (domain, callback) {
post('/api/v1/mail', { domain: domain }, null, function (error, data, status) {
if (error) return callback(error);

View File

@@ -81,6 +81,28 @@
</div>
</div>
<!-- Modal domain collision -->
<div class="modal fade" id="domainCollisionsModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Domain Collision</h4>
</div>
<div class="modal-body">
<p>By default Cloudron does not overwrite DNS records which exist outside of Cloudron.</p>
<p>The following domains already exist outside of Cloudron:</p>
<ul>
<li ng-repeat="domain in location.domainCollisions">{{ domain.subdomain + '.' + domain.domain }}</li>
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" ng-click="location.submit(true)">Overwrite existing DNS Records</button>
</div>
</div>
</div>
</div>
<!-- Modal update app -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog">
<div class="modal-dialog">

View File

@@ -3,6 +3,7 @@
/* global angular */
/* global $ */
/* global asyncSeries */
/* global asyncForEach */
// TODO use this once we enable custom SSL certificate ui again
@@ -156,7 +157,7 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
$scope.location = {
busy: false,
error: {},
success: false,
domainCollisions: [],
domain: null,
location: '',
@@ -182,6 +183,7 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
var app = $scope.app;
$scope.location.error = {};
$scope.location.domainCollisions = [];
$scope.location.location = app.location;
$scope.location.domain = $scope.domains.filter(function (d) { return d.domain === app.domain; })[0];
$scope.location.portBindingsInfo = angular.extend({}, app.manifest.tcpPorts, app.manifest.udpPorts); // Portbinding map only for information
@@ -199,9 +201,12 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
}
},
submit: function () {
submit: function (overwriteDns) {
$('#domainCollisionsModal').modal('hide');
$scope.location.busy = true;
$scope.location.error = {};
$scope.location.domainCollisions = [];
// only use enabled ports from portBindings
var portBindings = {};
@@ -212,30 +217,62 @@ angular.module('Application').controller('AppController', ['$scope', '$location'
}
var data = {
overwriteDns: !!overwriteDns,
location: $scope.location.location,
domain: $scope.location.domain.domain,
portBindings: portBindings,
alternateDomains: $scope.location.alternateDomains.map(function (a) { return { subdomain: a.subdomain, domain: a.domain.domain };})
};
Client.configureApp($scope.app.id, 'location', data, function (error) {
if (error && (error.statusCode === 409 || error.statusCode === 400)) {
if ((error.location && error.domain) || error.field === 'location') {
$scope.location.error.location = error.message;
$scope.locationForm.$setPristine();
} else {
$scope.location.error.alternateDomains = error.message;
}
// pre-flight only for changed domains
var domains = [];
if ($scope.app.domain !== data.domain || $scope.app.location !== data.location) domains.push({ subdomain: data.location, domain: data.domain });
data.alternateDomains.forEach(function (a) {
if ($scope.app.alternateDomains.some(function (d) { return d.domain === a.domain && d.subdomain === a.subdomain; })) return;
domains.push({ subdomain: a.subdomain, domain: a.domain });
});
asyncForEach(domains, function (domain, callback) {
if (overwriteDns) return callback();
Client.getDNSRecords(domain.domain, domain.subdomain, 'A', function (error, result) {
// TODO handle credential errors
if (error) return callback(error);
if (result.length) $scope.location.domainCollisions.push(domain);
callback();
});
}, function (error) {
if (error) {
$scope.location.busy = false;
return;
return Client.error(error);
}
if (error) return Client.error(error);
$scope.location.success = true;
$scope.location.busy = false;
if ($scope.location.domainCollisions.length) {
$scope.location.busy = false;
return $('#domainCollisionsModal').modal('show');
}
refreshApp();
Client.configureApp($scope.app.id, 'location', data, function (error) {
if (error && (error.statusCode === 409 || error.statusCode === 400)) {
if ((error.location && error.domain) || error.field === 'location') {
$scope.location.error.location = error.message;
$scope.locationForm.$setPristine();
} else {
$scope.location.error.alternateDomains = error.message;
}
$scope.location.busy = false;
return;
}
if (error) return Client.error(error);
$scope.locationForm.$setPristine();
$scope.location.busy = false;
refreshApp();
});
});
}
};