Convert most of the services view

This commit is contained in:
Johannes Zellner
2025-01-21 16:54:56 +01:00
parent 16c15548c1
commit d8c70c2498
5 changed files with 198 additions and 10 deletions

View File

@@ -0,0 +1,143 @@
<script setup>
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
import { computed, reactive, onMounted } from 'vue';
import { Button, TableView, ProgressBar, ButtonGroup } from 'pankow';
import { prettyBinarySize } from 'pankow/utils';
import { each } from 'async';
import Section from '../components/Section.vue';
import ServicesModel from '../models/ServicesModel.js';
import AppsModel from '../models/AppsModel.js';
const appsModel = AppsModel.create(API_ORIGIN, localStorage.token);
const servicesModel = ServicesModel.create(API_ORIGIN, localStorage.token);
const columns = {
status: {},
name: { label: t('services.service'), sort: true },
memoryPercent: { label: t('services.memoryUsage'), sort: true },
memoryLimit: { label: t('services.memoryLimit'), sort: true },
actions: {}
};
const services = reactive({
box: {
name: 'cloudron',
status: 'active',
memoryUsage: 0,
memoryLimit: 0,
config: {}
}
});
const servicesArray = computed(() => {
return Object.keys(services).map(s => {
services[s].id = s;
return services[s];
});
});
async function refresh(id) {
const [error, result] = await servicesModel.get(id);
if (error) console.error(error);
services[id] = result;
services[id].id = id;
services[id].name = id.indexOf('redis') === 0 ? id : result.name;
services[id].config = result.config || {};
services[id].memoryLimit = result.config.memoryLimit || 0;
services[id].memoryUsed = result.memoryUsed || 0;
services[id].memoryPercent = result.memoryPercent || 0;
if (id.indexOf('redis') === 0) {
// eslint-disable-next-line no-unused-vars
const [error, result] = await appsModel.get(id.slice('redis:'.length));
if (result) services[id].name = 'Redis (' + (result.label || result.fqdn) + ')';
else services[id].name = 'Redis (unknown app)';
}
// we will poll until active
if (result.status !== 'active') setTimeout(refresh.bind(null, id), 3000);
}
async function refreshAll() {
const [error, serviceList] = await servicesModel.list();
if (error) return console.error(error);
// init with all services
for (const s of serviceList) {
if (!services[s]) services[s] = { id: s, name: s, config: {} };
}
await each(serviceList, refresh);
}
async function onRestart(id) {
services[id].status = 'starting';
const [error] = await servicesModel.restart(id);
if (error) return console.error(error);
// this will poll till active
await refresh(id);
}
async function onConfigure(service) {
console.log('TODO configure service', service)
}
onMounted(async () => {
await refreshAll();
});
</script>
<template>
<div class="content">
<Section :title="$t('services.title')">
<template #header-buttons>
<Button @click="refreshAll()">{{ $t('services.refresh') }}</Button>
</template>
<p>{{ $t('services.description') }}</p>
<TableView :columns="columns" :model="servicesArray">
<template #status="slotProps">
<span v-show="slotProps.status">
<span v-if="slotProps.status === 'active'">
<i class="fa fa-circle status-active" v-tooltip="'active'"></i>
</span>
<span v-else-if="slotProps.status === 'starting'">
<i class="fa fa-circle status-starting" v-tooltip="'starting'" v-show="!slotProps.config.recoveryMode"></i>
<i class="fa fa-circle status-inactive" v-tooltip="'recovery mode'" v-show="slotProps.config.recoveryMode"></i>
</span>
<span v-else>
<i class="fa fa-circle status-error" uib-tooltip="{{ slotProps.status }}"></i>
</span>
</span>
<i class="fa fa-circle-notch fa-spin" v-show="!slotProps.status"></i>
</template>
<template #memoryPercent="slotProps">
<ProgressBar :value="slotProps.memoryPercent" v-show="slotProps.memoryPercent" />
</template>
<template #memoryLimit="slotProps">
<span v-show="slotProps.memoryLimit">{{ prettyBinarySize(slotProps.memoryLimit) }}</span>
</template>
<template #actions="slotProps">
<div class="table-actions">
<ButtonGroup>
<Button small tool secondary outline v-if="slotProps.status !== 'disabled' && slotProps.config.memoryLimit" @click="onConfigure(slotProps)" v-tooltip="$t('services.configureActionTooltip')" icon="fa-solid fa fa-pencil-alt"/>
<Button small tool secondary outline v-if="slotProps.id !== 'box'" @click="onRestart(slotProps.id)" :loading="slotProps.status === 'starting' && !slotProps.config.recoveryMode" v-tooltip="$t('services.restartActionTooltip')" icon="fa-solid fa-sync-alt"/>
<Button tool small secondary outline :href="`/logs.html?id=${slotProps.id}`" target="_blank" v-tooltip="$t('logs.title')" icon="fa-solid fa-file-alt" />
</ButtonGroup>
</div>
</template>
</TableView>
</Section>
</div>
</template>