Files
cloudron-box/frontend/src/views/Home.vue
T

643 lines
22 KiB
Vue
Raw Normal View History

<template>
2023-02-24 13:39:31 +01:00
<MainLayout>
2023-04-16 13:01:25 +02:00
<template #dialogs>
<Dialog v-model:visible="fatalError" modal header="Error" :closable="false" :closeOnEscape="false">
<p>{{ fatalError }}</p>
</Dialog>
<Dialog v-model:visible="extractInProgress" modal :header="$t('filemanager.extractionInProgress')" :closable="false" :closeOnEscape="false">
<div style="text-align: center;">
<ProgressSpinner style="width: 50px; height: 50px"/>
</div>
</Dialog>
<Dialog v-model:visible="pasteInProgress" modal :header="$t('filemanager.pasteInProgress')" :closable="false" :closeOnEscape="false">
<div style="text-align: center;">
<ProgressSpinner style="width: 50px; height: 50px"/>
</div>
</Dialog>
<Dialog v-model:visible="deleteInProgress" modal :header="$t('filemanager.deleteInProgress')" :closable="false" :closeOnEscape="false">
<div style="text-align: center;">
<ProgressSpinner style="width: 50px; height: 50px"/>
</div>
</Dialog>
2023-04-16 13:01:25 +02:00
<!-- have to use v-model instead of : bind - https://github.com/primefaces/primevue/issues/815 -->
<Dialog v-model:visible="newFileDialog.visible" modal :style="{ width: '50vw' }" @show="onDialogShow('newFileDialogNameInput')">
<template #header>
2023-06-16 18:18:16 +02:00
<label class="dialog-header" for="newFileDialogNameInput">{{ $t('filemanager.newFileDialog.title') }}</label>
2023-04-16 13:01:25 +02:00
</template>
<template #default>
<form @submit="onNewFileDialogSubmit" @submit.prevent>
<InputText class="dialog-single-input" id="newFileDialogNameInput" v-model="newFileDialog.name" :disabled="newFileDialog.busy" required/>
2023-06-16 18:18:16 +02:00
<Button class="dialog-single-input-submit" type="submit" :label="$t('filemanager.newFileDialog.create')" :loading="newFileDialog.busy" :disabled="newFileDialog.busy || !newFileDialog.name"/>
2023-04-16 13:01:25 +02:00
</form>
</template>
</Dialog>
<Dialog v-model:visible="newFolderDialog.visible" modal :style="{ width: '50vw' }" @show="onDialogShow('newFolderDialogNameInput')">
<template #header>
2023-06-16 18:18:16 +02:00
<label class="dialog-header" for="newFolderDialogNameInput">{{ $t('filemanager.newDirectoryDialog.title') }}</label>
2023-04-16 13:01:25 +02:00
</template>
<template #default>
<form @submit="onNewFolderDialogSubmit" @submit.prevent>
<InputText class="dialog-single-input" id="newFolderDialogNameInput" v-model="newFolderDialog.name" :disabled="newFolderDialog.busy" required/>
2023-06-16 18:18:16 +02:00
<Button class="dialog-single-input-submit" type="submit" :label="$t('filemanager.newFileDialog.create')" :loading="newFolderDialog.busy" :disabled="newFolderDialog.busy || !newFolderDialog.name"/>
2023-04-16 13:01:25 +02:00
</form>
</template>
</Dialog>
</template>
2023-02-24 13:39:31 +01:00
<template #header>
2023-02-25 03:18:07 +01:00
<TopBar class="navbar">
2023-02-24 13:39:31 +01:00
<template #left>
2023-05-15 09:57:58 +02:00
<Button icon="pi pi-refresh" @click="onRefresh()" text :loading="busyRefresh" style="margin-right: 5px;"/>
<PathBreadcrumbs :path="cwd" :activate-handler="onActivateBreadcrumb"/>
2023-02-24 13:39:31 +01:00
</template>
<template #right>
2023-06-16 18:18:16 +02:00
<Button type="button" :label="$t('filemanager.toolbar.new')" icon="pi pi-plus" @click="onCreateMenu" aria-haspopup="true" aria-controls="create_menu" style="margin-right: 5px" />
2023-03-28 23:32:44 +02:00
<Menu ref="createMenu" id="create_menu" :model="createMenuModel" :popup="true" />
2023-06-16 18:18:16 +02:00
<Button type="button" :label="$t('filemanager.toolbar.upload')" icon="pi pi-upload" @click="onUploadMenu" aria-haspopup="true" aria-controls="upload_menu" style="margin-right: 5px" />
2023-03-28 23:32:44 +02:00
<Menu ref="uploadMenu" id="upload_menu" :model="uploadMenuModel" :popup="true" />
2023-08-21 20:29:39 +02:00
<Button style="margin-left: 20px; margin-right: 5px;" type="button" v-tooltip.bottom="$t('filemanager.toolbar.restartApp')" severity="secondary" icon="pi pi-sync" @click="onRestartApp" :loading="busyRestart" v-show="resourceType === 'app'"/>
<a style="margin-right: 5px;" :href="'/frontend/terminal.html?id=' + resourceId" target="_blank" v-show="resourceType === 'app'"><Button severity="secondary" icon="pi pi-desktop" v-tooltip.bottom="$t('terminal.title')" /></a>
2023-08-22 14:28:32 +02:00
<a :href="'/frontend/logs.html?appId=' + resourceId" target="_blank" v-show="resourceType === 'app'"><Button severity="secondary" icon="pi pi-align-left" v-tooltip.bottom="$t('logs.title')" /></a>
2023-02-24 13:39:31 +01:00
</template>
2023-02-25 03:18:07 +01:00
</TopBar>
2023-02-24 13:39:31 +01:00
</template>
<template #body>
<div class="main-view">
2023-02-25 03:18:07 +01:00
<div class="main-view-col">
<DirectoryView
2023-04-17 17:07:25 +02:00
:show-owner="true"
:show-size="true"
:show-modified="true"
@selection-changed="onSelectionChanged"
@item-activated="onItemActivated"
:delete-handler="deleteHandler"
2023-04-01 10:41:35 +02:00
:rename-handler="renameHandler"
2023-04-17 17:51:19 +02:00
:change-owner-handler="changeOwnerHandler"
2023-04-02 10:41:24 +02:00
:copy-handler="copyHandler"
:cut-handler="cutHandler"
2023-04-25 22:45:22 +02:00
:paste-handler="pasteHandler"
:download-handler="downloadHandler"
:extract-handler="extractHandler"
2023-04-23 10:58:37 +02:00
:new-file-handler="onNewFile"
:new-folder-handler="onNewFolder"
:upload-file-handler="onUploadFile"
:upload-folder-handler="onUploadFolder"
2023-04-29 16:10:34 +02:00
:drop-handler="onDrop"
:items="items"
2023-04-25 22:45:22 +02:00
:clipboard="clipboard"
2023-04-17 17:51:19 +02:00
:owners-model="ownersModel"
2023-05-15 09:26:37 +02:00
:fallback-icon="fallbackIcon"
2023-06-18 19:19:29 +02:00
:tr="$t"
/>
2023-02-25 03:18:07 +01:00
</div>
<div class="main-view-col" style="max-width: 300px;">
2023-08-23 14:13:07 +02:00
<div class="title-bar">
<a v-show="appLink" :href="appLink" target="_blank">{{ title }}</a>
<span v-show="!appLink">{{ title }}</span>
</div>
2023-05-15 09:26:37 +02:00
<PreviewPanel :item="activeItem || activeDirectoryItem" :fallback-icon="fallbackIcon"/>
2023-02-25 03:18:07 +01:00
</div>
2023-02-24 13:39:31 +01:00
</div>
</template>
<template #footer>
2023-04-11 16:29:58 +02:00
<FileUploader
ref="fileUploader"
:upload-handler="uploadHandler"
@finished="onUploadFinished"
2023-06-18 19:19:29 +02:00
:tr="$t"
2023-04-11 16:29:58 +02:00
/>
<BottomBar>
<div v-html="footerContent" class="bottom-bar-content"></div>
</BottomBar>
2023-02-24 13:39:31 +01:00
</template>
</MainLayout>
</template>
<script>
2023-02-26 01:45:57 +01:00
import superagent from 'superagent';
import { marked } from 'marked';
2023-02-26 01:45:57 +01:00
import Button from 'primevue/button';
2023-04-16 13:01:25 +02:00
import Dialog from 'primevue/dialog';
import InputText from 'primevue/inputtext';
2023-03-28 23:32:44 +02:00
import Menu from 'primevue/menu';
import ProgressSpinner from 'primevue/progressspinner';
2023-03-29 09:46:07 +02:00
import { useConfirm } from 'primevue/useconfirm';
import { DirectoryView, TopBar, PathBreadcrumbs, BottomBar, MainLayout, FileUploader } from 'pankow';
2023-09-20 10:04:24 +02:00
import { sanitize, sleep } from 'pankow/utils';
2023-05-23 11:38:57 +02:00
import { ISTATES } from '../constants.js';
2023-02-24 13:39:31 +01:00
2023-02-22 19:31:12 +01:00
import PreviewPanel from '../components/PreviewPanel.vue';
2023-02-26 01:45:57 +01:00
import { createDirectoryModel } from '../models/DirectoryModel.js';
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? 'https://' + import.meta.env.VITE_API_ORIGIN : '';
2023-04-16 19:30:18 +02:00
const BASE_URL = import.meta.env.BASE_URL || '/';
const beforeUnloadListener = (event) => {
event.preventDefault();
2023-07-11 15:02:07 +02:00
return window.confirm('File operation still in progress. Really close?');
};
export default {
name: 'Home',
components: {
2023-02-25 03:18:07 +01:00
BottomBar,
2023-02-22 19:31:12 +01:00
Button,
2023-04-16 13:01:25 +02:00
Dialog,
2023-03-28 23:32:44 +02:00
DirectoryView,
FileUploader,
2023-04-16 13:01:25 +02:00
InputText,
2023-02-24 13:39:31 +01:00
MainLayout,
2023-03-28 23:32:44 +02:00
Menu,
PathBreadcrumbs,
2023-03-28 23:32:44 +02:00
PreviewPanel,
ProgressSpinner,
2023-03-28 23:32:44 +02:00
TopBar
2023-02-22 19:31:12 +01:00
},
data() {
return {
fallbackIcon: `${BASE_URL}mime-types/none.svg`,
2023-03-29 10:17:36 +02:00
cwd: '/',
2023-05-15 09:57:58 +02:00
busyRefresh: false,
2023-05-23 11:38:57 +02:00
busyRestart: false,
2023-05-11 19:42:54 +02:00
fatalError: false,
extractInProgress: false,
pasteInProgress: false,
deleteInProgress: false,
footerContent: '',
activeItem: null,
activeDirectoryItem: {},
2023-02-26 01:45:57 +01:00
items: [],
selectedItems: [],
2023-04-25 22:45:22 +02:00
clipboard: {
action: '', // copy or cut
files: []
},
2023-04-16 18:24:00 +02:00
accessToken: localStorage.token,
apiOrigin: API_ORIGIN || '',
title: 'Cloudron',
2023-08-23 14:13:07 +02:00
appLink: '',
resourceType: '',
resourceId: '',
2023-04-16 13:01:25 +02:00
visible: true,
newFileDialog: {
visible: false,
busy: false,
name: ''
},
newFolderDialog: {
visible: false,
busy: false,
name: ''
},
2023-11-27 13:09:42 +01:00
ownersModel: [],
2023-02-26 23:34:31 +01:00
// contextMenuModel will have activeItem attached if any command() is called
2023-03-28 23:32:44 +02:00
createMenuModel: [{
2023-06-16 18:18:16 +02:00
label: () => this.$t('filemanager.toolbar.newFile'),
2023-03-28 23:32:44 +02:00
icon: 'pi pi-file',
2023-04-16 13:01:25 +02:00
command: this.onNewFile
2023-03-28 23:32:44 +02:00
}, {
2023-06-16 18:18:16 +02:00
label: () => this.$t('filemanager.toolbar.newFolder'),
2023-03-28 23:32:44 +02:00
icon: 'pi pi-folder',
2023-04-16 13:01:25 +02:00
command: this.onNewFolder
2023-03-28 23:32:44 +02:00
}],
uploadMenuModel: [{
2023-06-16 18:18:16 +02:00
label: () => this.$t('filemanager.toolbar.uploadFile'),
2023-03-28 23:32:44 +02:00
icon: 'pi pi-file',
2023-04-23 10:58:37 +02:00
command: this.onUploadFile
2023-03-28 23:32:44 +02:00
}, {
2023-06-16 18:18:16 +02:00
label: () => this.$t('filemanager.toolbar.newFolder'),
2023-03-28 23:32:44 +02:00
icon: 'pi pi-folder',
2023-04-23 10:58:37 +02:00
command: this.onUploadFolder
2023-02-26 01:45:57 +01:00
}]
2023-02-22 19:31:12 +01:00
};
},
2023-02-26 01:45:57 +01:00
watch: {
2023-03-29 17:44:52 +02:00
cwd(newCwd, oldCwd) {
if (this.resourceType && this.resourceId) this.$router.push(`/home/${this.resourceType}/${this.resourceId}${this.cwd}`);
2023-03-29 17:44:52 +02:00
this.loadCwd();
2023-02-26 01:45:57 +01:00
}
},
methods: {
2023-03-28 23:32:44 +02:00
onCreateMenu(event) {
this.$refs.createMenu.toggle(event);
},
onUploadMenu(event) {
this.$refs.uploadMenu.toggle(event);
},
2023-04-16 13:01:25 +02:00
// generic dialog focus handler
onDialogShow(focusElementId) {
setTimeout(() => document.getElementById(focusElementId).focus(), 0);
},
onNewFile() {
this.newFileDialog.busy = false;
this.newFileDialog.name = '';
this.newFileDialog.visible = true;
},
2023-04-16 13:43:48 +02:00
async onNewFileDialogSubmit() {
this.newFileDialog.busy = true;
2023-09-20 10:04:24 +02:00
await this.directoryModel.newFile(this.directoryModel.buildFilePath(this.cwd, this.newFileDialog.name), this.newFileDialog.name);
2023-04-16 13:43:48 +02:00
await this.loadCwd();
2023-04-16 13:01:25 +02:00
this.newFileDialog.visible = false;
},
onNewFolder() {
this.newFolderDialog.busy = false;
this.newFolderDialog.name = '';
this.newFolderDialog.visible = true;
},
2023-04-16 13:43:48 +02:00
async onNewFolderDialogSubmit() {
this.newFolderDialog.busy = true;
2023-09-20 10:04:24 +02:00
await this.directoryModel.newFolder(this.directoryModel.buildFilePath(this.cwd, this.newFolderDialog.name));
2023-04-16 13:43:48 +02:00
await this.loadCwd();
2023-04-16 13:01:25 +02:00
this.newFolderDialog.visible = false;
},
2023-04-23 10:58:37 +02:00
onUploadFile() {
2023-04-29 16:10:34 +02:00
this.$refs.fileUploader.onUploadFile(this.cwd);
2023-04-23 10:58:37 +02:00
},
onUploadFolder() {
2023-04-29 16:10:34 +02:00
this.$refs.fileUploader.onUploadFolder(this.cwd);
2023-04-23 10:58:37 +02:00
},
2023-03-29 10:17:36 +02:00
onUploadFinished() {
2023-03-29 17:44:52 +02:00
this.loadCwd();
2023-03-29 10:17:36 +02:00
},
2023-02-26 23:34:31 +01:00
onAppChange(event) {
2023-04-11 12:14:47 +02:00
this.$router.push(`/home/${event.value.type}/${event.value.id}`);
2023-03-29 10:17:36 +02:00
this.cwd = '/';
2023-04-11 12:14:47 +02:00
this.loadResource(event.value);
2023-02-26 23:34:31 +01:00
},
2023-02-25 03:18:07 +01:00
onSelectionChanged(items) {
this.activeItem = items[0] || null;
this.selectedItems = items;
2023-02-26 01:45:57 +01:00
},
onActivateBreadcrumb(path) {
this.cwd = sanitize(path);
},
2023-05-15 09:57:58 +02:00
async onRefresh() {
this.busyRefresh = true;
await this.loadCwd();
setTimeout(() => { this.busyRefresh = false; }, 500);
},
// either dataTransfer (external drop) or files (internal drag)
async onDrop(targetFolder, dataTransfer, files) {
2023-04-29 16:10:34 +02:00
const fullTargetFolder = sanitize(this.cwd + '/' + targetFolder);
if (dataTransfer) {
// figure if a folder was dropped on a modern browser, in this case the first would have to be a directory
let folderItem;
try {
folderItem = dataTransfer.items[0].webkitGetAsEntry();
if (folderItem.isFile) return this.$refs.fileUploader.addFiles(dataTransfer.files, fullTargetFolder, false);
} catch (e) {
return this.$refs.fileUploader.addFiles(dataTransfer.files, fullTargetFolder, false);
}
2023-04-29 16:10:34 +02:00
// if we got here we have a folder drop and a modern browser
// now traverse the folder tree and create a file list
var that = this;
function traverseFileTree(item, path) {
if (item.isFile) {
item.file(function (file) {
that.$refs.fileUploader.addFiles([file], sanitize(`${that.cwd}/${targetFolder}`), false);
});
} else if (item.isDirectory) {
// Get folder contents
var dirReader = item.createReader();
dirReader.readEntries(function (entries) {
for (let i in entries) {
traverseFileTree(entries[i], item.name);
}
});
}
2023-04-29 16:10:34 +02:00
}
traverseFileTree(folderItem, '');
} else {
if (!files.length) return;
window.addEventListener('beforeunload', beforeUnloadListener, { capture: true });
this.pasteInProgress = true;
// check ctrl for cut/copy
await this.directoryModel.paste(fullTargetFolder, 'cut', files);
await this.loadCwd();
window.removeEventListener('beforeunload', beforeUnloadListener, { capture: true });
this.pasteInProgress = false;
}
2023-04-29 16:10:34 +02:00
},
2023-02-26 01:45:57 +01:00
onItemActivated(item) {
2023-03-29 21:39:12 +02:00
if (!item) return;
2023-05-07 13:50:41 +02:00
if (item.mimeType === 'inode/symlink') return;
2023-03-29 21:39:12 +02:00
2023-03-29 10:17:36 +02:00
if (item.type === 'directory') this.cwd = sanitize(this.cwd + '/' + item.name);
else this.$router.push(`/viewer/${this.resourceType}/${this.resourceId}${sanitize(this.cwd + '/' + item.name)}`);
2023-02-26 01:45:57 +01:00
},
async deleteHandler(files) {
2023-04-02 10:06:14 +02:00
if (!files) return;
function start_and_end(str) {
if (str.length > 100) {
return str.substr(0, 45) + ' ... ' + str.substr(str.length-45, str.length);
}
return str;
2023-04-02 10:06:14 +02:00
}
this.$confirm.require({
header: this.$t('filemanager.removeDialog.reallyDelete'),
message: start_and_end(files.map((f) => f.name).join(', ')),
icon: '',
acceptClass: 'p-button-danger',
accept: async () => {
window.addEventListener('beforeunload', beforeUnloadListener, { capture: true });
this.deleteInProgress = true;
for (let i in files) {
try {
await this.directoryModel.remove(this.directoryModel.buildFilePath(this.cwd, files[i].name));
} catch (e) {
console.error(`Failed to remove file ${files[i].name}:`, e);
}
}
await this.loadCwd();
this.$confirm.close();
window.removeEventListener('beforeunload', beforeUnloadListener, { capture: true });
this.deleteInProgress = false;
}
});
},
2023-04-01 10:41:35 +02:00
async renameHandler(file, newName) {
try {
await this.directoryModel.rename(this.directoryModel.buildFilePath(this.cwd, file.name), sanitize(this.cwd + '/' + newName));
await this.loadCwd();
} catch (e) {
if (e.status === 409) {
this.$confirm.require({
message: this.$t('filemanager.renameDialog.reallyOverwrite'),
icon: '',
acceptClass: 'p-button-danger',
accept: async () => {
await this.directoryModel.rename(this.directoryModel.buildFilePath(this.cwd, file.name), sanitize(this.cwd + '/' + newName), true /* overwrite */);
await this.loadCwd();
this.$confirm.close();
}
});
}
else console.error(`Failed to rename ${file} to ${newName}`, e);
}
2023-04-01 10:41:35 +02:00
},
2023-04-17 17:51:19 +02:00
async changeOwnerHandler(files, newOwnerUid) {
if (!files) return;
for (let i in files) {
2023-09-20 10:04:24 +02:00
await this.directoryModel.chown(this.directoryModel.buildFilePath(this.cwd, files[i].name), newOwnerUid);
2023-04-17 17:51:19 +02:00
}
await this.loadCwd();
},
2023-04-02 10:41:24 +02:00
async copyHandler(files) {
if (!files) return;
this.clipboard = {
action: 'copy',
files
};
},
async cutHandler(files) {
if (!files) return;
this.clipboard = {
action: 'cut',
files
};
},
2023-04-25 22:45:22 +02:00
async pasteHandler(target) {
if (!this.clipboard.files || !this.clipboard.files.length) return;
const targetPath = (target && target.isDirectory) ? sanitize(this.cwd + '/' + target.fileName) : this.cwd;
2023-04-25 22:45:22 +02:00
window.addEventListener('beforeunload', beforeUnloadListener, { capture: true });
this.pasteInProgress = true;
2023-04-25 22:45:22 +02:00
await this.directoryModel.paste(targetPath, this.clipboard.action, this.clipboard.files);
this.clipboard = {};
await this.loadCwd();
window.removeEventListener('beforeunload', beforeUnloadListener, { capture: true });
this.pasteInProgress = false;
2023-04-25 22:45:22 +02:00
},
async downloadHandler(file) {
2023-09-20 10:04:24 +02:00
await this.directoryModel.download(this.directoryModel.buildFilePath(this.cwd, file.name));
},
async extractHandler(file) {
this.extractInProgress = true;
2023-09-20 10:04:24 +02:00
await this.directoryModel.extract(this.directoryModel.buildFilePath(this.cwd, file.name));
await this.loadCwd();
this.extractInProgress = false;
},
2023-04-11 16:29:58 +02:00
async uploadHandler(targetDir, file, progressHandler) {
await this.directoryModel.upload(targetDir, file, progressHandler);
2023-04-16 13:43:48 +02:00
await this.loadCwd();
2023-04-11 16:29:58 +02:00
},
2023-03-29 17:44:52 +02:00
async loadCwd() {
2023-04-17 17:07:25 +02:00
this.items = await this.directoryModel.listFiles(this.cwd);
2023-03-29 10:17:36 +02:00
const tmp = this.cwd.split('/').slice(1);
2023-08-23 14:13:07 +02:00
let name = '';
if (tmp.length >= 1 && tmp[tmp.length-1]) name = tmp[tmp.length-1];
this.activeDirectoryItem = {
id: name,
name: name,
type: 'directory',
mimeType: 'inode/directory',
2023-04-16 19:30:18 +02:00
icon: `${BASE_URL}mime-types/inode-directory.svg`
};
2023-05-23 11:38:57 +02:00
},
async onRestartApp() {
if (this.resourceType !== 'app') return;
this.busyRestart = true;
let error, result;
try {
result = await superagent.post(`${this.apiOrigin}/api/v1/apps/${this.resourceId}/restart`).query({ access_token: this.accessToken });
} catch (e) {
error = e;
}
if (error || result.statusCode !== 202) {
console.error(`Failed to restart app ${this.resourceId}`, error || result.statusCode);
return;
}
while(true) {
let error, result;
try {
result = await superagent.get(`${this.apiOrigin}/api/v1/apps/${this.resourceId}`).query({ access_token: this.accessToken });
} catch (e) {
error = e;
}
if (result && result.statusCode === 200 && result.body.installationState === ISTATES.INSTALLED) break;
await sleep(2000);
}
this.busyRestart = false;
2023-02-26 01:45:57 +01:00
}
},
async mounted() {
2023-03-29 09:46:07 +02:00
useConfirm();
2023-04-11 12:14:47 +02:00
const type = this.$route.params.type || 'app';
const resourceId = this.$route.params.resourceId;
const cwd = this.$route.params.cwd;
2023-04-11 12:14:47 +02:00
if (type === 'app') {
let error, result;
try {
result = await superagent.get(`${this.apiOrigin}/api/v1/apps/${resourceId}`).query({ access_token: this.accessToken });
} catch (e) {
error = e;
}
2023-04-16 19:06:14 +02:00
if (error || result.statusCode !== 200) {
console.error(`Invalid resource ${type} ${resourceId}`, error || result.statusCode);
this.fatalError = `Invalid resource ${type} ${resourceId}`;
return;
}
2023-02-26 23:34:31 +01:00
2023-08-23 14:13:07 +02:00
this.appLink = `https://${result.body.fqdn}`;
2023-07-13 15:36:57 +02:00
this.title = `${result.body.label || result.body.fqdn} (${result.body.manifest.title})`;
} else if (type === 'volume') {
let error, result;
try {
result = await superagent.get(`${this.apiOrigin}/api/v1/volumes/${resourceId}`).query({ access_token: this.accessToken });
} catch (e) {
error = e;
}
if (error || result.statusCode !== 200) {
console.error(`Invalid resource ${type} ${resourceId}`, error || result.statusCode);
this.fatalError = `Invalid resource ${type} ${resourceId}`;
return;
}
this.title = result.body.name;
} else {
this.fatalError = `Unsupported type ${type}`;
return;
}
try {
2023-08-12 21:47:24 +05:30
const result = await superagent.get(`${this.apiOrigin}/api/v1/dashboard/config`).query({ access_token: this.accessToken });
this.footerContent = marked.parse(result.body.footer);
} catch (e) {
console.error('Failed to fetch Cloudron config.', e);
}
2023-07-13 15:36:57 +02:00
window.document.title = `File Manager - ${this.title}`;
this.cwd = sanitize('/' + (cwd ? cwd.join('/') : '/'));
this.resourceType = type;
this.resourceId = resourceId;
2023-05-09 10:58:27 +02:00
2023-11-27 13:09:42 +01:00
this.directoryModel = createDirectoryModel(this.apiOrigin, this.accessToken, type === 'volume' ? `volumes/${resourceId}` : `apps/${resourceId}`);
this.ownersModel = this.directoryModel.ownersModel;
this.loadCwd();
2023-02-26 15:00:16 +01:00
this.$watch(() => this.$route.params, (toParams, previousParams) => {
2023-05-08 16:09:33 +02:00
if (toParams.type !== 'app' && toParams.type !== 'volume') {
this.fatalError = `Unknown type ${toParams.type}`;
2023-05-08 16:09:33 +02:00
return;
}
if ((toParams.type !== this.resourceType) || (toParams.resourceId !== this.resourceId)) {
this.resourceType = toParams.type;
this.resourceId = toParams.resourceId;
2023-05-09 10:58:27 +02:00
this.directoryModel = createDirectoryModel(this.apiOrigin, this.accessToken, toParams.type === 'volume' ? `volumes/${toParams.resourceId}` : `apps/${toParams.resourceId}`);
2023-04-11 12:14:47 +02:00
}
2023-03-29 10:17:36 +02:00
this.cwd = toParams.cwd ? `/${toParams.cwd.join('/')}` : '/';
2023-02-26 15:00:16 +01:00
});
}
};
</script>
<style scoped>
.main-view {
2023-02-23 13:16:21 +01:00
flex-grow: 1;
2023-02-25 03:18:07 +01:00
overflow: hidden;
height: 100%;
2023-02-24 13:39:31 +01:00
display: flex;
2023-02-25 03:18:07 +01:00
padding: 0 10px
}
2023-08-23 14:13:07 +02:00
.title-bar {
text-align: center;
font-size: 20px;
margin-bottom: 20px;
color: #607d8b;
}
.title-bar > a {
color: #607d8b;
}
.title-bar > a:hover {
color: black;
text-decoration: none;
}
2023-02-25 03:18:07 +01:00
.main-view-col {
overflow: auto;
flex-grow: 1;
2023-04-16 13:01:25 +02:00
}
.dialog-header {
font-weight: 600;
font-size: 1.25rem;
}
.dialog-single-input {
display: block;
width: 100%;
margin-top: 5px;
margin-bottom: 1.5rem;
}
2023-02-25 03:18:07 +01:00
2023-04-16 13:01:25 +02:00
.dialog-single-input-submit {
margin-top: 5px;
}
</style>
<style>
.bottom-bar-content > p {
margin: 0;
}
/* this is actually calculated and in some situations the z-index would have to be higher but for the moment ok, needs fixing in primvue */
.p-dropdown-panel.p-component {
z-index: 5001 !important;
}
</style>