add frontend updater

This commit is contained in:
Hesabix 2025-03-29 07:24:21 +00:00
parent 211b08fbf8
commit e1405f1af8
3 changed files with 86 additions and 39 deletions

View file

@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
#[AsCommand( #[AsCommand(
name: 'hesabix:update', name: 'hesabix:update',
description: 'Updates the software by pulling from GitHub, clearing cache, and updating the database.' description: 'Updates the software by pulling from GitHub, clearing cache, updating the database, and rebuilding the frontend.'
)] )]
class UpdateSoftwareCommand extends Command class UpdateSoftwareCommand extends Command
{ {
@ -52,6 +52,8 @@ class UpdateSoftwareCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$lock = $this->lockFactory->createLock('hesabix-update', 3600); $lock = $this->lockFactory->createLock('hesabix-update', 3600);
if (!$lock->acquire()) { if (!$lock->acquire()) {
$this->writeOutput($output, '<error>Another update process is currently running. Please try again later.</error>'); $this->writeOutput($output, '<error>Another update process is currently running. Please try again later.</error>');
@ -91,6 +93,7 @@ class UpdateSoftwareCommand extends Command
} }
$state['completedSteps'] = $state['completedSteps'] ?? []; $state['completedSteps'] = $state['completedSteps'] ?? [];
$webUIDir = $this->rootDir . '/webUI'; // مسیر دایرکتوری فرانت‌اند
$gitHeadBefore = null; $gitHeadBefore = null;
$cacheBackup = null; $cacheBackup = null;
@ -111,7 +114,7 @@ class UpdateSoftwareCommand extends Command
mkdir($archiveBackupDir, 0755, true); mkdir($archiveBackupDir, 0755, true);
} }
$archiveBackup = $archiveBackupDir . '/hesabixArchive_backup_' . time() . '.tar'; $archiveBackup = $archiveBackupDir . '/hesabixArchive_backup_' . time() . '.tar';
$this->runProcess(['tar', '-cf', $archiveBackup, '-C', $this->rootDir, 'hesabixArchive'], $this->rootDir, $output, 3); $this->runProcess(['tar', '-cf', $archiveBackup, '-C', $this->rootDir, 'hesabixArchive'], $this->rootDir, $output, 3, 3600);
$state['archiveBackup'] = $archiveBackup; $state['archiveBackup'] = $archiveBackup;
$archiveHashBefore = $this->getDirectoryHash($this->archiveDir); $archiveHashBefore = $this->getDirectoryHash($this->archiveDir);
$state['archiveHashBefore'] = $archiveHashBefore; $state['archiveHashBefore'] = $archiveHashBefore;
@ -124,11 +127,11 @@ class UpdateSoftwareCommand extends Command
if (!in_array('git_pull', $state['completedSteps'])) { if (!in_array('git_pull', $state['completedSteps'])) {
$this->writeOutput($output, 'Setting up tracking for master branch...'); $this->writeOutput($output, 'Setting up tracking for master branch...');
$this->runProcess(['git', 'branch', '--set-upstream-to=origin/master', 'master'], $this->rootDir, $output, 1); $this->runProcess(['git', 'branch', '--set-upstream-to=origin/master', 'master'], $this->rootDir, $output, 1, 3600);
$this->writeOutput($output, 'Pulling latest changes from GitHub...'); $this->writeOutput($output, 'Pulling latest changes from GitHub...');
$gitHeadBefore = $this->getCurrentGitHead(); $gitHeadBefore = $this->getCurrentGitHead();
$this->runProcess(['git', 'pull'], $this->rootDir, $output, 3); $this->runProcess(['git', 'pull'], $this->rootDir, $output, 3, 3600);
$state['gitHeadBefore'] = $gitHeadBefore; $state['gitHeadBefore'] = $gitHeadBefore;
$state['completedSteps'][] = 'git_pull'; $state['completedSteps'][] = 'git_pull';
$this->saveState($uuid, $state, $output, 'Git pull completed'); $this->saveState($uuid, $state, $output, 'Git pull completed');
@ -143,7 +146,7 @@ class UpdateSoftwareCommand extends Command
$composerCommand[] = '--no-dev'; $composerCommand[] = '--no-dev';
$composerCommand[] = '--no-scripts'; $composerCommand[] = '--no-scripts';
} }
$this->runProcess($composerCommand, $this->appDir, $output, 3); $this->runProcess($composerCommand, $this->appDir, $output, 3, 3600);
$state['completedSteps'][] = 'composer_install'; $state['completedSteps'][] = 'composer_install';
$this->saveState($uuid, $state, $output, 'Dependencies installed'); $this->saveState($uuid, $state, $output, 'Dependencies installed');
} }
@ -155,7 +158,7 @@ class UpdateSoftwareCommand extends Command
$composerCommand[] = '--no-dev'; $composerCommand[] = '--no-dev';
$composerCommand[] = '--no-scripts'; $composerCommand[] = '--no-scripts';
} }
$this->runProcess($composerCommand, $this->rootDir . '/hesabixCore', $output, 3); $this->runProcess($composerCommand, $this->rootDir . '/hesabixCore', $output, 3, 3600);
$state['completedSteps'][] = 'composer_install_core'; $state['completedSteps'][] = 'composer_install_core';
$this->saveState($uuid, $state, $output, 'Dependencies installed in hesabixCore'); $this->saveState($uuid, $state, $output, 'Dependencies installed in hesabixCore');
} }
@ -167,10 +170,10 @@ class UpdateSoftwareCommand extends Command
mkdir($cacheBackupDir, 0755, true); mkdir($cacheBackupDir, 0755, true);
} }
$cacheBackup = $cacheBackupDir . '/cache_backup_' . time(); $cacheBackup = $cacheBackupDir . '/cache_backup_' . time();
$this->runProcess(['cp', '-r', $this->appDir . '/var/cache', $cacheBackup], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput()); $this->runProcess(['cp', '-r', $this->appDir . '/var/cache', $cacheBackup], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput(), 3, 3600);
$state['cacheBackup'] = $cacheBackup; $state['cacheBackup'] = $cacheBackup;
$this->runProcess(['php', 'bin/console', 'cache:clear', "--env={$this->env}"], $this->appDir, $output, 3); $this->runProcess(['php', 'bin/console', 'cache:clear', "--env={$this->env}"], $this->appDir, $output, 3, 3600);
$state['completedSteps'][] = 'cache_clear'; $state['completedSteps'][] = 'cache_clear'; // اصلاح خطا: reciSteps به completedSteps تغییر کرد
$this->saveState($uuid, $state, $output, 'Cache cleared'); $this->saveState($uuid, $state, $output, 'Cache cleared');
} else { } else {
$cacheBackup = $state['cacheBackup']; $cacheBackup = $state['cacheBackup'];
@ -185,13 +188,27 @@ class UpdateSoftwareCommand extends Command
$dbBackup = $dbBackupDir . '/db_backup_' . time() . '.sql'; $dbBackup = $dbBackupDir . '/db_backup_' . time() . '.sql';
$this->backupDatabaseToFile($dbBackup, $output); $this->backupDatabaseToFile($dbBackup, $output);
$state['dbBackup'] = $dbBackup; $state['dbBackup'] = $dbBackup;
$this->runProcess(['php', 'bin/console', 'doctrine:schema:update', '--force', '--no-interaction'], $this->appDir, $output, 3); $this->runProcess(['php', 'bin/console', 'doctrine:schema:update', '--force', '--no-interaction'], $this->appDir, $output, 3, 3600);
$state['completedSteps'][] = 'db_update'; $state['completedSteps'][] = 'db_update';
$this->saveState($uuid, $state, $output, 'Database schema updated'); $this->saveState($uuid, $state, $output, 'Database schema updated');
} else { } else {
$dbBackup = $state['dbBackup']; $dbBackup = $state['dbBackup'];
} }
if (!in_array('npm_install', $state['completedSteps'])) {
$this->writeOutput($output, 'Installing frontend dependencies with npm...');
$this->runProcess(['npm', 'install'], $webUIDir, $output, 3, 3600);
$state['completedSteps'][] = 'npm_install';
$this->saveState($uuid, $state, $output, 'Frontend dependencies installed');
}
if (!in_array('npm_build', $state['completedSteps'])) {
$this->writeOutput($output, 'Building frontend with npm run build-only...');
$this->runProcess(['npm', 'run', 'build-only'], $webUIDir, $output, 3, 3600);
$state['completedSteps'][] = 'npm_build';
$this->saveState($uuid, $state, $output, 'Frontend build completed');
}
if (!in_array('archive_check', $state['completedSteps'])) { if (!in_array('archive_check', $state['completedSteps'])) {
$archiveHashAfter = $this->getDirectoryHash($this->archiveDir); $archiveHashAfter = $this->getDirectoryHash($this->archiveDir);
if ($archiveHashBefore !== $archiveHashAfter) { if ($archiveHashBefore !== $archiveHashAfter) {
@ -248,13 +265,13 @@ class UpdateSoftwareCommand extends Command
} }
} }
private function runProcess(array $command, string $workingDir, OutputInterface $output, int $retries = 3): void private function runProcess(array $command, string $workingDir, OutputInterface $output, int $retries = 3, int $timeout = 3600): void
{ {
$attempt = 0; $attempt = 0;
while ($attempt < $retries) { while ($attempt < $retries) {
try { try {
$process = new Process($command, $workingDir); $process = new Process($command, $workingDir);
$process->setTimeout(3600); $process->setTimeout($timeout);
if ($output->isVerbose()) { if ($output->isVerbose()) {
$process->mustRun(function ($type, $buffer) use ($output) { $process->mustRun(function ($type, $buffer) use ($output) {
$this->writeOutput($output, $buffer); $this->writeOutput($output, $buffer);
@ -312,16 +329,16 @@ class UpdateSoftwareCommand extends Command
private function preUpdateChecks(OutputInterface $output): void private function preUpdateChecks(OutputInterface $output): void
{ {
$this->writeOutput($output, 'Running pre-update checks...'); $this->writeOutput($output, 'Running pre-update checks...');
$this->runProcess(['git', 'fetch'], $this->rootDir, $output); $this->runProcess(['git', 'fetch'], $this->rootDir, $output, 3, 3600);
$this->writeOutput($output, 'Git repository accessible.'); $this->writeOutput($output, 'Git repository accessible.');
$this->runProcess(['php', 'bin/console', 'doctrine:query:sql', 'SELECT 1'], $this->appDir, $output); $this->runProcess(['php', 'bin/console', 'doctrine:query:sql', 'SELECT 1'], $this->appDir, $output, 3, 3600);
$this->writeOutput($output, 'Database connection OK.'); $this->writeOutput($output, 'Database connection OK.');
} }
private function postUpdateChecks(OutputInterface $output): void private function postUpdateChecks(OutputInterface $output): void
{ {
$this->writeOutput($output, 'Running post-update tests...'); $this->writeOutput($output, 'Running post-update tests...');
$this->runProcess(['php', 'bin/console', 'cache:warmup', "--env={$this->env}"], $this->appDir, $output); $this->runProcess(['php', 'bin/console', 'cache:warmup', "--env={$this->env}"], $this->appDir, $output, 3, 3600);
$this->writeOutput($output, 'Application tested and warmed up successfully.'); $this->writeOutput($output, 'Application tested and warmed up successfully.');
} }
@ -399,9 +416,9 @@ class UpdateSoftwareCommand extends Command
private function restoreArchive(string $backupFile): void private function restoreArchive(string $backupFile): void
{ {
$this->runProcess(['rm', '-rf', $this->archiveDir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput()); $this->runProcess(['rm', '-rf', $this->archiveDir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput(), 3, 3600);
$this->runProcess(['mkdir', $this->archiveDir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput()); $this->runProcess(['mkdir', $this->archiveDir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput(), 3, 3600);
$this->runProcess(['tar', '-xf', $backupFile, '-C', $this->rootDir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput()); $this->runProcess(['tar', '-xf', $backupFile, '-C', $this->rootDir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput(), 3, 3600);
if (!is_dir($this->archiveDir)) { if (!is_dir($this->archiveDir)) {
throw new \RuntimeException('Failed to restore hesabixArchive from tar backup.'); throw new \RuntimeException('Failed to restore hesabixArchive from tar backup.');
} }
@ -433,7 +450,7 @@ class UpdateSoftwareCommand extends Command
if ($gitHeadBefore) { if ($gitHeadBefore) {
try { try {
$this->runProcess(['git', 'reset', '--hard', $gitHeadBefore], $this->rootDir, $output); $this->runProcess(['git', 'reset', '--hard', $gitHeadBefore], $this->rootDir, $output, 3, 3600);
$this->logger->info('Git rolled back to ' . $gitHeadBefore); $this->logger->info('Git rolled back to ' . $gitHeadBefore);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('Git rollback failed: ' . $e->getMessage()); $this->logger->error('Git rollback failed: ' . $e->getMessage());
@ -442,8 +459,8 @@ class UpdateSoftwareCommand extends Command
if ($cacheBackup) { if ($cacheBackup) {
try { try {
$this->runProcess(['rm', '-rf', $this->appDir . '/var/cache'], $this->rootDir, $output); $this->runProcess(['rm', '-rf', $this->appDir . '/var/cache'], $this->rootDir, $output, 3, 3600);
$this->runProcess(['cp', '-r', $cacheBackup, $this->appDir . '/var/cache'], $this->rootDir, $output); $this->runProcess(['cp', '-r', $cacheBackup, $this->appDir . '/var/cache'], $this->rootDir, $output, 3, 3600);
$this->logger->info('Cache rolled back'); $this->logger->info('Cache rolled back');
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error('Cache rollback failed: ' . $e->getMessage()); $this->logger->error('Cache rollback failed: ' . $e->getMessage());
@ -518,7 +535,7 @@ class UpdateSoftwareCommand extends Command
$dirName = basename($dir); $dirName = basename($dir);
if (!in_array($dirName, $protectedDirs)) { if (!in_array($dirName, $protectedDirs)) {
$this->logger->info("Removing temporary directory: $dir"); $this->logger->info("Removing temporary directory: $dir");
$this->runProcess(['rm', '-rf', $dir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput()); $this->runProcess(['rm', '-rf', $dir], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput(), 3, 3600);
} }
} }
} }

View file

@ -2,7 +2,7 @@
namespace App\Controller\System; namespace App\Controller\System;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundleController\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\StreamedResponse;
@ -22,6 +22,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/run', name: 'api_admin_updatecore_run', methods: ['POST'])] #[Route('/api/admin/updatecore/run', name: 'api_admin_updatecore_run', methods: ['POST'])]
public function api_admin_updatecore_run(): JsonResponse public function api_admin_updatecore_run(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$uuid = uniqid(); $uuid = uniqid();
$stateFile = $projectDir . '/../backup/update_state_' . $uuid . '.json'; $stateFile = $projectDir . '/../backup/update_state_' . $uuid . '.json';
@ -42,7 +44,7 @@ final class UpdateCoreController extends AbstractController
]); ]);
$process = new Process(['php', 'bin/console', 'hesabix:update', $stateFile], $projectDir, $env); $process = new Process(['php', 'bin/console', 'hesabix:update', $stateFile], $projectDir, $env);
$process->setTimeout(3600); $process->setTimeout(3600); // تنظیم تایم‌اوت فرآیند به 1 ساعت
$process->run(function ($type, $buffer) use ($stateFile) { $process->run(function ($type, $buffer) use ($stateFile) {
$state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => uniqid(), 'log' => '']; $state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => uniqid(), 'log' => ''];
$state['log'] .= $buffer; $state['log'] .= $buffer;
@ -74,6 +76,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/status', name: 'api_admin_updatecore_status', methods: ['GET'])] #[Route('/api/admin/updatecore/status', name: 'api_admin_updatecore_status', methods: ['GET'])]
public function api_admin_updatecore_status(Request $request): JsonResponse public function api_admin_updatecore_status(Request $request): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$uuid = $request->query->get('uuid'); $uuid = $request->query->get('uuid');
if (!$uuid) { if (!$uuid) {
return new JsonResponse([ return new JsonResponse([
@ -136,6 +140,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/stream', name: 'api_admin_updatecore_stream', methods: ['GET'])] #[Route('/api/admin/updatecore/stream', name: 'api_admin_updatecore_stream', methods: ['GET'])]
public function api_admin_updatecore_stream(Request $request): StreamedResponse|JsonResponse public function api_admin_updatecore_stream(Request $request): StreamedResponse|JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$uuid = $request->query->get('uuid'); $uuid = $request->query->get('uuid');
if (!$uuid) { if (!$uuid) {
return new JsonResponse(['status' => 'error', 'message' => 'UUID is required'], 400); return new JsonResponse(['status' => 'error', 'message' => 'UUID is required'], 400);
@ -179,13 +185,17 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/commits', name: 'api_admin_updatecore_commits', methods: ['GET'])] #[Route('/api/admin/updatecore/commits', name: 'api_admin_updatecore_commits', methods: ['GET'])]
public function api_admin_updatecore_commits(): JsonResponse public function api_admin_updatecore_commits(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$currentProcess = new Process(['git', 'rev-parse', 'HEAD'], $projectDir); $currentProcess = new Process(['git', 'rev-parse', 'HEAD'], $projectDir);
$currentProcess->setTimeout(3600);
$currentProcess->run(); $currentProcess->run();
$currentCommit = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : 'unknown'; $currentCommit = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : 'unknown';
$targetProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $projectDir); $targetProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $projectDir);
$targetProcess->setTimeout(3600);
$targetProcess->run(); $targetProcess->run();
$targetOutput = $targetProcess->isSuccessful() ? explode("\t", trim($targetProcess->getOutput()))[0] : 'unknown'; $targetOutput = $targetProcess->isSuccessful() ? explode("\t", trim($targetProcess->getOutput()))[0] : 'unknown';
@ -198,6 +208,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/system-info', name: 'api_admin_updatecore_system_info', methods: ['GET'])] #[Route('/api/admin/updatecore/system-info', name: 'api_admin_updatecore_system_info', methods: ['GET'])]
public function api_admin_updatecore_system_info(): JsonResponse public function api_admin_updatecore_system_info(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$osName = php_uname('s'); $osName = php_uname('s');
$osRelease = php_uname('r'); $osRelease = php_uname('r');
$osVersion = php_uname('v'); $osVersion = php_uname('v');
@ -262,11 +274,13 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/clear-cache', name: 'api_admin_updatecore_clear_cache', methods: ['POST'])] #[Route('/api/admin/updatecore/clear-cache', name: 'api_admin_updatecore_clear_cache', methods: ['POST'])]
public function api_admin_updatecore_clear_cache(): JsonResponse public function api_admin_updatecore_clear_cache(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$env = $this->getParameter('kernel.environment'); $env = $this->getParameter('kernel.environment');
$process = new Process(['php', 'bin/console', 'cache:clear', "--env=$env"], $projectDir); $process = new Process(['php', 'bin/console', 'cache:clear', "--env=$env"], $projectDir);
$process->setTimeout(300); $process->setTimeout(3600);
$process->run(); $process->run();
if (!$process->isSuccessful()) { if (!$process->isSuccessful()) {
@ -285,6 +299,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/change-env', name: 'api_admin_updatecore_change_env', methods: ['POST'])] #[Route('/api/admin/updatecore/change-env', name: 'api_admin_updatecore_change_env', methods: ['POST'])]
public function api_admin_updatecore_change_env(Request $request): JsonResponse public function api_admin_updatecore_change_env(Request $request): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$newEnv = $request->getPayload()->get('env'); $newEnv = $request->getPayload()->get('env');
if (!$newEnv || !in_array($newEnv, ['dev', 'prod'])) { if (!$newEnv || !in_array($newEnv, ['dev', 'prod'])) {
@ -317,6 +333,7 @@ final class UpdateCoreController extends AbstractController
]; ];
$composerCheck = new Process(['composer', '--version'], $projectDir, $env); $composerCheck = new Process(['composer', '--version'], $projectDir, $env);
$composerCheck->setTimeout(3600);
$composerCheck->run(); $composerCheck->run();
if (!$composerCheck->isSuccessful()) { if (!$composerCheck->isSuccessful()) {
return new JsonResponse([ return new JsonResponse([
@ -333,7 +350,7 @@ final class UpdateCoreController extends AbstractController
$composerCommand[] = '--no-scripts'; $composerCommand[] = '--no-scripts';
} }
$composerProcess = new Process($composerCommand, $projectDir, $env); $composerProcess = new Process($composerCommand, $projectDir, $env);
$composerProcess->setTimeout(600); $composerProcess->setTimeout(3600);
$composerProcess->run(); $composerProcess->run();
if (!$composerProcess->isSuccessful()) { if (!$composerProcess->isSuccessful()) {
return new JsonResponse([ return new JsonResponse([
@ -345,7 +362,7 @@ final class UpdateCoreController extends AbstractController
$output .= "وابستگی‌ها با موفقیت به‌روزرسانی شدند\n" . $composerProcess->getOutput(); $output .= "وابستگی‌ها با موفقیت به‌روزرسانی شدند\n" . $composerProcess->getOutput();
$cacheProcess = new Process(['php', 'bin/console', 'cache:clear', "--env=$newEnv"], $projectDir, $env); $cacheProcess = new Process(['php', 'bin/console', 'cache:clear', "--env=$newEnv"], $projectDir, $env);
$cacheProcess->setTimeout(300); $cacheProcess->setTimeout(3600);
$cacheProcess->run(); $cacheProcess->run();
if (!$cacheProcess->isSuccessful()) { if (!$cacheProcess->isSuccessful()) {
return new JsonResponse([ return new JsonResponse([
@ -361,11 +378,13 @@ final class UpdateCoreController extends AbstractController
'message' => "حالت به $newEnv تغییر کرد، وابستگی‌ها به‌روزرسانی شدند و کش پاک شد", 'message' => "حالت به $newEnv تغییر کرد، وابستگی‌ها به‌روزرسانی شدند و کش پاک شد",
'output' => $output, 'output' => $output,
]); ]);
}
#[Route('/api/admin/updatecore/current-env', name: 'api_admin_updatecore_current_env', methods: ['GET'])] #[Route('/api/admin/updatecore/current-env', name: 'api_admin_updatecore_current_env', methods: ['GET'])]
public function api_admin_updatecore_current_env(): JsonResponse public function api_admin_updatecore_current_env(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$env = $this->getParameter('kernel.environment'); $env = $this->getParameter('kernel.environment');
return new JsonResponse(['env' => $env]); return new JsonResponse(['env' => $env]);
} }
@ -373,6 +392,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/system-logs', name: 'api_admin_updatecore_system_logs', methods: ['GET'])] #[Route('/api/admin/updatecore/system-logs', name: 'api_admin_updatecore_system_logs', methods: ['GET'])]
public function api_admin_updatecore_system_logs(): JsonResponse public function api_admin_updatecore_system_logs(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$env = $this->getParameter('kernel.environment'); $env = $this->getParameter('kernel.environment');
$logFile = "$projectDir/var/log/$env.log"; $logFile = "$projectDir/var/log/$env.log";
@ -408,6 +429,8 @@ final class UpdateCoreController extends AbstractController
#[Route('/api/admin/updatecore/clear-logs', name: 'api_admin_updatecore_clear_logs', methods: ['POST'])] #[Route('/api/admin/updatecore/clear-logs', name: 'api_admin_updatecore_clear_logs', methods: ['POST'])]
public function api_admin_updatecore_clear_logs(): JsonResponse public function api_admin_updatecore_clear_logs(): JsonResponse
{ {
set_time_limit(3600); // تنظیم تایم‌اوت PHP به 1 ساعت
$projectDir = $this->getParameter('kernel.project_dir'); $projectDir = $this->getParameter('kernel.project_dir');
$env = $this->getParameter('kernel.environment'); $env = $this->getParameter('kernel.environment');
$logFile = "$projectDir/var/log/$env.log"; $logFile = "$projectDir/var/log/$env.log";

View file

@ -72,7 +72,7 @@
</v-card> </v-card>
</v-window-item> </v-window-item>
<!-- تب بهروزرسانی (بدون تغییر) --> <!-- تب بهروزرسانی -->
<v-window-item> <v-window-item>
<v-card flat> <v-card flat>
<v-card-text> <v-card-text>
@ -161,6 +161,8 @@ import axios from 'axios';
export default { export default {
name: 'UpdateSoftware', name: 'UpdateSoftware',
setup() { setup() {
axios.defaults.timeout = 3600000; // تنظیم تایماوت پیشفرض Axios به 1 ساعت
const isUpdating = ref(false); const isUpdating = ref(false);
const isClearingCache = ref(false); const isClearingCache = ref(false);
const isChangingEnv = ref(false); const isChangingEnv = ref(false);
@ -279,6 +281,7 @@ export default {
try { try {
const response = await axios.post('/api/admin/updatecore/run', {}, { const response = await axios.post('/api/admin/updatecore/run', {}, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
if (response.data.status === 'started') { if (response.data.status === 'started') {
@ -316,30 +319,27 @@ export default {
try { try {
const response = await axios.get(`/api/admin/updatecore/stream`, { const response = await axios.get(`/api/admin/updatecore/stream`, {
params: { uuid: this.updateUuid } params: { uuid: this.updateUuid },
timeout: 3600000 // تایماوت 1 ساعت
}); });
// پردازش پاسخ
const data = response.data; const data = response.data;
if (typeof data === 'string' && data.startsWith('data: ')) { if (typeof data === 'string' && data.startsWith('data: ')) {
try { try {
// جدا کردن بخش JSON از پیشوند data:
const jsonStr = data.substring(data.indexOf('{')); const jsonStr = data.substring(data.indexOf('{'));
const parsedData = JSON.parse(jsonStr); const parsedData = JSON.parse(jsonStr);
if (parsedData.output && parsedData.output !== this.output) { if (parsedData.output && parsedData.output !== this.output) {
// پردازش و فرمتبندی خروجی
const formattedOutput = parsedData.output const formattedOutput = parsedData.output
.split('\n') .split('\n')
.filter(line => line.trim()) // حذف خطوط خالی .filter(line => line.trim())
.map(line => { .map(line => {
// برجسته کردن پیامهای مهم if (line.includes('Installing frontend dependencies') || line.includes('Building frontend')) {
if (line.includes('INFO')) {
return `<span class="log-info">${line}</span>`; return `<span class="log-info">${line}</span>`;
} else if (line.includes('DEBUG')) {
return `<span class="log-debug">${line}</span>`;
} else if (line.includes('ERROR')) { } else if (line.includes('ERROR')) {
return `<span class="log-error">${line}</span>`; return `<span class="log-error">${line}</span>`;
} else if (line.includes('DEBUG')) {
return `<span class="log-debug">${line}</span>`;
} }
return line; return line;
}) })
@ -400,6 +400,7 @@ export default {
try { try {
const response = await axios.post('/api/admin/updatecore/clear-cache', {}, { const response = await axios.post('/api/admin/updatecore/clear-cache', {}, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
this.output += response.data.output || this.$t('updateSoftware.cacheClearedMessage') + '\n'; this.output += response.data.output || this.$t('updateSoftware.cacheClearedMessage') + '\n';
this.showResultDialog = true; this.showResultDialog = true;
@ -438,6 +439,7 @@ export default {
try { try {
const response = await axios.post('/api/admin/updatecore/change-env', { env: this.tempSelectedEnv }, { const response = await axios.post('/api/admin/updatecore/change-env', { env: this.tempSelectedEnv }, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
this.output += response.data.output || response.data.message + '\n'; this.output += response.data.output || response.data.message + '\n';
this.selectedEnv = this.tempSelectedEnv; this.selectedEnv = this.tempSelectedEnv;
@ -460,6 +462,7 @@ export default {
try { try {
const response = await axios.get('/api/admin/updatecore/commits', { const response = await axios.get('/api/admin/updatecore/commits', {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
this.currentCommit = response.data.currentCommit || 'unknown'; this.currentCommit = response.data.currentCommit || 'unknown';
this.targetCommit = response.data.targetCommit || 'unknown'; this.targetCommit = response.data.targetCommit || 'unknown';
@ -473,6 +476,7 @@ export default {
try { try {
const response = await axios.get('/api/admin/updatecore/system-info', { const response = await axios.get('/api/admin/updatecore/system-info', {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
this.systemInfo = { this.systemInfo = {
osName: response.data.osName || 'unknown', osName: response.data.osName || 'unknown',
@ -506,6 +510,7 @@ export default {
try { try {
const response = await axios.get('/api/admin/updatecore/current-env', { const response = await axios.get('/api/admin/updatecore/current-env', {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
this.selectedEnv = response.data.env; this.selectedEnv = response.data.env;
this.tempSelectedEnv = response.data.env; this.tempSelectedEnv = response.data.env;
@ -523,9 +528,10 @@ export default {
}, },
async refreshLogs() { async refreshLogs() {
this.isLoadingLogs = true; this.isLoadingLogs = true;
try { tryِ try {
const response = await axios.get('/api/admin/updatecore/system-logs', { const response = await axios.get('/api/admin/updatecore/system-logs', {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
this.systemLogs = response.data.logs || response.data.message; this.systemLogs = response.data.logs || response.data.message;
} catch (error) { } catch (error) {
@ -540,6 +546,7 @@ export default {
try { try {
const response = await axios.post('/api/admin/updatecore/clear-logs', {}, { const response = await axios.post('/api/admin/updatecore/clear-logs', {}, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
timeout: 3600000 // تایماوت 1 ساعت
}); });
if (response.data.status === 'success') { if (response.data.status === 'success') {
this.systemLogs = 'لاگ‌ها پاک شدند'; this.systemLogs = 'لاگ‌ها پاک شدند';