270 lines
12 KiB
JavaScript
270 lines
12 KiB
JavaScript
var TEAM_PATTERN = '^[a-zA-Z][a-zA-Z0-9]+$';
|
|
var ROBOT_PATTERN = '^[a-zA-Z][a-zA-Z0-9]{3,29}$';
|
|
var USER_PATTERN = '^[a-z0-9_]{4,30}$';
|
|
|
|
quayDependencies = ['ngRoute', 'chieffancypants.loadingBar', 'angular-tour', 'restangular', 'angularMoment',
|
|
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml',
|
|
'ngAnimate', 'core-ui', 'core-config-setup'];
|
|
|
|
if (window.__config && window.__config.MIXPANEL_KEY) {
|
|
quayDependencies.push('angulartics');
|
|
quayDependencies.push('angulartics.mixpanel');
|
|
}
|
|
|
|
// Define the application.
|
|
quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoadingBarProvider) {
|
|
cfpLoadingBarProvider.includeSpinner = false;
|
|
});
|
|
|
|
// Configure the routes.
|
|
quayApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
|
|
var title = window.__config['REGISTRY_TITLE'] || 'Quay.io';
|
|
|
|
$locationProvider.html5Mode(true);
|
|
|
|
// WARNING WARNING WARNING
|
|
// If you add a route here, you must add a corresponding route in thr endpoints/web.py
|
|
// index rule to make sure that deep links directly deep into the app continue to work.
|
|
// WARNING WARNING WARNING
|
|
$routeProvider.
|
|
when('/repository/:namespace/:name', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl,
|
|
fixFooter: false, reloadOnSearch: false}).
|
|
when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl,
|
|
fixFooter: false}).
|
|
when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl, reloadOnSearch: false}).
|
|
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl, reloadOnSearch: false}).
|
|
when('/repository/:namespace/:name/build', {templateUrl: '/static/partials/repo-build.html', controller:RepoBuildCtrl, reloadOnSearch: false}).
|
|
when('/repository/:namespace/:name/build/:buildid/buildpack', {templateUrl: '/static/partials/build-package.html', controller:BuildPackageCtrl, reloadOnSearch: false}).
|
|
when('/repository/', {title: 'Repositories', description: 'Public and private docker repositories list',
|
|
templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl, reloadOnSearch: false}).
|
|
when('/user/', {title: 'Account Settings', description:'Account settings for ' + title, templateUrl: '/static/partials/user-admin.html',
|
|
reloadOnSearch: false, controller: UserAdminCtrl}).
|
|
when('/superuser/', {title: 'Enterprise Registry Management', description:'Admin panel for ' + title, templateUrl: '/static/partials/super-user.html',
|
|
reloadOnSearch: false, controller: SuperUserAdminCtrl, newLayout: true}).
|
|
when('/setup/', {title: 'Enterprise Registry Setup', description:'Setup for ' + title, templateUrl: '/static/partials/setup.html',
|
|
reloadOnSearch: false, controller: SetupCtrl, newLayout: true}).
|
|
when('/guide/', {title: 'Guide', description:'Guide to using private docker repositories on ' + title,
|
|
templateUrl: '/static/partials/guide.html',
|
|
controller: GuideCtrl}).
|
|
when('/tutorial/', {title: 'Tutorial', description:'Interactive tutorial for using ' + title, templateUrl: '/static/partials/tutorial.html',
|
|
controller: TutorialCtrl}).
|
|
when('/contact/', {title: 'Contact Us', description:'Different ways for you to get a hold of us when you need us most.', templateUrl: '/static/partials/contact.html',
|
|
controller: ContactCtrl}).
|
|
when('/about/', {title: 'About Us', description:'Information about the Quay.io team and the company.', templateUrl: '/static/partials/about.html'}).
|
|
when('/plans/', {title: 'Plans and Pricing', description: 'Plans and pricing for private docker repositories on Quay.io',
|
|
templateUrl: '/static/partials/plans.html', controller: PlansCtrl}).
|
|
when('/security/', {title: 'Security', description: 'Security features used when transmitting and storing data',
|
|
templateUrl: '/static/partials/security.html'}).
|
|
when('/signin/', {title: 'Sign In', description: 'Sign into ' + title, templateUrl: '/static/partials/signin.html', controller: SignInCtrl, reloadOnSearch: false}).
|
|
when('/new/', {title: 'Create new repository', description: 'Create a new public or private docker repository, optionally constructing from a dockerfile',
|
|
templateUrl: '/static/partials/new-repo.html', controller: NewRepoCtrl}).
|
|
when('/organizations/', {title: 'Organizations', description: 'Private docker repository hosting for businesses and organizations',
|
|
templateUrl: '/static/partials/organizations.html', controller: OrgsCtrl}).
|
|
when('/organizations/new/', {title: 'New Organization', description: 'Create a new organization on ' + title,
|
|
templateUrl: '/static/partials/new-organization.html', controller: NewOrgCtrl}).
|
|
when('/organization/:orgname', {templateUrl: '/static/partials/org-view.html', controller: OrgViewCtrl}).
|
|
when('/organization/:orgname/admin', {templateUrl: '/static/partials/org-admin.html', controller: OrgAdminCtrl, reloadOnSearch: false}).
|
|
when('/organization/:orgname/teams/:teamname', {templateUrl: '/static/partials/team-view.html', controller: TeamViewCtrl}).
|
|
when('/organization/:orgname/logs/:membername', {templateUrl: '/static/partials/org-member-logs.html', controller: OrgMemberLogsCtrl}).
|
|
when('/organization/:orgname/application/:clientid', {templateUrl: '/static/partials/manage-application.html',
|
|
controller: ManageApplicationCtrl, reloadOnSearch: false}).
|
|
when('/v1/', {title: 'Activation information', templateUrl: '/static/partials/v1-page.html', controller: V1Ctrl}).
|
|
|
|
|
|
when('/tour/', {title: title + ' Tour', templateUrl: '/static/partials/tour.html', controller: TourCtrl}).
|
|
when('/tour/organizations', {title: 'Teams and Organizations Tour', templateUrl: '/static/partials/tour.html', controller: TourCtrl}).
|
|
when('/tour/features', {title: title + ' Features', templateUrl: '/static/partials/tour.html', controller: TourCtrl}).
|
|
when('/tour/enterprise', {title: 'Enterprise Edition', templateUrl: '/static/partials/tour.html', controller: TourCtrl}).
|
|
|
|
when('/confirminvite', {title: 'Confirm Invite', templateUrl: '/static/partials/confirm-invite.html', controller: ConfirmInviteCtrl, reloadOnSearch: false}).
|
|
|
|
when('/', {title: 'Hosted Private Docker Registry', templateUrl: '/static/partials/landing.html', controller: LandingCtrl,
|
|
pageClass: 'landing-page'}).
|
|
otherwise({redirectTo: '/'});
|
|
}]);
|
|
|
|
// Configure compile provider to add additional URL prefixes to the sanitization list. We use
|
|
// these on the Contact page.
|
|
quayApp.config(function($compileProvider) {
|
|
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|irc):/);
|
|
});
|
|
|
|
// Configure the API provider.
|
|
quayApp.config(function(RestangularProvider) {
|
|
RestangularProvider.setBaseUrl('/api/v1/');
|
|
});
|
|
|
|
// Configure analytics.
|
|
if (window.__config && window.__config.MIXPANEL_KEY) {
|
|
quayApp.config(['$analyticsProvider', function($analyticsProvider) {
|
|
$analyticsProvider.virtualPageviews(true);
|
|
}]);
|
|
}
|
|
|
|
// Configure sentry.
|
|
if (window.__config && window.__config.SENTRY_PUBLIC_DSN) {
|
|
quayApp.config(function($provide) {
|
|
$provide.decorator("$exceptionHandler", function($delegate) {
|
|
return function(ex, cause) {
|
|
$delegate(ex, cause);
|
|
Raven.captureException(ex, {extra: {cause: cause}});
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
// Run the application.
|
|
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout', 'CookieService', 'Features', '$anchorScroll', 'UtilService',
|
|
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout, CookieService, Features, $anchorScroll, UtilService) {
|
|
|
|
// Handle session security.
|
|
Restangular.setDefaultRequestParams(['post', 'put', 'remove', 'delete'], {'_csrf_token': window.__token || ''});
|
|
|
|
// Handle session expiration.
|
|
Restangular.setErrorInterceptor(function(response) {
|
|
if (response.status == 401 && response.data['error_type'] == 'invalid_token' &&
|
|
response.data['session_required'] !== false) {
|
|
$('#sessionexpiredModal').modal({});
|
|
return false;
|
|
}
|
|
|
|
if (response.status == 503) {
|
|
$('#cannotContactService').modal({});
|
|
return false;
|
|
}
|
|
|
|
if (response.status == 500) {
|
|
document.location = '/500';
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
// Check if we need to redirect based on a previously chosen plan.
|
|
var result = PlanService.handleNotedPlan();
|
|
|
|
// Check to see if we need to show a redirection page.
|
|
var redirectUrl = CookieService.get('quay.redirectAfterLoad');
|
|
CookieService.clear('quay.redirectAfterLoad');
|
|
|
|
if (!result && redirectUrl && redirectUrl.indexOf(window.location.href) == 0) {
|
|
window.location = redirectUrl;
|
|
return;
|
|
}
|
|
|
|
var changeTab = function(activeTab, opt_timeout) {
|
|
var checkCount = 0;
|
|
$timeout(function() {
|
|
if (checkCount > 5) { return; }
|
|
checkCount++;
|
|
|
|
$('a[data-toggle="tab"]').each(function(index) {
|
|
var tabName = this.getAttribute('data-target').substr(1);
|
|
if (tabName != activeTab) {
|
|
return;
|
|
}
|
|
|
|
if (this.clientWidth == 0) {
|
|
changeTab(activeTab, 500);
|
|
return;
|
|
}
|
|
|
|
UtilService.clickElement(this);
|
|
});
|
|
}, opt_timeout);
|
|
};
|
|
|
|
var resetDefaultTab = function() {
|
|
$timeout(function() {
|
|
$('a[data-toggle="tab"]').each(function(index) {
|
|
if (index == 0) {
|
|
UtilService.clickElement(this);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
$rootScope.$watch('description', function(description) {
|
|
if (!description) {
|
|
description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.';
|
|
}
|
|
|
|
// Note: We set the content of the description tag manually here rather than using Angular binding
|
|
// because we need the <meta> tag to have a default description that is not of the form "{{ description }}",
|
|
// we read by tools that do not properly invoke the Angular code.
|
|
$('#descriptionTag').attr('content', description);
|
|
});
|
|
|
|
$rootScope.$on('$routeUpdate', function(){
|
|
if ($location.search()['tab']) {
|
|
changeTab($location.search()['tab']);
|
|
} else {
|
|
resetDefaultTab();
|
|
}
|
|
});
|
|
|
|
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
|
|
$rootScope.pageClass = '';
|
|
$rootScope.current = current.$$route;
|
|
|
|
if (!current.$$route) { return; }
|
|
|
|
if (current.$$route.title) {
|
|
$rootScope.title = current.$$route.title;
|
|
}
|
|
|
|
if (current.$$route.pageClass) {
|
|
$rootScope.pageClass = current.$$route.pageClass;
|
|
}
|
|
|
|
$rootScope.newLayout = !!current.$$route.newLayout;
|
|
|
|
if (current.$$route.description) {
|
|
$rootScope.description = current.$$route.description;
|
|
} else {
|
|
$rootScope.description = '';
|
|
}
|
|
|
|
$rootScope.fixFooter = !!current.$$route.fixFooter;
|
|
$anchorScroll();
|
|
});
|
|
|
|
$rootScope.$on('$viewContentLoaded', function(event, current) {
|
|
var activeTab = $location.search()['tab'];
|
|
|
|
// Setup deep linking of tabs. This will change the search field of the URL whenever a tab
|
|
// is changed in the UI.
|
|
$timeout(function() {
|
|
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
|
var tabName = e.target.getAttribute('data-target').substr(1);
|
|
$rootScope.$apply(function() {
|
|
var isDefaultTab = $('a[data-toggle="tab"]')[0] == e.target;
|
|
var newSearch = $.extend($location.search(), {});
|
|
if (isDefaultTab) {
|
|
delete newSearch['tab'];
|
|
} else {
|
|
newSearch['tab'] = tabName;
|
|
}
|
|
|
|
$location.search(newSearch);
|
|
});
|
|
|
|
e.preventDefault();
|
|
});
|
|
|
|
if (activeTab) {
|
|
changeTab(activeTab);
|
|
}
|
|
}, 400); // 400ms to make sure angular has rendered.
|
|
});
|
|
|
|
var initallyChecked = false;
|
|
window.__isLoading = function() {
|
|
if (!initallyChecked) {
|
|
initallyChecked = true;
|
|
return true;
|
|
}
|
|
return $http.pendingRequests.length > 0;
|
|
};
|
|
}]);
|