Files
cloudron-box/dashboard/src/views/AppConfigureView.vue

205 lines
6.6 KiB
Vue
Raw Normal View History

2025-02-20 10:54:43 +01:00
<script setup>
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN || window.location.origin;
import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
2025-02-22 11:13:15 +01:00
import { ref, onMounted, onBeforeUnmount, useTemplateRef, computed } from 'vue';
2025-02-20 10:54:43 +01:00
import { Button, ButtonGroup, TabView } from 'pankow';
import Info from '../components/app/Info.vue';
2025-02-22 11:13:15 +01:00
import Cron from '../components/app/Cron.vue';
2025-02-21 16:30:59 +01:00
import Eventlog from '../components/app/Eventlog.vue';
2025-02-21 20:58:43 +01:00
import Updates from '../components/app/Updates.vue';
import Uninstall from '../components/app/Uninstall.vue';
2025-02-20 10:54:43 +01:00
import AppsModel from '../models/AppsModel.js';
import { APP_TYPES } from '../constants.js';
const appsModel = AppsModel.create();
2025-02-20 16:12:36 +01:00
const tabView = useTemplateRef('tabView');
2025-02-20 10:54:43 +01:00
const tabs = ref({
info: t('app.infoTabTitle'),
display: t('app.displayTabTitle'),
location: t('app.locationTabTitle'),
proxy: 'Proxy',
access: t('app.accessControlTabTitle'),
resources: t('app.resourcesTabTitle'),
services: t('app.servicesTabTitle'),
storage: t('app.storageTabTitle'),
graphs: t('app.graphsTabTitle'),
security: t('app.securityTabTitle'),
email: t('app.emailTabTitle'),
cron: t('app.cronTabTitle'),
updates: t('app.updatesTabTitle'),
backups: t('app.backupsTabTitle'),
repair: t('app.repairTabTitle'),
eventlog: t('app.eventlogTabTitle'),
uninstall: t('app.uninstallTabTitle'),
});
const id = ref('');
const app = ref({});
const link = ref('');
const infoMenu = ref([]);
const hasLocalStorage = ref(false);
2025-02-21 14:07:07 +01:00
const isAppStopped = computed(() => {
return appsModel.isStopped(app.value);
});
function onTabChanged(tab) {
window.location.hash = `/app/${id.value}/${tab}`;
}
2025-02-21 14:07:07 +01:00
async function onToggleRunState() {
if (isAppStopped.value) {
const [error] = await appsModel.start(app.value.id);
if (error) return console.error(error);
} else {
const [error] = await appsModel.stop(app.value.id);
if (error) return console.error(error);
}
}
let refreshTimer = null;
2025-02-20 10:54:43 +01:00
async function refresh() {
const [error, result] = await appsModel.get(id.value);
if (error) return console.error(error);
app.value = result;
// app links have http already in the fqdn
link.value = result.fqdn.indexOf('http') !== 0 ? 'https://' + result.fqdn : result.fqdn;
hasLocalStorage.value = result.manifest && result.manifest.addons && result.manifest.addons.localstorage;
2025-02-21 14:07:07 +01:00
// TODO info menu will likely not change during polling
infoMenu.value = [];
2025-02-20 10:54:43 +01:00
infoMenu.value.push({
label: t('app.docsAction'),
disabled: !result.manifest?.documentationUrl,
// TODO support real href links
action: () => { window.location.href = result.manifest.documentationUrl; }
});
if (result.manifest?.postInstallMessage) {
infoMenu.value.push({
label: t('app.firstTimeSetupAction'),
// TODO action
});
}
if (result.manifest?.configurePath) {
infoMenu.value.push({
label: t('app.adminPageAction'),
// TODO support real href links
action: () => { window.location.href = link.value + result.manifest.configurePath; }
});
}
if (result.manifest?.addons?.localstorage?.ftp) {
infoMenu.value.push({
label: t('app.sftpInfoAction'),
// TODO action
});
}
infoMenu.value.push({ separator: true });
infoMenu.value.push({
label: t('app.forumUrlAction'),
disabled: !result.manifest?.forumUrl,
// TODO support real href links
action: () => { window.location.href = result.manifest.forumUrl; }
});
infoMenu.value.push({ separator: true });
infoMenu.value.push({
label: t('app.projectWebsiteAction'),
disabled: !result.manifest?.website,
// TODO support real href links
action: () => { window.location.href = result.manifest.website; }
});
refreshTimer = setTimeout(refresh, 2000);
2025-02-20 10:54:43 +01:00
}
onMounted(async () => {
const tmp = window.location.hash.slice('#/app/'.length);
if (!tmp) return;
const parts = tmp.split('/');
if (parts.length !== 2) return;
id.value = parts[0];
await refresh();
2025-02-20 16:12:36 +01:00
tabView.value.open(parts[1] || 'info');
2025-02-20 10:54:43 +01:00
});
2025-02-22 11:13:15 +01:00
onBeforeUnmount(() => {
if (refreshTimer) clearTimeout(refreshTimer);
});
2025-02-20 10:54:43 +01:00
</script>
<template>
2025-02-20 16:12:36 +01:00
<div class="content">
2025-02-21 23:20:02 +01:00
<div class="titlebar">
2025-02-20 10:54:43 +01:00
<div style="display: flex;">
2025-02-21 14:07:07 +01:00
<img :src="API_ORIGIN + app.iconUrl" v-fallback-image="API_ORIGIN + '/img/appicon_fallback.png'" style="width: 64px; margin-right: 10px;"/>
2025-02-20 10:54:43 +01:00
<h2>{{ app.label || app.fqdn }}</h2>
</div>
2025-02-20 16:12:36 +01:00
<div style="display: flex; gap: 10px; align-items: center;">
2025-02-20 10:54:43 +01:00
<Button outline tool
@click="onToggleRunState()"
:disabled="app.taskId || app.error || app.installationState === 'pending_start' || app.installationState === 'pending_stop'"
2025-02-21 14:07:07 +01:00
v-tooltip="$t(isAppStopped ? 'app.uninstall.startStop.startAction' : 'app.uninstall.startStop.stopAction')"
2025-02-20 10:54:43 +01:00
:loading="app.installationState === 'pending_start' || app.installationState === 'pending_stop'"
2025-02-21 14:07:07 +01:00
:icon="isAppStopped ? 'fa-solid fa-play' : 'fa-solid fa-power-off'"
2025-02-20 10:54:43 +01:00
/>
<ButtonGroup>
<Button outline tool :href="`/logs.html?appId=${app.id}`" target="_blank" v-tooltip="$t('app.logsActionTooltip')" icon="fa-solid fa-align-left" />
<Button outline tool v-if="app.type !== APP_TYPES.PROXIED" :href="`/terminal.html?id=${app.id}`" target="_blank" v-tooltip="$t('app.terminalActionTooltip')" icon="fa fa-terminal" />
<Button outline tool v-if="hasLocalStorage" :href="`/filemanager.html#/home/app/${app.id}`" target="_blank" v-tooltip="$t('app.filemanagerActionTooltip')" icon="fas fa-folder" />
</ButtonGroup>
<Button outline tool icon="fa-solid fa-book" v-tooltip="$t('app.docsActionTooltip')" :menu="infoMenu" />
</div>
</div>
2025-02-21 15:19:48 +01:00
<TabView ref="tabView" :tabs="tabs" @changed="onTabChanged" tab-position="left">
2025-02-20 10:54:43 +01:00
<template #info><Info :app="app"/></template>
2025-02-21 16:30:59 +01:00
<template #display></template>
2025-02-20 10:54:43 +01:00
<template #location></template>
<template #proxy></template>
<template #access></template>
<template #resources></template>
<template #services></template>
<template #storage></template>
<template #graphs></template>
<template #security></template>
<template #email></template>
2025-02-22 11:13:15 +01:00
<template #cron><Cron :app="app"/></template>
2025-02-21 20:58:43 +01:00
<template #updates><Updates :app="app"/></template>
2025-02-20 10:54:43 +01:00
<template #backups></template>
<template #repair></template>
2025-02-21 16:30:59 +01:00
<template #eventlog><Eventlog :app="app"/></template>
<template #uninstall><Uninstall :app="app"/></template>
2025-02-20 10:54:43 +01:00
</TabView>
</div>
</template>
2025-02-21 23:20:02 +01:00
<style scoped>
.titlebar {
display: flex;
margin-bottom: 20px;
justify-content: space-between;
position: sticky;
top: 0;
background-color: var(--pankow-body-background-color);
}
</style>