2018-07-27 10:18:29 -04:00
|
|
|
|
/*
|
|
|
|
|
* Copyright 2018 Comcast Cable Communications Management, LLC
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2019-11-04 11:00:14 -05:00
|
|
|
|
angular.module('dns-change')
|
|
|
|
|
.controller('DnsChangeNewController', function($scope, $log, $location, $timeout, $q, dnsChangeService, utilityService, groupsService){
|
2019-08-21 14:26:51 -04:00
|
|
|
|
groupsService.getGroups()
|
2019-02-06 16:21:43 -05:00
|
|
|
|
.then(function (results) {
|
|
|
|
|
$scope.myGroups = results['data']['groups'];
|
2019-07-03 10:50:33 -04:00
|
|
|
|
if ($scope.myGroups.length == 1) {
|
|
|
|
|
$scope.newBatch.ownerGroupId = $scope.myGroups[0]['id']
|
|
|
|
|
}
|
2019-02-06 16:21:43 -05:00
|
|
|
|
})
|
|
|
|
|
.catch(function (error) {
|
2019-08-21 14:26:51 -04:00
|
|
|
|
handleError(error, 'groupsService::getGroups-failure');
|
2019-02-06 16:21:43 -05:00
|
|
|
|
});
|
2018-07-27 10:18:29 -04:00
|
|
|
|
|
|
|
|
|
$scope.batch = {};
|
2019-08-21 10:41:54 -04:00
|
|
|
|
var tomorrow = moment().startOf('hour').add(1, 'day');
|
|
|
|
|
$scope.newBatch = {comments: "", changes: [{changeType: "Add", type: "A+PTR"}], scheduledTime: tomorrow.format('LL hh:mm A')};
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$scope.alerts = [];
|
|
|
|
|
$scope.batchChangeErrors = false;
|
2019-07-12 11:38:33 -04:00
|
|
|
|
$scope.ownerGroupError = false;
|
2019-08-14 14:04:06 -04:00
|
|
|
|
$scope.softErrors = false;
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$scope.formStatus = "pendingSubmit";
|
2019-08-02 17:02:18 -04:00
|
|
|
|
$scope.scheduledOption = false;
|
2019-08-14 14:04:06 -04:00
|
|
|
|
$scope.allowManualReview = false;
|
|
|
|
|
$scope.confirmationPrompt = "Are you sure you want to submit this batch change request?";
|
|
|
|
|
$scope.manualReviewEnabled;
|
2023-05-22 11:40:05 +05:30
|
|
|
|
$scope.naptrFlags = ["U", "S", "A", "P"];
|
2018-07-27 10:18:29 -04:00
|
|
|
|
|
2024-09-25 18:27:32 +05:30
|
|
|
|
// Initialize Bootstrap tooltips
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
});
|
|
|
|
|
|
2024-07-18 23:53:48 +05:30
|
|
|
|
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$scope.addSingleChange = function() {
|
2019-06-03 17:21:41 -04:00
|
|
|
|
$scope.newBatch.changes.push({changeType: "Add", type: "A+PTR"});
|
2019-05-07 11:03:55 -04:00
|
|
|
|
var changesLength = $scope.newBatch.changes.length;
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$timeout(function() {document.getElementsByClassName("changeType")[changesLength - 1].focus()});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.cancelSubmit = function() {
|
|
|
|
|
$scope.formStatus = "pendingSubmit";
|
2019-08-14 14:04:06 -04:00
|
|
|
|
$scope.allowManualReview = false;
|
|
|
|
|
$scope.confirmationPrompt = "Are you sure you want to submit this batch change request?";
|
2018-07-27 10:18:29 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.confirmSubmit = function(form) {
|
|
|
|
|
if(form.$invalid){
|
|
|
|
|
form.$setSubmitted();
|
|
|
|
|
$scope.formStatus = "pendingSubmit";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-28 15:26:11 -04:00
|
|
|
|
$scope.clearRecordData = function(changeIndex) {
|
|
|
|
|
delete $scope.newBatch.changes[changeIndex].record;
|
|
|
|
|
};
|
|
|
|
|
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$scope.createBatchChange = function() {
|
|
|
|
|
//flag to prevent multiple clicks until previous promise has resolved.
|
|
|
|
|
$scope.processing = true;
|
|
|
|
|
|
|
|
|
|
var payload = $scope.newBatch;
|
2019-08-02 17:02:18 -04:00
|
|
|
|
|
2019-03-04 16:52:35 -05:00
|
|
|
|
function formatData(payload) {
|
2019-08-21 10:41:54 -04:00
|
|
|
|
if (!$scope.newBatch.ownerGroupId) {
|
|
|
|
|
delete payload.ownerGroupId
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($scope.scheduledOption && $scope.newBatch.scheduledTime) {
|
|
|
|
|
payload.scheduledTime = moment($scope.newBatch.scheduledTime, 'LL hh:mm A').utc().format();
|
|
|
|
|
} else {
|
|
|
|
|
delete payload.scheduledTime;
|
|
|
|
|
}
|
2019-03-04 16:52:35 -05:00
|
|
|
|
for (var i = 0; i < payload.changes.length; i++) {
|
|
|
|
|
var entry = payload.changes[i]
|
|
|
|
|
if(entry.type == 'A+PTR' || entry.type == 'AAAA+PTR') {
|
|
|
|
|
entry.type = entry.type.slice(0, -4);
|
|
|
|
|
var newEntry = {changeType: entry.changeType, type: "PTR", ttl: entry.ttl, inputName: entry.record.address, record: {ptrdname: entry.inputName}}
|
|
|
|
|
payload.changes.splice(i+1, 0, newEntry)
|
|
|
|
|
}
|
2023-05-08 12:29:18 +05:30
|
|
|
|
if(entry.type == 'NAPTR') {
|
|
|
|
|
// Since regexp can be left empty
|
|
|
|
|
if(entry.record.regexp == undefined){
|
|
|
|
|
var newEntry = {changeType: entry.changeType, type: "NAPTR", ttl: entry.ttl, inputName: entry.inputName, record: {order: entry.record.order, preference: entry.record.preference, flags: entry.record.flags, service: entry.record.service, regexp: '', replacement: entry.record.replacement}}
|
|
|
|
|
payload.changes[i] = newEntry;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-28 15:26:11 -04:00
|
|
|
|
if(entry.changeType == 'DeleteRecordSet' && entry.record) {
|
|
|
|
|
var recordDataEmpty = true;
|
|
|
|
|
for (var attr in entry.record) {
|
|
|
|
|
if (entry.record[attr] != undefined && entry.record[attr].toString().length > 0) {
|
|
|
|
|
recordDataEmpty = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (recordDataEmpty) {
|
|
|
|
|
delete entry.record
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-04 16:52:35 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-27 10:18:29 -04:00
|
|
|
|
function success(response) {
|
2019-08-07 12:18:17 -04:00
|
|
|
|
var alert = utilityService.success('Successfully created DNS Change', response, 'createBatchChange: createBatchChange successful');
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$scope.alerts.push(alert);
|
2024-08-12 17:07:12 +05:30
|
|
|
|
$timeout(function(){
|
|
|
|
|
location.href = "/dnschanges/" + response.data.id;
|
|
|
|
|
}, 2000);
|
|
|
|
|
$scope.batch = response.data;
|
2018-07-27 10:18:29 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-04 16:52:35 -05:00
|
|
|
|
formatData(payload);
|
|
|
|
|
|
2019-11-04 11:00:14 -05:00
|
|
|
|
return dnsChangeService.createBatchChange(payload, true)
|
2018-07-27 10:18:29 -04:00
|
|
|
|
.then(success)
|
|
|
|
|
.catch(function (error){
|
2019-08-21 10:41:54 -04:00
|
|
|
|
if(payload.scheduledTime) {
|
|
|
|
|
$scope.newBatch.scheduledTime = moment(payload.scheduledTime).local().format('LL hh:mm A')
|
|
|
|
|
}
|
2024-07-13 00:41:07 +05:30
|
|
|
|
if(error.data.errors || error.status !== 400 || typeof error.data == "string"){
|
2019-11-04 11:00:14 -05:00
|
|
|
|
handleError(error, 'dnsChangesService::createBatchChange-failure');
|
2018-07-27 10:18:29 -04:00
|
|
|
|
} else {
|
|
|
|
|
$scope.newBatch.changes = error.data;
|
|
|
|
|
$scope.batchChangeErrors = true;
|
2019-08-14 14:04:06 -04:00
|
|
|
|
$scope.listOfErrors = error.data.flatMap(d => d.errors)
|
|
|
|
|
$scope.ownerGroupError = $scope.listOfErrors.some(e => e.includes('owner group ID must be specified for record'));
|
2019-08-22 14:52:44 -04:00
|
|
|
|
$scope.softErrors = false;
|
|
|
|
|
$scope.formStatus = "pendingSubmit";
|
|
|
|
|
$scope.alerts.push({type: 'danger', content: 'Errors found. Please correct and submit again.'});
|
2018-07-27 10:18:29 -04:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.deleteSingleChange = function(changeNumber) {
|
|
|
|
|
$('.batch-change-delete').blur();
|
|
|
|
|
$scope.newBatch.changes.splice(changeNumber, 1);
|
|
|
|
|
};
|
|
|
|
|
|
2019-08-14 14:04:06 -04:00
|
|
|
|
$scope.submitChange = function(manualReviewEnabled) {
|
2018-07-27 10:18:29 -04:00
|
|
|
|
$scope.formStatus = "pendingConfirm";
|
2019-08-14 14:04:06 -04:00
|
|
|
|
$scope.manualReviewEnabled = manualReviewEnabled;
|
2018-07-27 10:18:29 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-02 17:02:18 -04:00
|
|
|
|
$scope.getLocalTimeZone = function() {
|
|
|
|
|
return new Date().toLocaleString('en-us', {timeZoneName:'short'}).split(' ')[3];
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-27 10:18:29 -04:00
|
|
|
|
function handleError(error, type) {
|
|
|
|
|
var alert = utilityService.failure(error, type);
|
|
|
|
|
$scope.alerts.push(alert);
|
|
|
|
|
}
|
2019-05-07 11:03:55 -04:00
|
|
|
|
|
2024-09-16 12:44:54 +05:30
|
|
|
|
function resetFileInput() {
|
|
|
|
|
$scope.csvInput = null;
|
|
|
|
|
var inputElement = document.getElementById('batchChangeCsv');
|
|
|
|
|
if (inputElement) {
|
|
|
|
|
inputElement.value = null;
|
|
|
|
|
}
|
|
|
|
|
if ($scope.createBatchChangeForm && $scope.createBatchChangeForm.batchChangeCsv) {
|
|
|
|
|
$scope.createBatchChangeForm.batchChangeCsv.$setViewValue(null);
|
|
|
|
|
$scope.createBatchChangeForm.batchChangeCsv.$render();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-18 23:53:48 +05:30
|
|
|
|
$scope.uploadCSV = function(file, batchChangeLimit) {
|
|
|
|
|
parseFile(file, batchChangeLimit).then(function(dataLength){
|
2024-07-19 10:40:09 +05:30
|
|
|
|
$scope.alerts.push({type: 'success', content: 'Successfully imported ' + dataLength + ' DNS changes.' });
|
2024-09-16 12:44:54 +05:30
|
|
|
|
resetFileInput();
|
2019-06-03 16:44:45 -04:00
|
|
|
|
}, function(error) {
|
|
|
|
|
$scope.alerts.push({type: 'danger', content: error});
|
|
|
|
|
});
|
|
|
|
|
|
2024-07-18 23:53:48 +05:30
|
|
|
|
function parseFile(file, batchChangeLimit) {
|
2019-06-03 16:44:45 -04:00
|
|
|
|
return $q(function(resolve, reject) {
|
2024-09-16 12:44:54 +05:30
|
|
|
|
if (!file || !file.name) {
|
|
|
|
|
$log.debug('No file selected or file has no name property');
|
|
|
|
|
}
|
|
|
|
|
else if (!file.name.endsWith('.csv')) {
|
2024-02-12 13:38:23 -05:00
|
|
|
|
reject("Import failed. File should be of ‘.csv’ type.");
|
|
|
|
|
}
|
2024-02-27 11:35:58 -05:00
|
|
|
|
else {
|
|
|
|
|
var reader = new FileReader();
|
|
|
|
|
reader.onload = function(e) {
|
|
|
|
|
var rows = e.target.result.split("\n");
|
2024-07-19 10:40:09 +05:30
|
|
|
|
if(rows.length - 1 > batchChangeLimit)
|
2024-07-18 23:53:48 +05:30
|
|
|
|
{reject("Import failed. Cannot add more than " + batchChangeLimit + " records per DNS change.");
|
2024-07-19 10:40:09 +05:30
|
|
|
|
} else {
|
2024-02-27 11:35:58 -05:00
|
|
|
|
if (rows[0].trim() == "Change Type,Record Type,Input Name,TTL,Record Data") {
|
|
|
|
|
$scope.newBatch.changes = [];
|
|
|
|
|
for(var i = 1; i < rows.length; i++) {
|
|
|
|
|
var lengthCheck = rows[i].replace(/,+/g, '').trim().length
|
|
|
|
|
if (lengthCheck == 0) { continue; }
|
|
|
|
|
parseRow(rows[i])
|
|
|
|
|
}
|
|
|
|
|
$scope.$apply()
|
|
|
|
|
resolve($scope.newBatch.changes.length);
|
2024-07-18 23:53:48 +05:30
|
|
|
|
} else {
|
2024-02-27 11:35:58 -05:00
|
|
|
|
reject("Import failed. CSV header must be: Change Type,Record Type,Input Name,TTL,Record Data");
|
2024-02-12 13:38:23 -05:00
|
|
|
|
}
|
2024-07-19 10:40:09 +05:30
|
|
|
|
}}
|
2024-02-27 11:35:58 -05:00
|
|
|
|
reader.readAsText(file);
|
2024-02-12 13:38:23 -05:00
|
|
|
|
}
|
2019-06-03 16:44:45 -04:00
|
|
|
|
});
|
|
|
|
|
}
|
2019-05-07 11:03:55 -04:00
|
|
|
|
|
2022-01-12 17:06:28 -05:00
|
|
|
|
function decode(str) {
|
|
|
|
|
// regex from:
|
|
|
|
|
// https://www.bennadel.com/blog/1504-ask-ben-parsing-csv-strings-with-javascript-exec-regular-expression-command.htm
|
|
|
|
|
// matches[0] is full match text with delimiter if any
|
|
|
|
|
// matches[1] is delimiter (usually ',')
|
|
|
|
|
// matches[2] is quoted field or undefined, internal quotes are doubled by convention
|
|
|
|
|
// matches[3] is standard field or undefined
|
|
|
|
|
// one of [2] or [3] will be undefined
|
|
|
|
|
const regex = /(,|\r?\n|\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\r\n]*))/gi;
|
|
|
|
|
const matches = [...str.matchAll(regex)];
|
|
|
|
|
return matches.map(match => match[2] !== undefined ? match[2].replace(/""/g, '"') : match[3]);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-07 11:03:55 -04:00
|
|
|
|
function parseRow(row) {
|
|
|
|
|
var change = {};
|
|
|
|
|
var headers = ["changeType", "type", "inputName", "ttl", "record"];
|
2022-01-12 17:06:28 -05:00
|
|
|
|
var rowContent = decode(row);
|
2019-05-07 11:03:55 -04:00
|
|
|
|
for (var j = 0; j < rowContent.length; j++) {
|
|
|
|
|
if (headers[j] == "changeType") {
|
|
|
|
|
if (rowContent[j].match(/add/i)) {
|
|
|
|
|
change[headers[j]] = "Add"
|
2019-09-30 13:03:47 -04:00
|
|
|
|
} else if (rowContent[j].match(/delete/i)) {
|
2019-05-07 11:03:55 -04:00
|
|
|
|
change[headers[j]] = "DeleteRecordSet"
|
|
|
|
|
}
|
|
|
|
|
} else if (headers[j] == "type") {
|
|
|
|
|
change[headers[j]] = rowContent[j].trim().toUpperCase()
|
|
|
|
|
} else if (headers[j] == "ttl") {
|
|
|
|
|
change[headers[j]] = parseInt(rowContent[j].trim())
|
|
|
|
|
} else if (headers[j] == "record"){
|
|
|
|
|
if (change["type"] == "A" || change["type"] == "AAAA" || change["type"] == "A+PTR" || change["type"] == "AAAA+PTR"){
|
|
|
|
|
change[headers[j]] = {"address": rowContent[j].trim()}
|
|
|
|
|
} else if (change["type"] == "CNAME") {
|
|
|
|
|
change[headers[j]] = {"cname": rowContent[j].trim()}
|
|
|
|
|
} else if (change["type"] == "PTR") {
|
|
|
|
|
change[headers[j]] = {"ptrdname": rowContent[j].trim()}
|
2021-12-02 09:19:39 -05:00
|
|
|
|
} else if (change["type"] == "TXT") {
|
|
|
|
|
change[headers[j]] = {"text": rowContent[j].trim()}
|
2023-05-08 12:29:18 +05:30
|
|
|
|
} else if (change["type"] == "NS") {
|
|
|
|
|
change[headers[j]] = {"nsdname": rowContent[j].trim()}
|
|
|
|
|
} else if (change["type"] == "MX") {
|
|
|
|
|
var mxData = rowContent[j].trim().split(' ');
|
|
|
|
|
change[headers[j]] = {"preference": parseInt(mxData[0]), "exchange": mxData[1]}
|
|
|
|
|
} else if (change["type"] == "NAPTR") {
|
|
|
|
|
var naptrData = rowContent[j].trim().split(' ');
|
|
|
|
|
if(naptrData.length == 6){
|
|
|
|
|
change[headers[j]] = {"order": parseInt(naptrData[0]), "preference": parseInt(naptrData[1]), "flags": naptrData[2], "service": naptrData[3], "regexp": naptrData[4], "replacement": naptrData[5]}
|
|
|
|
|
} else {
|
|
|
|
|
change[headers[j]] = {"order": parseInt(naptrData[0]), "preference": parseInt(naptrData[1]), "flags": naptrData[2], "service": naptrData[3], "regexp": '', "replacement": naptrData[4]}
|
|
|
|
|
}
|
|
|
|
|
} else if (change["type"] == "SRV") {
|
|
|
|
|
var srvData = rowContent[j].trim().split(' ');
|
|
|
|
|
change[headers[j]] = {"priority": parseInt(srvData[0]), "weight": parseInt(srvData[1]), "port": parseInt(srvData[2]), "target": srvData[3]}
|
2019-05-07 11:03:55 -04:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
change[headers[j]] = rowContent[j].trim()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$scope.newBatch.changes.push(change);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-21 10:41:54 -04:00
|
|
|
|
|
|
|
|
|
$('input[name="scheduledTime"]').daterangepicker({
|
|
|
|
|
singleDatePicker: true,
|
|
|
|
|
timePicker: true,
|
|
|
|
|
startDate: tomorrow,
|
|
|
|
|
locale: {
|
|
|
|
|
format: 'LL hh:mm A'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2018-07-27 10:18:29 -04:00
|
|
|
|
});
|
|
|
|
|
})();
|