336 lines
11 KiB
JavaScript
336 lines
11 KiB
JavaScript
// Start the application code itself.
|
|
quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel', '$strap.directives'], function($provide) {
|
|
$provide.factory('UserService', ['Restangular', function(Restangular) {
|
|
var userResponse = {
|
|
verified: false,
|
|
anonymous: true,
|
|
username: null,
|
|
email: null,
|
|
askForPassword: false,
|
|
}
|
|
|
|
var userService = {}
|
|
|
|
userService.load = function() {
|
|
var userFetch = Restangular.one('user/');
|
|
userFetch.get().then(function(loadedUser) {
|
|
userResponse = loadedUser;
|
|
|
|
if (!userResponse.anonymous) {
|
|
mixpanel.identify(userResponse.username);
|
|
mixpanel.people.set({
|
|
'$email': userResponse.email,
|
|
'$username': userResponse.username,
|
|
'verified': userResponse.verified
|
|
});
|
|
mixpanel.people.set_once({
|
|
'$created': new Date()
|
|
})
|
|
}
|
|
});
|
|
};
|
|
|
|
userService.currentUser = function() {
|
|
return userResponse;
|
|
}
|
|
|
|
// Load the user the first time.
|
|
userService.load();
|
|
|
|
return userService;
|
|
}]);
|
|
|
|
$provide.factory('KeyService', ['$location', function($location) {
|
|
var keyService = {}
|
|
|
|
if ($location.host() === 'quay.io') {
|
|
keyService['stripePublishableKey'] = 'pk_live_P5wLU0vGdHnZGyKnXlFG4oiu';
|
|
keyService['githubClientId'] = '5a8c08b06c48d89d4d1e';
|
|
} else {
|
|
keyService['stripePublishableKey'] = 'pk_test_uEDHANKm9CHCvVa2DLcipGRh';
|
|
keyService['githubClientId'] = 'cfbc4aca88e5c1b40679';
|
|
}
|
|
|
|
return keyService;
|
|
}]);
|
|
|
|
$provide.factory('PlanService', ['Restangular', 'KeyService', function(Restangular, KeyService) {
|
|
var plans = [
|
|
{
|
|
title: 'Open Source',
|
|
price: 0,
|
|
privateRepos: 0,
|
|
stripeId: 'free',
|
|
audience: 'Share with the world',
|
|
},
|
|
{
|
|
title: 'Micro',
|
|
price: 700,
|
|
privateRepos: 5,
|
|
stripeId: 'micro',
|
|
audience: 'For smaller teams',
|
|
},
|
|
{
|
|
title: 'Basic',
|
|
price: 1200,
|
|
privateRepos: 10,
|
|
stripeId: 'small',
|
|
audience: 'For your basic team',
|
|
},
|
|
{
|
|
title: 'Medium',
|
|
price: 2200,
|
|
privateRepos: 20,
|
|
stripeId: 'medium',
|
|
audience: 'For medium-sized teams',
|
|
},
|
|
];
|
|
|
|
var planDict = {};
|
|
var i;
|
|
for(i = 0; i < plans.length; i++) {
|
|
planDict[plans[i].stripeId] = plans[i];
|
|
}
|
|
|
|
var planService = {}
|
|
|
|
planService.planList = function() {
|
|
return plans;
|
|
};
|
|
|
|
planService.getPlan = function(planId) {
|
|
return planDict[planId];
|
|
};
|
|
|
|
planService.getMinimumPlan = function(privateCount) {
|
|
for (var i = 0; i < plans.length; i++) {
|
|
var plan = plans[i];
|
|
if (plan.privateRepos >= privateCount) {
|
|
return plan;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
planService.showSubscribeDialog = function($scope, planId, started, success, failed) {
|
|
var submitToken = function(token) {
|
|
$scope.$apply(function() {
|
|
started();
|
|
});
|
|
|
|
mixpanel.track('plan_subscribe');
|
|
|
|
var subscriptionDetails = {
|
|
token: token.id,
|
|
plan: planId,
|
|
};
|
|
|
|
var createSubscriptionRequest = Restangular.one('user/plan');
|
|
$scope.$apply(function() {
|
|
createSubscriptionRequest.customPUT(subscriptionDetails).then(success, failed);
|
|
});
|
|
};
|
|
|
|
var planDetails = planService.getPlan(planId)
|
|
StripeCheckout.open({
|
|
key: KeyService.stripePublishableKey,
|
|
address: false, // TODO change to true
|
|
amount: planDetails.price,
|
|
currency: 'usd',
|
|
name: 'Quay ' + planDetails.title + ' Subscription',
|
|
description: 'Up to ' + planDetails.privateRepos + ' private repositories',
|
|
panelLabel: 'Subscribe',
|
|
token: submitToken
|
|
});
|
|
};
|
|
|
|
return planService;
|
|
}]);
|
|
}).
|
|
directive('match', function($parse) {
|
|
return {
|
|
require: 'ngModel',
|
|
link: function(scope, elem, attrs, ctrl) {
|
|
scope.$watch(function() {
|
|
return $parse(attrs.match)(scope) === ctrl.$modelValue;
|
|
}, function(currentValue) {
|
|
ctrl.$setValidity('mismatch', currentValue);
|
|
});
|
|
}
|
|
};
|
|
}).
|
|
directive('onresize', function ($window, $parse) {
|
|
return function (scope, element, attr) {
|
|
var fn = $parse(attr.onresize);
|
|
|
|
var notifyResized = function() {
|
|
scope.$apply(function () {
|
|
fn(scope);
|
|
});
|
|
};
|
|
|
|
angular.element($window).on('resize', null, notifyResized);
|
|
|
|
scope.$on('$destroy', function() {
|
|
angular.element($window).off('resize', null, notifyResized);
|
|
});
|
|
};
|
|
}).
|
|
config(['$routeProvider', '$locationProvider', '$analyticsProvider',
|
|
function($routeProvider, $locationProvider, $analyticsProvider) {
|
|
|
|
$analyticsProvider.virtualPageviews(true);
|
|
|
|
$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, reloadOnSearch: false}).
|
|
when('/repository/:namespace/:name/tag/:tag', {templateUrl: '/static/partials/view-repo.html', controller: RepoCtrl}).
|
|
when('/repository/:namespace/:name/image/:image', {templateUrl: '/static/partials/image-view.html', controller: ImageViewCtrl}).
|
|
when('/repository/:namespace/:name/admin', {templateUrl: '/static/partials/repo-admin.html', controller:RepoAdminCtrl}).
|
|
when('/repository/', {title: 'Repositories', templateUrl: '/static/partials/repo-list.html', controller: RepoListCtrl}).
|
|
when('/user/', {title: 'User Admin', templateUrl: '/static/partials/user-admin.html', controller: UserAdminCtrl}).
|
|
when('/guide/', {title: 'User Guide', templateUrl: '/static/partials/guide.html', controller: GuideCtrl}).
|
|
when('/plans/', {title: 'Plans and Pricing', templateUrl: '/static/partials/plans.html', controller: PlansCtrl}).
|
|
when('/signin/', {title: 'Signin', templateUrl: '/static/partials/signin.html', controller: SigninCtrl}).
|
|
when('/new/', {title: 'Create new repository', templateUrl: '/static/partials/new-repo.html', controller: NewRepoCtrl}).
|
|
|
|
when('/organization/:orgname', {templateUrl: '/static/partials/org-view.html', controller: OrgViewCtrl}).
|
|
when('/organization/:orgname/admin', {templateUrl: '/static/partials/org-admin.html', controller: OrgAdminCtrl}).
|
|
when('/organization/:orgname/teams', {templateUrl: '/static/partials/org-teams.html', controller: OrgTeamsCtrl}).
|
|
when('/organization/:orgname/teams/:teamname', {templateUrl: '/static/partials/team-view.html', controller: TeamViewCtrl}).
|
|
|
|
when('/v1/', {title: 'Activation information', templateUrl: '/static/partials/v1-page.html', controller: V1Ctrl}).
|
|
|
|
when('/', {title: 'Hosted Private Docker Registry', templateUrl: '/static/partials/landing.html', controller: LandingCtrl}).
|
|
otherwise({redirectTo: '/'});
|
|
}]).
|
|
config(function(RestangularProvider) {
|
|
RestangularProvider.setBaseUrl('/api/');
|
|
});
|
|
|
|
quayApp.directive('repoCircle', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/repo-circle.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'repo': '=repo'
|
|
},
|
|
controller: function($scope, $element) {
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
});
|
|
|
|
|
|
quayApp.directive('namespaceSelector', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/namespace-selector.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'user': '=user',
|
|
'namespace': '=namespace'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.setNamespace = function(namespaceObj) {
|
|
if (!namespaceObj) {
|
|
namespaceObj = {'name': '', 'gravatar': ''};
|
|
}
|
|
$scope.namespaceObj = namespaceObj;
|
|
$scope.namespace = namespaceObj.name || namespaceObj.username;
|
|
};
|
|
$scope.setNamespace($scope.user);
|
|
|
|
$scope.$watch('user', function(user) {
|
|
$scope.setNamespace(user);
|
|
});
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
});
|
|
|
|
|
|
quayApp.directive('buildStatus', function () {
|
|
var directiveDefinitionObject = {
|
|
priority: 0,
|
|
templateUrl: '/static/directives/build-status.html',
|
|
replace: false,
|
|
transclude: false,
|
|
restrict: 'C',
|
|
scope: {
|
|
'build': '=build'
|
|
},
|
|
controller: function($scope, $element) {
|
|
$scope.getBuildProgress = function(buildInfo) {
|
|
switch (buildInfo.status) {
|
|
case 'building':
|
|
return (buildInfo.current_command / buildInfo.total_commands) * 100;
|
|
break;
|
|
|
|
case 'pushing':
|
|
var imagePercentDecimal = (buildInfo.image_completion_percent / 100);
|
|
return ((buildInfo.current_image + imagePercentDecimal) / buildInfo.total_images) * 100;
|
|
break;
|
|
|
|
case 'complete':
|
|
return 100;
|
|
break;
|
|
|
|
case 'initializing':
|
|
case 'starting':
|
|
case 'waiting':
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
};
|
|
|
|
$scope.getBuildMessage = function(buildInfo) {
|
|
switch (buildInfo.status) {
|
|
case 'initializing':
|
|
return 'Starting Dockerfile build';
|
|
break;
|
|
|
|
case 'starting':
|
|
case 'waiting':
|
|
case 'building':
|
|
return 'Building image from Dockerfile';
|
|
break;
|
|
|
|
case 'pushing':
|
|
return 'Pushing image built from Dockerfile';
|
|
break;
|
|
|
|
case 'complete':
|
|
return 'Dockerfile build completed and pushed';
|
|
break;
|
|
|
|
case 'error':
|
|
return 'Dockerfile build failed: ' + buildInfo.message;
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
return directiveDefinitionObject;
|
|
});
|
|
|
|
quayApp.run(['$location', '$rootScope', function($location, $rootScope) {
|
|
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
|
|
if (current.$$route.title) {
|
|
$rootScope.title = current.$$route.title;
|
|
}
|
|
});
|
|
}]);
|