Convert most of the services view
This commit is contained in:
143
dashboard/src/views/ServicesView.vue
Normal file
143
dashboard/src/views/ServicesView.vue
Normal 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>
|
||||
Reference in New Issue
Block a user