'use strict'; /* global tld */ // create main application module var app = angular.module('Application', ['angular-md5', 'ui-notification', 'ui.bootstrap']); app.filter('zoneName', function () { return function (domain) { return tld.getDomain(domain); }; }); app.controller('SetupDNSController', ['$scope', '$http', '$timeout', 'Client', function ($scope, $http, $timeout, Client) { var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {}); $scope.state = null; // 'initialized', 'waitingForDnsSetup', 'waitingForBox' $scope.error = null; $scope.provider = ''; $scope.showDNSSetup = false; $scope.instanceId = ''; $scope.isDomain = false; $scope.isSubdomain = false; $scope.hyphenatedSubdomains = false; $scope.tlsProvider = [ { name: 'Let\'s Encrypt Prod', value: 'letsencrypt-prod' }, { name: 'Let\'s Encrypt Prod - Wildcard', value: 'letsencrypt-prod-wildcard' }, { name: 'Let\'s Encrypt Staging', value: 'letsencrypt-staging' }, { name: 'Let\'s Encrypt Staging - Wildcard', value: 'letsencrypt-staging-wildcard' }, { name: 'Self-Signed', value: 'fallback' }, // this is not 'Custom' because we don't allow user to upload certs during setup phase ]; $scope.needsPort80 = function (dnsProvider, tlsProvider) { return ((dnsProvider === 'manual' || dnsProvider === 'noop' || dnsProvider === 'wildcard') && (tlsProvider === 'letsencrypt-prod' || tlsProvider === 'letsencrypt-staging')); }; // If we migrate the api origin we have to poll the new location if (search.admin_fqdn) Client.apiOrigin = 'https://' + search.admin_fqdn; $scope.$watch('dnsCredentials.domain', function (newVal) { if (!newVal) { $scope.isDomain = false; $scope.isSubdomain = false; } else if (!tld.getDomain(newVal) || newVal[newVal.length-1] === '.') { $scope.isDomain = false; $scope.isSubdomain = false; } else { $scope.isDomain = true; $scope.isSubdomain = tld.getDomain(newVal) !== newVal; } }); // keep in sync with domains.js $scope.dnsProvider = [ { name: 'AWS Route53', value: 'route53' }, { name: 'Cloudflare (DNS only)', value: 'cloudflare' }, { name: 'Digital Ocean', value: 'digitalocean' }, { name: 'Gandi LiveDNS', value: 'gandi' }, { name: 'GoDaddy', value: 'godaddy' }, { name: 'Google Cloud DNS', value: 'gcdns' }, { name: 'Name.com', value: 'namecom' }, { name: 'Wildcard', value: 'wildcard' }, { name: 'Manual (not recommended)', value: 'manual' }, { name: 'No-op (only for development)', value: 'noop' } ]; $scope.dnsCredentials = { error: null, busy: false, advancedVisible: false, domain: '', accessKeyId: '', secretAccessKey: '', gcdnsKey: { keyFileName: '', content: '' }, digitalOceanToken: '', gandiApiKey: '', cloudflareEmail: '', cloudflareToken: '', godaddyApiKey: '', godaddyApiSecret: '', nameComUsername: '', nameComToken: '', provider: 'route53', zoneName: '', tlsConfig: { provider: 'letsencrypt-prod-wildcard' }, hyphenatedSubdomains: false }; $scope.setDefaultTlsProvider = function () { var dnsProvider = $scope.dnsCredentials.provider; // wildcard LE won't work without automated DNS if (dnsProvider === 'manual' || dnsProvider === 'noop' || dnsProvider === 'wildcard') { $scope.dnsCredentials.tlsConfig.provider = 'letsencrypt-prod'; } else { $scope.dnsCredentials.tlsConfig.provider = 'letsencrypt-prod-wildcard'; } }; function readFileLocally(obj, file, fileName) { return function (event) { $scope.$apply(function () { obj[file] = null; obj[fileName] = event.target.files[0].name; var reader = new FileReader(); reader.onload = function (result) { if (!result.target || !result.target.result) return console.error('Unable to read local file'); obj[file] = result.target.result; }; reader.readAsText(event.target.files[0]); }); }; } document.getElementById('gcdnsKeyFileInput').onchange = readFileLocally($scope.dnsCredentials.gcdnsKey, 'content', 'keyFileName'); $scope.setDnsCredentials = function () { $scope.dnsCredentials.busy = true; $scope.dnsCredentials.error = null; $scope.error = null; var provider = $scope.dnsCredentials.provider; var data = { providerToken: $scope.instanceId, hyphenatedSubdomains: $scope.dnsCredentials.hyphenatedSubdomains }; if (provider === 'route53') { data.accessKeyId = $scope.dnsCredentials.accessKeyId; data.secretAccessKey = $scope.dnsCredentials.secretAccessKey; } else if (provider === 'gcdns') { try { var serviceAccountKey = JSON.parse($scope.dnsCredentials.gcdnsKey.content); data.projectId = serviceAccountKey.project_id; data.credentials = { client_email: serviceAccountKey.client_email, private_key: serviceAccountKey.private_key }; if (!data.projectId || !data.credentials || !data.credentials.client_email || !data.credentials.private_key) { throw 'fields_missing'; } } catch(e) { $scope.dnsCredentials.error = 'Cannot parse Google Service Account Key'; $scope.dnsCredentials.busy = false; return; } } else if (provider === 'digitalocean') { data.token = $scope.dnsCredentials.digitalOceanToken; } else if (provider === 'gandi') { data.token = $scope.dnsCredentials.gandiApiKey; } else if (provider === 'godaddy') { data.apiKey = $scope.dnsCredentials.godaddyApiKey; data.apiSecret = $scope.dnsCredentials.godaddyApiSecret; } else if (provider === 'cloudflare') { data.email = $scope.dnsCredentials.cloudflareEmail; data.token = $scope.dnsCredentials.cloudflareToken; } else if (provider === 'namecom') { data.username = $scope.dnsCredentials.nameComUsername; data.token = $scope.dnsCredentials.nameComToken; } var tlsConfig = { provider: $scope.dnsCredentials.tlsConfig.provider, wildcard: false }; if ($scope.dnsCredentials.tlsConfig.provider.indexOf('-wildcard') !== -1) { tlsConfig.provider = tlsConfig.provider.replace('-wildcard', ''); tlsConfig.wildcard = true; } Client.setupDnsConfig($scope.dnsCredentials.domain, $scope.dnsCredentials.zoneName, provider, data, tlsConfig, function (error) { if (error && error.statusCode === 401) { $scope.dnsCredentials.busy = false; $scope.error = 'Wrong instance id provided.'; return; } else if (error) { $scope.dnsCredentials.busy = false; $scope.dnsCredentials.error = error.message; return; } waitForDnsSetup(); }); }; function waitForDnsSetup() { $scope.state = 'waitingForDnsSetup'; Client.getStatus(function (error, status) { // webadminStatus.dns is intentionally not tested. it can be false if dns creds are invalid // runConfigurationChecks() in main.js will pick the .dns and show a notification if (!error && status.adminFqdn && status.webadminStatus.tls) { window.location.href = 'https://' + status.adminFqdn + '/setup.html'; } setTimeout(waitForDnsSetup, 5000); }); } function initialize() { Client.getStatus(function (error, status) { if (error) { // During domain migration, the box code restarts and can result in getStatus() failing temporarily console.error(error); $scope.state = 'waitingForBox'; return $timeout(initialize, 3000); } // domain is currently like a lock flag if (status.adminFqdn) return waitForDnsSetup(); if (status.provider === 'digitalocean') $scope.dnsCredentials.provider = 'digitalocean'; if (status.provider === 'gce') $scope.dnsCredentials.provider = 'gcdns'; if (status.provider === 'ami') { // remove route53 on ami $scope.dnsProvider.shift(); $scope.dnsCredentials.provider = 'wildcard'; } $scope.instanceId = search.instanceId; $scope.provider = status.provider; $scope.hyphenatedSubdomains = status.edition === 'hostingprovider'; $scope.state = 'initialized'; }); } initialize(); }]);