diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js
index 0ebce3b86..f43216186 100644
--- a/dashboard/src/js/client.js
+++ b/dashboard/src/js/client.js
@@ -3336,15 +3336,16 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout
});
};
- Client.prototype.updateVolume = function (volumeId, mountType, mountOptions, callback) {
+ Client.prototype.updateVolume = function (volumeId, mountOptions, callback) {
var data = {
- mountType: mountType,
mountOptions: mountOptions
};
+ console.log('---update', data)
+
post('/api/v1/volumes/' + volumeId, data, null, function (error, data, status) {
if (error) return callback(error);
- if (status !== 200) return callback(new ClientError(status, data));
+ if (status !== 204) return callback(new ClientError(status, data));
callback();
});
diff --git a/dashboard/src/views/volumes.html b/dashboard/src/views/volumes.html
index 50c6c4745..cbf63822f 100644
--- a/dashboard/src/views/volumes.html
+++ b/dashboard/src/views/volumes.html
@@ -97,6 +97,74 @@
+
+
+
@@ -138,7 +206,7 @@
{{ 'volumes.name' | tr }} |
{{ 'volumes.type' | tr }} |
{{ 'volumes.hostPath' | tr }} |
-
{{ 'main.actions' | tr }} |
+
{{ 'main.actions' | tr }} |
@@ -157,6 +225,7 @@
{{ volume.hostPath }} |
+
|
diff --git a/dashboard/src/views/volumes.js b/dashboard/src/views/volumes.js
index 89b157e7c..e72ed5e2d 100644
--- a/dashboard/src/views/volumes.js
+++ b/dashboard/src/views/volumes.js
@@ -62,6 +62,10 @@ angular.module('Application').controller('VolumesController', ['$scope', '$locat
return provider === 'sshfs' || provider === 'cifs' || provider === 'nfs' || provider === 'ext4' || provider === 'xfs';
};
+ $scope.isNetworkProvider = function (provider) {
+ return provider === 'sshfs' || provider === 'cifs' || provider === 'nfs';
+ };
+
$scope.remount = function (volume) {
volume.remounting = true;
@@ -194,6 +198,116 @@ angular.module('Application').controller('VolumesController', ['$scope', '$locat
}
};
+ $scope.volumeEdit = {
+ error: null,
+ busy: false,
+
+ // cannot be changed
+ volume: '',
+ name: '',
+ mountType: 'mountpoint',
+
+ // can be changed
+ host: '',
+ remoteDir: '',
+ username: '',
+ password: '',
+ user: '',
+ seal: false,
+ port: 22,
+ privateKey: '',
+
+ reset: function () {
+ $scope.volumeEdit.error = null;
+ $scope.volumeEdit.busy = false;
+ $scope.volumeEdit.volume = null;
+ $scope.volumeEdit.name = '';
+ $scope.volumeEdit.mountType = '';
+ $scope.volumeEdit.host = '';
+ $scope.volumeEdit.seal = '';
+ $scope.volumeEdit.port = '';
+ $scope.volumeEdit.remoteDir = '';
+ $scope.volumeEdit.username = '';
+ $scope.volumeEdit.password = '';
+ $scope.volumeEdit.user = '';
+ $scope.volumeEdit.privateKey = '';
+
+ $scope.volumeEditForm.$setPristine();
+ $scope.volumeEditForm.$setUntouched();
+ },
+
+ show: function (volume) {
+ $scope.volumeEdit.reset();
+
+ Client.getVolume(volume.id, function (error, result) {
+ if (error) console.error('Failed to get volume:', error);
+
+ $scope.volumeEdit.volume = volume;
+ $scope.volumeEdit.mountType = result.mountType;
+ $scope.volumeEdit.host = result.mountOptions.host;
+ $scope.volumeEdit.seal = result.mountOptions.seal;
+ $scope.volumeEdit.port = result.mountOptions.port;
+ $scope.volumeEdit.remoteDir = result.mountOptions.remoteDir;
+ $scope.volumeEdit.username = result.mountOptions.username;
+ $scope.volumeEdit.password = result.mountOptions.password;
+ $scope.volumeEdit.user = result.mountOptions.user;
+ $scope.volumeEdit.privateKey = result.mountOptions.privateKey;
+
+ $('#volumeEditModal').modal('show');
+ });
+ },
+
+ submit: function () {
+ $scope.volumeEdit.busy = true;
+ $scope.volumeEdit.error = null;
+
+ var mountOptions = null;
+
+ if ($scope.volumeEdit.mountType === 'cifs') {
+ mountOptions = {
+ host: $scope.volumeEdit.host,
+ remoteDir: $scope.volumeEdit.remoteDir,
+ username: $scope.volumeEdit.username,
+ password: $scope.volumeEdit.password,
+ seal: $scope.volumeEdit.seal
+ };
+ } else if ($scope.volumeEdit.mountType === 'nfs') {
+ mountOptions = {
+ host: $scope.volumeEdit.host,
+ remoteDir: $scope.volumeEdit.remoteDir,
+ };
+ } else if ($scope.volumeEdit.mountType === 'sshfs') {
+ mountOptions = {
+ host: $scope.volumeEdit.host,
+ port: $scope.volumeEdit.port,
+ remoteDir: $scope.volumeEdit.remoteDir,
+ user: $scope.volumeEdit.user,
+ privateKey: $scope.volumeEdit.privateKey,
+ };
+ } else {
+ console.error('Should not come here. Only network volumes can be edited');
+
+ $('#volumeEditModal').modal('hide');
+ $scope.volumeEdit.reset();
+
+ return;
+ }
+
+ Client.updateVolume($scope.volumeEdit.volume.id, mountOptions, function (error) {
+ $scope.volumeEdit.busy = false;
+ if (error) {
+ $scope.volumeEdit.error = error.message;
+ return;
+ }
+
+ $('#volumeEditModal').modal('hide');
+ $scope.volumeEdit.reset();
+
+ refreshVolumes();
+ });
+ }
+ };
+
$scope.volumeRemove = {
busy: false,
error: null,
diff --git a/src/server.js b/src/server.js
index 491f06b93..ad7c66765 100644
--- a/src/server.js
+++ b/src/server.js
@@ -385,7 +385,7 @@ async function initializeExpressSync() {
router.post('/api/v1/volumes', json, token, authorizeAdmin, routes.volumes.add);
router.get ('/api/v1/volumes', token, authorizeAdmin, routes.volumes.list);
router.get ('/api/v1/volumes/:id', token, authorizeAdmin, routes.volumes.load, routes.volumes.get);
- router.post('/api/v1/volumes/:id', token, authorizeAdmin, routes.volumes.load, routes.volumes.update);
+ router.post('/api/v1/volumes/:id', json, token, authorizeAdmin, routes.volumes.load, routes.volumes.update);
router.del ('/api/v1/volumes/:id', token, authorizeAdmin, routes.volumes.load, routes.volumes.del);
router.get ('/api/v1/volumes/:id/status', token, authorizeAdmin, routes.volumes.load, routes.volumes.getStatus);
router.post('/api/v1/volumes/:id/remount', token, authorizeAdmin, routes.volumes.load, routes.volumes.remount);
diff --git a/src/volumes.js b/src/volumes.js
index 362e73e59..40333bc2e 100644
--- a/src/volumes.js
+++ b/src/volumes.js
@@ -135,28 +135,33 @@ function removePrivateFields(volume) {
return newVolume;
}
+// only network mounts can be updated here through mountOptions to update logon information
async function update(id, mountOptions, auditSource) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof mountOptions, 'object');
assert.strictEqual(typeof auditSource, 'object');
const volume = await get(id);
+ const mountType = volume.mountType;
+ const name = volume.name;
- const error = mounts.validateMountOptions(volume.mountType, mountOptions);
+ if (mountType !== 'cifs' && mountType !== 'sshfs' && mountType !== 'nfs') return;
+
+ const error = mounts.validateMountOptions(mountType, mountOptions);
if (error) throw error;
- let hostPath;
- if (volume.mountType === 'mountpoint' || volume.mountType === 'filesystem') {
- const error = validateHostPath(mountOptions.hostPath, volume.mountType);
- if (error) throw error;
- hostPath = mountOptions.hostPath;
- } else {
- hostPath = path.join(paths.VOLUMES_MOUNT_DIR, id);
- const mount = { name: volume.name, hostPath, mountType: volume.mountType, mountOptions };
- await mounts.tryAddMount(mount, { timeout: 10 }); // 10 seconds
+ // put old secret back in place if no new secret is provided
+ if (mountType === 'sshfs') {
+ if (mountOptions.privateKey === constants.SECRET_PLACEHOLDER) mountOptions.privateKey = volume.mountOptions.privateKey;
+ } else if (mountType === 'cifs') {
+ if (mountOptions.password === constants.SECRET_PLACEHOLDER) mountOptions.password = volume.mountOptions.password;
}
- await safe(database.query('UPDATE volumes SET hostPath=?,mountOptionsJson=? WHERE id=?)', [ hostPath, JSON.stringify(mountOptions), id ]));
+ const hostPath = path.join(paths.VOLUMES_MOUNT_DIR, id);
+ const mount = { name, hostPath, mountType: mountType, mountOptions };
+ await mounts.tryAddMount(mount, { timeout: 10 }); // 10 seconds
+
+ await database.query('UPDATE volumes SET hostPath=?,mountOptionsJson=? WHERE id=?', [ hostPath, JSON.stringify(mountOptions), id ]);
await eventlog.add(eventlog.ACTION_VOLUME_UPDATE, auditSource, { id, name, hostPath });
// in theory, we only need to do this mountpoint volumes. but for some reason a restart is required to detect new "mounts"