Added some quick commands to WebUI backends page.

This commit is contained in:
Abdulmhsen B. A. A.
2024-06-26 18:56:53 +03:00
parent 89e40c0654
commit dabbf317a3
4 changed files with 92 additions and 52 deletions

View File

@@ -85,7 +85,7 @@
</div>
</div>
</div>
<footer class="card-footer">
<div class="card-footer">
<div class="card-footer-item" v-if="backend.export.enabled">
<NuxtLink class="button is-danger is-fullwidth"
:to="makeConsoleCommand(`state:export -v -s ${backend.name}`)">
@@ -100,8 +100,8 @@
<span>Run import now</span>
</NuxtLink>
</div>
</footer>
<footer class="card-footer">
</div>
<div class="card-footer">
<div class="card-footer-item">
<div class="field">
<input :id="backend.name+'_export'" type="checkbox" class="switch is-success"
@@ -125,6 +125,18 @@
<span class="is-hidden-tablet">Webhook</span>
</NuxtLink>
</div>
</div>
<footer class="card-footer">
<div class="card-footer-item">
<div class="select is-fullwidth">
<select v-model="selectedCommand" @change="forwardCommand(backend)">
<option value="" disabled>Quick commands</option>
<option v-for="(command, index) in usefulCommands" :key="`command_${index}`" :value="index">
{{ command.title }}
</option>
</select>
</div>
</div>
</footer>
</div>
</div>
@@ -151,7 +163,7 @@ import 'assets/css/bulma-switch.css'
import moment from 'moment'
import request from '~/utils/request.js'
import BackendAdd from '~/components/BackendAdd.vue'
import {copyText, makeConsoleCommand, notification, TOOLTIP_DATE_FORMAT} from '~/utils/index.js'
import {copyText, makeConsoleCommand, notification, r, TOOLTIP_DATE_FORMAT} from '~/utils/index.js'
import {useStorage} from "@vueuse/core";
import Message from "~/components/Message.vue";
@@ -162,6 +174,36 @@ const toggleForm = ref(false)
const api_url = useStorage('api_url', '')
const show_page_tips = useStorage('show_page_tips', true)
const isLoading = ref(false)
const selectedCommand = ref('')
const usefulCommands = [
{
title: "Force export local play state to this backend.",
command: 'state:export -fi -v -s {name}'
},
{
title: "Backup this backend play state.",
command: "state:backup -v -s {name} --file '{date}.manual_{name}.json'",
},
{
title: "Import backend metadata only.",
command: "state:import -v --metadata-only -s {name}",
},
]
const forwardCommand = async backend => {
if ('' === selectedCommand.value) {
return
}
const Index = selectedCommand.value
selectedCommand.value = ''
const util = {
date: moment().format('YYYYMMDD'),
}
await navigateTo(makeConsoleCommand(r(usefulCommands[Index].command, {...backend, ...util})));
}
const loadContent = async () => {
backends.value = []
@@ -191,5 +233,4 @@ const updateValue = async (backend, key, newValue) => {
backends.value[backends.value.findIndex(b => b.name === backend.name)] = await response.json()
}
</script>

View File

@@ -379,30 +379,23 @@ const makePagination = (current, last, delta = 5) => {
const strR = '-'.repeat(9 + `${last}`.length)
const left = current - delta,
right = current + delta + 1;
const left = current - delta, right = current + delta + 1;
for (let i = 1; i <= last; i++) {
if (i === 1 || i === last || (i >= left && i < right)) {
if (i === left && i > 2) {
pagination.push({
page: 0,
text: strR,
selected: false,
page: 0, text: strR, selected: false,
});
}
pagination.push({
page: i,
text: `Page #${i}`,
selected: i === current
page: i, text: `Page #${i}`, selected: i === current
});
if (i === right - 1 && i < last - 1) {
pagination.push({
page: 0,
text: strR,
selected: false,
page: 0, text: strR, selected: false,
});
}
}
@@ -412,6 +405,7 @@ const makePagination = (current, last, delta = 5) => {
}
export {
r,
ag_set,
ag,
humanFileSize,

View File

@@ -26,9 +26,9 @@ use Throwable;
#[Cli(command: self::ROUTE)]
class BackupCommand extends Command
{
public const ROUTE = 'state:backup';
public const string ROUTE = 'state:backup';
public const TASK_NAME = 'backup';
public const string TASK_NAME = 'backup';
/**
* Constructs a new instance of the class.
@@ -180,31 +180,35 @@ class BackupCommand extends Command
$type = strtolower(ag($backend, 'type', 'unknown'));
if ($isCustom && $input->getOption('exclude') === in_array($backendName, $selected, true)) {
$this->logger->info('SYSTEM: Ignoring [{backend}] as requested by select backends flag.', [
'backend' => $backendName,
$this->logger->info("SYSTEM: Ignoring '{backend}' as requested by [-s, --select-backend].", [
'backend' => $backendName
]);
continue;
}
if (true !== (bool)ag($backend, 'import.enabled')) {
$this->logger->info('SYSTEM: Ignoring [{backend}] imports are disabled for this backend.', [
'backend' => $backendName,
$this->logger->info("SYSTEM: Ignoring '{backend}' as the backend has import disabled.", [
'backend' => $backendName
]);
continue;
}
if (!isset($supported[$type])) {
$this->logger->error('SYSTEM: Ignoring [{backend}] because of the unexpected type [{type}].', [
'type' => $type,
'backend' => $backendName,
]);
$this->logger->error(
"SYSTEM: Ignoring '{backend}' due to unexpected type '{type}'. Expecting '{types}'.",
[
'type' => $type,
'backend' => $backendName,
'types' => implode(', ', array_keys($supported)),
]
);
continue;
}
if (null === ($url = ag($backend, 'url')) || false === isValidURL($url)) {
$this->logger->error('SYSTEM: Ignoring [{backend}] because of invalid URL.', [
'backend' => $backendName,
$this->logger->error("SYSTEM: Ignoring '{backend}' due to invalid URL. '{url}'.", [
'url' => $url ?? 'None',
'backend' => $backendName,
]);
continue;
}
@@ -221,7 +225,7 @@ class BackupCommand extends Command
}
if (true !== $input->getOption('no-enhance')) {
$this->logger->notice('SYSTEM: Preloading {mapper} data.', [
$this->logger->notice("SYSTEM: Preloading '{mapper}' data.", [
'mapper' => afterLast($this->mapper::class, '\\'),
'memory' => [
'now' => getMemoryUsage(),
@@ -229,10 +233,12 @@ class BackupCommand extends Command
],
]);
$start = microtime(true);
$this->mapper->loadData();
$this->logger->notice('SYSTEM: Preloading {mapper} data is complete.', [
$this->logger->notice("SYSTEM: Preloading '{mapper}' data completed in '{duration}s'.", [
'mapper' => afterLast($this->mapper::class, '\\'),
'duration' => round(microtime(true) - $start, 2),
'memory' => [
'now' => getMemoryUsage(),
'peak' => getPeakMemoryUsage(),
@@ -243,7 +249,7 @@ class BackupCommand extends Command
/** @var array<array-key,ResponseInterface> $queue */
$queue = [];
$this->logger->notice('Using WatchState Version - \'{version}\'.', ['version' => getAppVersion()]);
$this->logger->notice("Using WatchState version - '{version}'.", ['version' => getAppVersion()]);
foreach ($list as $name => &$backend) {
$opts = ag($backend, 'options', []);
@@ -275,8 +281,8 @@ class BackupCommand extends Command
$fileName = Config::get('path') . '/backup/{backend}.json';
}
if (count($list) <= 1 && null === ($file = $input->getOption('file'))) {
$fileName = $file;
if (count($list) <= 1 && null !== ($file = $input->getOption('file'))) {
$fileName = str_starts_with($file, '/') ? $file : Config::get('path') . '/backup' . '/' . $file;
}
if (false === $input->getOption('dry-run')) {
@@ -289,6 +295,11 @@ class BackupCommand extends Command
touch($fileName);
}
$this->logger->notice("SYSTEM: '{backend}' is using '{file}' as backup target.", [
'file' => realpath($fileName),
'backend' => $name,
]);
$backend['fp'] = new Stream($fileName, 'wb+');
$backend['fp']->write('[');
}
@@ -308,12 +319,9 @@ class BackupCommand extends Command
unset($backend);
$start = makeDate();
$this->logger->notice('SYSTEM: Waiting on [{total}] requests.', [
$start = microtime(true);
$this->logger->notice("SYSTEM: Waiting on '{total}' requests.", [
'total' => number_format(count($queue)),
'time' => [
'start' => $start,
],
'memory' => [
'now' => getMemoryUsage(),
'peak' => getPeakMemoryUsage(),
@@ -347,13 +355,8 @@ class BackupCommand extends Command
}
}
$end = makeDate();
$this->logger->notice('SYSTEM: Operation is finished.', [
'time' => [
'start' => $start,
'end' => $end,
'duration' => $end->getTimestamp() - $start->getTimestamp(),
],
$this->logger->notice("SYSTEM: Backup operation finished in '{duration}s'.", [
'duration' => round(microtime(true) - $start, 2),
'memory' => [
'now' => getMemoryUsage(),
'peak' => getPeakMemoryUsage(),

View File

@@ -42,21 +42,23 @@ final class Stream implements StreamInterface, Stringable
$resource = $stream;
if (is_string($stream)) {
set_error_handler(function ($e) use (&$error) {
if ($e !== E_WARNING) {
set_error_handler(function ($severity, $message, $file, $line) use (&$error) {
if ($severity !== E_WARNING) {
return;
}
$error = $e;
$error = r("Stream: Failed to open stream. '{message}' at '{file}:{line}'", [
'message' => trim($message),
'file' => $file,
'line' => $line
]);
});
$resource = fopen($stream, $mode);
restore_error_handler();
}
if ($error) {
throw new RuntimeException(r('Stream: Invalid stream reference provided. Error {error}.', [
'error' => ag(error_get_last() ?? [], 'message', '??'),
]));
if (null !== $error) {
throw new RuntimeException($error);
}
if (!self::isValidStreamResourceType($resource)) {