import * as URI from 'urijs'; (function() { /** * The Setup page provides a nice GUI walkthrough experience for setting up Quay Enterprise. */ angular.module('quayPages').config(['pages', function(pages) { pages.create('setup', 'setup.html', SetupCtrl, { 'newLayout': true, 'title': 'Quay Enterprise Setup' }) }]); function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) { if (!Features.SUPER_USERS) { return; } $scope.HOSTNAME_REGEX = '^[a-zA-Z-0-9_\.\-]+(:[0-9]+)?$'; $scope.validateHostname = function(hostname) { if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) { return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.' } return null; }; // Note: The values of the enumeration are important for isStepFamily. For example, // *all* states under the "configuring db" family must start with "config-db". $scope.States = { // Loading the state of the product. 'LOADING': 'loading', // The configuration directory is missing. 'MISSING_CONFIG_DIR': 'missing-config-dir', // The config.yaml exists but it is invalid. 'INVALID_CONFIG': 'config-invalid', // License is being uploaded. 'UPLOAD_LICENSE': 'upload-license', // DB is being configured. 'CONFIG_DB': 'config-db', // DB information is being validated. 'VALIDATING_DB': 'config-db-validating', // DB information is being saved to the config. 'SAVING_DB': 'config-db-saving', // A validation error occurred with the database. 'DB_ERROR': 'config-db-error', // Database is being setup. 'DB_SETUP': 'setup-db', // Database setup has succeeded. 'DB_SETUP_SUCCESS': 'setup-db-success', // An error occurred when setting up the database. 'DB_SETUP_ERROR': 'setup-db-error', // The container is being restarted for the database changes. 'DB_RESTARTING': 'setup-db-restarting', // A superuser is being configured. 'CREATE_SUPERUSER': 'create-superuser', // The superuser is being created. 'CREATING_SUPERUSER': 'create-superuser-creating', // An error occurred when setting up the superuser. 'SUPERUSER_ERROR': 'create-superuser-error', // The superuser was created successfully. 'SUPERUSER_CREATED': 'create-superuser-created', // General configuration is being setup. 'CONFIG': 'config', // The configuration is fully valid. 'VALID_CONFIG': 'valid-config', // The container is being restarted for the configuration changes. 'CONFIG_RESTARTING': 'config-restarting', // The product is ready for use. 'READY': 'ready' } $scope.csrf_token = window.__token; $scope.currentStep = $scope.States.LOADING; $scope.errors = {}; $scope.stepProgress = []; $scope.hasSSL = false; $scope.hostname = null; $scope.currentConfig = null; $scope.currentState = { 'hasDatabaseSSLCert': false, 'licenseValid': false }; $scope.$watch('currentStep', function(currentStep) { $scope.stepProgress = $scope.getProgress(currentStep); switch (currentStep) { case $scope.States.CONFIG: $('#setupModal').modal('hide'); break; case $scope.States.MISSING_CONFIG_DIR: $scope.showMissingConfigDialog(); break; case $scope.States.INVALID_CONFIG: $scope.showInvalidConfigDialog(); break; case $scope.States.DB_SETUP: $scope.performDatabaseSetup(); // Fall-through. case $scope.States.CREATE_SUPERUSER: case $scope.States.DB_RESTARTING: case $scope.States.CONFIG_DB: case $scope.States.UPLOAD_LICENSE: case $scope.States.VALID_CONFIG: case $scope.States.READY: $('#setupModal').modal({ keyboard: false, backdrop: 'static' }); break; } }); $scope.restartContainer = function(state) { $scope.currentStep = state; ContainerService.restartContainer(function() { $scope.checkStatus() }); }; $scope.showSuperuserPanel = function() { $('#setupModal').modal('hide'); var prefix = $scope.hasSSL ? 'https' : 'http'; var hostname = $scope.hostname; if (!hostname) { hostname = document.location.hostname; if (document.location.port) { hostname = hostname + ':' + document.location.port; } } window.location = prefix + '://' + hostname + '/superuser'; }; $scope.configurationSaved = function(config) { $scope.hasSSL = config['PREFERRED_URL_SCHEME'] == 'https'; $scope.hostname = config['SERVER_HOSTNAME']; $scope.currentConfig = config; $scope.currentStep = $scope.States.VALID_CONFIG; }; $scope.getProgress = function(step) { var isStep = $scope.isStep; var isStepFamily = $scope.isStepFamily; var States = $scope.States; return [ isStepFamily(step, States.UPLOAD_LICENSE), isStepFamily(step, States.CONFIG_DB), isStepFamily(step, States.DB_SETUP), isStep(step, States.DB_RESTARTING), isStepFamily(step, States.CREATE_SUPERUSER), isStep(step, States.CONFIG), isStep(step, States.VALID_CONFIG), isStep(step, States.CONFIG_RESTARTING), isStep(step, States.READY) ]; }; $scope.isStepFamily = function(step, family) { if (!step) { return false; } return step.indexOf(family) == 0; }; $scope.isStep = function(step) { for (var i = 1; i < arguments.length; ++i) { if (arguments[i] == step) { return true; } } return false; }; $scope.beginSetup = function() { $scope.currentStep = $scope.States.CONFIG_DB; }; $scope.showInvalidConfigDialog = function() { var message = "The config.yaml file found in conf/stack could not be parsed." var title = "Invalid configuration file"; CoreDialog.fatal(title, message); }; $scope.showMissingConfigDialog = function() { var message = "A volume should be mounted into the container at /conf/stack: " + "

docker run -v /path/to/config:/conf/stack
" + "
Once fixed, restart the container. For more information, " + "" + "Read the Setup Guide" var title = "Missing configuration volume"; CoreDialog.fatal(title, message); }; $scope.parseDbUri = function(value) { if (!value) { return null; } // Format: mysql+pymysql://:@/ var uri = URI(value); return { 'kind': uri.protocol(), 'username': uri.username(), 'password': uri.password(), 'server': uri.host(), 'database': uri.path() ? uri.path().substr(1) : '' }; }; $scope.serializeDbUri = function(fields) { if (!fields['server']) { return ''; } var uri = URI(); try { if (!fields['server']) { return ''; } if (!fields['database']) { return ''; } uri = uri && uri.host(fields['server']); uri = uri && uri.protocol(fields['kind']); uri = uri && uri.username(fields['username']); uri = uri && uri.password(fields['password']); uri = uri && uri.path('/' + (fields['database'] || '')); uri = uri && uri.toString(); } catch (ex) { return ''; } return uri; }; $scope.createSuperUser = function() { $scope.currentStep = $scope.States.CREATING_SUPERUSER; ApiService.scCreateInitialSuperuser($scope.superUser, null).then(function(resp) { UserService.load(); $scope.checkStatus(); }, function(resp) { $scope.currentStep = $scope.States.SUPERUSER_ERROR; $scope.errors.SuperuserCreationError = ApiService.getErrorMessage(resp, 'Could not create superuser'); }); }; $scope.performDatabaseSetup = function() { $scope.currentStep = $scope.States.DB_SETUP; ApiService.scSetupDatabase(null, null).then(function(resp) { if (resp['error']) { $scope.currentStep = $scope.States.DB_SETUP_ERROR; $scope.errors.DatabaseSetupError = resp['error']; } else { $scope.currentStep = $scope.States.DB_SETUP_SUCCESS; } }, ApiService.errorDisplay('Could not setup database. Please report this to support.')) }; $scope.validateDatabase = function() { $scope.currentStep = $scope.States.VALIDATING_DB; $scope.databaseInvalid = null; var data = { 'config': { 'DB_URI': $scope.databaseUri }, 'hostname': window.location.host }; if ($scope.currentState.hasDatabaseSSLCert) { data['config']['DB_CONNECTION_ARGS'] = { 'ssl': { 'ca': 'conf/stack/database.pem' } }; } var params = { 'service': 'database' }; ApiService.scValidateConfig(data, params).then(function(resp) { var status = resp.status; if (status) { $scope.currentStep = $scope.States.SAVING_DB; ApiService.scUpdateConfig(data, null).then(function(resp) { $scope.checkStatus(); }, ApiService.errorDisplay('Cannot update config. Please report this to support')); } else { $scope.currentStep = $scope.States.DB_ERROR; $scope.errors.DatabaseValidationError = resp.reason; } }, ApiService.errorDisplay('Cannot validate database. Please report this to support')); }; $scope.checkStatus = function() { ContainerService.checkStatus(function(resp) { $scope.currentStep = resp['status']; }, $scope.currentConfig); }; // Load the initial status. $scope.checkStatus(); }; })();