Port volumes view to composition style
This commit is contained in:
+171
-179
@@ -1,199 +1,191 @@
|
|||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
const i18n = useI18n();
|
||||||
|
const t = i18n.t;
|
||||||
|
|
||||||
|
import { computed, ref, useTemplateRef, onMounted } from 'vue';
|
||||||
import { Button, ButtonGroup, Checkbox, Dialog, Dropdown, FormGroup, InputDialog, NumberInput, PasswordInput, TableView, TextInput } from 'pankow';
|
import { Button, ButtonGroup, Checkbox, Dialog, Dropdown, FormGroup, InputDialog, NumberInput, PasswordInput, TableView, TextInput } from 'pankow';
|
||||||
import Section from '../components/Section.vue';
|
import Section from '../components/Section.vue';
|
||||||
import VolumesModel from '../models/VolumesModel.js';
|
import VolumesModel from '../models/VolumesModel.js';
|
||||||
|
|
||||||
const volumesModel = VolumesModel.create();
|
const volumesModel = VolumesModel.create();
|
||||||
|
|
||||||
export default {
|
const mountTypeOptions = [
|
||||||
name: 'VolumesView',
|
{ name: 'CIFS', value: 'cifs' },
|
||||||
components: {
|
{ name: 'EXT4', value: 'ext4' },
|
||||||
Button,
|
{ name: 'Filesystem', value: 'filesystem' },
|
||||||
ButtonGroup,
|
{ name: 'Filesystem (Mountpoint)', value: 'mountpoint' },
|
||||||
Section,
|
{ name: 'NFS', value: 'nfs' },
|
||||||
Checkbox,
|
{ name: 'SSHFS', value: 'sshfs' },
|
||||||
Dialog,
|
{ name: 'XFS', value: 'xfs' },
|
||||||
Dropdown,
|
];
|
||||||
FormGroup,
|
|
||||||
InputDialog,
|
|
||||||
NumberInput,
|
|
||||||
PasswordInput,
|
|
||||||
TableView,
|
|
||||||
TextInput,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
volumeDialogValid() {
|
|
||||||
const data = this.volumeDialogData;
|
|
||||||
|
|
||||||
if (data.mode === 'new') {
|
const columns = {
|
||||||
if (!data.name) return false;
|
status: {},
|
||||||
if (!data.mountType) return false;
|
name: { label: 'Name', sort: true },
|
||||||
}
|
mountType: { label: 'Type', sort: true },
|
||||||
|
target: { label: 'Target', sort: true },
|
||||||
|
actions: {}
|
||||||
|
};
|
||||||
|
|
||||||
switch (data.mountType) {
|
const busy = ref(true);
|
||||||
case 'filesystem':
|
const volumes = ref([]);
|
||||||
case 'mountpoint':
|
const volumeDialogData = ref({
|
||||||
if (!data.hostPath) return false;
|
error: null,
|
||||||
if (!data.hostPath) return false;
|
busy: false,
|
||||||
break;
|
mode: '', // edit or new
|
||||||
case 'ext4':
|
name: '',
|
||||||
case 'xfs':
|
// dynamic extra props from openVolumeDialog
|
||||||
if (!data.diskPath) return false;
|
});
|
||||||
break;
|
|
||||||
case 'nfs':
|
|
||||||
if (!data.host) return false;
|
|
||||||
if (!data.remoteDirectory) return false;
|
|
||||||
break;
|
|
||||||
case 'sshfs':
|
|
||||||
if (!data.host) return false;
|
|
||||||
if (!data.remoteDirectory) return false;
|
|
||||||
if (!data.post) return false;
|
|
||||||
if (!data.user) return false;
|
|
||||||
if (!data.privateKey) return false;
|
|
||||||
break;
|
|
||||||
case 'cifs':
|
|
||||||
if (!data.host) return false;
|
|
||||||
if (!data.remoteDirectory) return false;
|
|
||||||
if (!data.username) return false;
|
|
||||||
if (!data.password) return false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
const volumeDialogValid = computed(() => {
|
||||||
}
|
const data = volumeDialogData.value;
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
busy: true,
|
|
||||||
mountTypeOptions: [
|
|
||||||
{ name: 'CIFS', value: 'cifs' },
|
|
||||||
{ name: 'EXT4', value: 'ext4' },
|
|
||||||
{ name: 'Filesystem', value: 'filesystem' },
|
|
||||||
{ name: 'Filesystem (Mountpoint)', value: 'mountpoint' },
|
|
||||||
{ name: 'NFS', value: 'nfs' },
|
|
||||||
{ name: 'SSHFS', value: 'sshfs' },
|
|
||||||
{ name: 'XFS', value: 'xfs' },
|
|
||||||
],
|
|
||||||
columns: {
|
|
||||||
status: {},
|
|
||||||
name: { label: 'Name', sort: true },
|
|
||||||
mountType: { label: 'Type', sort: true },
|
|
||||||
target: { label: 'Target', sort: true },
|
|
||||||
actions: {}
|
|
||||||
},
|
|
||||||
volumes: [],
|
|
||||||
volumeDialogData: {
|
|
||||||
error: null,
|
|
||||||
busy: false,
|
|
||||||
mode: '', // edit or new
|
|
||||||
name: '',
|
|
||||||
// dynamic extra props from openVolumeDialog
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async refresh() {
|
|
||||||
this.busy = true;
|
|
||||||
this.volumes = await volumesModel.list();
|
|
||||||
this.busy = false;
|
|
||||||
|
|
||||||
for (const v of this.volumes) {
|
if (data.mode === 'new') {
|
||||||
const status = await volumesModel.getStatus(v.id);
|
if (!data.name) return false;
|
||||||
v.state = status.state;
|
if (!data.mountType) return false;
|
||||||
v.message = status.message;
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
async openVolumeDialog(volume) {
|
|
||||||
this.volumeDialogData.error = null;
|
|
||||||
this.volumeDialogData.mode = volume ? 'edit' : 'new';
|
|
||||||
this.volumeDialogData.id = volume ? volume.id : '';
|
|
||||||
this.volumeDialogData.name = volume ? volume.name : '';
|
|
||||||
this.volumeDialogData.mountType = volume ? volume.mountType : '';
|
|
||||||
this.volumeDialogData.host = volume ? volume.mountOptions.host : '';
|
|
||||||
this.volumeDialogData.seal = volume ? volume.mountOptions.seal : false;
|
|
||||||
this.volumeDialogData.port = volume ? volume.mountOptions.port : 0;
|
|
||||||
this.volumeDialogData.remoteDir = volume ? volume.mountOptions.remoteDir : '';
|
|
||||||
this.volumeDialogData.username = volume ? volume.mountOptions.username : '';
|
|
||||||
this.volumeDialogData.password = volume ? volume.mountOptions.password : '';
|
|
||||||
this.volumeDialogData.diskPath = volume ? volume.mountOptions.diskPath : '';
|
|
||||||
this.volumeDialogData.hostPath = volume ? volume.mountOptions.hostPath : '';
|
|
||||||
|
|
||||||
let blockDevices = await volumesModel.getBlockDevices();
|
switch (data.mountType) {
|
||||||
|
case 'filesystem':
|
||||||
|
case 'mountpoint':
|
||||||
|
if (!data.hostPath) return false;
|
||||||
|
if (!data.hostPath) return false;
|
||||||
|
break;
|
||||||
|
case 'ext4':
|
||||||
|
case 'xfs':
|
||||||
|
if (!data.diskPath) return false;
|
||||||
|
break;
|
||||||
|
case 'nfs':
|
||||||
|
if (!data.host) return false;
|
||||||
|
if (!data.remoteDir) return false;
|
||||||
|
break;
|
||||||
|
case 'sshfs':
|
||||||
|
if (!data.host) return false;
|
||||||
|
if (!data.remoteDir) return false;
|
||||||
|
if (!data.post) return false;
|
||||||
|
if (!data.user) return false;
|
||||||
|
if (!data.privateKey) return false;
|
||||||
|
break;
|
||||||
|
case 'cifs':
|
||||||
|
if (!data.host) return false;
|
||||||
|
if (!data.remoteDir) return false;
|
||||||
|
if (!data.username) return false;
|
||||||
|
if (!data.password) return false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// only offer unmounted disks
|
return true;
|
||||||
blockDevices = blockDevices.filter(d => !d.mountpoint);
|
});
|
||||||
|
|
||||||
// amend label for UI
|
async function refresh() {
|
||||||
blockDevices.forEach(d => d.label = d.path);
|
busy.value = true;
|
||||||
|
volumes.value = await volumesModel.list();
|
||||||
|
busy.value = false;
|
||||||
|
|
||||||
this.volumeDialogData.ext4BlockDevices = blockDevices.filter(d => d.type === 'ext4');
|
for (const v of volumes.value) {
|
||||||
this.volumeDialogData.xfsBlockDevices = blockDevices.filter(d => d.type === 'xfs');
|
const status = await volumesModel.getStatus(v.id);
|
||||||
|
v.state = status.state;
|
||||||
this.$refs.volumeDialog.open();
|
v.message = status.message;
|
||||||
},
|
|
||||||
async submitVolumeDialog() {
|
|
||||||
this.volumeDialogData.busy = true;
|
|
||||||
|
|
||||||
const mountOptions = {
|
|
||||||
host: this.volumeDialogData.host,
|
|
||||||
seal: this.volumeDialogData.seal,
|
|
||||||
port: this.volumeDialogData.port,
|
|
||||||
remoteDir: this.volumeDialogData.remoteDir,
|
|
||||||
username: this.volumeDialogData.username,
|
|
||||||
password: this.volumeDialogData.password,
|
|
||||||
diskPath: this.volumeDialogData.diskPath,
|
|
||||||
hostPath: this.volumeDialogData.hostPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.volumeDialogData.mode === 'new') {
|
|
||||||
await volumesModel.add(this.volumeDialogData.name, this.volumeDialogData.mountType, mountOptions);
|
|
||||||
} else {
|
|
||||||
await volumesModel.update(this.volumeDialogData.id, mountOptions);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.volumeDialogData.error = error.body ? error.body.message : 'Internal error';
|
|
||||||
this.volumeDialogData.busy = false;
|
|
||||||
console.error(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.refresh();
|
|
||||||
|
|
||||||
this.$refs.volumeDialog.close();
|
|
||||||
this.volumeDialogData.busy = false;
|
|
||||||
},
|
|
||||||
async onRemove(volume) {
|
|
||||||
const yes = await this.$refs.inputDialog.confirm({
|
|
||||||
message: `Really remove volume ${volume.name}?`,
|
|
||||||
confirmStyle: 'danger',
|
|
||||||
confirmLabel: this.$t('volumes.removeVolumeDialog.removeAction'),
|
|
||||||
rejectLabel: this.$t('main.dialog.cancel')
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!yes) return;
|
|
||||||
|
|
||||||
await volumesModel.remove(volume.id);
|
|
||||||
await this.refresh();
|
|
||||||
},
|
|
||||||
async remount(volume) {
|
|
||||||
await volumesModel.remount(volume.id);
|
|
||||||
|
|
||||||
const status = await volumesModel.getStatus(volume.id);
|
|
||||||
volume.state = status.state;
|
|
||||||
volume.message = status.message;
|
|
||||||
|
|
||||||
window.pankow.notify('Remount attempt finished');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
await this.refresh();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const volumeDialog = useTemplateRef('volumeDialog');
|
||||||
|
const inputDialog = useTemplateRef('inputDialog');
|
||||||
|
|
||||||
|
async function openVolumeDialog(volume) {
|
||||||
|
volumeDialogData.value.error = null;
|
||||||
|
volumeDialogData.value.mode = volume ? 'edit' : 'new';
|
||||||
|
volumeDialogData.value.id = volume ? volume.id : '';
|
||||||
|
volumeDialogData.value.name = volume ? volume.name : '';
|
||||||
|
volumeDialogData.value.mountType = volume ? volume.mountType : '';
|
||||||
|
volumeDialogData.value.host = volume ? volume.mountOptions.host : '';
|
||||||
|
volumeDialogData.value.seal = volume ? volume.mountOptions.seal : false;
|
||||||
|
volumeDialogData.value.port = volume ? volume.mountOptions.port : 0;
|
||||||
|
volumeDialogData.value.remoteDir = volume ? volume.mountOptions.remoteDir : '';
|
||||||
|
volumeDialogData.value.username = volume ? volume.mountOptions.username : '';
|
||||||
|
volumeDialogData.value.password = volume ? volume.mountOptions.password : '';
|
||||||
|
volumeDialogData.value.diskPath = volume ? volume.mountOptions.diskPath : '';
|
||||||
|
volumeDialogData.value.hostPath = volume ? volume.mountOptions.hostPath : '';
|
||||||
|
|
||||||
|
let blockDevices = await volumesModel.getBlockDevices();
|
||||||
|
|
||||||
|
// only offer unmounted disks
|
||||||
|
blockDevices = blockDevices.filter(d => !d.mountpoint);
|
||||||
|
|
||||||
|
// amend label for UI
|
||||||
|
blockDevices.forEach(d => d.label = d.path);
|
||||||
|
|
||||||
|
volumeDialogData.value.ext4BlockDevices = blockDevices.filter(d => d.type === 'ext4');
|
||||||
|
volumeDialogData.value.xfsBlockDevices = blockDevices.filter(d => d.type === 'xfs');
|
||||||
|
|
||||||
|
volumeDialog.value.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitVolumeDialog() {
|
||||||
|
volumeDialogData.value.busy = true;
|
||||||
|
|
||||||
|
const mountOptions = {
|
||||||
|
host: volumeDialogData.value.host,
|
||||||
|
seal: volumeDialogData.value.seal,
|
||||||
|
port: volumeDialogData.value.port,
|
||||||
|
remoteDir: volumeDialogData.value.remoteDir,
|
||||||
|
username: volumeDialogData.value.username,
|
||||||
|
password: volumeDialogData.value.password,
|
||||||
|
diskPath: volumeDialogData.value.diskPath,
|
||||||
|
hostPath: volumeDialogData.value.hostPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (volumeDialogData.value.mode === 'new') {
|
||||||
|
await volumesModel.add(volumeDialogData.value.name, volumeDialogData.value.mountType, mountOptions);
|
||||||
|
} else {
|
||||||
|
await volumesModel.update(volumeDialogData.value.id, mountOptions);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
volumeDialogData.value.error = error.body ? error.body.message : 'Internal error';
|
||||||
|
volumeDialogData.value.busy = false;
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await refresh();
|
||||||
|
|
||||||
|
volumeDialog.value.close();
|
||||||
|
volumeDialogData.value.busy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onRemove(volume) {
|
||||||
|
const yes = await inputDialog.value.confirm({
|
||||||
|
message: `Really remove volume ${volume.name}?`,
|
||||||
|
confirmStyle: 'danger',
|
||||||
|
confirmLabel: t('volumes.removeVolumeDialog.removeAction'),
|
||||||
|
rejectLabel: t('main.dialog.cancel')
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!yes) return;
|
||||||
|
|
||||||
|
await volumesModel.remove(volume.id);
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remount(volume) {
|
||||||
|
await volumesModel.remount(volume.id);
|
||||||
|
|
||||||
|
const status = await volumesModel.getStatus(volume.id);
|
||||||
|
volume.state = status.state;
|
||||||
|
volume.message = status.message;
|
||||||
|
|
||||||
|
window.pankow.notify('Remount attempt finished');
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () =>{
|
||||||
|
await refresh();
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -203,8 +195,8 @@ export default {
|
|||||||
<Dialog ref="volumeDialog"
|
<Dialog ref="volumeDialog"
|
||||||
:title="volumeDialogData.mode === 'edit' ? $t('volumes.editVolumeDialog.title', { name: volumeDialogData.name }) : $t('volumes.addVolumeDialog.title')"
|
:title="volumeDialogData.mode === 'edit' ? $t('volumes.editVolumeDialog.title', { name: volumeDialogData.name }) : $t('volumes.addVolumeDialog.title')"
|
||||||
:reject-label="$t('main.dialog.cancel')"
|
:reject-label="$t('main.dialog.cancel')"
|
||||||
|
reject-style="secondary"
|
||||||
:confirm-label="$t('main.dialog.save')"
|
:confirm-label="$t('main.dialog.save')"
|
||||||
confirm-style="success"
|
|
||||||
:confirm-active="volumeDialogValid"
|
:confirm-active="volumeDialogValid"
|
||||||
:confirm-busy="volumeDialogData.busy"
|
:confirm-busy="volumeDialogData.busy"
|
||||||
@confirm="submitVolumeDialog()"
|
@confirm="submitVolumeDialog()"
|
||||||
|
|||||||
Reference in New Issue
Block a user