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