Viewing File: /usr/local/cpanel/whostmgr/docroot/templates/zone_editor/services/zones.js

/*
# zone_editor/services/zones.js                                   Copyright 2022 cPanel, L.L.C.
#                                                                           All rights reserved.
# copyright@cpanel.net                                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* define: false */

define(
    [
        "angular",
        "lodash",
        "cjt/util/locale",
        "cjt/io/api",
        "cjt/io/whm-v1-request",
        "cjt/util/httpStatus",
        "cjt/core",
        "cjt/util/base64",
        "app/services/recordTypes",
        "cjt/io/whm-v1",
    ],
    function(angular, _, LOCALE, API, APIREQUEST, HTTP_STATUS, CJT, BASE64, RecordTypesService) {

        "use strict";


        var MODULE_NAMESPACE = "whm.zoneEditor.services.zones";
        var app = angular.module(MODULE_NAMESPACE, []);
        var SERVICE_NAME = "Zones";
        var SERVICE_INJECTABLES = ["$q", "$log", "alertService", RecordTypesService.serviceName];
        var SERVICE_FACTORY = function($q, $log, $alertService, $recordTypes) {

            var store = {};

            store.zones = [];
            store.generated_domains = [];

            function _saveRecords(zone, records, serial) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize("", "mass_edit_dns_zone");
                apiCall.addArgument("zone", zone);
                apiCall.addArgument("serial", serial);

                // adding MX from the quick add options in the domain list does not provide a "name" input- without the trailing dot added the parsing of the records believes the record name to actually be a subdomain
                records.forEach(function(record) {
                    if (record.record_type === "MX") {
                        if (record.dname === zone) {
                            record.dname = record.dname + ".";
                        }
                    }
                });

                var add = records.filter(function(record) {
                    return record.is_new;
                });
                var edit = records.filter(function(record) {
                    return !record.is_new;
                });

                // remove unneeded property before submission to API
                add.forEach(function(record) {
                    delete record.is_new;
                });

                edit.forEach(function(record) {
                    delete record.is_new;
                });

                var jsonAdd = add.map(function(record) {
                    return JSON.stringify(record);
                });
                apiCall.addArgument("add", jsonAdd);

                var jsonEdit = edit.map(function(record) {
                    return JSON.stringify(record);
                });
                apiCall.addArgument("edit", jsonEdit);

                return store._promise(apiCall)
                    .then(store._parseAPISuccess)
                    .catch(store._parseAPIFailure);
            }

            /**
             * Save new or modified zone records
             * @param zone - name of zone
             * @param records - records to update or create
             * @param serial - serial number of record
             * @returns - a promise resolving to true if successful, or an error
             */
            store.saveRecords = function(zone, records, serial) {

                // the serial and default TTL will not exist if this request comes from any of the 'quick add' records on the domain lister page
                // SOA record  and default TTL for given zone must be fetched first
                if (!serial) {
                    return store.fetch(zone)
                        .then(function(response) {
                            var soaRec = response.parsedZoneData.filter(function(record) {
                                return record.record_type === "SOA";
                            });
                            soaRec = soaRec[0];
                            var serial = parseInt(soaRec.serial, 10);
                            records[0]["ttl"] = parseInt(response.defaultTTL, 10);
                            return _saveRecords(zone, records, serial);
                        });
                } else {
                    return _saveRecords(zone, records, serial);
                }
            };

            /**
             * Get the raw zone file content in Base64
             * @param zone - the name of the zone to get records for
             * @returns - decoded Base64 raw zone file contents
             */
            store.exportZoneFile = function(zone) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize("", "export_zone_files");
                apiCall.addArgument("zone", zone);

                return store._promise(apiCall).then(function(response) {
                    var parsedResponse = response.parsedResponse;
                    if (parsedResponse.status) {
                        var b64 = parsedResponse.data[0].text_b64;
                        var decoded = BASE64.decodeUTF8(b64);
                        return decoded;
                    } else {
                        return $q.reject(response);
                    }
                }).catch(store._parseAPIFailure);
            };

            /**
             * Trim the trailing dot (.) if present. This is used at places where a qualified
             * domain value is sent to the ‘addzonerecord’ API call because the API appends the dot (.)
             * again.
             *
             * @param value - the DNS type value that could be a qualified domain name.
             *
             * @return value without the trailing dot (.)
             */
            store.trimTrailingDot = function(value) {
                value = value.replace(/\.$/, "");
                return value;
            };

            /**
             * Fetch all DNZ Zone Records for a given zone
             * @param zone - name of zone to get records for
             * @returns - an array of parsed and decoded zone record data
             */
            store.fetch = function(zone) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize("", "parse_dns_zone");
                apiCall.addArgument("zone", zone);

                return store._promise(apiCall)
                    .then(function(response) {
                        var parsedResponse = response.parsedResponse;
                        if (parsedResponse.status) {
                            var defaultTTL;
                            var record;
                            var parsedZoneData = [];
                            for (var i = 0, len = parsedResponse.data.length; i < len; i++) {
                                record = parsedResponse.data[i];
                                if (record.type === "record") {
                                    record["txtdata"] = [];
                                    record["name"] = BASE64.decodeUTF8(record.dname_b64);
                                    if (record.name !== zone + ".") {
                                        record.name = record.name + "." + zone + ".";
                                    }

                                    // Helps QA to identify records.
                                    record["id_prefix"] = record.record_type.toLowerCase();
                                    record.data_b64.forEach(function(data) {
                                        record.txtdata.push(BASE64.decodeUTF8(data));
                                    });

                                    switch (record.record_type) {
                                        case "AFSDB":
                                            record["subtype"] = record.txtdata[0];
                                            record["hostname"] = store.trimTrailingDot(record.txtdata[1]);
                                            break;
                                        case "CAA":
                                            record["flag"] = record.txtdata[0];
                                            record["tag"] = record.txtdata[1];
                                            record["value"] = record.txtdata[2];
                                            break;
                                        case "DS":
                                            record["keytag"] = record.txtdata[0];
                                            record["algorithm"] = record.txtdata[1];
                                            record["digtype"] = record.txtdata[2];
                                            record["digest"] = record.txtdata[3];
                                            break;
                                        case "HINFO":
                                            record["cpu"] = record.txtdata[0];
                                            record["os"] = record.txtdata[1];
                                            break;
                                        case "LOC":

                                            var latSplit = record.txtdata[0].split("N");
                                            var latPart;
                                            if (latSplit.length < 2) {
                                                latSplit = record.txtdata[0].split("S");
                                                latPart = latSplit[0];
                                                record["latitude"] = latPart + " S".trim();
                                            } else {
                                                latPart = latSplit[0];
                                                record["latitude"] = latPart + " N".trim();
                                            }

                                            var longSplit = latSplit[1].split("E");
                                            var longPart;
                                            if (longSplit.length < 2) {
                                                longSplit = latSplit[1].split("W");
                                                longPart = longSplit[0];
                                                record["longitude"] = longPart + " W".trim();
                                            } else {
                                                longPart = longSplit[0];
                                                record["longitude"] = longPart + " E".trim();
                                            }

                                            var dataSplit = longSplit[1];
                                            dataSplit = dataSplit.trim().split(" ");

                                            // /\.$/, ""
                                            record["altitude"] = dataSplit[0];
                                            record["size"] = dataSplit[1];
                                            record["horiz_pre"] = dataSplit[2];
                                            record["vert_pre"] = dataSplit[3];

                                            record.altitude = record.altitude.replace(/m$/, "");
                                            record.size = record.size.replace(/m$/, "");
                                            record.horiz_pre = record.horiz_pre.replace(/m$/, "");
                                            record.vert_pre = record.vert_pre.replace(/m$/, "");
                                            break;
                                        case "MX":
                                            record["priority"] = record.txtdata[0];
                                            record["exchange"] = store.trimTrailingDot(record.txtdata[1]);
                                            break;
                                        case "NAPTR":
                                            record["order"] = record.txtdata[0];
                                            record["preference"] = record.txtdata[1];
                                            record["flags"] = record.txtdata[2];
                                            record["service"] = record.txtdata[3];
                                            record["regexp"] = record.txtdata[4];
                                            record["replacement"] = store.trimTrailingDot(record.txtdata[5]);
                                            break;
                                        case "RP":
                                            record["mbox"] = store.trimTrailingDot(record.txtdata[0]);
                                            record["txtdname"] = store.trimTrailingDot(record.txtdata[1]);
                                            break;
                                        case "SOA":
                                            record["serial"] = record.txtdata[2];
                                            record["mname"] = store.trimTrailingDot(record.txtdata[0]);
                                            record["retry"] = record.txtdata[4];
                                            record["refresh"] = record.txtdata[3];
                                            record["expire"] = record.txtdata[5];
                                            record["rname"] = store.trimTrailingDot(record.txtdata[1]);
                                            break;
                                        case "SRV":
                                            record["priority"] = record.txtdata[0];
                                            record["weight"] = record.txtdata[1];
                                            record["port"] = record.txtdata[2];
                                            record["target"] = store.trimTrailingDot(record.txtdata[3]);
                                            break;
                                        case "CNAME":
                                            record["record"] = store.trimTrailingDot(record.txtdata[0]);
                                            break;
                                        default:
                                            record["record"] = record.txtdata[0];
                                            break;
                                    }

                                    parsedZoneData.push(record);
                                } else if (record.type === "control") {
                                    defaultTTL = BASE64.decodeUTF8(record.text_b64);
                                    defaultTTL = defaultTTL.split(" ");
                                    defaultTTL = defaultTTL[1];
                                }
                            }
                            return {
                                parsedZoneData: parsedZoneData,
                                defaultTTL: defaultTTL,
                            };

                        } else {
                            return $q.reject(parsedResponse);
                        }
                    }).catch(store._parseAPIFailure);
            };

            function _removeZoneRecord(zone, line, serial) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize("", "mass_edit_dns_zone");
                apiCall.addArgument("zone", zone);
                apiCall.addArgument("serial", serial);
                apiCall.addArgument("remove", line);

                return store._promise(apiCall)
                    .then(store._parseAPISuccess)
                    .catch(store._parseAPIFailure);
            }

            /**
             * Remove a record based on the type.
             * NOTE: After removing a record, we need to fetch the list of records from the
             * server since the api calls do some special serialization of the records.
             *
             * @param domain - the domain on which the record should be created
             * @param record - the record object we are sending. the fields in the object
             *                  depend on the type of record.
             * @return Promise
             */
            store.remove_zone_record = function(domain, line, serial) {
                return _removeZoneRecord(domain, line, serial);
            };

            store._promise = function(apiCall) {
                return $q.when(API.promise(apiCall.getRunArguments()));
            };

            store._parseAPIFailure = function(response) {
                if (!response.status) {
                    return $q.reject(response.error);
                }
                return $q.reject(store.request_failure_message(response.status));
            };

            store._parseAPISuccess = function(response) {
                response = response.parsedResponse;
                if (response.status) {
                    return true;
                } else {
                    return $q.reject(response);
                }
            };

            store.reset_zone = function(domain) {
                var apiCall = new APIREQUEST.Class();
                apiCall.initialize("", "resetzone");
                apiCall.addArgument("domain", domain);

                return store._promise(apiCall)
                    .then(store._parseAPISuccess)
                    .catch(store._parseAPIFailure);
            };

            store.fetch_generated_domains = function(domain, force) {
                store.generated_domains = {};
                return $q.when(store.generated_domains);
            };

            store.format_zone_name = function(domain, zoneName) {
                var name = zoneName;
                if (!angular.isDefined(name) || name === null || name === "") {
                    return "";
                }

                // add a dot at the end of the name, if needed
                if (zoneName.charAt(zoneName.length - 1) !== ".") {
                    name += ".";
                }

                // return what we have if a domain is not specified
                if (!angular.isDefined(domain) || domain === null || domain === "") {
                    return name;
                }

                // add the domain, if it does not already exist
                var domainPart = domain + ".";
                var endOfZoneName = name.slice(domainPart.length * -1);
                if (endOfZoneName.toLowerCase() !== domainPart.toLowerCase()) {
                    name += domainPart;
                }

                return name;
            };

            /**
             * Generates the error text for when an API request fails.
             *
             * @method request_failure_message
             * @param  {Number|String} status   A relevant status code.
             * @return {String}                 The text to be presented to the user.
             */
            store.request_failure_message = function(status) {
                var message = LOCALE.maketext("The API request failed with the following error: [_1] - [_2].", status, HTTP_STATUS.convertHttpStatusToReadable(status));
                if (status === 401 || status === 403) {
                    message += " " + LOCALE.maketext("Your session may have expired or you logged out of the system. [output,url,_1,Login] again to continue.", CJT.getLoginPath());
                }

                return message;
            };

            return store;
        };
        SERVICE_INJECTABLES.push(SERVICE_FACTORY);
        app.factory(SERVICE_NAME, SERVICE_INJECTABLES);

        return {
            namespace: MODULE_NAMESPACE,
            serviceName: SERVICE_NAME,
            class: SERVICE_FACTORY,
        };
    }
);
Back to Directory File Manager