2
0
mirror of https://github.com/VinylDNS/vinyldns synced 2025-08-22 02:02:14 +00:00
vinyldns/modules/portal/public/lib/dns-change/dns-change-new.controller.js

295 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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';
angular.module('dns-change')
.controller('DnsChangeNewController', function($scope, $log, $location, $timeout, $q, dnsChangeService, utilityService, groupsService){
groupsService.getGroups()
.then(function (results) {
$scope.myGroups = results['data']['groups'];
if ($scope.myGroups.length == 1) {
$scope.newBatch.ownerGroupId = $scope.myGroups[0]['id']
}
})
.catch(function (error) {
handleError(error, 'groupsService::getGroups-failure');
});
$scope.batch = {};
var tomorrow = moment().startOf('hour').add(1, 'day');
$scope.newBatch = {comments: "", changes: [{changeType: "Add", type: "A+PTR"}], scheduledTime: tomorrow.format('LL hh:mm A')};
$scope.alerts = [];
$scope.batchChangeErrors = false;
$scope.ownerGroupError = false;
$scope.softErrors = false;
$scope.formStatus = "pendingSubmit";
$scope.scheduledOption = false;
$scope.allowManualReview = false;
$scope.confirmationPrompt = "Are you sure you want to submit this batch change request?";
$scope.manualReviewEnabled;
$scope.naptrFlags = ["U", "S", "A", "P"];
// Initialize Bootstrap tooltips
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
});
$scope.addSingleChange = function() {
$scope.newBatch.changes.push({changeType: "Add", type: "A+PTR"});
var changesLength = $scope.newBatch.changes.length;
$timeout(function() {document.getElementsByClassName("changeType")[changesLength - 1].focus()});
};
$scope.cancelSubmit = function() {
$scope.formStatus = "pendingSubmit";
$scope.allowManualReview = false;
$scope.confirmationPrompt = "Are you sure you want to submit this batch change request?";
};
$scope.confirmSubmit = function(form) {
if(form.$invalid){
form.$setSubmitted();
$scope.formStatus = "pendingSubmit";
}
};
$scope.clearRecordData = function(changeIndex) {
delete $scope.newBatch.changes[changeIndex].record;
};
$scope.createBatchChange = function() {
//flag to prevent multiple clicks until previous promise has resolved.
$scope.processing = true;
var payload = $scope.newBatch;
function formatData(payload) {
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;
}
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)
}
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;
}
}
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
}
}
}
}
function success(response) {
var alert = utilityService.success('Successfully created DNS Change', response, 'createBatchChange: createBatchChange successful');
$scope.alerts.push(alert);
$timeout(function(){
location.href = "/dnschanges/" + response.data.id;
}, 2000);
$scope.batch = response.data;
}
formatData(payload);
return dnsChangeService.createBatchChange(payload, true)
.then(success)
.catch(function (error){
if(payload.scheduledTime) {
$scope.newBatch.scheduledTime = moment(payload.scheduledTime).local().format('LL hh:mm A')
}
if(error.data.errors || error.status !== 400 || typeof error.data == "string"){
handleError(error, 'dnsChangesService::createBatchChange-failure');
} else {
$scope.newBatch.changes = error.data;
$scope.batchChangeErrors = true;
$scope.listOfErrors = error.data.flatMap(d => d.errors)
$scope.ownerGroupError = $scope.listOfErrors.some(e => e.includes('owner group ID must be specified for record'));
$scope.softErrors = false;
$scope.formStatus = "pendingSubmit";
$scope.alerts.push({type: 'danger', content: 'Errors found. Please correct and submit again.'});
}
});
};
$scope.deleteSingleChange = function(changeNumber) {
$('.batch-change-delete').blur();
$scope.newBatch.changes.splice(changeNumber, 1);
};
$scope.submitChange = function(manualReviewEnabled) {
$scope.formStatus = "pendingConfirm";
$scope.manualReviewEnabled = manualReviewEnabled;
}
$scope.getLocalTimeZone = function() {
return new Date().toLocaleString('en-us', {timeZoneName:'short'}).split(' ')[3];
}
function handleError(error, type) {
var alert = utilityService.failure(error, type);
$scope.alerts.push(alert);
}
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();
}
}
$scope.uploadCSV = function(file, batchChangeLimit) {
parseFile(file, batchChangeLimit).then(function(dataLength){
$scope.alerts.push({type: 'success', content: 'Successfully imported ' + dataLength + ' DNS changes.' });
resetFileInput();
}, function(error) {
$scope.alerts.push({type: 'danger', content: error});
});
function parseFile(file, batchChangeLimit) {
return $q(function(resolve, reject) {
if (!file || !file.name) {
$log.debug('No file selected or file has no name property');
}
else if (!file.name.endsWith('.csv')) {
reject("Import failed. File should be of .csv type.");
}
else {
var reader = new FileReader();
reader.onload = function(e) {
var rows = e.target.result.split("\n");
if(rows.length - 1 > batchChangeLimit)
{reject("Import failed. Cannot add more than " + batchChangeLimit + " records per DNS change.");
} else {
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);
} else {
reject("Import failed. CSV header must be: Change Type,Record Type,Input Name,TTL,Record Data");
}
}}
reader.readAsText(file);
}
});
}
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]);
}
function parseRow(row) {
var change = {};
var headers = ["changeType", "type", "inputName", "ttl", "record"];
var rowContent = decode(row);
for (var j = 0; j < rowContent.length; j++) {
if (headers[j] == "changeType") {
if (rowContent[j].match(/add/i)) {
change[headers[j]] = "Add"
} else if (rowContent[j].match(/delete/i)) {
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()}
} else if (change["type"] == "TXT") {
change[headers[j]] = {"text": rowContent[j].trim()}
} 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]}
}
} else {
change[headers[j]] = rowContent[j].trim()
}
}
$scope.newBatch.changes.push(change);
}
}
$('input[name="scheduledTime"]').daterangepicker({
singleDatePicker: true,
timePicker: true,
startDate: tomorrow,
locale: {
format: 'LL hh:mm A'
}
});
});
})();