edit in updateCore

This commit is contained in:
Hesabix 2025-03-15 11:18:00 +00:00
parent a25d80d437
commit 11526aa8b8
3 changed files with 182 additions and 11545 deletions

11537
hesabixCore/composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -64,7 +64,6 @@ class UpdateSoftwareCommand extends Command
mkdir(dirname($this->stateFile), 0755, true); mkdir(dirname($this->stateFile), 0755, true);
} }
// اگر فایل وضعیت وجود ندارد، آن را ایجاد کن
if (!file_exists($this->stateFile)) { if (!file_exists($this->stateFile)) {
file_put_contents($this->stateFile, json_encode([ file_put_contents($this->stateFile, json_encode([
'uuid' => Uuid::uuid4()->toString(), 'uuid' => Uuid::uuid4()->toString(),
@ -79,7 +78,7 @@ class UpdateSoftwareCommand extends Command
$this->writeOutput($output, "Starting software update (UUID: $uuid) in {$this->env} mode"); $this->writeOutput($output, "Starting software update (UUID: $uuid) in {$this->env} mode");
$state = $this->loadState($uuid); $state = $this->loadState($uuid);
$state['log'] = $state['log'] ?? ''; // اطمینان از وجود کلید log $state['log'] = $state['log'] ?? '';
if ($this->isUpToDate()) { if ($this->isUpToDate()) {
$this->writeOutput($output, '<info>The software is already up to date with the remote repository.</info>'); $this->writeOutput($output, '<info>The software is already up to date with the remote repository.</info>');
@ -125,7 +124,6 @@ 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...');
// تنظیم ردیابی شاخه master به origin/master
$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);
$this->writeOutput($output, 'Pulling latest changes from GitHub...'); $this->writeOutput($output, 'Pulling latest changes from GitHub...');
@ -150,6 +148,18 @@ class UpdateSoftwareCommand extends Command
$this->saveState($uuid, $state, $output, 'Dependencies installed'); $this->saveState($uuid, $state, $output, 'Dependencies installed');
} }
if (!in_array('composer_install_core', $state['completedSteps'])) {
$this->writeOutput($output, 'Installing dependencies in hesabixCore...');
$composerCommand = ['composer', 'install', '--optimize-autoloader'];
if ($this->env !== 'dev') {
$composerCommand[] = '--no-dev';
$composerCommand[] = '--no-scripts';
}
$this->runProcess($composerCommand, $this->rootDir . '/hesabixCore', $output, 3);
$state['completedSteps'][] = 'composer_install_core';
$this->saveState($uuid, $state, $output, 'Dependencies installed in hesabixCore');
}
if (!in_array('cache_clear', $state['completedSteps'])) { if (!in_array('cache_clear', $state['completedSteps'])) {
$this->writeOutput($output, 'Clearing cache...'); $this->writeOutput($output, 'Clearing cache...');
$cacheBackupDir = $this->backupDir . '/' . Uuid::uuid4(); $cacheBackupDir = $this->backupDir . '/' . Uuid::uuid4();
@ -258,6 +268,10 @@ class UpdateSoftwareCommand extends Command
} catch (ProcessFailedException $e) { } catch (ProcessFailedException $e) {
$attempt++; $attempt++;
$errorMessage = $e->getProcess()->getErrorOutput() ?: $e->getMessage(); $errorMessage = $e->getProcess()->getErrorOutput() ?: $e->getMessage();
if (stripos($errorMessage, 'permission denied') !== false || stripos($errorMessage, 'access denied') !== false) {
$this->logger->error("Permission error: $errorMessage");
$this->writeOutput($output, "<error>خطای دسترسی به فایل: $errorMessage</error>");
}
$this->logger->warning("Attempt $attempt failed for " . implode(' ', $command) . ": $errorMessage"); $this->logger->warning("Attempt $attempt failed for " . implode(' ', $command) . ": $errorMessage");
$this->writeOutput($output, "<comment>Attempt $attempt failed: $errorMessage</comment>"); $this->writeOutput($output, "<comment>Attempt $attempt failed: $errorMessage</comment>");
if ($attempt === $retries) { if ($attempt === $retries) {

View file

@ -5,6 +5,7 @@ namespace App\Controller\System;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\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\Process\Process; use Symfony\Component\Process\Process;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
@ -96,9 +97,9 @@ final class UpdateCoreController extends AbstractController
$output = $state['log'] ?? ''; $output = $state['log'] ?? '';
$isRunning = !isset($state['error']) && $isRunning = !isset($state['error']) &&
!in_array('post_update_test', $state['completedSteps'] ?? []) && !in_array('post_update_test', $state['completedSteps'] ?? []) &&
!str_contains($output, 'No update needed') && !str_contains($output, 'No update needed') &&
!str_contains($output, 'Software update completed successfully'); !str_contains($output, 'Software update completed successfully');
if ($state['error'] ?? false) { if ($state['error'] ?? false) {
return new JsonResponse([ return new JsonResponse([
@ -109,7 +110,6 @@ final class UpdateCoreController extends AbstractController
} }
if (!$isRunning) { if (!$isRunning) {
// عملیات موفق بوده، فایل‌های update_state_*.json رو حذف کن
$backupDir = $this->getParameter('kernel.project_dir') . '/../backup'; $backupDir = $this->getParameter('kernel.project_dir') . '/../backup';
$stateFiles = glob($backupDir . '/update_state_*.json'); $stateFiles = glob($backupDir . '/update_state_*.json');
foreach ($stateFiles as $file) { foreach ($stateFiles as $file) {
@ -133,6 +133,49 @@ final class UpdateCoreController extends AbstractController
]); ]);
} }
#[Route('/api/admin/updatecore/stream', name: 'api_admin_updatecore_stream', methods: ['GET'])]
public function api_admin_updatecore_stream(Request $request): StreamedResponse|JsonResponse
{
$uuid = $request->query->get('uuid');
if (!$uuid) {
return new JsonResponse(['status' => 'error', 'message' => 'UUID is required'], 400);
}
$stateFile = $this->getParameter('kernel.project_dir') . '/../backup/update_state_' . $uuid . '.json';
return new StreamedResponse(function () use ($stateFile) {
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
while (true) {
if (!file_exists($stateFile)) {
echo "data: " . json_encode(['status' => 'idle', 'output' => '']) . "\n\n";
ob_flush();
flush();
break;
}
$state = json_decode(file_get_contents($stateFile), true) ?? ['log' => ''];
$output = $state['log'] ?? '';
$isRunning = !isset($state['error']) &&
!in_array('post_update_test', $state['completedSteps'] ?? []);
$status = $state['error'] ? 'error' : ($isRunning ? 'running' : 'success');
echo "data: " . json_encode(['status' => $status, 'output' => $output]) . "\n\n";
ob_flush();
flush();
if (!$isRunning) {
break;
}
sleep(1);
}
});
}
#[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
{ {
@ -215,4 +258,121 @@ final class UpdateCoreController extends AbstractController
'dbVersion' => $dbVersion, 'dbVersion' => $dbVersion,
]); ]);
} }
#[Route('/api/admin/updatecore/clear-cache', name: 'api_admin_updatecore_clear_cache', methods: ['POST'])]
public function api_admin_updatecore_clear_cache(): JsonResponse
{
$projectDir = $this->getParameter('kernel.project_dir');
$env = $this->getParameter('kernel.environment');
$process = new Process(['php', 'bin/console', 'cache:clear', "--env=$env"], $projectDir);
$process->setTimeout(300);
$process->run();
if (!$process->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'Failed to clear cache: ' . $process->getErrorOutput(),
], 500);
}
return new JsonResponse([
'status' => 'success',
'output' => $process->getOutput(),
]);
}
#[Route('/api/admin/updatecore/change-env', name: 'api_admin_updatecore_change_env', methods: ['POST'])]
public function api_admin_updatecore_change_env(Request $request): JsonResponse
{
$newEnv = $request->getPayload()->get('env');
if (!$newEnv || !in_array($newEnv, ['dev', 'prod'])) {
return new JsonResponse([
'status' => 'error',
'message' => 'Invalid environment',
'output' => 'خطا: محیط نامعتبر است',
'debug' => 'Received env: ' . var_export($newEnv, true)
], 400);
}
$projectDir = $this->getParameter('kernel.project_dir');
$envFile = $projectDir . '/.env.local.php';
$composerLock = $projectDir . '/composer.lock';
$output = '';
// بارگذاری تنظیمات از .env.local.php و تغییر APP_ENV
$envConfig = file_exists($envFile) ? require $envFile : [];
$envConfig['APP_ENV'] = $newEnv;
file_put_contents($envFile, '<?php return ' . var_export($envConfig, true) . ';');
$output .= "حالت به $newEnv تغییر کرد\n";
// حذف composer.lock
if (file_exists($composerLock)) {
unlink($composerLock);
$output .= "فایل composer.lock حذف شد\n";
}
// تنظیم متغیرهای محیطی حداقلی برای Composer
$env = [
'HOME' => sys_get_temp_dir(), // استفاده از دایرکتوری موقت سیستم
'COMPOSER_HOME' => sys_get_temp_dir() . '/composer' // دایرکتوری موقت برای Composer
];
// چک کردن نصب بودن Composer در سطح سیستم
$composerCheck = new Process(['composer', '--version'], $projectDir, $env);
$composerCheck->run();
if (!$composerCheck->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'Composer is not installed',
'output' => $output . "خطا: Composer روی سرور نصب نیست. لطفاً Composer را نصب کنید.\n" . $composerCheck->getErrorOutput()
], 500);
}
$output .= "Composer نسخه " . trim($composerCheck->getOutput()) . " تشخیص داده شد\n";
// اجرای composer install
$composerCommand = ['composer', 'install', '--optimize-autoloader'];
if ($newEnv !== 'dev') {
$composerCommand[] = '--no-dev';
$composerCommand[] = '--no-scripts';
}
$composerProcess = new Process($composerCommand, $projectDir, $env);
$composerProcess->setTimeout(600);
$composerProcess->run();
if (!$composerProcess->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'Failed to update dependencies',
'output' => $output . "خطا در به‌روزرسانی وابستگی‌ها: " . $composerProcess->getErrorOutput(),
], 500);
}
$output .= "وابستگی‌ها با موفقیت به‌روزرسانی شدند\n" . $composerProcess->getOutput();
// خالی کردن کش
$cacheProcess = new Process(['php', 'bin/console', 'cache:clear', "--env=$newEnv"], $projectDir, $env);
$cacheProcess->setTimeout(300);
$cacheProcess->run();
if (!$cacheProcess->isSuccessful()) {
return new JsonResponse([
'status' => 'error',
'message' => 'Failed to clear cache',
'output' => $output . "خطا در پاک کردن کش: " . $cacheProcess->getErrorOutput(),
], 500);
}
$output .= "کش با موفقیت پاک شد\n" . $cacheProcess->getOutput();
return new JsonResponse([
'status' => 'success',
'message' => "حالت به $newEnv تغییر کرد، وابستگی‌ها به‌روزرسانی شدند و کش پاک شد",
'output' => $output,
]);
}
#[Route('/api/admin/updatecore/current-env', name: 'api_admin_updatecore_current_env', methods: ['GET'])]
public function api_admin_updatecore_current_env(): JsonResponse
{
$env = $this->getParameter('kernel.environment');
return new JsonResponse(['env' => $env]);
}
} }