initial import for Open Source 🎉
This commit is contained in:
parent
1898c361f3
commit
9c0dd3b722
2048 changed files with 218743 additions and 0 deletions
107
config_app/js/services/angular-poll-channel.js
vendored
Normal file
107
config_app/js/services/angular-poll-channel.js
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* Specialized class for conducting an HTTP poll, while properly preventing multiple calls.
|
||||
*/
|
||||
angular.module('quay-config').factory('AngularPollChannel',
|
||||
['ApiService', '$timeout', 'DocumentVisibilityService', 'CORE_EVENT', '$rootScope',
|
||||
function(ApiService, $timeout, DocumentVisibilityService, CORE_EVENT, $rootScope) {
|
||||
var _PollChannel = function(scope, requester, opt_sleeptime) {
|
||||
this.scope_ = scope;
|
||||
this.requester_ = requester;
|
||||
this.sleeptime_ = opt_sleeptime || (60 * 1000 /* 60s */);
|
||||
this.timer_ = null;
|
||||
|
||||
this.working = false;
|
||||
this.polling = false;
|
||||
this.skipping = false;
|
||||
|
||||
var that = this;
|
||||
|
||||
var visibilityHandler = $rootScope.$on(CORE_EVENT.DOC_VISIBILITY_CHANGE, function() {
|
||||
// If the poll channel was skipping because the visibility was hidden, call it immediately.
|
||||
if (that.skipping && !DocumentVisibilityService.isHidden()) {
|
||||
that.call_();
|
||||
}
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
that.stop();
|
||||
visibilityHandler();
|
||||
});
|
||||
};
|
||||
|
||||
_PollChannel.prototype.setSleepTime = function(sleepTime) {
|
||||
this.sleeptime_ = sleepTime;
|
||||
this.stop();
|
||||
this.start(true);
|
||||
};
|
||||
|
||||
_PollChannel.prototype.stop = function() {
|
||||
if (this.timer_) {
|
||||
$timeout.cancel(this.timer_);
|
||||
this.timer_ = null;
|
||||
this.polling = false;
|
||||
}
|
||||
|
||||
this.skipping = false;
|
||||
this.working = false;
|
||||
};
|
||||
|
||||
_PollChannel.prototype.start = function(opt_skipFirstCall) {
|
||||
// Make sure we invoke call outside the normal digest cycle, since
|
||||
// we'll call $scope.$apply ourselves.
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
if (opt_skipFirstCall) {
|
||||
that.setupTimer_();
|
||||
return;
|
||||
}
|
||||
|
||||
that.call_();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
_PollChannel.prototype.call_ = function() {
|
||||
if (this.working) { return; }
|
||||
|
||||
// If the document is currently hidden, skip the call.
|
||||
if (DocumentVisibilityService.isHidden()) {
|
||||
this.skipping = true;
|
||||
this.setupTimer_();
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
this.working = true;
|
||||
|
||||
$timeout(function() {
|
||||
that.requester_(function(status) {
|
||||
if (status) {
|
||||
that.working = false;
|
||||
that.skipping = false;
|
||||
that.setupTimer_();
|
||||
} else {
|
||||
that.stop();
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
_PollChannel.prototype.setupTimer_ = function() {
|
||||
if (this.timer_) { return; }
|
||||
|
||||
var that = this;
|
||||
this.polling = true;
|
||||
this.timer_ = $timeout(function() {
|
||||
that.timer_ = null;
|
||||
that.call_();
|
||||
}, this.sleeptime_)
|
||||
};
|
||||
|
||||
var service = {
|
||||
'create': function(scope, requester, opt_sleeptime) {
|
||||
return new _PollChannel(scope, requester, opt_sleeptime);
|
||||
}
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
335
config_app/js/services/api-service.js
Normal file
335
config_app/js/services/api-service.js
Normal file
|
@ -0,0 +1,335 @@
|
|||
/**
|
||||
* Service which exposes the server-defined API as a nice set of helper methods and automatic
|
||||
* callbacks. Any method defined on the server is exposed here as an equivalent method. Also
|
||||
* defines some helper functions for working with API responses.
|
||||
*/
|
||||
angular.module('quay-config').factory('ApiService', ['Restangular', '$q', 'UtilService', function(Restangular, $q, UtilService) {
|
||||
var apiService = {};
|
||||
|
||||
if (!window.__endpoints) {
|
||||
return apiService;
|
||||
}
|
||||
|
||||
var getResource = function(getMethod, operation, opt_parameters, opt_background) {
|
||||
var resource = {};
|
||||
resource.withOptions = function(options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
};
|
||||
|
||||
resource.get = function(processor, opt_errorHandler) {
|
||||
var options = this.options;
|
||||
var result = {
|
||||
'loading': true,
|
||||
'value': null,
|
||||
'hasError': false
|
||||
};
|
||||
|
||||
getMethod(options, opt_parameters, opt_background, true).then(function(resp) {
|
||||
result.value = processor(resp);
|
||||
result.loading = false;
|
||||
}, function(resp) {
|
||||
result.hasError = true;
|
||||
result.loading = false;
|
||||
if (opt_errorHandler) {
|
||||
opt_errorHandler(resp);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return resource;
|
||||
};
|
||||
|
||||
var buildUrl = function(path, parameters) {
|
||||
// We already have /api/v1/ on the URLs, so remove them from the paths.
|
||||
path = path.substr('/api/v1/'.length, path.length);
|
||||
|
||||
// Build the path, adjusted with the inline parameters.
|
||||
var used = {};
|
||||
var url = '';
|
||||
for (var i = 0; i < path.length; ++i) {
|
||||
var c = path[i];
|
||||
if (c == '{') {
|
||||
var end = path.indexOf('}', i);
|
||||
var varName = path.substr(i + 1, end - i - 1);
|
||||
|
||||
if (!parameters[varName]) {
|
||||
throw new Error('Missing parameter: ' + varName);
|
||||
}
|
||||
|
||||
used[varName] = true;
|
||||
url += parameters[varName];
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
url += c;
|
||||
}
|
||||
|
||||
// Append any query parameters.
|
||||
var isFirst = true;
|
||||
for (var paramName in parameters) {
|
||||
if (!parameters.hasOwnProperty(paramName)) { continue; }
|
||||
if (used[paramName]) { continue; }
|
||||
|
||||
var value = parameters[paramName];
|
||||
if (value) {
|
||||
url += isFirst ? '?' : '&';
|
||||
url += paramName + '=' + encodeURIComponent(value)
|
||||
isFirst = false;
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
var getGenericOperationName = function(userOperationName) {
|
||||
return userOperationName.replace('User', '');
|
||||
};
|
||||
|
||||
var getMatchingUserOperationName = function(orgOperationName, method, userRelatedResource) {
|
||||
if (userRelatedResource) {
|
||||
if (userRelatedResource[method.toLowerCase()]) {
|
||||
return userRelatedResource[method.toLowerCase()]['operationId'];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Could not find user operation matching org operation: ' + orgOperationName);
|
||||
};
|
||||
|
||||
var freshLoginInProgress = [];
|
||||
var reject = function(msg) {
|
||||
for (var i = 0; i < freshLoginInProgress.length; ++i) {
|
||||
freshLoginInProgress[i].deferred.reject({'data': {'message': msg}});
|
||||
}
|
||||
freshLoginInProgress = [];
|
||||
};
|
||||
|
||||
var retry = function() {
|
||||
for (var i = 0; i < freshLoginInProgress.length; ++i) {
|
||||
freshLoginInProgress[i].retry();
|
||||
}
|
||||
freshLoginInProgress = [];
|
||||
};
|
||||
|
||||
var freshLoginFailCheck = function(opName, opArgs) {
|
||||
return function(resp) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
// If the error is a fresh login required, show the dialog.
|
||||
// TODO: remove error_type (old style error)
|
||||
var fresh_login_required = resp.data['title'] == 'fresh_login_required' || resp.data['error_type'] == 'fresh_login_required';
|
||||
if (resp.status == 401 && fresh_login_required) {
|
||||
var retryOperation = function() {
|
||||
apiService[opName].apply(apiService, opArgs).then(function(resp) {
|
||||
deferred.resolve(resp);
|
||||
}, function(resp) {
|
||||
deferred.reject(resp);
|
||||
});
|
||||
};
|
||||
|
||||
var verifyNow = function() {
|
||||
if (!$('#freshPassword').val()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var info = {
|
||||
'password': $('#freshPassword').val()
|
||||
};
|
||||
|
||||
$('#freshPassword').val('');
|
||||
|
||||
// Conduct the sign in of the user.
|
||||
apiService.verifyUser(info).then(function() {
|
||||
// On success, retry the operations. if it succeeds, then resolve the
|
||||
// deferred promise with the result. Otherwise, reject the same.
|
||||
retry();
|
||||
}, function(resp) {
|
||||
// Reject with the sign in error.
|
||||
reject('Invalid verification credentials');
|
||||
});
|
||||
};
|
||||
|
||||
// Add the retry call to the in progress list. If there is more than a single
|
||||
// in progress call, we skip showing the dialog (since it has already been
|
||||
// shown).
|
||||
freshLoginInProgress.push({
|
||||
'deferred': deferred,
|
||||
'retry': retryOperation
|
||||
})
|
||||
|
||||
if (freshLoginInProgress.length > 1) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var box = bootbox.dialog({
|
||||
"message": 'It has been more than a few minutes since you last logged in, ' +
|
||||
'so please verify your password to perform this sensitive operation:' +
|
||||
'<form style="margin-top: 10px" action="javascript:$(\'.btn-continue\').click();void(0)">' +
|
||||
'<input id="freshPassword" class="form-control" type="password" placeholder="Current Password">' +
|
||||
'</form>',
|
||||
"title": 'Please Verify',
|
||||
"buttons": {
|
||||
"verify": {
|
||||
"label": "Verify",
|
||||
"className": "btn-success btn-continue",
|
||||
"callback": verifyNow
|
||||
},
|
||||
"close": {
|
||||
"label": "Cancel",
|
||||
"className": "btn-default",
|
||||
"callback": function() {
|
||||
reject('Verification canceled')
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
box.bind('shown.bs.modal', function(){
|
||||
box.find("input").focus();
|
||||
box.find("form").submit(function() {
|
||||
if (!$('#freshPassword').val()) { return; }
|
||||
|
||||
box.modal('hide');
|
||||
verifyNow();
|
||||
});
|
||||
});
|
||||
|
||||
// Return a new promise. We'll accept or reject it based on the result
|
||||
// of the login.
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Otherwise, we just 'raise' the error via the reject method on the promise.
|
||||
return $q.reject(resp);
|
||||
};
|
||||
};
|
||||
|
||||
var buildMethodsForOperation = function(operation, method, path, resourceMap) {
|
||||
var operationName = operation['operationId'];
|
||||
var urlPath = path['x-path'];
|
||||
|
||||
// Add the operation itself.
|
||||
apiService[operationName] = function(opt_options, opt_parameters, opt_background, opt_forceget, opt_responseType) {
|
||||
var one = Restangular.one(buildUrl(urlPath, opt_parameters));
|
||||
|
||||
if (opt_background || opt_responseType) {
|
||||
let httpConfig = {};
|
||||
|
||||
if (opt_background) {
|
||||
httpConfig['ignoreLoadingBar'] = true;
|
||||
}
|
||||
if (opt_responseType) {
|
||||
httpConfig['responseType'] = opt_responseType;
|
||||
}
|
||||
|
||||
one.withHttpConfig(httpConfig);
|
||||
}
|
||||
|
||||
var opObj = one[opt_forceget ? 'get' : 'custom' + method.toUpperCase()](opt_options);
|
||||
|
||||
// If the operation requires_fresh_login, then add a specialized error handler that
|
||||
// will defer the operation's result if sudo is requested.
|
||||
if (operation['x-requires-fresh-login']) {
|
||||
opObj = opObj.catch(freshLoginFailCheck(operationName, arguments));
|
||||
}
|
||||
return opObj;
|
||||
};
|
||||
|
||||
// If the method for the operation is a GET, add an operationAsResource method.
|
||||
if (method == 'get') {
|
||||
apiService[operationName + 'AsResource'] = function(opt_parameters, opt_background) {
|
||||
var getMethod = apiService[operationName];
|
||||
return getResource(getMethod, operation, opt_parameters, opt_background);
|
||||
};
|
||||
}
|
||||
|
||||
// If the operation has a user-related operation, then make a generic operation for this operation
|
||||
// that can call both the user and the organization versions of the operation, depending on the
|
||||
// parameters given.
|
||||
if (path['x-user-related']) {
|
||||
var userOperationName = getMatchingUserOperationName(operationName, method, resourceMap[path['x-user-related']]);
|
||||
var genericOperationName = getGenericOperationName(userOperationName);
|
||||
apiService[genericOperationName] = function(orgname, opt_options, opt_parameters, opt_background) {
|
||||
if (orgname) {
|
||||
if (orgname.name) {
|
||||
orgname = orgname.name;
|
||||
}
|
||||
|
||||
var params = jQuery.extend({'orgname' : orgname}, opt_parameters || {}, opt_background);
|
||||
return apiService[operationName](opt_options, params);
|
||||
} else {
|
||||
return apiService[userOperationName](opt_options, opt_parameters, opt_background);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var allowedMethods = ['get', 'post', 'put', 'delete'];
|
||||
var resourceMap = {};
|
||||
var forEachOperation = function(callback) {
|
||||
for (var path in window.__endpoints) {
|
||||
if (!window.__endpoints.hasOwnProperty(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var method in window.__endpoints[path]) {
|
||||
if (!window.__endpoints[path].hasOwnProperty(method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allowedMethods.indexOf(method.toLowerCase()) < 0) { continue; }
|
||||
callback(window.__endpoints[path][method], method, window.__endpoints[path]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Build the map of resource names to their objects.
|
||||
forEachOperation(function(operation, method, path) {
|
||||
resourceMap[path['x-name']] = path;
|
||||
});
|
||||
|
||||
// Construct the methods for each API endpoint.
|
||||
forEachOperation(function(operation, method, path) {
|
||||
buildMethodsForOperation(operation, method, path, resourceMap);
|
||||
});
|
||||
|
||||
apiService.getErrorMessage = function(resp, defaultMessage) {
|
||||
var message = defaultMessage;
|
||||
if (resp && resp['data']) {
|
||||
//TODO: remove error_message and error_description (old style error)
|
||||
message = resp['data']['detail'] || resp['data']['error_message'] || resp['data']['message'] || resp['data']['error_description'] || message;
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
apiService.errorDisplay = function(defaultMessage, opt_handler) {
|
||||
return function(resp) {
|
||||
var message = apiService.getErrorMessage(resp, defaultMessage);
|
||||
if (opt_handler) {
|
||||
var handlerMessage = opt_handler(resp);
|
||||
if (handlerMessage) {
|
||||
message = handlerMessage;
|
||||
}
|
||||
}
|
||||
|
||||
message = UtilService.stringToHTML(message);
|
||||
bootbox.dialog({
|
||||
"message": message,
|
||||
"title": defaultMessage || 'Request Failure',
|
||||
"buttons": {
|
||||
"close": {
|
||||
"label": "Close",
|
||||
"className": "btn-primary"
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return apiService;
|
||||
}]);
|
43
config_app/js/services/container-service.js
Normal file
43
config_app/js/services/container-service.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Helper service for working with the registry's container. Only works in enterprise.
|
||||
*/
|
||||
angular.module('quay-config')
|
||||
.factory('ContainerService', ['ApiService', '$timeout', 'Restangular',
|
||||
function(ApiService, $timeout, Restangular) {
|
||||
var containerService = {};
|
||||
containerService.restartContainer = function(callback) {
|
||||
ApiService.errorDisplay('Removed Endpoint. This error should never be seen.')
|
||||
};
|
||||
|
||||
containerService.scheduleStatusCheck = function(callback, opt_config) {
|
||||
$timeout(function() {
|
||||
containerService.checkStatus(callback, opt_config);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
containerService.checkStatus = function(callback, opt_config) {
|
||||
var errorHandler = function(resp) {
|
||||
if (resp.status == 404 || resp.status == 502 || resp.status == -1) {
|
||||
// Container has not yet come back up, so we schedule another check.
|
||||
containerService.scheduleStatusCheck(callback, opt_config);
|
||||
return;
|
||||
}
|
||||
|
||||
return ApiService.errorDisplay('Cannot load status. Please report this to support')(resp);
|
||||
};
|
||||
|
||||
// If config is specified, override the API base URL from this point onward.
|
||||
// TODO: Find a better way than this. This is safe, since this will only be called
|
||||
// for a restart, but it is still ugly.
|
||||
if (opt_config && opt_config['SERVER_HOSTNAME']) {
|
||||
var scheme = opt_config['PREFERRED_URL_SCHEME'] || 'http';
|
||||
var baseUrl = scheme + '://' + opt_config['SERVER_HOSTNAME'] + '/api/v1/';
|
||||
Restangular.setBaseUrl(baseUrl);
|
||||
}
|
||||
|
||||
ApiService.scRegistryStatus(null, null, /* background */true)
|
||||
.then(callback, errorHandler);
|
||||
};
|
||||
|
||||
return containerService;
|
||||
}]);
|
23
config_app/js/services/cookie-service.js
Normal file
23
config_app/js/services/cookie-service.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Helper service for working with cookies.
|
||||
*/
|
||||
angular.module('quay-config').factory('CookieService', ['$cookies', function($cookies) {
|
||||
var cookieService = {};
|
||||
cookieService.putPermanent = function(name, value) {
|
||||
document.cookie = escape(name) + "=" + escape(value) + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
|
||||
};
|
||||
|
||||
cookieService.putSession = function(name, value) {
|
||||
$cookies.put(name, value);
|
||||
};
|
||||
|
||||
cookieService.clear = function(name) {
|
||||
$cookies.remove(name);
|
||||
};
|
||||
|
||||
cookieService.get = function(name) {
|
||||
return $cookies.get(name);
|
||||
};
|
||||
|
||||
return cookieService;
|
||||
}]);
|
60
config_app/js/services/document-visibility-service.js
Normal file
60
config_app/js/services/document-visibility-service.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Helper service which fires off events when the document's visibility changes, as well as allowing
|
||||
* other Angular code to query the state of the document's visibility directly.
|
||||
*/
|
||||
angular.module('quay-config').constant('CORE_EVENT', {
|
||||
DOC_VISIBILITY_CHANGE: 'core.event.doc_visibility_change'
|
||||
});
|
||||
|
||||
angular.module('quay-config').factory('DocumentVisibilityService', ['$rootScope', '$document', 'CORE_EVENT',
|
||||
function($rootScope, $document, CORE_EVENT) {
|
||||
var document = $document[0],
|
||||
features,
|
||||
detectedFeature;
|
||||
|
||||
function broadcastChangeEvent() {
|
||||
$rootScope.$broadcast(CORE_EVENT.DOC_VISIBILITY_CHANGE,
|
||||
document[detectedFeature.propertyName]);
|
||||
}
|
||||
|
||||
features = {
|
||||
standard: {
|
||||
eventName: 'visibilitychange',
|
||||
propertyName: 'hidden'
|
||||
},
|
||||
moz: {
|
||||
eventName: 'mozvisibilitychange',
|
||||
propertyName: 'mozHidden'
|
||||
},
|
||||
ms: {
|
||||
eventName: 'msvisibilitychange',
|
||||
propertyName: 'msHidden'
|
||||
},
|
||||
webkit: {
|
||||
eventName: 'webkitvisibilitychange',
|
||||
propertyName: 'webkitHidden'
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(features).some(function(feature) {
|
||||
if (document[features[feature].propertyName] !== undefined) {
|
||||
detectedFeature = features[feature];
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (detectedFeature) {
|
||||
$document.on(detectedFeature.eventName, broadcastChangeEvent);
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Is the window currently hidden or not.
|
||||
*/
|
||||
isHidden: function() {
|
||||
if (detectedFeature) {
|
||||
return document[detectedFeature.propertyName];
|
||||
}
|
||||
}
|
||||
};
|
||||
}]);
|
91
config_app/js/services/features-config.js
Normal file
91
config_app/js/services/features-config.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Feature flags.
|
||||
*/
|
||||
angular.module('quay-config').factory('Features', [function() {
|
||||
if (!window.__features) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var features = window.__features;
|
||||
features.getFeature = function(name, opt_defaultValue) {
|
||||
var value = features[name];
|
||||
if (value == null) {
|
||||
return opt_defaultValue;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
features.hasFeature = function(name) {
|
||||
return !!features.getFeature(name);
|
||||
};
|
||||
|
||||
features.matchesFeatures = function(list) {
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
var value = features.getFeature(list[i]);
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return features;
|
||||
}]);
|
||||
|
||||
/**
|
||||
* Application configuration.
|
||||
*/
|
||||
angular.module('quay-config').factory('Config', ['Features', function(Features) {
|
||||
if (!window.__config) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var config = window.__config;
|
||||
config.getDomain = function() {
|
||||
return config['SERVER_HOSTNAME'];
|
||||
};
|
||||
|
||||
config.getHost = function(opt_auth) {
|
||||
var auth = opt_auth;
|
||||
if (auth) {
|
||||
auth = auth + '@';
|
||||
}
|
||||
|
||||
return config['PREFERRED_URL_SCHEME'] + '://' + auth + config['SERVER_HOSTNAME'];
|
||||
};
|
||||
|
||||
config.getHttp = function() {
|
||||
return config['PREFERRED_URL_SCHEME'];
|
||||
};
|
||||
|
||||
config.getUrl = function(opt_path) {
|
||||
var path = opt_path || '';
|
||||
return config['PREFERRED_URL_SCHEME'] + '://' + config['SERVER_HOSTNAME'] + path;
|
||||
};
|
||||
|
||||
config.getValue = function(name, opt_defaultValue) {
|
||||
var value = config[name];
|
||||
if (value == null) {
|
||||
return opt_defaultValue;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
config.getEnterpriseLogo = function(opt_defaultValue) {
|
||||
if (!config.ENTERPRISE_LOGO_URL) {
|
||||
if (opt_defaultValue) {
|
||||
return opt_defaultValue;
|
||||
}
|
||||
|
||||
if (Features.BILLING) {
|
||||
return '/static/img/quay-horizontal-color.svg';
|
||||
} else {
|
||||
return '/static/img/QuayEnterprise_horizontal_color.svg';
|
||||
}
|
||||
}
|
||||
|
||||
return config.ENTERPRISE_LOGO_URL;
|
||||
};
|
||||
|
||||
return config;
|
||||
}]);
|
15
config_app/js/services/services.types.ts
Normal file
15
config_app/js/services/services.types.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export interface AngularPollChannel {
|
||||
create: PollConstructor
|
||||
}
|
||||
|
||||
type PollConstructor = (scope: MockAngularScope, requester: ShouldContinueCallback, opt_sleeptime?: number) => PollHandle;
|
||||
type MockAngularScope = {
|
||||
'$on': Function
|
||||
};
|
||||
type ShouldContinueCallback = (boolean) => void;
|
||||
|
||||
export interface PollHandle {
|
||||
start(opt_skipFirstCall?: boolean): void,
|
||||
stop(): void,
|
||||
setSleepTime(sleepTime: number): void,
|
||||
}
|
177
config_app/js/services/user-service.js
Normal file
177
config_app/js/services/user-service.js
Normal file
|
@ -0,0 +1,177 @@
|
|||
import * as Raven from 'raven-js';
|
||||
|
||||
|
||||
/**
|
||||
* Service which monitors the current user session and provides methods for returning information
|
||||
* about the user.
|
||||
*/
|
||||
angular.module('quay-config')
|
||||
.factory('UserService', ['ApiService', 'CookieService', '$rootScope', 'Config', '$location', '$timeout',
|
||||
|
||||
function(ApiService, CookieService, $rootScope, Config, $location, $timeout) {
|
||||
var userResponse = {
|
||||
verified: false,
|
||||
anonymous: true,
|
||||
username: null,
|
||||
email: null,
|
||||
organizations: [],
|
||||
logins: [],
|
||||
beforeload: true
|
||||
};
|
||||
|
||||
var userService = {};
|
||||
|
||||
userService.hasEverLoggedIn = function() {
|
||||
return CookieService.get('quay.loggedin') == 'true';
|
||||
};
|
||||
|
||||
userService.updateUserIn = function(scope, opt_callback) {
|
||||
scope.$watch(function () { return userService.currentUser(); }, function (currentUser) {
|
||||
if (currentUser) {
|
||||
$timeout(function(){
|
||||
scope.user = currentUser;
|
||||
if (opt_callback) {
|
||||
opt_callback(currentUser);
|
||||
}
|
||||
}, 0, false);
|
||||
};
|
||||
}, true);
|
||||
};
|
||||
|
||||
userService.load = function(opt_callback) {
|
||||
var handleUserResponse = function(loadedUser) {
|
||||
userResponse = loadedUser;
|
||||
|
||||
if (!userResponse.anonymous) {
|
||||
if (Config.MIXPANEL_KEY) {
|
||||
try {
|
||||
mixpanel.identify(userResponse.username);
|
||||
mixpanel.people.set({
|
||||
'$email': userResponse.email,
|
||||
'$username': userResponse.username,
|
||||
'verified': userResponse.verified
|
||||
});
|
||||
mixpanel.people.set_once({
|
||||
'$created': new Date()
|
||||
})
|
||||
} catch (e) {
|
||||
window.console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.MARKETO_MUNCHKIN_ID && userResponse['marketo_user_hash']) {
|
||||
var associateLeadBody = {'Email': userResponse.email};
|
||||
if (window.Munchkin !== undefined) {
|
||||
try {
|
||||
Munchkin.munchkinFunction(
|
||||
'associateLead',
|
||||
associateLeadBody,
|
||||
userResponse['marketo_user_hash']
|
||||
);
|
||||
} catch (e) {
|
||||
}
|
||||
} else {
|
||||
window.__quay_munchkin_queue.push([
|
||||
'associateLead',
|
||||
associateLeadBody,
|
||||
userResponse['marketo_user_hash']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.Raven !== undefined) {
|
||||
try {
|
||||
Raven.setUser({
|
||||
email: userResponse.email,
|
||||
id: userResponse.username
|
||||
});
|
||||
} catch (e) {
|
||||
window.console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
CookieService.putPermanent('quay.loggedin', 'true');
|
||||
} else {
|
||||
if (window.Raven !== undefined) {
|
||||
Raven.setUser();
|
||||
}
|
||||
}
|
||||
|
||||
// If the loaded user has a prompt, redirect them to the update page.
|
||||
if (loadedUser.prompts && loadedUser.prompts.length) {
|
||||
$location.path('/updateuser');
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_callback) {
|
||||
opt_callback(loadedUser);
|
||||
}
|
||||
};
|
||||
|
||||
ApiService.getLoggedInUser().then(function(loadedUser) {
|
||||
handleUserResponse(loadedUser);
|
||||
}, function() {
|
||||
handleUserResponse({'anonymous': true});
|
||||
});
|
||||
};
|
||||
|
||||
userService.isOrganization = function(name) {
|
||||
return !!userService.getOrganization(name);
|
||||
};
|
||||
|
||||
userService.getOrganization = function(name) {
|
||||
if (!userResponse || !userResponse.organizations) { return null; }
|
||||
for (var i = 0; i < userResponse.organizations.length; ++i) {
|
||||
var org = userResponse.organizations[i];
|
||||
if (org.name == name) {
|
||||
return org;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
userService.isNamespaceAdmin = function(namespace) {
|
||||
if (namespace == userResponse.username) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var org = userService.getOrganization(namespace);
|
||||
if (!org) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return org.is_org_admin;
|
||||
};
|
||||
|
||||
userService.isKnownNamespace = function(namespace) {
|
||||
if (namespace == userResponse.username) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var org = userService.getOrganization(namespace);
|
||||
return !!org;
|
||||
};
|
||||
|
||||
userService.getNamespace = function(namespace) {
|
||||
var org = userService.getOrganization(namespace);
|
||||
if (org) {
|
||||
return org;
|
||||
}
|
||||
|
||||
if (namespace == userResponse.username) {
|
||||
return userResponse;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
userService.currentUser = function() {
|
||||
return userResponse;
|
||||
};
|
||||
|
||||
// Update the user in the root scope.
|
||||
userService.updateUserIn($rootScope);
|
||||
|
||||
return userService;
|
||||
}]);
|
83
config_app/js/services/util-service.js
Normal file
83
config_app/js/services/util-service.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Service which exposes various utility methods.
|
||||
*/
|
||||
angular.module('quay-config').factory('UtilService', ['$sanitize',
|
||||
function($sanitize) {
|
||||
var utilService = {};
|
||||
|
||||
var adBlockEnabled = null;
|
||||
|
||||
utilService.isAdBlockEnabled = function(callback) {
|
||||
if (adBlockEnabled !== null) {
|
||||
callback(adBlockEnabled);
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof blockAdBlock === 'undefined') {
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var bab = new BlockAdBlock({
|
||||
checkOnLoad: false,
|
||||
resetOnEnd: true
|
||||
});
|
||||
|
||||
bab.onDetected(function() { adBlockEnabled = true; callback(true); });
|
||||
bab.onNotDetected(function() { adBlockEnabled = false; callback(false); });
|
||||
bab.check();
|
||||
};
|
||||
|
||||
utilService.isEmailAddress = function(val) {
|
||||
var emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
|
||||
return emailRegex.test(val);
|
||||
};
|
||||
|
||||
utilService.escapeHtmlString = function(text) {
|
||||
var textStr = (text || '').toString();
|
||||
var adjusted = textStr.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
|
||||
return adjusted;
|
||||
};
|
||||
|
||||
utilService.stringToHTML = function(text) {
|
||||
text = utilService.escapeHtmlString(text);
|
||||
text = text.replace(/\n/g, '<br>');
|
||||
return text;
|
||||
};
|
||||
|
||||
utilService.getRestUrl = function(args) {
|
||||
var url = '';
|
||||
for (var i = 0; i < arguments.length; ++i) {
|
||||
if (i > 0) {
|
||||
url += '/';
|
||||
}
|
||||
url += encodeURI(arguments[i])
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
utilService.textToSafeHtml = function(text) {
|
||||
return $sanitize(utilService.escapeHtmlString(text));
|
||||
};
|
||||
|
||||
return utilService;
|
||||
}])
|
||||
.factory('CoreDialog', [() => {
|
||||
var service = {};
|
||||
service['fatal'] = function(title, message) {
|
||||
bootbox.dialog({
|
||||
"title": title,
|
||||
"message": "<div class='alert-icon-container-container'><div class='alert-icon-container'><div class='alert-icon'></div></div></div>" + message,
|
||||
"buttons": {},
|
||||
"className": "co-dialog fatal-error",
|
||||
"closeButton": false
|
||||
});
|
||||
};
|
||||
|
||||
return service;
|
||||
}]);
|
Reference in a new issue