feat: maintenance tasks, logs
- add interval for periodic maintenance tasks - add cache prune periodic task - use rotating handler for monolog - update UI logs component - correctly reset cache from admin - increase cache timeout for tg data - fix UI errors in admin
This commit is contained in:
@@ -1,16 +1,13 @@
|
||||
<template>
|
||||
<textarea v-text="rows" rows="40" class="tw:w-full"/>
|
||||
<textarea v-text="logs.lines" rows="40" class="tw:w-full" readonly/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref} from "vue";
|
||||
import {apiGet} from "@/utils/http.js";
|
||||
import {onMounted} from "vue";
|
||||
import {useLogsStore} from "@/stores/logs.js";
|
||||
|
||||
const rows = ref('');
|
||||
onMounted(async () => {
|
||||
const response = await apiGet('getLogs');
|
||||
rows.value = response.data;
|
||||
});
|
||||
const logs = useLogsStore();
|
||||
onMounted(async () => logs.fetchLogsFromServer());
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -65,27 +65,30 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw:mt-6 tw:lg:mt-0 tw:flex tw:items-center tw:gap-4">
|
||||
<ButtonGroup>
|
||||
<ResetCacheBtn/>
|
||||
</ButtonGroup>
|
||||
<div class="btn-group">
|
||||
<a
|
||||
class="btn btn-primary"
|
||||
:class="{'disabled': (tgMe?.result?.has_main_web_app !== true)}"
|
||||
rounded
|
||||
:href="`https://t.me/${tgMe?.result?.username}?startapp`"
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
icon="fa fa-play"
|
||||
v-tooltip.top="(tgMe?.result?.has_main_web_app !== true) ? 'Вы не привязали Telegram Mini App к боту.' : 'Открыть Telegram магазин'"
|
||||
as="a"
|
||||
target="_blank"
|
||||
:title="(tgMe?.result?.has_main_web_app !== true) ? 'Вы не привязали Telegram Mini App к боту.' : 'Открыть Telegram магазин'"
|
||||
>
|
||||
<i class="fa fa-play"></i>
|
||||
</a>
|
||||
<a class="btn btn-default" target="_blank" href="https://telecart-labs.github.io/docs/" title="Документация по модулю TeleCart">
|
||||
<i class="fa fa-book"></i>
|
||||
</a>
|
||||
<a class="btn btn-default" target="_blank" href="https://t.me/ocstore3" title="Официальная Telegram группа модуля TeleCart">
|
||||
<i class="fa fa-group"></i>
|
||||
</a>
|
||||
</div>
|
||||
:href="`https://t.me/${tgMe?.result?.username}?startapp`"
|
||||
/>
|
||||
<Button
|
||||
icon="fa fa-book"
|
||||
v-tooltip.top="'Документация по модулю TeleCart'"
|
||||
as="a"
|
||||
target="_blank"
|
||||
href="https://telecart-labs.github.io/docs/"
|
||||
/>
|
||||
<Button
|
||||
icon="fa fa-group"
|
||||
v-tooltip.top="'Официальная Telegram группа модуля TeleCart'"
|
||||
as="a"
|
||||
target="_blank"
|
||||
href="https://t.me/ocstore3"
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,6 +101,7 @@ import {onMounted, ref} from "vue";
|
||||
import OcImagePicker from "@/components/OcImagePicker.vue";
|
||||
import {apiGet} from "@/utils/http.js";
|
||||
import ResetCacheBtn from "@/components/Form/ResetCacheBtn.vue";
|
||||
import {Button, ButtonGroup} from "primevue";
|
||||
|
||||
const settings = useSettingsStore();
|
||||
const stats = useStatsStore();
|
||||
|
||||
16
frontend/admin/src/stores/logs.js
Normal file
16
frontend/admin/src/stores/logs.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {apiGet} from "@/utils/http.js";
|
||||
|
||||
export const useLogsStore = defineStore('logs', {
|
||||
state: () => ({
|
||||
lines: '',
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async fetchLogsFromServer() {
|
||||
if (this.lines) return;
|
||||
const response = await apiGet('getLogs');
|
||||
this.lines = response.data;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use Bastion\ApplicationFactory;
|
||||
use Cart\User;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
use Monolog\Logger;
|
||||
use Openguru\OpenCartFramework\Application;
|
||||
use Openguru\OpenCartFramework\Http\Response as HttpResponse;
|
||||
@@ -252,7 +252,7 @@ class ControllerExtensionModuleTgshop extends Controller
|
||||
'language_id' => (int) $this->config->get('config_language_id'),
|
||||
],
|
||||
'logs' => [
|
||||
'path' => DIR_LOGS . '/telecart.log',
|
||||
'path' => DIR_LOGS,
|
||||
],
|
||||
'database' => [
|
||||
'host' => DB_HOSTNAME,
|
||||
@@ -298,10 +298,9 @@ class ControllerExtensionModuleTgshop extends Controller
|
||||
{
|
||||
$log = new Logger('TeleCart_Admin');
|
||||
$log->pushHandler(
|
||||
new StreamHandler(
|
||||
DIR_LOGS . '/telecart.log',
|
||||
$debug ? Logger::DEBUG : Logger::INFO,
|
||||
)
|
||||
new RotatingFileHandler(
|
||||
DIR_LOGS . '/telecart.log', 14, $debug ? Logger::DEBUG : Logger::INFO
|
||||
),
|
||||
);
|
||||
|
||||
return $log;
|
||||
|
||||
@@ -5,7 +5,7 @@ use App\ApplicationFactory;
|
||||
use Cart\Cart;
|
||||
use Cart\Currency;
|
||||
use Cart\Tax;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
use Monolog\Logger;
|
||||
use Openguru\OpenCartFramework\Http\Response as HttpResponse;
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageTool;
|
||||
@@ -154,10 +154,7 @@ class ControllerExtensionTgshopHandle extends Controller
|
||||
{
|
||||
$log = new Logger($app);
|
||||
$log->pushHandler(
|
||||
new StreamHandler(
|
||||
DIR_LOGS . '/telecart.log',
|
||||
$appDebug ? Logger::DEBUG : Logger::INFO,
|
||||
)
|
||||
new RotatingFileHandler(DIR_LOGS . '/telecart.log', 14, $appDebug ? Logger::DEBUG : Logger::INFO),
|
||||
);
|
||||
|
||||
return $log;
|
||||
|
||||
@@ -19,9 +19,15 @@ class LogsHandler
|
||||
|
||||
public function getLogs(): JsonResponse
|
||||
{
|
||||
$logsPath = $this->settings->get('logs.path');
|
||||
$data = [];
|
||||
|
||||
$data = implode(PHP_EOL, $this->readLastLogsRows($logsPath));
|
||||
$logsPath = $this->findLastLogsFileInDir(
|
||||
$this->settings->get('logs.path')
|
||||
);
|
||||
|
||||
if ($logsPath) {
|
||||
$data = implode(PHP_EOL, $this->readLastLogsRows($logsPath, 100));
|
||||
}
|
||||
|
||||
return new JsonResponse(compact('data'));
|
||||
}
|
||||
@@ -54,6 +60,13 @@ class LogsHandler
|
||||
|
||||
$linesArray = explode("\n", $chunk);
|
||||
|
||||
return array_reverse(array_slice($linesArray, -$lines));
|
||||
return array_slice($linesArray, -$lines);
|
||||
}
|
||||
|
||||
private function findLastLogsFileInDir(string $dir): ?string
|
||||
{
|
||||
$files = glob($dir . '/telecart-*.log');
|
||||
|
||||
return $files ? end($files) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Http\Response;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class SettingsHandler
|
||||
{
|
||||
@@ -19,17 +20,20 @@ class SettingsHandler
|
||||
private Settings $settings;
|
||||
private SettingsService $settingsUpdateService;
|
||||
private CacheInterface $cache;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
BotTokenConfigurator $botTokenConfigurator,
|
||||
Settings $settings,
|
||||
SettingsService $settingsUpdateService,
|
||||
CacheInterface $cache
|
||||
CacheInterface $cache,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->botTokenConfigurator = $botTokenConfigurator;
|
||||
$this->settings = $settings;
|
||||
$this->settingsUpdateService = $settingsUpdateService;
|
||||
$this->cache = $cache;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function configureBotToken(Request $request): JsonResponse
|
||||
@@ -78,7 +82,9 @@ class SettingsHandler
|
||||
|
||||
public function resetCache(): JsonResponse
|
||||
{
|
||||
$this->cache->prune();
|
||||
$this->cache->clear();
|
||||
|
||||
$this->logger->info('Cache cleared manually.');
|
||||
|
||||
return new JsonResponse([], Response::HTTP_ACCEPTED);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class TelegramHandler
|
||||
|
||||
if (! $data) {
|
||||
$data = $this->telegramService->exec('getMe');
|
||||
$this->cache->set('tg_me_info', $data, 60);
|
||||
$this->cache->set('tg_me_info', $data, 60 * 5);
|
||||
}
|
||||
|
||||
return new JsonResponse(compact('data'));
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Bastion\Tasks;
|
||||
|
||||
use DateInterval;
|
||||
use Openguru\OpenCartFramework\Cache\CacheInterface;
|
||||
use Openguru\OpenCartFramework\MaintenanceTasks\BaseMaintenanceTask;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class CachePruneTask extends BaseMaintenanceTask
|
||||
{
|
||||
private CacheInterface $cache;
|
||||
|
||||
public function __construct(LoggerInterface $logger, CacheInterface $cache)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$this->cache->prune();
|
||||
}
|
||||
|
||||
public function interval(): ?DateInterval
|
||||
{
|
||||
return new DateInterval('P1D');
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace Bastion\Tasks;
|
||||
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use JsonException;
|
||||
use Openguru\OpenCartFramework\MaintenanceTasks\BaseMaintenanceTask;
|
||||
use RuntimeException;
|
||||
|
||||
class CleanUpOldAssets extends BaseMaintenanceTask
|
||||
class CleanUpOldAssetsTask extends BaseMaintenanceTask
|
||||
{
|
||||
public function handle(): void
|
||||
{
|
||||
@@ -61,8 +62,12 @@ class CleanUpOldAssets extends BaseMaintenanceTask
|
||||
} catch (JsonException $e) {
|
||||
$this->logger->error('Ошибка декодирования файла manifest.json: ' . $e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('Ошибка удаления старых assets: ' . $e->getMessage());
|
||||
$this->logger->logException($e);
|
||||
$this->logger->error('Ошибка удаления старых assets: ' . $e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
|
||||
public function interval(): ?DateInterval
|
||||
{
|
||||
return new DateInterval('PT1H');
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Bastion\Tasks\CleanUpOldAssets;
|
||||
use Bastion\Tasks\CachePruneTask;
|
||||
use Bastion\Tasks\CleanUpOldAssetsTask;
|
||||
|
||||
return [
|
||||
'tasks' => [
|
||||
CleanUpOldAssets::class,
|
||||
CleanUpOldAssetsTask::class,
|
||||
CachePruneTask::class,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Openguru\OpenCartFramework\MaintenanceTasks;
|
||||
|
||||
use DateInterval;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
abstract class BaseMaintenanceTask implements MaintenanceTaskInterface
|
||||
@@ -12,4 +13,9 @@ abstract class BaseMaintenanceTask implements MaintenanceTaskInterface
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function interval(): ?DateInterval
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace Openguru\OpenCartFramework\MaintenanceTasks;
|
||||
|
||||
use DateInterval;
|
||||
|
||||
interface MaintenanceTaskInterface
|
||||
{
|
||||
public function handle(): void;
|
||||
|
||||
public function interval(): ?DateInterval;
|
||||
}
|
||||
|
||||
@@ -2,22 +2,27 @@
|
||||
|
||||
namespace Openguru\OpenCartFramework\MaintenanceTasks;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Carbon\Carbon;
|
||||
use Openguru\OpenCartFramework\Cache\CacheInterface;
|
||||
use Openguru\OpenCartFramework\Migrations\MigratorService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class MaintenanceTasksService
|
||||
{
|
||||
private MigratorService $migrator;
|
||||
private LoggerInterface $logger;
|
||||
private CacheInterface $cache;
|
||||
private array $maintenanceTasks;
|
||||
|
||||
public function __construct(
|
||||
MigratorService $migrator,
|
||||
LoggerInterface $logger,
|
||||
CacheInterface $cache,
|
||||
array $maintenanceTasks = []
|
||||
) {
|
||||
$this->migrator = $migrator;
|
||||
$this->logger = $logger;
|
||||
$this->cache = $cache;
|
||||
$this->maintenanceTasks = $maintenanceTasks;
|
||||
}
|
||||
|
||||
@@ -30,9 +35,11 @@ class MaintenanceTasksService
|
||||
private function performMaintenanceTasks(): void
|
||||
{
|
||||
foreach ($this->maintenanceTasks as $maintenanceTask) {
|
||||
$startTime = microtime(true);
|
||||
/** @var MaintenanceTaskInterface $instance */
|
||||
$instance = $maintenanceTask();
|
||||
|
||||
if ($this->shouldPerformTask($instance)) {
|
||||
$startTime = microtime(true);
|
||||
$instance->handle();
|
||||
$endTime = microtime(true);
|
||||
$this->logger->info(
|
||||
@@ -45,3 +52,25 @@ class MaintenanceTasksService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldPerformTask(MaintenanceTaskInterface $task): bool
|
||||
{
|
||||
$cacheKey = 'maintenance_tasks.' . md5(get_class($task));
|
||||
$lastExecuted = $this->cache->get($cacheKey);
|
||||
|
||||
if (!$lastExecuted) {
|
||||
$this->cache->set($cacheKey, Carbon::now());
|
||||
return true;
|
||||
}
|
||||
|
||||
$last = Carbon::parse($lastExecuted);
|
||||
$next = $last->copy()->add($task->interval());
|
||||
|
||||
if (Carbon::now()->gte($next)) {
|
||||
$this->cache->set($cacheKey, Carbon::now());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Openguru\OpenCartFramework\MaintenanceTasks;
|
||||
|
||||
use Openguru\OpenCartFramework\Cache\CacheInterface;
|
||||
use Openguru\OpenCartFramework\Container\Container;
|
||||
use Openguru\OpenCartFramework\Container\ServiceProvider;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -19,6 +20,7 @@ class MaintenanceTasksServiceProvider extends ServiceProvider
|
||||
return new MaintenanceTasksService(
|
||||
$container->get(MigratorService::class),
|
||||
$container->get(LoggerInterface::class),
|
||||
$container->get(CacheInterface::class),
|
||||
$tasks,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user