feat: add UI for CRON Scheduler

This commit is contained in:
2025-12-06 23:49:17 +03:00
parent 65973d2d79
commit 7372b9c330
5 changed files with 155 additions and 0 deletions

View File

@@ -45,6 +45,10 @@
<li :class="{active: route.name === 'logs'}">
<RouterLink :to="{name: 'logs'}">Журнал событий</RouterLink>
</li>
<li :class="{active: route.name === 'cron'}">
<RouterLink :to="{name: 'cron'}">CRON</RouterLink>
</li>
</ul>
<section class="form-horizontal tab-content">

View File

@@ -10,6 +10,7 @@ import LogsView from "@/views/LogsView.vue";
import FormBuilderView from "@/views/FormBuilderView.vue";
import CustomersView from "@/views/CustomersView.vue";
import TeleCartPulseView from "@/views/TeleCartPulseView.vue";
import CronView from "@/views/CronView.vue";
const router = createRouter({
history: createMemoryHistory(),
@@ -25,6 +26,7 @@ const router = createRouter({
{path: '/store', name: 'store', component: StoreView},
{path: '/telegram', name: 'telegram', component: TelegramView},
{path: '/texts', name: 'texts', component: TextsView},
{path: '/cron', name: 'cron', component: CronView},
],
});

View File

@@ -78,6 +78,10 @@ export const useSettingsStore = defineStore('settings', {
pulse: {
api_key: '',
},
cron: {
mode: 'disabled',
},
},
}),

View File

@@ -0,0 +1,108 @@
<template>
<SettingsItem label="Режим работы планировщика">
<template #default>
<SelectButton
v-model="settings.items.cron.mode"
:options="cronModes"
optionLabel="label"
optionValue="value"
:allowEmpty="false"
/>
</template>
<template #help>
<div v-if="settings.items.cron.mode === 'disabled'" class="tw:text-red-600 tw:font-bold">
Все фоновые задачи отключены.
</div>
<div v-else>
Рекомендуемый режим. Использует системный планировщик задач Linux.
</div>
<div class="tw:mt-2">
<p>
<strong>Системный CRON (рекомендуется):</strong> Стабильное выполнение задач по расписанию, независимо от
посещаемости сайта. Добавьте команду в CRON для автоматического выполнения каждые 5 минут.
</p>
<p>
<strong>Выключено:</strong> Все фоновые задачи отключены. Планировщик не будет выполнять никаких задач.
</p>
</div>
</template>
</SettingsItem>
<SettingsItem label="Последний запуск CRON">
<template #default>
<div v-if="lastRunDate" class="tw:text-green-600 tw:font-bold tw:py-2">
{{ lastRunDate }}
</div>
<div v-else class="tw:text-gray-500 tw:py-2">
Еще не запускался
</div>
</template>
<template #help>
Время последнего успешного выполнения планировщика задач.
</template>
</SettingsItem>
<SettingsItem
v-if="settings.items.cron.mode === 'system'"
label="Команда для CRON"
>
<template #default>
<InputGroup>
<Button icon="fa fa-copy" severity="secondary" @click="copyToClipboard(cronCommand)"/>
<InputText readonly :model-value="cronCommand" class="tw:w-full"/>
</InputGroup>
</template>
<template #help>
Добавьте эту строку в конфигурацию CRON на вашем сервере (обычно `crontab -e`), чтобы запускать планировщик каждые
5 минут.
</template>
</SettingsItem>
</template>
<script setup>
import {computed} from 'vue';
import {useSettingsStore} from "@/stores/settings.js";
import SettingsItem from "@/components/SettingsItem.vue";
import SelectButton from "primevue/selectbutton";
import InputText from "primevue/inputtext";
import Button from "primevue/button";
import InputGroup from 'primevue/inputgroup';
import {toastBus} from "@/utils/toastHelper.js";
const settings = useSettingsStore();
const cronModes = [
{value: 'system', label: 'Системный CRON (Linux)'},
{value: 'disabled', label: 'Выключено'},
];
const cronCommand = computed(() => {
const cliPath = settings.items.cron?.cli_path;
return cliPath
? `*/5 * * * * php ${cliPath} schedule:run`
: 'Путь не определен. Проверьте конфигурацию модуля.';
});
const lastRunDate = computed(() => settings.items.cron?.last_run);
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
toastBus.emit('show', {
severity: 'success',
summary: 'Скопировано',
detail: 'Команда скопирована в буфер обмена',
life: 2000,
});
} catch (err) {
toastBus.emit('show', {
severity: 'error',
summary: 'Ошибка',
detail: 'Не удалось скопировать текст',
life: 2000,
});
}
}
</script>