Restructure code

Now all files dealing with repositories reside in app/repository and all
dealing with tag reside in app/tag. That's far more easy to maintain
than having tag files spread across multiple folders.
This commit is contained in:
Konrad Kleine 2015-08-31 14:23:31 +02:00
parent 9a4ba3c4bb
commit 3f988e7309
33 changed files with 237 additions and 112 deletions

View file

@ -39,7 +39,7 @@ module.exports = function (grunt) {
tasks: ['wiredep']
},
js: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
files: ['<%= yeoman.app %>/{,*/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: '<%= connect.options.livereload %>'
@ -78,9 +78,9 @@ module.exports = function (grunt) {
},
proxies: [
{
context: '/v1',
host: 'path-to-your-registry',
port: 80,
context: '/v2',
host: 'path-to-your-registry-v2',
port: 5000,
https: false,
xforward: false,
headers: {
@ -201,7 +201,7 @@ module.exports = function (grunt) {
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'<%= yeoman.dist %>/{,*/}*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
@ -297,7 +297,7 @@ module.exports = function (grunt) {
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html', 'views/{,*/}*.html', 'scripts/directives/{,*/}*.html'],
src: ['*.html', '{,*/}*.html'],
dest: '<%= yeoman.dist %>'
}]
}
@ -335,8 +335,7 @@ module.exports = function (grunt) {
'*.{ico,png,txt}',
'.htaccess',
'*.html',
'views/{,*/}*.html',
'scripts/directives/{,*/}*.html',
'{,*/}*.html',
'images/{,*/}*.{webp}',
'fonts/*'
]

View file

@ -15,7 +15,7 @@ angular
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch',
'ngTouch',
'registry-services', // TODO: Maybe the following dependencies are not needed? At least they weren't in the "yo angular" output.
'main-controller',
'repository-controller',
@ -24,7 +24,7 @@ angular
'repository-list-directive',
'tag-list-directive',
'image-details-directive',
'tag-item-controller',
'tag-item-controller',
'image-controller',
'create-tag-controller',
'delete-tags-controller',
@ -44,41 +44,41 @@ angular
$locationProvider.html5Mode(true);
// Don't show the spinner when making XHR requests.
// Also, show the bar only if an XHR request takes longer than 50ms.
// Also, show the bar only if an XHR request takes longer than 50ms.
cfpLoadingBarProvider.includeSpinner = false;
cfpLoadingBarProvider.latencyThreshold = 10;
cfpLoadingBarProvider.latencyThreshold = 10;
// Don't strip trailing slashes from calculated URLs
$resourceProvider.defaults.stripTrailingSlashes = false;
$resourceProvider.defaults.stripTrailingSlashes = false;
$routeProvider.
when('/home', {
templateUrl: 'views/home.html',
templateUrl: 'home.html',
}).
when('/repositories/:searchTerm?', {
templateUrl: 'views/repository-list.html',
templateUrl: 'repository/repository-list.html',
}).
when('/repository/:repositoryUser/:repositoryName/', {
templateUrl: 'views/repository-detail.html',
templateUrl: 'repository/repository-detail.html',
controller: 'RepositoryController',
}).
when('/repository/:repositoryUser/:repositoryName/tags/:searchName?', {
templateUrl: 'views/repository-detail.html',
templateUrl: 'repository/repository-detail.html',
controller: 'RepositoryController',
}).
when('/about', {
templateUrl: 'views/about.html',
}).
when('/tag/:repositoryUser/:repositoryName/:tagName/:imageId', {
templateUrl: 'views/tag-detail.html',
templateUrl: 'tag/tag-detail.html',
controller: 'TagController',
}).
when('/image/:imageId', {
templateUrl: 'views/image-detail.html',
templateUrl: 'tag/image-detail.html',
controller: 'ImageController',
}).
when('/image/:imageId/tag/:repositoryUser?/:repositoryName?', {
templateUrl: 'views/create-tag.html',
templateUrl: 'tag/create-tag.html',
controller: 'CreateTagController',
}).
otherwise({

View file

@ -4,7 +4,7 @@ angular.module('image-details-directive', [])
.directive('imageDetails', function(){
return {
restrict: 'E',
templateUrl: 'scripts/directives/image-details-directive.html',
templateUrl: 'image/image-details-directive.html',
controller: 'ImageController',
};
});
});

View file

@ -24,7 +24,7 @@
<![endif]-->
<!-- Add your site or application content here -->
<div class="container">
@ -75,22 +75,26 @@
<!-- endbuild -->
<!-- build:js({.tmp,app}) scripts/scripts.js -->
<script src="scripts/app.js"></script>
<script src="scripts/controllers/repository-controller.js"></script>
<script src="scripts/controllers/tag-controller.js"></script>
<script src="scripts/controllers/main-controller.js"></script>
<script src="scripts/controllers/tag-item-controller.js"></script>
<script src="scripts/controllers/image-controller.js"></script>
<script src="scripts/controllers/create-tag-controller.js"></script>
<script src="scripts/controllers/delete-tags-controller.js"></script>
<script src="scripts/controllers/delete-repository-controller.js"></script>
<script src="scripts/directives/repository-selector-directive.js"></script>
<script src="scripts/directives/repository-list-directive.js"></script>
<script src="scripts/directives/tag-list-directive.js"></script>
<script src="scripts/directives/image-details-directive.js"></script>
<script src="scripts/services/registry-services.js"></script>
<script src="scripts/services/app-version-services.js"></script>
<script src="scripts/services/app-mode-services.js"></script>
<script src="app.js"></script>
<script src="main-controller.js"></script>
<script src="repository/repository-controller.js"></script>
<script src="repository/delete-repository-controller.js"></script>
<script src="repository/repository-selector-directive.js"></script>
<script src="repository/repository-list-directive.js"></script>
<script src="tag/tag-controller.js"></script>
<script src="tag/tag-item-controller.js"></script>
<script src="tag/create-tag-controller.js"></script>
<script src="tag/delete-tags-controller.js"></script>
<script src="tag/tag-list-directive.js"></script>
<script src="image/image-controller.js"></script>
<script src="image/image-details-directive.js"></script>
<script src="services/registry-services.js"></script>
<script src="services/app-version-services.js"></script>
<script src="services/app-mode-services.js"></script>
<!-- endbuild -->
</body>
</html>

View file

@ -12,19 +12,19 @@ angular.module('delete-repository-controller', ['registry-services'])
function($scope, $route, $modalInstance, $window, Repository, items, information){
$scope.items = items;
$scope.information = information;
// Callback that triggers deletion of tags and reloading of page
$scope.ok = function () {
angular.forEach($scope.items, function(value, key) {
var repoStr = value;
var repoUser = value.split("/")[0];
var repoName = value.split("/")[1];
var repo = {
repoUser: repoUser,
repoName: repoName
};
Repository.delete(repo,
// success
function(value, responseHeaders) {
@ -36,16 +36,16 @@ angular.module('delete-repository-controller', ['registry-services'])
}
);
});
$modalInstance.close();
// Go to the repositories page
$window.location.href = '#/repositories';
$window.location.href = 'repositories';
$route.reload();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);

View file

@ -10,39 +10,45 @@
angular.module('repository-controller', ['registry-services', 'app-mode-services'])
.controller('RepositoryController', ['$scope', '$route', '$routeParams', '$location', '$modal', 'Repository', 'AppMode',
function($scope, $route, $routeParams, $location, $modal, Repository, AppMode){
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
$scope.searchTerm = $route.current.params.searchTerm;
$scope.repositoryUser = $route.current.params.repositoryUser;
$scope.repositoryName = $route.current.params.repositoryName;
$scope.repository = $scope.repositoryUser + '/' + $scope.repositoryName;
$scope.repositories = Repository.query();
$scope.appMode = AppMode.query();
// selected repos
$scope.selectedRepositories = [];
// helper method to get selected tags
$scope.selectedRepos = function selectedRepos() {
return filterFilter($scope.repositories, { selected: true });
return filterFilter($scope.repositories.repos, { selected: true });
};
// watch fruits for changes
$scope.$watch('repositories|filter:{selected:true}', function(nv) {
$scope.selectedRepositories = nv.map(function (repo) {
return repo.name;
});
// watch repos for changes
// Actually we would like to monitor the "selected" portion of an object
// in repositories.repos but since there's only one boolean property right
// now it will automatically test for "selected". This can cause trouble
// in the future though.
$scope.$watch('repositories|filter:{$:true}', function(nv) {
if (nv.repos) {
$scope.selectedRepositories = nv.repos.map(function (repo) {
return repo.name;
});
}
}, true);
$scope.openConfirmRepoDeletionDialog = function(size) {
var modalInstance = $modal.open({
animation: true,
templateUrl: 'views/modalConfirmDeleteItems.html',
templateUrl: 'modalConfirmDeleteItems.html',
controller: 'DeleteRepositoryController',
size: size,
resolve: {
@ -61,5 +67,5 @@ angular.module('repository-controller', ['registry-services', 'app-mode-services
}
});
};
}]);

View file

@ -1,7 +1,7 @@
<div class="input-group repository-list-search">
<span class="input-group-addon"><span class="glyphicon glyphicon-search"></span></span>
<input type="search" placeholder="Search for a repository" ng-model="searchTerm" class="form-control">
<span class="input-group-addon">Repositories <span ng-bind-template="({{filteredRepositories.length}}/{{repositories.length}})"></span></span>
<span class="input-group-addon">Repositories <span ng-bind-template="({{filteredRepositories.length}}/{{repositories.repos.length}})"></span></span>
</div>
<div ng-hide="appMode.browseOnly">
@ -14,7 +14,7 @@
</button>
</div>
<span ng-repeat-start="(username, repos) in (filteredRepositories=(repositories | filter:searchTerm) | groupBy: 'username')"></span>
<span ng-repeat-start="(username, repos) in (filteredRepositories=(repositories.repos | filter:searchTerm) | groupBy: 'username')"></span>
<div class="table-responsive">
<table class="table table-hover">

View file

@ -4,7 +4,7 @@ angular.module('repository-list-directive', [])
.directive('repositoryList', function(){
return {
restrict: 'E',
templateUrl: 'scripts/directives/repository-list-directive.html',
templateUrl: 'repository/repository-list-directive.html',
controller: 'RepositoryController',
};
});
});

View file

@ -4,7 +4,7 @@ angular.module('repository-selector-directive', [])
.directive('repositorySelector', function(){
return {
restrict: 'E',
templateUrl: 'scripts/directives/repository-selector-directive.html',
templateUrl: 'repository/repository-selector-directive.html',
controller: 'RepositoryController',
};
});
});

View file

@ -0,0 +1,11 @@
'use strict';
angular.module('registry-host-services', ['ngResource'])
.factory('RegistryHost', ['$resource', function($resource){
return $resource('/registry-host.json', {}, {
'query': {
method:'GET',
isArray: false,
},
});
}]);

View file

@ -0,0 +1,99 @@
'use strict';
// This is the main entrypoint to interact with the Docker registry.
// Helpful resources
//
// https://docs.angularjs.org/tutorial/step_11
// https://docs.angularjs.org/api/ngResource/service/$resource
angular.module('registry-services', ['ngResource'])
.factory('RegistryHost', ['$resource', function($resource){
return $resource('registry-host.json', {}, {
'query': {
method:'GET',
isArray: false,
},
});
}])
// Repository returns:
// {
// repos: [
// {username: 'SomeNamespace', name: 'SomeNamespace/SomeRepo1', selected: true|false},
// {username: 'SomeOtherNamespace', name: 'SomeOtherNamespace/SomeRepo2', selected: true|false},
// {username: 'SomeCompletelyDifferenNamespace', name: 'SomeCompletelyDifferenNamespace/SomeRepo3', selected: true|false}
// ],
// nextLink: '/v2/_catalog?last=SomeNamespace%F2SomeRepo&n=1'
// }
.factory('Repository', ['$resource', function($resource){
return $resource('/v2/_catalog?last=:last&n=:n', {}, {
'query': {
method:'GET',
isArray: false,
transformResponse: function(data, headers){
var repos = angular.fromJson(data).repositories;
var ret = {
repos: [],
nextLink: headers()['link']
};
angular.forEach(repos, function(value/*, key*/) {
ret.repos.push({
username: ''+value.split('/')[0],
name: value,
selected: false
});
});
return ret;
}
},
'delete': {
url: '/v2/repositories/:repoUser/:repoName/',
method: 'DELETE',
}
});
}])
.factory('Tag', ['$resource', function($resource){
// TODO: rename :repo to repoUser/repoString for convenience.
return $resource('/v1/repositories/:repoUser/:repoName/tags', {}, {
'query': {
method:'GET',
isArray: true,
transformResponse: function(data/*, headers*/){
var res = [];
var resp = angular.fromJson(data);
for (var i in resp){
res.push({name: i, imageId: resp[i], selected: false});
}
return res;
},
},
'delete': {
url: '/v1/repositories/:repoUser/:repoName/tags/:tagName',
method: 'DELETE',
},
'exists': {
url: '/v1/repositories/:repoUser/:repoName/tags/:tagName',
method: 'GET',
transformResponse: function(data/*, headers*/){
// data will be the image ID if successful or an error object.
data = angular.isString(angular.fromJson(data));
return data;
},
},
// Usage: Tag.save({repoUser:'someuser', repoName: 'someRepo', tagName: 'someTagName'}, imageId);
'save': {
method:'PUT',
url: '/v1/repositories/:repoUser/:repoName/tags/:tagName',
},
});
}])
.factory('Image', ['$resource', function($resource){
return $resource('/v1/images/:imageId/json', {}, {
'query': { method:'GET', isArray: false},
});
}])
.factory('Ancestry', ['$resource', function($resource){
return $resource('/v1/images/:imageId/ancestry', {}, {
'query': { method:'GET', isArray: true},
});
}]);

View file

@ -3,46 +3,49 @@
// This is the main entrypoint to interact with the Docker registry.
// Helpful resources
//
//
// https://docs.angularjs.org/tutorial/step_11
// https://docs.angularjs.org/api/ngResource/service/$resource
angular.module('registry-services', ['ngResource'])
.factory('RegistryHost', ['$resource', '$log', function($resource, $log){
return $resource('/registry-host.json', {}, {
.factory('RegistryHost', ['$resource', function($resource){
return $resource('registry-host.json', {}, {
'query': {
method:'GET',
isArray: false,
},
});
}])
.factory('Repository', ['$resource', '$log', function($resource, $log){
return $resource('/v1/search?q=:searchTerm', {}, {
.factory('Repository', ['$resource', function($resource){
return $resource('/v2/_catalog', {}, {
'query': {
method:'GET',
isArray: true,
transformResponse: function(data, headers){
var res = angular.fromJson(data).results;
angular.forEach(res, function(value, key) {
value.username = ""+value.name.split("/")[0];
transformResponse: function(data/*, headers*/){
console.log(data);
var res = angular.fromJson(data).repositories;
angular.forEach(res, function(value/*, key*/) {
value.name = value;
value.username = ''+value.split('/')[0];
value.selected = false;
});
return res;
}
},
'delete': {
url: '/v1/repositories/:repoUser/:repoName/',
url: '/v2/repositories/:repoUser/:repoName/',
method: 'DELETE',
},
}
});
}])
.factory('Tag', ['$resource', '$log', function($resource, $log){
.factory('Tag', ['$resource', function($resource){
// TODO: rename :repo to repoUser/repoString for convenience.
return $resource('/v1/repositories/:repoUser/:repoName/tags', {}, {
'query': {
method:'GET',
isArray: true,
transformResponse: function(data, headers){
transformResponse: function(data/*, headers*/){
var res = [];
var resp = angular.fromJson(data);
for (var i in resp){
@ -58,7 +61,7 @@ angular.module('registry-services', ['ngResource'])
'exists': {
url: '/v1/repositories/:repoUser/:repoName/tags/:tagName',
method: 'GET',
transformResponse: function(data, headers){
transformResponse: function(data/*, headers*/){
// data will be the image ID if successful or an error object.
data = angular.isString(angular.fromJson(data));
return data;
@ -71,12 +74,12 @@ angular.module('registry-services', ['ngResource'])
},
});
}])
.factory('Image', ['$resource', '$log', function($resource, $log){
.factory('Image', ['$resource', function($resource){
return $resource('/v1/images/:imageId/json', {}, {
'query': { method:'GET', isArray: false},
});
}])
.factory('Ancestry', ['$resource', '$log', function($resource, $log){
.factory('Ancestry', ['$resource', function($resource){
return $resource('/v1/images/:imageId/ancestry', {}, {
'query': { method:'GET', isArray: true},
});

View file

@ -10,15 +10,15 @@
angular.module('create-tag-controller', ['registry-services', 'app-mode-services'])
.controller('CreateTagController', ['$scope', '$route', '$routeParams', '$location', '$log', '$filter', '$window', 'Tag', 'Repository', 'AppMode',
function($scope, $route, $routeParams, $location, $log, $filter, $window, Tag, Repository, AppMode){
$scope.imageId = $route.current.params.imageId;
$scope.repositoryUser = $route.current.params.repositoryUser;
$scope.repositoryName = $route.current.params.repositoryName;
$scope.imageId = $route.current.params.imageId;
$scope.repositoryUser = $route.current.params.repositoryUser;
$scope.repositoryName = $route.current.params.repositoryName;
$scope.master = {};
$scope.repositories = Repository.query();
$scope.appMode = AppMode.query();
$scope.tag = {
repoUser: $scope.repositoryUser,
repoName: $scope.repositoryName
@ -26,9 +26,9 @@ angular.module('create-tag-controller', ['registry-services', 'app-mode-services
$scope.selectRepo = function(repoStr) {
var res = repoStr.split('/');
$scope.tag.repoUser = res[0];
$scope.tag.repoName = res[1];
$scope.tag.repoName = res[1];
};
$scope.doCreateTag = function(tag) {
var tagStr = tag.repoUser + '/' + tag.repoName + ':' + tag.tagName;
Tag.save(tag, '"'+$scope.imageId+'"',
@ -36,7 +36,7 @@ angular.module('create-tag-controller', ['registry-services', 'app-mode-services
function() {
toastr.success('Created tag: ' + tagStr);
// Redirect to new tag page
$window.location.href = '#/tag/' + tag.repoUser + '/' + tag.repoName + '/' + tag.tagName + '/' + $scope.imageId;
$window.location.href = 'tag/' + tag.repoUser + '/' + tag.repoName + '/' + tag.tagName + '/' + $scope.imageId;
},
// error
function(httpResponse) {
@ -44,7 +44,7 @@ angular.module('create-tag-controller', ['registry-services', 'app-mode-services
}
);
};
$scope.createTag = function(tag, forceOverwrite) {
$scope.master = angular.copy(tag);
var tagStr = tag.repoUser + '/' + tag.repoName + ':' + tag.tagName;

View file

@ -13,7 +13,7 @@ angular.module('delete-tags-controller', ['registry-services'])
{
$scope.items = items;
$scope.information = information;
// Callback that triggers deletion of tags and reloading of page
$scope.ok = function () {
angular.forEach($scope.items, function(value, key) {
@ -21,18 +21,18 @@ angular.module('delete-tags-controller', ['registry-services'])
var tagName = value.split(":")[1];
var repoUser = value.split("/")[0];
var repoName = value.split("/")[1].split(":")[0];
var tag = {
repoUser: repoUser,
repoName: repoName,
tagName: tagName
};
if (!Tag.exists(tag)) {
toastr.warning('Tag does no longer exist: ' + tagStr);
return;
}
Tag.delete(tag,
// success
function(value, responseHeaders) {
@ -45,13 +45,13 @@ angular.module('delete-tags-controller', ['registry-services'])
);
});
$modalInstance.close();
// Go to the repositories page
$window.location.href = '#/repositories';
$window.location.href = 'repositories';
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);

View file

@ -10,44 +10,44 @@
angular.module('tag-controller', ['registry-services'])
.controller('TagController', ['$scope', '$route', '$routeParams', '$location', '$log', '$filter', 'Tag', 'filterFilter', '$modal',
function($scope, $route, $routeParams, $location, $log, $filter, Tag, filterFilter, $modal){
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
$scope.searchName = $route.current.params.searchName;
$scope.repositoryUser = $route.current.params.repositoryUser;
$scope.repositoryName = $route.current.params.repositoryName;
$scope.repository = $scope.repositoryUser + '/' + $scope.repositoryName;
$scope.tagName = $route.current.params.tagName;
$scope.tags = Tag.query({
repoUser: $scope.repositoryUser,
repoName: $scope.repositoryName
});
// Copy collection for rendering in a smart-table
$scope.displayedTags = [].concat($scope.tags);
// selected tags
$scope.selection = [];
// helper method to get selected tags
$scope.selectedTags = function selectedTags() {
return filterFilter($scope.displayedTags, { selected: true });
};
// watch fruits for changes
$scope.$watch('tags|filter:{selected:true}', function(nv) {
$scope.selection = nv.map(function (tag) {
return $scope.repository + ':' + tag.name;
});
}, true);
$scope.openConfirmTagDeletionDialog = function(size) {
var modalInstance = $modal.open({
animation: true,
templateUrl: 'views/modalConfirmDeleteItems.html',
templateUrl: 'modalConfirmDeleteItems.html',
controller: 'DeleteTagsController',
size: size,
resolve: {

View file

@ -4,7 +4,7 @@ angular.module('tag-list-directive', [])
.directive('tagList', function(){
return {
restrict: 'E',
templateUrl: 'scripts/directives/tag-list-directive.html',
templateUrl: 'tag/tag-list-directive.html',
controller: 'TagController',
};
});

View file

@ -3,7 +3,10 @@ frontend:
links:
- registry:path-to-your-registry
ports:
# Serves the page via grunt
- "9000:9000"
# For live reload with grunt
- "35729:35729"
volumes:
- ../:/source:rw
- start-develop.sh:/root/start-develop.sh:ro