diff --git a/src/index.html b/src/index.html index 3a8bc778d..016b9580d 100644 --- a/src/index.html +++ b/src/index.html @@ -153,6 +153,7 @@
  • Network
  • Services
  • Settings
  • +
  • Volumes
  • Support
  • System
  • diff --git a/src/js/client.js b/src/js/client.js index 9cee23a57..2011f8721 100644 --- a/src/js/client.js +++ b/src/js/client.js @@ -2451,6 +2451,57 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; + // Volumes + Client.prototype.getVolumes = function (callback) { + get('/api/v1/volumes', null, function (error, data, status) { + if (error) return callback(error); + if (status !== 200) return callback(new ClientError(status, data)); + + callback(null, data.volumes); + }); + }; + + Client.prototype.getVolume = function (volume, callback) { + get('/api/v1/volumes/' + volume, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 200) return callback(new ClientError(status, data)); + + callback(null, data); + }); + }; + + Client.prototype.addVolume = function (name, hostPath, callback) { + var data = { + name: name, + hostPath: hostPath + }; + var that = this; + + post('/api/v1/volumes', data, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 201) return callback(new ClientError(status, data)); + + callback(); + }); + }; + + Client.prototype.removeVolume = function (volume, callback) { + var config = { + data: { + }, + headers: { + 'Content-Type': 'application/json' + } + }; + + del('/api/v1/volumes/' + volume, config, function (error, data, status) { + if (error) return callback(error); + if (status !== 204) return callback(new ClientError(status, data)); + + callback(null); + }); + }; + Client.prototype.getAppstoreUserToken = function (callback) { post('/api/v1/appstore/user_token', {}, null, function (error, data, status) { if (error) return callback(error); diff --git a/src/js/index.js b/src/js/index.js index e49b45f7b..bc07e8897 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -102,6 +102,9 @@ app.config(['$routeProvider', function ($routeProvider) { }).when('/services', { controller: 'ServicesController', templateUrl: 'views/services.html?<%= revision %>' + }).when('/volumes', { + controller: 'VolumesController', + templateUrl: 'views/volumes.html?<%= revision %>' }).otherwise({ redirectTo: '/'}); }]); diff --git a/src/views/activity.js b/src/views/activity.js index 7155615ef..da8408d0f 100644 --- a/src/views/activity.js +++ b/src/views/activity.js @@ -63,6 +63,9 @@ angular.module('Application').controller('ActivityController', ['$scope', '$loca { name: 'user.remove', value: 'user.remove' }, { name: 'user.transfer', value: 'user.transfer' }, { name: 'user.update', value: 'user.update' }, + { name: 'volume.add', value: 'volume.add' }, + { name: 'volume.update', value: 'volume.update' }, + { name: 'volume.remove', value: 'volume.update' }, { name: 'System Crash', value: 'system.crash' } ]; @@ -134,6 +137,10 @@ angular.module('Application').controller('ActivityController', ['$scope', '$loca var ACTION_SUPPORT_TICKET = 'support.ticket'; var ACTION_SUPPORT_SSH = 'support.ssh'; + var ACTION_VOLUME_ADD = 'volume.add'; + var ACTION_VOLUME_UPDATE = 'volume.update'; + var ACTION_VOLUME_REMOVE = 'volume.remove'; + var ACTION_DYNDNS_UPDATE = 'dyndns.update'; var ACTION_SYSTEM_CRASH = 'system.crash'; @@ -390,6 +397,15 @@ angular.module('Application').controller('ActivityController', ['$scope', '$loca case ACTION_SYSTEM_CRASH: return 'A system process crashed'; + case ACTION_VOLUME_ADD: + return 'Volume "' + data.volume.name + '" was added'; + + case ACTION_VOLUME_UPDATE: + return 'Volme "' + data.volume.name + '" was updated'; + + case ACTION_VOLUME_REMOVE: + return 'Volume "' + data.volume.name + '" was removed'; + default: return eventLog.action; } } diff --git a/src/views/volumes.html b/src/views/volumes.html new file mode 100644 index 000000000..4c9913b94 --- /dev/null +++ b/src/views/volumes.html @@ -0,0 +1,98 @@ + + + + + + +
    +
    +

    Volumes

    +
    + +
    +
    +
    +

    +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    NameHost PathActions
    + {{ volume.name }} + + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/src/views/volumes.js b/src/views/volumes.js new file mode 100644 index 000000000..03b991a7a --- /dev/null +++ b/src/views/volumes.js @@ -0,0 +1,121 @@ +'use strict'; + +/* global angular */ +/* global $ */ + +angular.module('Application').controller('VolumesController', ['$scope', '$location', '$timeout', 'Client', function ($scope, $location, $timeout, Client) { + Client.onReady(function () { if (!Client.getUserInfo().isAtLeastUserManager) $location.path('/'); }); + + $scope.config = Client.getConfig(); + $scope.volumes = []; + $scope.ready = false; + + function refreshVolumes(callback) { + Client.getVolumes(function (error, results) { + if (error) return console.error(error); + + $scope.volumes = results; + if (callback) callback(); + }); + } + + $scope.volumeAdd = { + error: null, + busy: false, + + name: '', + hostPath: '', + + + reset: function () { + $scope.volumeAdd.busy = false; + $scope.volumeAdd.error = null; + $scope.volumeAdd.name = ''; + $scope.volumeAdd.hostPath = ''; + + $scope.volumeAddForm.$setPristine(); + $scope.volumeAddForm.$setUntouched(); + }, + + show: function () { + $scope.volumeAdd.reset(); + + $('#volumeAddModal').modal('show'); + }, + + submit: function () { + $scope.volumeAdd.busy = true; + $scope.volumeAdd.error = null; + + Client.addVolume($scope.volumeAdd.name, $scope.volumeAdd.hostPath, function (error) { + $scope.volumeAdd.busy = false; + if (error) { + $scope.volumeAdd.error = error.message; + return; + } + + $('#volumeAddModal').modal('hide'); + $scope.volumeAdd.reset(); + + refreshVolumes(); + }); + } + }; + + $scope.volumeRemove = { + busy: false, + error: null, + volume: null, + + reset: function () { + $scope.volumeRemove.busy = false; + $scope.volumeRemove.error = null; + $scope.volumeRemove.volume = null; + }, + + show: function (volume) { + $scope.volumeRemove.reset(); + + $scope.volumeRemove.volume = volume; + + $('#volumeRemoveModal').modal('show'); + }, + + submit: function () { + $scope.volumeRemove.busy = true; + $scope.volumeRemove.error = null; + + Client.removeVolume($scope.volumeRemove.volume.id, function (error) { + if (error && (error.statusCode === 403 || error.statusCode === 409)) { + $scope.volumeRemove.error = error.message; + } else if (error) { + Client.error(error); + } else { + $('#volumeRemoveModal').modal('hide'); + $scope.volumeRemove.reset(); + + refreshVolumes(); + } + + $scope.volumeRemove.busy = false; + }); + }, + }; + + Client.onReady(function () { + refreshVolumes(function (error) { + if (error) return console.error(error); + + $scope.ready = true; + }); + }); + + // setup all the dialog focus handling + ['volumeAddModal', 'volumeRemoveModal'].forEach(function (id) { + $('#' + id).on('shown.bs.modal', function () { + $(this).find('[autofocus]:first').focus(); + }); + }); + + $('.modal-backdrop').remove(); +}]);