diff --git a/Makefile b/Makefile deleted file mode 100644 index efc1779fb..000000000 --- a/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -REF = HEAD -VERSION = $(shell git describe --always $(REF)) - -all: less - -less: - less css/*.less > css/app.css - -.PHONY: all less diff --git a/README.md b/README.md index ca73374fb..05e7aa42f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ DockerUI is a web interface to interact with the Remote API. The goal is to pro ![Container](/container.png) ###Installation -Open js/app.(ts/js - js file if you don't want to compile typescript) and change the DOCKER_ENDPOINT constant to your docker ip and port. Then host the site like any other html/js application. +Open js/app.js and change the DOCKER_ENDPOINT constant to your docker ip and port. Then host the site like any other html/js application. .constant('DOCKER_ENDPOINT', 'http://192.168.1.9:4243\:4243'); @@ -14,7 +14,6 @@ Open js/app.(ts/js - js file if you don't want to compile typescript) and change ###Stack * Angular.js -* Less * Flatstrap ( Flat Twitter Bootstrap ) diff --git a/css/app.less b/css/app.less deleted file mode 100644 index 62c986b38..000000000 --- a/css/app.less +++ /dev/null @@ -1,85 +0,0 @@ - body { - padding-top: 20px; - padding-bottom: 60px; - } - - /* Custom container */ - .container { - margin: 0 auto; - max-width: 1000px; - } - .container > hr { - margin: 60px 0; - } - - /* Main marketing message and sign up button */ - .jumbotron { - margin: 80px 0; - text-align: center; - } - .jumbotron h1 { - font-size: 100px; - line-height: 1; - } - .jumbotron .lead { - font-size: 24px; - line-height: 1.25; - } - .jumbotron .btn { - font-size: 21px; - padding: 14px 24px; - } - - /* Supporting marketing content */ - .marketing { - margin: 60px 0; - } - .marketing p + h4 { - margin-top: 28px; - } - - - /* Customize the navbar links to be fill the entire space of the .navbar */ - .navbar .navbar-inner { - padding: 0; - } - .navbar .nav { - margin: 0; - } - .navbar .nav li { - display: table-cell; - width: 1%; - float: none; - } - .navbar .nav li a { - font-weight: bold; - text-align: center; - border-left: 1px solid rgba(255,255,255,.75); - border-right: 1px solid rgba(0,0,0,.1); - } - .navbar .nav li:first-child a { - border-left: 0; - border-radius: 3px 0 0 3px; - } - .navbar .nav li:last-child a { - border-right: 0; - border-radius: 0 3px 3px 0; - } - - .btn-group button { - margin: 3px; - } - - .detail { - width: 80%; - margin: 0 auto; - } - - .btn-remove { - margin: 0 auto; - max-width: 70%; - } - - .actions { - margin: 0 auto; - } diff --git a/js/controllers.js b/js/controllers.js index 6df90d70b..362d097b0 100644 --- a/js/controllers.js +++ b/js/controllers.js @@ -1,4 +1,5 @@ +// Controller for the top masthead function MastheadController($scope) { $scope.template = 'partials/masthead.html'; @@ -13,6 +14,7 @@ function MastheadController($scope) { $scope.iclass = ''; $scope.sclass = ''; + //This is shitty, I need help with this crap. switch(link) { case 'home': $scope.hclass = 'active'; @@ -32,40 +34,124 @@ function MastheadController($scope) { }; } -function SideBarController($scope, Container) { - $scope.template = 'partials/sidebar.html'; - - Container.query({}, function(d) { - $scope.containers = d; - }); -} - function HomeController() { } -function SettingsController() { - +function SettingsController($scope, Auth, System, Docker, Settings) { + $scope.auth = {}; + $scope.info = {}; + $scope.docker = {}; + + $('#response').hide(); + $scope.alertClass = 'block'; + + var showAndHide = function(hide) { + $('#response').show(); + if (hide) { + setTimeout(function() { $('#response').hide();}, 5000); + } + }; + + $scope.updateAuthInfo = function() { + if ($scope.auth.password != $scope.auth.cpassword) { + $scope.response = 'Your passwords do not match.'; + showAndHide(true); + return; + } + Auth.update( + {username: $scope.auth.username, email: $scope.auth.email, password: $scope.auth.password}, function(d) { + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Auth information updated.'; + showAndHide(true); + }, function(e) { + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); + }); + }; + + Auth.get({}, function(d) { + $scope.auth = d; + }); + + Docker.get({}, function(d) { + $scope.docker = d; + }); + + System.get({}, function(d) { + $scope.info = d; + }); } -function ContainerController($scope, $routeParams, Container) { +// Controls the page that displays a single container and actions on that container. +function ContainerController($scope, $routeParams, $location, Container) { + $('#response').hide(); + $scope.alertClass = 'block'; + + var showAndHide = function(hide) { + $('#response').show(); + if (hide) { + setTimeout(function() { $('#response').hide();}, 5000); + } + }; $scope.start = function(){ Container.start({id: $routeParams.id}, function(d) { - $scope.response = d; - }); + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Container started.'; + showAndHide(true); + }, function(e) { + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); + }); }; $scope.stop = function() { Container.stop({id: $routeParams.id}, function(d) { - $scope.response = d; + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Container stopped.'; + showAndHide(true); + }, function(e) { + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); + }); + }; + + $scope.kill = function() { + Container.kill({id: $routeParams.id}, function(d) { + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Container killed.'; + showAndHide(true); + }, function(e) { + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); }); }; $scope.remove = function() { if (confirm("Are you sure you want to remove the container?")) { Container.remove({id: $routeParams.id}, function(d) { - $scope.response = d; + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Container removed.'; + showAndHide(true); + }, function(e){ + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); }); } }; @@ -80,13 +166,149 @@ function ContainerController($scope, $routeParams, Container) { Container.get({id: $routeParams.id}, function(d) { $scope.container = d; + }, function(e) { + console.log(e); + $location.path('/containers/'); }); $scope.getChanges(); } -function ContainersController($scope, Container) { - Container.query({}, function(d) { - $scope.containers = d; - }); +// Controller for the list of containers +function ContainersController($scope, Container, Settings) { + $scope.displayAll = Settings.displayAll; + $scope.predicate = '-Created'; + + var update = function(data) { + Container.query(data, function(d) { + $scope.containers = d; + }); + }; + + $scope.toggleGetAll = function() { + Settings.displayAll = $scope.displayAll; + var u = update; + var data = {all: 0}; + + if ($scope.displayAll) { + data.all = 1; + } + u(data); + }; + + update({all: $scope.displayAll ? 1 : 0}); +} + +// Controller for the list of images +function ImagesController($scope, Image) { + $scope.predicate = '-Created'; + + Image.query({}, function(d) { + $scope.images = d; + }); +} + +// Controller for a single image and actions on that image +function ImageController($scope, $routeParams, $location, Image) { + $scope.history = []; + $scope.tag = {repo: '', force: false}; + + $('#response').hide(); + $scope.alertClass = 'block'; + + var showAndHide = function(hide) { + $('#response').show(); + if (hide) { + setTimeout(function() { $('#response').hide();}, 5000); + } + }; + + $scope.remove = function() { + if (confirm("Are you sure you want to delete this image?")) { + Image.remove({id: $routeParams.id}, function(d) { + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Image removed.'; + showAndHide(true); + }, function(e) { + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); + }); + } + }; + + $scope.getHistory = function() { + Image.history({id: $routeParams.id}, function(d) { + $scope.history = d; + }); + }; + + $scope.updateTag = function() { + var tag = $scope.tag; + Image.tag({id: $routeParams.id, repo: tag.repo, force: tag.force ? 1 : 0}, function(d) { + console.log(d); + $scope.alertClass = 'success'; + $scope.response = 'Tag added.'; + showAndHide(true); + }, function(e) { + console.log(e); + $scope.alertClass = 'error'; + $scope.response = e.data; + showAndHide(false); + }); + }; + + Image.get({id: $routeParams.id}, function(d) { + $scope.image = d; + }, function(e) { + console.log(e); + $location.path('/images/'); + }); + + $scope.getHistory(); +} + +function StartContainerController($scope, $routeParams, $location, Container) { + $scope.template = 'partials/startcontainer.html'; + $scope.memory = 0; + $scope.memorySwap = 0; + $scope.env = ''; + $scope.dns = ''; + $scope.volumesFrom = ''; + $scope.commands = ''; + + $scope.launchContainer = function() { + var cmds = null; + if ($scope.commands !== '') { + cmds = $scope.commands.split('\n'); + } + var id = $routeParams.id; + var ctor = Container; + var loc = $location; + var s = $scope; + + Container.create({ + Image: id, + Memory: $scope.memory, + MemorySwap: $scope.memorySwap, + Cmd: cmds, + VolumesFrom: $scope.volumesFrom + }, function(d) { + console.log(d); + if (d.Id) { + ctor.start({id: d.Id}, function(cd) { + console.log(cd); + loc.path('/containers/' + d.Id + '/'); + }, function(e) { + console.log(e); + s.resonse = e.data; + }); + } + }, function(e) { + console.log(e); + $scope.response = e.data; + }); + }; } diff --git a/js/filters.js b/js/filters.js index c068dd12a..bac632e30 100644 --- a/js/filters.js +++ b/js/filters.js @@ -21,7 +21,42 @@ angular.module('dockerui.filters', []) return function(text) { if (text === 'Ghost') { return 'important'; + } else if (text.indexOf('Exit') != -1 && text !== 'Exit 0') { + return 'warning'; } return 'success'; }; + }) + .filter('getstatetext', function() { + return function(state) { + if (state == undefined) return ''; + + if (state.Ghost && state.Running) { + return 'Ghost'; + } + if (state.Running) { + return 'Running'; + } + return 'Stopped'; + }; + }) + .filter('getstatelabel', function() { + return function(state) { + if (state == undefined) return ''; + + if (state.Ghost && state.Running) { + return 'label-important'; + } + if (state.Running) { + return 'label-success'; + } + return ''; + }; + }) + .filter('getdate', function() { + return function(data) { + //Multiply by 1000 for the unix format + var date = new Date(data * 1000); + return date.toDateString(); + }; }); diff --git a/js/services.js b/js/services.js index daab27383..21a15056a 100644 --- a/js/services.js +++ b/js/services.js @@ -7,27 +7,54 @@ angular.module('dockerui.services', ['ngResource']) return $resource(DOCKER_ENDPOINT + '/containers/:id/:action', {}, { query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true}, get :{method: 'GET', params: { action:'json'}}, - start: {method: 'POST', params: { action: 'start'}}, - stop: {method: 'POST', params: {t: 5, action: 'stop'}}, - restart: {method: 'POST', params: {t: 5, action: 'restart' }}, - kill :{method: 'POST', params: {action:'kill'}}, + start: {method: 'POST', params: {id: '@id', action: 'start'}}, + stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}}, + restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart' }}, + kill :{method: 'POST', params: {id: '@id', action:'kill'}}, changes :{method: 'GET', params: {action:'changes'}, isArray: true}, create :{method: 'POST', params: {action:'create'}}, - remove :{method: 'DELETE', params: {v:0}} + remove :{method: 'DELETE', params: {id: '@id', v:0}} }); }) .factory('Image', function($resource, DOCKER_ENDPOINT) { // Resource for docker images // http://docs.docker.io/en/latest/api/docker_remote_api.html#images - return $resource(DOCKER_ENDPOINT + '/images/:name/:action', {}, { + return $resource(DOCKER_ENDPOINT + '/images/:id/:action', {}, { query: {method: 'GET', params:{ all: 0, action: 'json'}, isArray: true}, get :{method: 'GET', params: { action:'json'}}, search :{method: 'GET', params: { action:'search'}}, - history :{method: 'GET', params: { action:'history'}}, + history :{method: 'GET', params: { action:'history'}, isArray: true}, create :{method: 'POST', params: {action:'create'}}, - insert :{method: 'POST', params: {action:'insert'}}, - push :{method: 'POST', params: {action:'push'}}, - tag :{method: 'POST', params: {action:'tag'}}, - delete :{method: 'DELETE'} + insert :{method: 'POST', params: {id: '@id', action:'insert'}}, + push :{method: 'POST', params: {id: '@id', action:'push'}}, + tag :{method: 'POST', params: {id: '@id', action:'tag'}}, + delete :{id: '@id', method: 'DELETE'} }); + }) + .factory('Docker', function($resource, DOCKER_ENDPOINT) { + // Information for docker + // http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information + return $resource(DOCKER_ENDPOINT + '/version', {}, { + get: {method: 'GET'} + }); + }) + .factory('Auth', function($resource, DOCKER_ENDPOINT) { + // Auto Information for docker + // http://docs.docker.io/en/latest/api/docker_remote_api.html#set-auth-configuration + return $resource(DOCKER_ENDPOINT + '/auth', {}, { + get: {method: 'GET'}, + update: {method: 'POST'} + }); + }) + .factory('System', function($resource, DOCKER_ENDPOINT) { + // System for docker + // http://docs.docker.io/en/latest/api/docker_remote_api.html#display-system-wide-information + return $resource(DOCKER_ENDPOINT + '/info', {}, { + get: {method: 'GET'} + }); + }) + .factory('Settings', function() { + return { + displayAll: false + }; }); diff --git a/partials/container.html b/partials/container.html index b72d3789e..df0d0e84d 100644 --- a/partials/container.html +++ b/partials/container.html @@ -1,10 +1,14 @@ -
+
+ {{ response }} +
+

Container: {{ container.Id }}

+
@@ -27,11 +31,11 @@ - + - - + +
Image:{{ container.Image }}{{ container.Image }}
Running:{{ container.State.Running }}State:{{ container.State|getstatetext }}
diff --git a/partials/containers.html b/partials/containers.html index bced94dd0..aaf8aaf4c 100644 --- a/partials/containers.html +++ b/partials/containers.html @@ -1,6 +1,9 @@

Containers:

+
+ Display All +
@@ -12,11 +15,11 @@ - + - - - + + + diff --git a/partials/image.html b/partials/image.html new file mode 100644 index 000000000..05f7b021a --- /dev/null +++ b/partials/image.html @@ -0,0 +1,89 @@ +
+ +
+ {{ response }} +
+ +

Image: {{ image.id }}

+ +
{{ container.Id|truncate:10}}{{ container.Image }}{{ container.Command }}{{ container.Created }}{{ container.Image }}{{ container.Command|truncate:40 }}{{ container.Created|getdate }} {{ container.Status }}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Created:{{ image.created }}
Parent:{{ image.parent }}
Container:{{ image.container }}
Hostname:{{ image.container_config.Hostname }}
User:{{ image.container_config.User }}
Cmd:{{ image.container_config.Cmd }}
Volumes:{{ image.container_config.Volumes }}
Volumes from:{{ image.container_config.VolumesFrom }}
+ +
+
+ History: +
+
+ +
+
+ +
+ +
+ +
+ +
+
+
+ Tag to Repo + + + + +
+
+
+ +
+ +
+ +
+ +
+ +
+
diff --git a/partials/images.html b/partials/images.html new file mode 100644 index 000000000..0840f3938 --- /dev/null +++ b/partials/images.html @@ -0,0 +1,21 @@ + +

Images:

+ + + + + + + + + + + + + + + + + + +
IdTagRepositoryCreated
{{ image.Id|truncate:10}}{{ image.Tag }}{{ image.Repository }}{{ image.Created|getdate }}
diff --git a/partials/settings.html b/partials/settings.html new file mode 100644 index 000000000..24bb92a37 --- /dev/null +++ b/partials/settings.html @@ -0,0 +1,67 @@ +
+
+ {{ response }} +
+ +

Docker Information

+
+

+ Version{{ docker.Version }}
+ GitCommit{{ docker.GitCommit }}
+ GoVersion{{ docker.GoVersion }}
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Containers:{{ info.Containers }}
Images:{{ info.Images }}
Debug:{{ info.Debug }}
NFd:{{ info.NFd }}
NGoroutines:{{ info.NGoroutines }}
MemoryLimit:{{ info.MemoryLimit }}
SwapLimit:{{ info.SwapLimit }}
NFd:{{ info.NFd }}
+ +
+
+ Auth Information + + + + + + + + +
+ +
+
+
diff --git a/partials/startcontainer.html b/partials/startcontainer.html new file mode 100644 index 000000000..98a81f608 --- /dev/null +++ b/partials/startcontainer.html @@ -0,0 +1,27 @@ + +
+ {{ response }} +
+ + +
+
+ Start container from Image + + + + Place each command on a new line + + + + + + + + + + +
+ +
+