157 lines
6.6 KiB
Vue
157 lines
6.6 KiB
Vue
<script setup>
|
|
|
|
import { ref, onMounted, computed, nextTick } from 'vue';
|
|
import { Button, FormGroup, TagInput } from '@cloudron/pankow';
|
|
import { prettyBinarySize } from '@cloudron/pankow/utils';
|
|
import { ISTATES } from '../../constants.js';
|
|
import AppsModel from '../../models/AppsModel.js';
|
|
import SystemModel from '../../models/SystemModel.js';
|
|
|
|
const appsModel = AppsModel.create();
|
|
const systemModel = SystemModel.create();
|
|
|
|
const props = defineProps([ 'app' ]);
|
|
|
|
const memoryLimitBusy = ref(false);
|
|
const memoryLimit = ref(0);
|
|
const currentMemoryLimit = ref(0);
|
|
const memoryTicks = ref([]);
|
|
const cpuQuotaBusy = ref(false);
|
|
const cpuQuota = ref(0);
|
|
const currentCpuQuota = ref(0);
|
|
const devicesBusy = ref(false);
|
|
const devicesError = ref('');
|
|
const devices = ref([]);
|
|
const currentDevices = ref([]);
|
|
|
|
async function onSubmitMemoryLimit() {
|
|
memoryLimitBusy.value = true;
|
|
|
|
const tmp = parseInt(memoryLimit.value);
|
|
const limit = tmp === memoryTicks.value[0] ? 0 : tmp; // this will reset to app minimum
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'memory_limit', { memoryLimit: limit });
|
|
if (error) return console.error(error);
|
|
|
|
// give polling some time
|
|
setTimeout(() => memoryLimitBusy.value = false, 4000);
|
|
}
|
|
|
|
async function onSubmitCpuQuota() {
|
|
cpuQuotaBusy.value = true;
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'cpu_quota', { cpuQuota: parseInt(cpuQuota.value) });
|
|
if (error) return console.error(error);
|
|
|
|
currentCpuQuota.value = parseInt(cpuQuota.value);
|
|
|
|
// give polling some time
|
|
setTimeout(() => cpuQuotaBusy.value = false, 4000);
|
|
}
|
|
|
|
async function onSubmitDevices() {
|
|
devicesBusy.value = true;
|
|
devicesError.value = '';
|
|
|
|
const devs = {};
|
|
devices.value.forEach(d => {
|
|
if (!d.trim()) return;
|
|
devs[d.trim()] = {};
|
|
});
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'devices', { devices: devs });
|
|
if (error && error.status === 400) {
|
|
devicesError.value = error.body.message;
|
|
devicesBusy.value = false;
|
|
return;
|
|
} else if (error) {
|
|
devicesBusy.value = false;
|
|
console.error(error);
|
|
return;
|
|
}
|
|
|
|
// give polling some time
|
|
setTimeout(() => {
|
|
devicesBusy.value = false;
|
|
currentDevices.value = Object.keys(devs);
|
|
}, 4000);
|
|
}
|
|
|
|
const devicesChanged = computed(() => {
|
|
return !(devices.value.toString() == currentDevices.value.toString());
|
|
});
|
|
|
|
onMounted(async () => {
|
|
const [error, result] = await systemModel.memory();
|
|
if (error) return console.error(error);
|
|
|
|
cpuQuota.value = props.app.cpuQuota;
|
|
currentCpuQuota.value = props.app.cpuQuota;
|
|
devices.value = Object.keys(props.app.devices);
|
|
currentDevices.value = Object.keys(props.app.devices);
|
|
|
|
const tmpMemoryLimit = props.app.memoryLimit || props.app.manifest.memoryLimit || (256 * 1024 * 1024);
|
|
|
|
// create ticks starting from manifest memory limit. the memory limit here is just RAM
|
|
memoryTicks.value = [];
|
|
// we max system memory and current app memory for the case where the user configured the app on another server with more resources
|
|
const nearest256m = Math.ceil(Math.max(result.memory, tmpMemoryLimit) / (256*1024*1024)) * 256 * 1024 * 1024;
|
|
const startTick = props.app.manifest.memoryLimit || (256 * 1024 * 1024);
|
|
|
|
// code below ensure we atleast have 2 ticks to keep the slider usable
|
|
memoryTicks.value.push(startTick); // start tick
|
|
for (var i = startTick * 2; i < nearest256m; i *= 2) memoryTicks.value.push(i);
|
|
memoryTicks.value.push(nearest256m); // end tick
|
|
|
|
// we need to wait to set slider value until next DOM tick for firefox to pick it up!
|
|
await nextTick();
|
|
memoryLimit.value = tmpMemoryLimit;
|
|
currentMemoryLimit.value = tmpMemoryLimit;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<FormGroup>
|
|
<label for="memoryLimitInput">{{ $t('app.resources.memory.title') }} <sup><a href="https://docs.cloudron.io/apps/#memory-limit" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup> : <b>{{ prettyBinarySize(memoryLimit, 'Default (256 MiB)') }}</b></label>
|
|
<p>{{ $t('app.resources.memory.description') }}</p>
|
|
<input type="range" id="memoryLimitInput" v-model="memoryLimit" step="134217728" :min="memoryTicks[0]" :max="memoryTicks[memoryTicks.length-1]" list="memoryLimitTicks" />
|
|
<datalist id="memoryLimitTicks">
|
|
<option v-for="value of memoryTicks" :key="value" :value="value"></option>
|
|
</datalist>
|
|
</FormGroup>
|
|
<br/>
|
|
<Button @click="onSubmitMemoryLimit()" :loading="memoryLimitBusy" :disabled="memoryLimitBusy || (!app.error && memoryLimit === currentMemoryLimit) || (app.error && app.error.installationState !== ISTATES.PENDING_RESIZE) || app.taskId">{{ $t('app.resources.memory.resizeAction') }}</Button>
|
|
|
|
<hr style="margin-top: 20px"/>
|
|
|
|
<FormGroup>
|
|
<label for="cpuQuotaInput">{{ $t('app.resources.cpu.title') }} <sup><a href="https://docs.cloudron.io/apps/#cpu-limit" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup> : <b>{{ cpuQuota + ' %' }}</b></label>
|
|
<p>{{ $t('app.resources.cpu.description') }}</p>
|
|
<input type="range" id="cpuQuotaInput" v-model="cpuQuota" step="1" min="1" max="100" list="cpuQuotaTicks" />
|
|
<datalist id="cpuQuotaTicks">
|
|
<option value="25"></option>
|
|
<option value="50"></option>
|
|
<option value="75"></option>
|
|
</datalist>
|
|
</FormGroup>
|
|
<br/>
|
|
<Button @click="onSubmitCpuQuota()" :loading="cpuQuotaBusy" :disabled="cpuQuotaBusy || (!app.error && cpuQuota === currentCpuQuota) || (app.error && app.error.installationState !== ISTATES.PENDING_RESIZE) || app.taskId">{{ $t('app.resources.cpu.setAction') }}</Button>
|
|
|
|
<hr style="margin-top: 20px"/>
|
|
|
|
<form @submit.prevent="onSubmitDevices()" autocomplete="off">
|
|
<fieldset :disabled="devicesBusy || (!app.error && !devicesChanged) || (app.error && app.error.installationState !== ISTATES.PENDING_RECREATE_CONTAINER) || app.taskId">
|
|
<input style="display: none;" type="submit"/>
|
|
<FormGroup>
|
|
<label for="devicesInput">{{ $t('app.resources.devices.label') }} <sup><a href="https://docs.cloudron.io/apps/#devices" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
<TagInput id="devicesInput" v-model="devices" placeholder="/dev/ttyUSB0, /dev/hidraw0, ..."/>
|
|
<div class="text-danger" v-if="devicesError">{{ devicesError }}</div>
|
|
</FormGroup>
|
|
</fieldset>
|
|
</form>
|
|
<br/>
|
|
<Button @click="onSubmitDevices()" :loading="devicesBusy" :disabled="devicesBusy || (!app.error && !devicesChanged) || (app.error && app.error.installationState !== ISTATES.PENDING_RECREATE_CONTAINER) || app.taskId">{{ $t('main.dialog.save') }}</Button>
|
|
</div>
|
|
</template> |