bug fix in automatic update
This commit is contained in:
parent
789618927d
commit
2f144c0d9d
|
@ -272,8 +272,88 @@ class UpdateSoftwareCommand extends Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to fix Git "dubious ownership" error
|
||||||
|
*/
|
||||||
|
private function fixGitOwnershipIssue(string $gitRoot): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Check if the directory is a Git repository
|
||||||
|
if (!is_dir($gitRoot . '/.git')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add the directory to safe.directory when this method is called
|
||||||
|
// This handles cases where Git detects dubious ownership for other reasons
|
||||||
|
$safeDirProcess = new Process(['git', 'config', '--global', '--add', 'safe.directory', $gitRoot], $gitRoot);
|
||||||
|
$safeDirProcess->setTimeout(300);
|
||||||
|
$safeDirProcess->run();
|
||||||
|
|
||||||
|
if ($safeDirProcess->isSuccessful()) {
|
||||||
|
$this->logger->info("Fixed Git ownership issue for directory: $gitRoot");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->warning("Failed to fix Git ownership issue: " . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to run Git command with ownership fix
|
||||||
|
*/
|
||||||
|
private function runGitCommand(array $command, string $workingDir, OutputInterface $output, int $retries = 3): void
|
||||||
|
{
|
||||||
|
$attempt = 0;
|
||||||
|
while ($attempt < $retries) {
|
||||||
|
try {
|
||||||
|
$process = new Process($command, $workingDir);
|
||||||
|
$process->setTimeout(3600);
|
||||||
|
|
||||||
|
if ($output->isVerbose()) {
|
||||||
|
$process->mustRun(function ($type, $buffer) use ($output) {
|
||||||
|
$this->writeOutput($output, $buffer);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$process->mustRun();
|
||||||
|
$this->writeOutput($output, $process->getOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->info('Git command executed successfully: ' . implode(' ', $command));
|
||||||
|
return;
|
||||||
|
} catch (ProcessFailedException $e) {
|
||||||
|
$attempt++;
|
||||||
|
$errorMessage = $e->getProcess()->getErrorOutput() ?: $e->getMessage();
|
||||||
|
$this->logger->warning("Attempt $attempt failed for " . implode(' ', $command) . ": $errorMessage");
|
||||||
|
$this->writeOutput($output, "<comment>Attempt $attempt failed: $errorMessage</comment>");
|
||||||
|
|
||||||
|
// If the command failed with "dubious ownership" error, try to fix it
|
||||||
|
if (str_contains($errorMessage, 'dubious ownership')) {
|
||||||
|
$this->writeOutput($output, "<comment>Detected Git ownership issue, attempting to fix...</comment>");
|
||||||
|
if ($this->fixGitOwnershipIssue($workingDir)) {
|
||||||
|
$this->writeOutput($output, "<info>Git ownership issue fixed, retrying command...</info>");
|
||||||
|
continue; // Retry the command without incrementing attempt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($attempt === $retries) {
|
||||||
|
throw new \RuntimeException('Command "' . implode(' ', $command) . '" failed after ' . $retries . ' attempts: ' . $errorMessage);
|
||||||
|
}
|
||||||
|
sleep(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function runProcess(array $command, string $workingDir, OutputInterface $output, int $retries = 3, bool $isComposer = false): void
|
private function runProcess(array $command, string $workingDir, OutputInterface $output, int $retries = 3, bool $isComposer = false): void
|
||||||
{
|
{
|
||||||
|
// If this is a Git command, use the specialized Git command runner
|
||||||
|
if (in_array($command[0], ['git'])) {
|
||||||
|
$this->runGitCommand($command, $workingDir, $output, $retries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$attempt = 0;
|
$attempt = 0;
|
||||||
while ($attempt < $retries) {
|
while ($attempt < $retries) {
|
||||||
try {
|
try {
|
||||||
|
@ -318,13 +398,28 @@ class UpdateSoftwareCommand extends Command
|
||||||
|
|
||||||
private function getCurrentGitHead(): string
|
private function getCurrentGitHead(): string
|
||||||
{
|
{
|
||||||
$process = new Process(['git', 'rev-parse', 'HEAD'], $this->rootDir);
|
try {
|
||||||
$process->run();
|
$process = new Process(['git', 'rev-parse', 'HEAD'], $this->rootDir);
|
||||||
if (!$process->isSuccessful()) {
|
$process->run();
|
||||||
$this->logger->warning('Failed to get current Git HEAD: ' . $process->getErrorOutput());
|
|
||||||
|
// If the command failed with "dubious ownership" error, try to fix it
|
||||||
|
if (!$process->isSuccessful() && str_contains($process->getErrorOutput(), 'dubious ownership')) {
|
||||||
|
if ($this->fixGitOwnershipIssue($this->rootDir)) {
|
||||||
|
// Retry the command after fixing ownership
|
||||||
|
$process = new Process(['git', 'rev-parse', 'HEAD'], $this->rootDir);
|
||||||
|
$process->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$process->isSuccessful()) {
|
||||||
|
$this->logger->warning('Failed to get current Git HEAD: ' . $process->getErrorOutput());
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
return trim($process->getOutput());
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->warning('Failed to get current Git HEAD: ' . $e->getMessage());
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
}
|
}
|
||||||
return trim($process->getOutput());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isUpToDate(): bool
|
private function isUpToDate(): bool
|
||||||
|
@ -333,6 +428,16 @@ class UpdateSoftwareCommand extends Command
|
||||||
$this->runProcess(['git', 'fetch', 'origin'], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput());
|
$this->runProcess(['git', 'fetch', 'origin'], $this->rootDir, new \Symfony\Component\Console\Output\NullOutput());
|
||||||
$process = new Process(['git', 'status', '-uno'], $this->rootDir);
|
$process = new Process(['git', 'status', '-uno'], $this->rootDir);
|
||||||
$process->run();
|
$process->run();
|
||||||
|
|
||||||
|
// If the command failed with "dubious ownership" error, try to fix it
|
||||||
|
if (!$process->isSuccessful() && str_contains($process->getErrorOutput(), 'dubious ownership')) {
|
||||||
|
if ($this->fixGitOwnershipIssue($this->rootDir)) {
|
||||||
|
// Retry the command after fixing ownership
|
||||||
|
$process = new Process(['git', 'status', '-uno'], $this->rootDir);
|
||||||
|
$process->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$status = $process->getOutput();
|
$status = $process->getOutput();
|
||||||
return strpos($status, 'Your branch is up to date') !== false;
|
return strpos($status, 'Your branch is up to date') !== false;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -558,6 +663,16 @@ class UpdateSoftwareCommand extends Command
|
||||||
$this->logger->warning('Command executed as root user.');
|
$this->logger->warning('Command executed as root user.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check and fix Git ownership issues proactively
|
||||||
|
if (is_dir($this->rootDir . '/.git')) {
|
||||||
|
$this->writeOutput($output, 'Checking Git repository ownership...');
|
||||||
|
if ($this->fixGitOwnershipIssue($this->rootDir)) {
|
||||||
|
$this->writeOutput($output, '<info>Git ownership issue detected and fixed.</info>');
|
||||||
|
} else {
|
||||||
|
$this->writeOutput($output, '<info>Git repository ownership is correct.</info>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->writeOutput($output, 'Pre-update checks completed successfully.');
|
$this->writeOutput($output, 'Pre-update checks completed successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,55 @@ final class UpdateCoreController extends AbstractController
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to fix Git "dubious ownership" error
|
||||||
|
*/
|
||||||
|
private function fixGitOwnershipIssue(string $gitRoot): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Check if the directory is a Git repository
|
||||||
|
if (!is_dir($gitRoot . '/.git')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add the directory to safe.directory when this method is called
|
||||||
|
// This handles cases where Git detects dubious ownership for other reasons
|
||||||
|
$safeDirProcess = new Process(['git', 'config', '--global', '--add', 'safe.directory', $gitRoot], $gitRoot);
|
||||||
|
$safeDirProcess->setTimeout(300);
|
||||||
|
$safeDirProcess->run();
|
||||||
|
|
||||||
|
if ($safeDirProcess->isSuccessful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to run Git command with ownership fix
|
||||||
|
*/
|
||||||
|
private function runGitCommand(array $command, string $gitRoot, int $timeout = 7200): Process
|
||||||
|
{
|
||||||
|
$process = new Process($command, $gitRoot);
|
||||||
|
$process->setTimeout($timeout);
|
||||||
|
$process->run();
|
||||||
|
|
||||||
|
// If the command failed with "dubious ownership" error, try to fix it
|
||||||
|
if (!$process->isSuccessful() && str_contains($process->getErrorOutput(), 'dubious ownership')) {
|
||||||
|
if ($this->fixGitOwnershipIssue($gitRoot)) {
|
||||||
|
// Retry the command after fixing ownership
|
||||||
|
$process = new Process($command, $gitRoot);
|
||||||
|
$process->setTimeout($timeout);
|
||||||
|
$process->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $process;
|
||||||
|
}
|
||||||
|
|
||||||
#[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
|
||||||
{
|
{
|
||||||
|
@ -42,20 +91,39 @@ final class UpdateCoreController extends AbstractController
|
||||||
'COMPOSER_HOME' => '/var/www/.composer',
|
'COMPOSER_HOME' => '/var/www/.composer',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// اجرای command به صورت synchronous برای اطمینان از اجرا
|
||||||
$process = new Process(['php', 'hesabixCore/bin/console', 'hesabix:update', $stateFile], $gitRoot, $env);
|
$process = new Process(['php', 'hesabixCore/bin/console', 'hesabix:update', $stateFile], $gitRoot, $env);
|
||||||
$process->setTimeout(7200); // افزایش تایماوت به 2 ساعت
|
$process->setTimeout(7200); // افزایش تایماوت به 2 ساعت
|
||||||
$process->start(function ($type, $buffer) use ($stateFile) {
|
|
||||||
|
// اجرای command و دریافت خروجی
|
||||||
|
$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;
|
||||||
file_put_contents($stateFile, json_encode($state));
|
file_put_contents($stateFile, json_encode($state));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// بررسی نتیجه اجرا
|
||||||
|
if (!$process->isSuccessful()) {
|
||||||
|
$state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => $uuid, 'log' => ''];
|
||||||
|
$state['error'] = $process->getErrorOutput();
|
||||||
|
$state['log'] .= "\nError: " . $process->getErrorOutput();
|
||||||
|
file_put_contents($stateFile, json_encode($state));
|
||||||
|
|
||||||
|
return new JsonResponse([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Update process failed: ' . $process->getErrorOutput(),
|
||||||
|
'uuid' => $uuid,
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// خواندن وضعیت نهایی
|
||||||
$state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => $uuid, 'log' => ''];
|
$state = json_decode(file_get_contents($stateFile), true) ?? ['uuid' => $uuid, 'log' => ''];
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'status' => 'started',
|
'status' => 'started',
|
||||||
'message' => 'Update process started',
|
'message' => 'Update process completed',
|
||||||
'uuid' => $uuid,
|
'uuid' => $uuid,
|
||||||
|
'output' => $state['log'] ?? '',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +192,7 @@ 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): JsonResponse
|
||||||
{
|
{
|
||||||
$uuid = $request->query->get('uuid');
|
$uuid = $request->query->get('uuid');
|
||||||
if (!$uuid) {
|
if (!$uuid) {
|
||||||
|
@ -135,37 +203,27 @@ final class UpdateCoreController extends AbstractController
|
||||||
$gitRoot = dirname($projectDir);
|
$gitRoot = dirname($projectDir);
|
||||||
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json';
|
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json';
|
||||||
|
|
||||||
return new StreamedResponse(function () use ($stateFile) {
|
if (!file_exists($stateFile)) {
|
||||||
header('Content-Type: text/event-stream');
|
return new JsonResponse(['status' => 'idle', 'output' => '']);
|
||||||
header('Cache-Control: no-cache');
|
}
|
||||||
header('Connection: keep-alive');
|
|
||||||
|
|
||||||
while (true) {
|
$state = json_decode(file_get_contents($stateFile), true) ?? ['log' => ''];
|
||||||
if (!file_exists($stateFile)) {
|
$output = $state['log'] ?? '';
|
||||||
echo "data: " . json_encode(['status' => 'idle', 'output' => '']) . "\n\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$state = json_decode(file_get_contents($stateFile), true) ?? ['log' => ''];
|
$isRunning = !isset($state['error']) &&
|
||||||
$output = $state['log'] ?? '';
|
!in_array('post_update_test', $state['completedSteps'] ?? []) &&
|
||||||
|
!str_contains($output, 'No update needed') &&
|
||||||
|
!str_contains($output, 'Software update completed successfully');
|
||||||
|
|
||||||
$isRunning = !isset($state['error']) &&
|
$status = isset($state['error']) ? 'error' : ($isRunning ? 'running' : 'success');
|
||||||
!in_array('post_update_test', $state['completedSteps'] ?? []);
|
|
||||||
|
return new JsonResponse([
|
||||||
$status = isset($state['error']) ? 'error' : ($isRunning ? 'running' : 'success');
|
'status' => $status,
|
||||||
echo "data: " . json_encode(['status' => $status, 'output' => $output]) . "\n\n";
|
'output' => $output,
|
||||||
ob_flush();
|
'completedSteps' => $state['completedSteps'] ?? [],
|
||||||
flush();
|
'error' => $state['error'] ?? null,
|
||||||
|
'commit_hash' => $state['commit_hash'] ?? null
|
||||||
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'])]
|
||||||
|
@ -174,14 +232,10 @@ final class UpdateCoreController extends AbstractController
|
||||||
$projectDir = $this->getParameter('kernel.project_dir');
|
$projectDir = $this->getParameter('kernel.project_dir');
|
||||||
$gitRoot = dirname($projectDir); // رفتن به ریشه پروژه
|
$gitRoot = dirname($projectDir); // رفتن به ریشه پروژه
|
||||||
|
|
||||||
$currentProcess = new Process(['git', 'rev-parse', 'HEAD'], $gitRoot);
|
$currentProcess = $this->runGitCommand(['git', 'rev-parse', 'HEAD'], $gitRoot);
|
||||||
$currentProcess->setTimeout(7200); // افزایش تایماوت
|
|
||||||
$currentProcess->run();
|
|
||||||
$currentCommit = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : 'unknown';
|
$currentCommit = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : 'unknown';
|
||||||
|
|
||||||
$targetProcess = new Process(['git', 'ls-remote', 'origin', 'HEAD'], $gitRoot);
|
$targetProcess = $this->runGitCommand(['git', 'ls-remote', 'origin', 'HEAD'], $gitRoot);
|
||||||
$targetProcess->setTimeout(7200); // افزایش تایماوت
|
|
||||||
$targetProcess->run();
|
|
||||||
$targetOutput = $targetProcess->isSuccessful() ? explode("\t", trim($targetProcess->getOutput()))[0] : 'unknown';
|
$targetOutput = $targetProcess->isSuccessful() ? explode("\t", trim($targetProcess->getOutput()))[0] : 'unknown';
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
|
@ -453,9 +507,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// دریافت آدرس مخزن origin فعلی
|
// دریافت آدرس مخزن origin فعلی
|
||||||
$process = new Process(['git', 'remote', 'get-url', 'origin'], $gitRoot);
|
$process = $this->runGitCommand(['git', 'remote', 'get-url', 'origin'], $gitRoot);
|
||||||
$process->setTimeout(7200); // افزایش تایماوت
|
|
||||||
$process->run();
|
|
||||||
|
|
||||||
if (!$process->isSuccessful()) {
|
if (!$process->isSuccessful()) {
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
|
@ -483,6 +535,76 @@ final class UpdateCoreController extends AbstractController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/admin/updatecore/run-manual', name: 'api_admin_updatecore_run_manual', methods: ['POST'])]
|
||||||
|
public function api_admin_updatecore_run_manual(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$uuid = $request->getPayload()->get('uuid');
|
||||||
|
if (!$uuid) {
|
||||||
|
return new JsonResponse([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'UUID is required',
|
||||||
|
'output' => '',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectDir = $this->getParameter('kernel.project_dir');
|
||||||
|
$gitRoot = dirname($projectDir);
|
||||||
|
$stateFile = $gitRoot . '/hesabixBackup/update_state_' . $uuid . '.json';
|
||||||
|
|
||||||
|
if (!file_exists($stateFile)) {
|
||||||
|
return new JsonResponse([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'State file not found',
|
||||||
|
'output' => '',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = '';
|
||||||
|
try {
|
||||||
|
$output .= "شروع اجرای دستی بهروزرسانی...\n";
|
||||||
|
|
||||||
|
// اجرای command hesabix:update
|
||||||
|
$env = array_merge($_SERVER, [
|
||||||
|
'HOME' => '/var/www',
|
||||||
|
'COMPOSER_HOME' => '/var/www/.composer',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$process = new Process(['php', 'hesabixCore/bin/console', 'hesabix:update', $stateFile], $gitRoot, $env);
|
||||||
|
$process->setTimeout(7200);
|
||||||
|
$process->run();
|
||||||
|
|
||||||
|
$output .= $process->getOutput();
|
||||||
|
|
||||||
|
if (!$process->isSuccessful()) {
|
||||||
|
$output .= "\nخطا: " . $process->getErrorOutput();
|
||||||
|
return new JsonResponse([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'خطا در اجرای بهروزرسانی',
|
||||||
|
'output' => $output,
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی فایل state بعد از اجرا
|
||||||
|
if (file_exists($stateFile)) {
|
||||||
|
$state = json_decode(file_get_contents($stateFile), true) ?? [];
|
||||||
|
$output .= "\nوضعیت نهایی: " . json_encode($state, JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResponse([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'بهروزرسانی با موفقیت انجام شد',
|
||||||
|
'output' => $output,
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return new JsonResponse([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'خطا در اجرای دستی: ' . $e->getMessage(),
|
||||||
|
'output' => $output,
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[Route('/api/admin/updatecore/change-source', name: 'api_admin_updatecore_change_source', methods: ['POST'])]
|
#[Route('/api/admin/updatecore/change-source', name: 'api_admin_updatecore_change_source', methods: ['POST'])]
|
||||||
public function api_admin_updatecore_change_source(Request $request): JsonResponse
|
public function api_admin_updatecore_change_source(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
|
@ -513,9 +635,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
$output .= "شروع تغییر آدرس مخزن...\n";
|
$output .= "شروع تغییر آدرس مخزن...\n";
|
||||||
|
|
||||||
// دریافت آدرس مخزن فعلی
|
// دریافت آدرس مخزن فعلی
|
||||||
$currentProcess = new Process(['git', 'remote', 'get-url', 'origin'], $gitRoot);
|
$currentProcess = $this->runGitCommand(['git', 'remote', 'get-url', 'origin'], $gitRoot);
|
||||||
$currentProcess->setTimeout(7200);
|
|
||||||
$currentProcess->run();
|
|
||||||
$currentUrl = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : '';
|
$currentUrl = $currentProcess->isSuccessful() ? trim($currentProcess->getOutput()) : '';
|
||||||
|
|
||||||
if ($currentUrl) {
|
if ($currentUrl) {
|
||||||
|
@ -523,9 +643,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// تغییر آدرس مخزن origin
|
// تغییر آدرس مخزن origin
|
||||||
$changeProcess = new Process(['git', 'remote', 'set-url', 'origin', $sourceUrl], $gitRoot);
|
$changeProcess = $this->runGitCommand(['git', 'remote', 'set-url', 'origin', $sourceUrl], $gitRoot);
|
||||||
$changeProcess->setTimeout(7200);
|
|
||||||
$changeProcess->run();
|
|
||||||
|
|
||||||
if (!$changeProcess->isSuccessful()) {
|
if (!$changeProcess->isSuccessful()) {
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
|
@ -538,9 +656,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
$output .= "آدرس مخزن به $sourceUrl تغییر یافت\n";
|
$output .= "آدرس مخزن به $sourceUrl تغییر یافت\n";
|
||||||
|
|
||||||
// بررسی اتصال به مخزن جدید
|
// بررسی اتصال به مخزن جدید
|
||||||
$testProcess = new Process(['git', 'remote', 'show', 'origin'], $gitRoot);
|
$testProcess = $this->runGitCommand(['git', 'remote', 'show', 'origin'], $gitRoot);
|
||||||
$testProcess->setTimeout(7200);
|
|
||||||
$testProcess->run();
|
|
||||||
|
|
||||||
if (!$testProcess->isSuccessful()) {
|
if (!$testProcess->isSuccessful()) {
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
|
@ -553,9 +669,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
$output .= "اتصال به مخزن جدید با موفقیت برقرار شد\n";
|
$output .= "اتصال به مخزن جدید با موفقیت برقرار شد\n";
|
||||||
|
|
||||||
// دریافت اطلاعات مخزن جدید
|
// دریافت اطلاعات مخزن جدید
|
||||||
$fetchProcess = new Process(['git', 'fetch', 'origin'], $gitRoot);
|
$fetchProcess = $this->runGitCommand(['git', 'fetch', 'origin'], $gitRoot);
|
||||||
$fetchProcess->setTimeout(7200);
|
|
||||||
$fetchProcess->run();
|
|
||||||
|
|
||||||
if (!$fetchProcess->isSuccessful()) {
|
if (!$fetchProcess->isSuccessful()) {
|
||||||
$output .= "هشدار: خطا در دریافت اطلاعات از مخزن جدید: " . $fetchProcess->getErrorOutput() . "\n";
|
$output .= "هشدار: خطا در دریافت اطلاعات از مخزن جدید: " . $fetchProcess->getErrorOutput() . "\n";
|
||||||
|
@ -564,9 +678,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// بررسی branch های موجود
|
// بررسی branch های موجود
|
||||||
$branchProcess = new Process(['git', 'branch', '-r'], $gitRoot);
|
$branchProcess = $this->runGitCommand(['git', 'branch', '-r'], $gitRoot);
|
||||||
$branchProcess->setTimeout(7200);
|
|
||||||
$branchProcess->run();
|
|
||||||
|
|
||||||
if ($branchProcess->isSuccessful()) {
|
if ($branchProcess->isSuccessful()) {
|
||||||
$branches = trim($branchProcess->getOutput());
|
$branches = trim($branchProcess->getOutput());
|
||||||
|
@ -578,9 +690,7 @@ final class UpdateCoreController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// پاک کردن کش Git
|
// پاک کردن کش Git
|
||||||
$cleanProcess = new Process(['git', 'gc', '--prune=now'], $gitRoot);
|
$cleanProcess = $this->runGitCommand(['git', 'gc', '--prune=now'], $gitRoot);
|
||||||
$cleanProcess->setTimeout(7200);
|
|
||||||
$cleanProcess->run();
|
|
||||||
|
|
||||||
if ($cleanProcess->isSuccessful()) {
|
if ($cleanProcess->isSuccessful()) {
|
||||||
$output .= "کش Git پاک شد\n";
|
$output .= "کش Git پاک شد\n";
|
||||||
|
|
|
@ -112,17 +112,17 @@
|
||||||
<v-row justify="end" class="mb-4">
|
<v-row justify="end" class="mb-4">
|
||||||
<v-col cols="auto">
|
<v-col cols="auto">
|
||||||
<v-btn-group size="small">
|
<v-btn-group size="small">
|
||||||
<v-btn :color="buttonColor" :loading="isUpdating"
|
<v-btn :color="buttonColor" :loading="isUpdating || isStreaming"
|
||||||
:disabled="isUpdating || status === 'running'"
|
:disabled="isUpdating || isStreaming || status === 'running'"
|
||||||
@click="startUpdate">
|
@click="startUpdate">
|
||||||
{{ buttonText }}
|
{{ buttonText }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="warning" :loading="isClearingCache"
|
<v-btn color="warning" :loading="isClearingCache"
|
||||||
:disabled="isUpdating || isClearingCache"
|
:disabled="isUpdating || isStreaming || isClearingCache"
|
||||||
@click="clearCache">
|
@click="clearCache">
|
||||||
{{ $t('updateSoftware.clearCacheButton') }}
|
{{ $t('updateSoftware.clearCacheButton') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="info" :disabled="isUpdating || isChangingEnv"
|
<v-btn color="info" :loading="isChangingEnv" :disabled="isUpdating || isStreaming || isChangingEnv"
|
||||||
@click="openEnvDialog">
|
@click="openEnvDialog">
|
||||||
{{ $t('updateSoftware.changeEnvButton') }}
|
{{ $t('updateSoftware.changeEnvButton') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
@ -134,11 +134,28 @@
|
||||||
<v-toolbar flat color="white" density="compact">
|
<v-toolbar flat color="white" density="compact">
|
||||||
<v-toolbar-title>{{ $t('updateSoftware.progressTitle') }}</v-toolbar-title>
|
<v-toolbar-title>{{ $t('updateSoftware.progressTitle') }}</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
<v-chip v-if="isStreaming" color="primary" size="small" class="mr-2">
|
||||||
|
<v-icon left size="small">mdi-sync</v-icon>
|
||||||
|
در حال بهروزرسانی...
|
||||||
|
</v-chip>
|
||||||
|
<v-btn icon size="small" @click="checkStatusManually" class="mr-1" :loading="isCheckingStatus">
|
||||||
|
<v-icon>mdi-refresh</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon size="small" @click="refreshPage" class="mr-1" title="Refresh صفحه">
|
||||||
|
<v-icon>mdi-reload</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn icon size="small" @click="runUpdateManually" class="mr-1" title="اجرای دستی بهروزرسانی" :loading="isRunningManually">
|
||||||
|
<v-icon>mdi-play</v-icon>
|
||||||
|
</v-btn>
|
||||||
<v-btn icon size="small" @click="copyToClipboard">
|
<v-btn icon size="small" @click="copyToClipboard">
|
||||||
<v-icon>mdi-content-copy</v-icon>
|
<v-icon>mdi-content-copy</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
|
<v-alert v-if="isStreaming && !isPolling" type="info" dense class="mb-2">
|
||||||
|
<v-icon left size="small">mdi-information</v-icon>
|
||||||
|
اگر وضعیت بهروزرسانی نمایش داده نمیشود، روی دکمه refresh کلیک کنید
|
||||||
|
</v-alert>
|
||||||
<pre class="output-pre" ref="outputPre" v-html="formattedOutput"></pre>
|
<pre class="output-pre" ref="outputPre" v-html="formattedOutput"></pre>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -191,8 +208,8 @@
|
||||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
// تنظیم تایماوت Axios به 2 ساعت (7200000 میلیثانیه)
|
// تنظیم تایماوت Axios به 5 دقیقه (300000 میلیثانیه)
|
||||||
axios.defaults.timeout = 7200000;
|
axios.defaults.timeout = 300000;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UpdateSoftware',
|
name: 'UpdateSoftware',
|
||||||
|
@ -234,6 +251,9 @@ export default {
|
||||||
const isLoadingLogs = ref(false);
|
const isLoadingLogs = ref(false);
|
||||||
const isClearingLogs = ref(false);
|
const isClearingLogs = ref(false);
|
||||||
const isPolling = ref(false);
|
const isPolling = ref(false);
|
||||||
|
const isStreaming = ref(false);
|
||||||
|
const isCheckingStatus = ref(false);
|
||||||
|
const isRunningManually = ref(false);
|
||||||
const updateSourceUrl = ref('');
|
const updateSourceUrl = ref('');
|
||||||
const isChangingSource = ref(false);
|
const isChangingSource = ref(false);
|
||||||
|
|
||||||
|
@ -264,6 +284,9 @@ export default {
|
||||||
isLoadingLogs,
|
isLoadingLogs,
|
||||||
isClearingLogs,
|
isClearingLogs,
|
||||||
isPolling,
|
isPolling,
|
||||||
|
isStreaming,
|
||||||
|
isCheckingStatus,
|
||||||
|
isRunningManually,
|
||||||
updateSourceUrl,
|
updateSourceUrl,
|
||||||
isChangingSource,
|
isChangingSource,
|
||||||
};
|
};
|
||||||
|
@ -327,110 +350,414 @@ export default {
|
||||||
async startUpdate() {
|
async startUpdate() {
|
||||||
if (this.isUpdating || this.status === 'running') return;
|
if (this.isUpdating || this.status === 'running') return;
|
||||||
|
|
||||||
|
console.log('=== شروع فرآیند بهروزرسانی ===');
|
||||||
|
console.log('وضعیت فعلی:', { isUpdating: this.isUpdating, status: this.status });
|
||||||
|
|
||||||
this.isUpdating = true;
|
this.isUpdating = true;
|
||||||
this.buttonText = this.$t('updateSoftware.updatingButton');
|
this.isStreaming = false;
|
||||||
|
this.buttonText = 'در حال اجرا...';
|
||||||
this.buttonColor = 'primary';
|
this.buttonColor = 'primary';
|
||||||
this.showOutput = true;
|
this.showOutput = true;
|
||||||
this.output = this.$t('updateSoftware.startingMessage') + '\n';
|
this.output = this.$t('updateSoftware.startingMessage') + '\n';
|
||||||
|
|
||||||
|
console.log('درخواست شروع بهروزرسانی ارسال میشود...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('ارسال درخواست POST به /api/admin/updatecore/run');
|
||||||
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: 7200000 // تایماوت 2 ساعته برای درخواست اولیه
|
timeout: 120000 // افزایش timeout به 2 دقیقه برای اجرای کامل
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('پاسخ دریافتی:', response.data);
|
||||||
|
|
||||||
if (response.data.status === 'started') {
|
if (response.data.status === 'started') {
|
||||||
this.updateUuid = response.data.uuid;
|
this.updateUuid = response.data.uuid;
|
||||||
this.output += this.$t('updateSoftware.startedMessage') + '\n';
|
console.log('UUID دریافت شد:', this.updateUuid);
|
||||||
this.startLogStream();
|
|
||||||
|
// اگر خروجی در پاسخ وجود دارد، آن را نمایش بده
|
||||||
|
if (response.data.output) {
|
||||||
|
this.output = response.data.output;
|
||||||
|
console.log('خروجی مستقیم دریافت شد:', response.data.output);
|
||||||
|
} else {
|
||||||
|
this.output += this.$t('updateSoftware.startedMessage') + '\n';
|
||||||
|
console.log('شروع polling برای فرآیند طولانی...');
|
||||||
|
this.startLogStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی اینکه آیا فرآیند تکمیل شده
|
||||||
|
if (response.data.output && (
|
||||||
|
response.data.output.includes('Software is already up to date') ||
|
||||||
|
response.data.output.includes('No update needed') ||
|
||||||
|
response.data.output.includes('completed successfully')
|
||||||
|
)) {
|
||||||
|
console.log('فرآیند تکمیل شده - توقف polling');
|
||||||
|
this.status = 'success';
|
||||||
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.buttonText = this.$t('updateSoftware.completedButton');
|
||||||
|
this.buttonColor = 'success';
|
||||||
|
|
||||||
|
// نمایش پیام مناسب
|
||||||
|
if (response.data.output.includes('Software is already up to date')) {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = 'اطلاعیه';
|
||||||
|
this.dialogMessage = 'نرمافزار در حال حاضر بهروز است. هیچ بهروزرسانی جدیدی موجود نیست.';
|
||||||
|
this.dialogColor = 'info';
|
||||||
|
} else {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogSuccessTitle');
|
||||||
|
this.dialogMessage = 'بهروزرسانی با موفقیت انجام شد';
|
||||||
|
this.dialogColor = 'success';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// شروع polling برای فرآیندهای طولانی
|
||||||
|
this.startLogStream();
|
||||||
|
}
|
||||||
} else if (response.data.status === 'error') {
|
} else if (response.data.status === 'error') {
|
||||||
|
console.log('خطا در پاسخ:', response.data.message);
|
||||||
this.output += '\n' + this.$t('updateSoftware.errorPrefix') + response.data.message;
|
this.output += '\n' + this.$t('updateSoftware.errorPrefix') + response.data.message;
|
||||||
this.buttonColor = 'error';
|
this.buttonColor = 'error';
|
||||||
this.buttonText = this.$t('updateSoftware.failedButton');
|
this.buttonText = this.$t('updateSoftware.failedButton');
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
this.showResultDialog = true;
|
this.showResultDialog = true;
|
||||||
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
this.dialogMessage = this.$t('updateSoftware.dialogErrorSimpleMessage');
|
this.dialogMessage = response.data.message || this.$t('updateSoftware.dialogErrorSimpleMessage');
|
||||||
this.dialogColor = 'error';
|
this.dialogColor = 'error';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log('خطا در درخواست شروع:', error);
|
||||||
|
console.log('جزئیات خطا:', {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
response: error.response?.data,
|
||||||
|
status: error.response?.status
|
||||||
|
});
|
||||||
|
|
||||||
this.output += '\n' + this.$t('updateSoftware.errorPrefix') + (error.response?.data?.message || error.message);
|
this.output += '\n' + this.$t('updateSoftware.errorPrefix') + (error.response?.data?.message || error.message);
|
||||||
this.buttonColor = 'error';
|
this.buttonColor = 'error';
|
||||||
this.buttonText = this.$t('updateSoftware.failedButton');
|
this.buttonText = this.$t('updateSoftware.failedButton');
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
this.showResultDialog = true;
|
this.showResultDialog = true;
|
||||||
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
this.dialogMessage = this.$t('updateSoftware.dialogErrorSimpleMessage');
|
this.dialogMessage = error.response?.data?.message || error.message || this.$t('updateSoftware.dialogErrorSimpleMessage');
|
||||||
this.dialogColor = 'error';
|
this.dialogColor = 'error';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async startLogStream() {
|
async startLogStream() {
|
||||||
const pollInterval = 2000; // افزایش فاصله polling به 2 ثانیه
|
console.log('=== شروع polling ===');
|
||||||
|
console.log('UUID برای polling:', this.updateUuid);
|
||||||
|
|
||||||
|
const pollInterval = 2000; // فاصله polling 2 ثانیه
|
||||||
|
const maxPollingTime = 300000; // حداکثر 5 دقیقه polling
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
this.isPolling = true;
|
this.isPolling = true;
|
||||||
|
this.isStreaming = true;
|
||||||
this.isUpdating = true;
|
this.isUpdating = true;
|
||||||
|
let retryCount = 0;
|
||||||
|
const maxRetries = 10;
|
||||||
|
|
||||||
const pollStream = async () => {
|
const pollStream = async () => {
|
||||||
if (!this.isPolling) return;
|
if (!this.isPolling) {
|
||||||
|
console.log('Polling متوقف شد');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی timeout
|
||||||
|
const elapsedTime = Date.now() - startTime;
|
||||||
|
if (elapsedTime > maxPollingTime) {
|
||||||
|
console.log('زمان polling تمام شد');
|
||||||
|
this.output += '\nزمان انتظار تمام شد. فرآیند بهروزرسانی احتمالاً تکمیل شده است.\n';
|
||||||
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.buttonText = this.$t('updateSoftware.completedButton');
|
||||||
|
this.buttonColor = 'success';
|
||||||
|
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = 'اطلاعیه';
|
||||||
|
this.dialogMessage = 'زمان انتظار تمام شد. فرآیند بهروزرسانی احتمالاً تکمیل شده است.';
|
||||||
|
this.dialogColor = 'info';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`--- Polling attempt ${retryCount + 1} --- (${Math.round(elapsedTime/1000)}s elapsed)`);
|
||||||
|
console.log('ارسال درخواست GET به /api/admin/updatecore/stream با UUID:', this.updateUuid);
|
||||||
|
|
||||||
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: 7200000 // تایماوت 2 ساعته برای استریم
|
timeout: 10000 // کاهش timeout برای polling
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('پاسخ polling دریافتی:', response.data);
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
if (typeof data === 'string' && data.startsWith('data: ')) {
|
retryCount = 0; // reset retry count on success
|
||||||
try {
|
|
||||||
const jsonStr = data.substring(data.indexOf('{'));
|
console.log('وضعیت دریافتی:', data.status);
|
||||||
const parsedData = JSON.parse(jsonStr);
|
console.log('خروجی قبلی:', this.output);
|
||||||
|
console.log('خروجی جدید:', data.output);
|
||||||
if (parsedData.output && parsedData.output !== this.output) {
|
|
||||||
this.output = parsedData.output;
|
if (data.output && data.output !== this.output) {
|
||||||
}
|
console.log('خروجی بهروزرسانی شد');
|
||||||
|
this.output = data.output;
|
||||||
this.status = parsedData.status;
|
} else {
|
||||||
} catch (parseError) {
|
console.log('خروجی تغییری نکرده');
|
||||||
console.error('خطا در پردازش پاسخ:', parseError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.status = data.status;
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
if (this.$refs.outputPre) {
|
if (this.$refs.outputPre) {
|
||||||
this.$refs.outputPre.scrollTop = this.$refs.outputPre.scrollHeight;
|
this.$refs.outputPre.scrollTop = this.$refs.outputPre.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.status === 'success' || this.status === 'error') {
|
if (this.status === 'success' || this.status === 'error') {
|
||||||
|
console.log('فرآیند تکمیل شد. وضعیت:', this.status);
|
||||||
this.isPolling = false;
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
this.buttonText = this.status === 'success'
|
this.buttonText = this.status === 'success'
|
||||||
? this.$t('updateSoftware.completedButton')
|
? this.$t('updateSoftware.completedButton')
|
||||||
: this.$t('updateSoftware.failedButton');
|
: this.$t('updateSoftware.failedButton');
|
||||||
this.buttonColor = this.status === 'success' ? 'success' : 'error';
|
this.buttonColor = this.status === 'success' ? 'success' : 'error';
|
||||||
|
|
||||||
this.showResultDialog = true;
|
this.showResultDialog = true;
|
||||||
this.dialogTitle = this.status === 'success'
|
this.dialogTitle = this.status === 'success'
|
||||||
? this.$t('updateSoftware.dialogSuccessTitle')
|
? this.$t('updateSoftware.dialogSuccessTitle')
|
||||||
: this.$t('updateSoftware.dialogErrorTitle');
|
: this.$t('updateSoftware.dialogErrorTitle');
|
||||||
this.dialogMessage = this.status === 'success'
|
this.dialogMessage = this.status === 'success'
|
||||||
? this.$t('updateSoftware.successMessage')
|
? this.$t('updateSoftware.successMessage')
|
||||||
: this.$t('updateSoftware.dialogErrorSimpleMessage');
|
: (data.error || this.$t('updateSoftware.dialogErrorSimpleMessage'));
|
||||||
this.dialogColor = this.status === 'success' ? 'success' : 'error';
|
this.dialogColor = this.status === 'success' ? 'success' : 'error';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`انتظار ${pollInterval}ms برای polling بعدی...`);
|
||||||
setTimeout(pollStream, pollInterval);
|
setTimeout(pollStream, pollInterval);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('خطا در دریافت جریان داده:', error);
|
console.error('خطا در polling:', error);
|
||||||
|
console.log('جزئیات خطای polling:', {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
response: error.response?.data,
|
||||||
|
status: error.response?.status,
|
||||||
|
retryCount: retryCount
|
||||||
|
});
|
||||||
|
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
// اگر خطای شبکه است و تعداد تلاشها کم است، دوباره تلاش کن
|
||||||
|
if ((error.code === 'ECONNABORTED' || error.code === 'NETWORK_ERROR' || !error.response) && retryCount < maxRetries) {
|
||||||
|
console.log(`تلاش مجدد ${retryCount}/${maxRetries}...`);
|
||||||
|
this.output += `\nتلاش مجدد برای دریافت وضعیت... (${retryCount}/${maxRetries})\n`;
|
||||||
|
setTimeout(pollStream, pollInterval * 2); // افزایش فاصله برای retry
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('تعداد تلاشها تمام شد یا خطای غیرقابل حل');
|
||||||
this.isPolling = false;
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
this.output += '\n' + this.$t('updateSoftware.streamError');
|
this.output += '\n' + this.$t('updateSoftware.streamError');
|
||||||
this.isUpdating = false;
|
this.isUpdating = false;
|
||||||
this.buttonColor = 'error';
|
this.buttonColor = 'error';
|
||||||
this.buttonText = this.$t('updateSoftware.failedButton');
|
this.buttonText = this.$t('updateSoftware.failedButton');
|
||||||
|
|
||||||
|
// نمایش خطا به کاربر
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = error.response?.data?.message || error.message || this.$t('updateSoftware.dialogErrorSimpleMessage');
|
||||||
|
this.dialogColor = 'error';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pollStream();
|
pollStream();
|
||||||
},
|
},
|
||||||
|
async checkStatusManually() {
|
||||||
|
console.log('=== بررسی دستی وضعیت ===');
|
||||||
|
console.log('UUID فعلی:', this.updateUuid);
|
||||||
|
|
||||||
|
if (!this.updateUuid) {
|
||||||
|
console.log('هیچ UUID موجود نیست');
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = 'هیچ فرآیند بهروزرسانی فعال نیست';
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isCheckingStatus = true;
|
||||||
|
console.log('ارسال درخواست بررسی وضعیت...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`/api/admin/updatecore/stream`, {
|
||||||
|
params: { uuid: this.updateUuid },
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('پاسخ بررسی وضعیت:', response.data);
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
console.log('وضعیت دریافتی:', data.status);
|
||||||
|
console.log('خروجی دریافتی:', data.output);
|
||||||
|
|
||||||
|
if (data.output && data.output !== this.output) {
|
||||||
|
console.log('خروجی بهروزرسانی شد');
|
||||||
|
this.output = data.output;
|
||||||
|
} else {
|
||||||
|
console.log('خروجی تغییری نکرده');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = data.status;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
if (this.$refs.outputPre) {
|
||||||
|
this.$refs.outputPre.scrollTop = this.$refs.outputPre.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.status === 'success' || this.status === 'error') {
|
||||||
|
console.log('فرآیند تکمیل شد. وضعیت:', this.status);
|
||||||
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.buttonText = this.status === 'success'
|
||||||
|
? this.$t('updateSoftware.completedButton')
|
||||||
|
: this.$t('updateSoftware.failedButton');
|
||||||
|
this.buttonColor = this.status === 'success' ? 'success' : 'error';
|
||||||
|
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.status === 'success'
|
||||||
|
? this.$t('updateSoftware.dialogSuccessTitle')
|
||||||
|
: this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = this.status === 'success'
|
||||||
|
? this.$t('updateSoftware.successMessage')
|
||||||
|
: (data.error || this.$t('updateSoftware.dialogErrorSimpleMessage'));
|
||||||
|
this.dialogColor = this.status === 'success' ? 'success' : 'error';
|
||||||
|
} else {
|
||||||
|
console.log('فرآیند هنوز در حال اجرا است');
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = 'وضعیت فعلی';
|
||||||
|
this.dialogMessage = 'فرآیند بهروزرسانی هنوز در حال اجرا است';
|
||||||
|
this.dialogColor = 'info';
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('خطا در بررسی دستی وضعیت:', error);
|
||||||
|
console.log('جزئیات خطای بررسی دستی:', {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
response: error.response?.data,
|
||||||
|
status: error.response?.status
|
||||||
|
});
|
||||||
|
|
||||||
|
// اگر خطای شبکه است، وضعیت فایل state را نمایش بده
|
||||||
|
if (!error.response) {
|
||||||
|
console.log('خطای شبکه - نمایش وضعیت محلی');
|
||||||
|
this.output += '\nخطا در اتصال به سرور. وضعیت فعلی:\n';
|
||||||
|
this.output += `UUID: ${this.updateUuid}\n`;
|
||||||
|
this.output += 'فرآیند بهروزرسانی احتمالاً تکمیل شده است.\n';
|
||||||
|
this.output += 'لطفاً صفحه را refresh کنید.\n';
|
||||||
|
|
||||||
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.buttonText = this.$t('updateSoftware.completedButton');
|
||||||
|
this.buttonColor = 'success';
|
||||||
|
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = 'اطلاعیه';
|
||||||
|
this.dialogMessage = 'فرآیند بهروزرسانی احتمالاً تکمیل شده است. لطفاً صفحه را refresh کنید.';
|
||||||
|
this.dialogColor = 'info';
|
||||||
|
} else {
|
||||||
|
console.log('خطای HTTP - نمایش پیام خطا');
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = error.response?.data?.message || error.message || 'خطا در بررسی وضعیت';
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.isCheckingStatus = false;
|
||||||
|
console.log('=== پایان بررسی دستی وضعیت ===');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshPage() {
|
||||||
|
console.log('=== Refresh page ===');
|
||||||
|
console.log('وضعیت فعلی قبل از refresh:', {
|
||||||
|
isUpdating: this.isUpdating,
|
||||||
|
isStreaming: this.isStreaming,
|
||||||
|
isPolling: this.isPolling,
|
||||||
|
status: this.status,
|
||||||
|
updateUuid: this.updateUuid
|
||||||
|
});
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
async runUpdateManually() {
|
||||||
|
console.log('=== اجرای دستی بهروزرسانی ===');
|
||||||
|
|
||||||
|
if (!this.updateUuid) {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = 'هیچ فرآیند بهروزرسانی فعال نیست';
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunningManually = true;
|
||||||
|
this.output += '\n=== اجرای دستی بهروزرسانی ===\n';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// اجرای command به صورت دستی
|
||||||
|
const response = await axios.post('/api/admin/updatecore/run-manual', {
|
||||||
|
uuid: this.updateUuid
|
||||||
|
}, {
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
|
timeout: 60000
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('پاسخ اجرای دستی:', response.data);
|
||||||
|
this.output += response.data.output || response.data.message + '\n';
|
||||||
|
|
||||||
|
if (response.data.status === 'success') {
|
||||||
|
this.status = 'success';
|
||||||
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.buttonText = this.$t('updateSoftware.completedButton');
|
||||||
|
this.buttonColor = 'success';
|
||||||
|
|
||||||
|
// بررسی اینکه آیا نرمافزار بهروز است
|
||||||
|
if (response.data.output && response.data.output.includes('Software is already up to date')) {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = 'اطلاعیه';
|
||||||
|
this.dialogMessage = 'نرمافزار در حال حاضر بهروز است. هیچ بهروزرسانی جدیدی موجود نیست.';
|
||||||
|
this.dialogColor = 'info';
|
||||||
|
} else {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogSuccessTitle');
|
||||||
|
this.dialogMessage = response.data.message || 'بهروزرسانی با موفقیت انجام شد';
|
||||||
|
this.dialogColor = 'success';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = response.data.message || 'خطا در اجرای دستی';
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('خطا در اجرای دستی:', error);
|
||||||
|
this.output += 'خطا: ' + (error.response?.data?.message || error.message) + '\n';
|
||||||
|
this.showResultDialog = true;
|
||||||
|
this.dialogTitle = this.$t('updateSoftware.dialogErrorTitle');
|
||||||
|
this.dialogMessage = error.response?.data?.message || error.message || 'خطا در اجرای دستی';
|
||||||
|
this.dialogColor = 'error';
|
||||||
|
} finally {
|
||||||
|
this.isRunningManually = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
async clearCache() {
|
async clearCache() {
|
||||||
this.isClearingCache = true;
|
this.isClearingCache = true;
|
||||||
this.showOutput = true;
|
this.showOutput = true;
|
||||||
|
@ -439,7 +766,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: 7200000 // تایماوت 2 ساعته
|
timeout: 60000 // کاهش timeout به 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;
|
||||||
|
@ -478,7 +805,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: 7200000 // تایماوت 2 ساعته
|
timeout: 300000 // کاهش timeout به 5 دقیقه
|
||||||
});
|
});
|
||||||
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;
|
||||||
|
@ -490,7 +817,7 @@ export default {
|
||||||
this.output += 'خطا: ' + (error.response?.data?.message || error.message) + '\n';
|
this.output += 'خطا: ' + (error.response?.data?.message || error.message) + '\n';
|
||||||
this.showResultDialog = true;
|
this.showResultDialog = true;
|
||||||
this.dialogTitle = 'خطا';
|
this.dialogTitle = 'خطا';
|
||||||
this.dialogMessage = 'خطایی در تغییر حالت رخ داد';
|
this.dialogMessage = error.response?.data?.message || error.message || 'خطایی در تغییر حالت رخ داد';
|
||||||
this.dialogColor = 'error';
|
this.dialogColor = 'error';
|
||||||
} finally {
|
} finally {
|
||||||
this.isChangingEnv = false;
|
this.isChangingEnv = false;
|
||||||
|
@ -501,7 +828,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: 7200000 // تایماوت 2 ساعته
|
timeout: 30000 // کاهش timeout به 30 ثانیه
|
||||||
});
|
});
|
||||||
this.currentCommit = response.data.currentCommit || 'unknown';
|
this.currentCommit = response.data.currentCommit || 'unknown';
|
||||||
this.targetCommit = response.data.targetCommit || 'unknown';
|
this.targetCommit = response.data.targetCommit || 'unknown';
|
||||||
|
@ -515,7 +842,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: 7200000 // تایماوت 2 ساعته
|
timeout: 30000 // کاهش timeout به 30 ثانیه
|
||||||
});
|
});
|
||||||
this.systemInfo = {
|
this.systemInfo = {
|
||||||
osName: response.data.osName || 'unknown',
|
osName: response.data.osName || 'unknown',
|
||||||
|
@ -549,7 +876,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/admin/updatecore/current-source', {
|
const response = await axios.get('/api/admin/updatecore/current-source', {
|
||||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
timeout: 7200000 // تایماوت 2 ساعته
|
timeout: 30000 // کاهش timeout به 30 ثانیه
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.status === 'success') {
|
if (response.data.status === 'success') {
|
||||||
|
@ -575,7 +902,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: 7200000 // تایماوت 2 ساعته
|
timeout: 30000 // کاهش timeout به 30 ثانیه
|
||||||
});
|
});
|
||||||
this.selectedEnv = response.data.env;
|
this.selectedEnv = response.data.env;
|
||||||
this.tempSelectedEnv = response.data.env;
|
this.tempSelectedEnv = response.data.env;
|
||||||
|
@ -596,7 +923,7 @@ export default {
|
||||||
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: 7200000 // تایماوت 2 ساعته
|
timeout: 30000 // کاهش timeout به 30 ثانیه
|
||||||
});
|
});
|
||||||
this.systemLogs = response.data.logs || response.data.message;
|
this.systemLogs = response.data.logs || response.data.message;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -611,7 +938,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: 7200000 // تایماوت 2 ساعته
|
timeout: 30000 // کاهش timeout به 30 ثانیه
|
||||||
});
|
});
|
||||||
if (response.data.status === 'success') {
|
if (response.data.status === 'success') {
|
||||||
this.systemLogs = 'لاگها پاک شدند';
|
this.systemLogs = 'لاگها پاک شدند';
|
||||||
|
@ -651,7 +978,7 @@ export default {
|
||||||
sourceUrl: this.updateSourceUrl.trim()
|
sourceUrl: this.updateSourceUrl.trim()
|
||||||
}, {
|
}, {
|
||||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
timeout: 7200000 // تایماوت 2 ساعته
|
timeout: 120000 // کاهش timeout به 2 دقیقه
|
||||||
});
|
});
|
||||||
|
|
||||||
this.output += response.data.output || response.data.message + '\n';
|
this.output += response.data.output || response.data.message + '\n';
|
||||||
|
@ -689,15 +1016,25 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
console.log('=== Component mounted ===');
|
||||||
|
console.log('شروع بارگذاری اطلاعات اولیه...');
|
||||||
|
|
||||||
this.fetchCommits();
|
this.fetchCommits();
|
||||||
this.fetchSystemInfo();
|
this.fetchSystemInfo();
|
||||||
this.fetchCurrentEnv();
|
this.fetchCurrentEnv();
|
||||||
this.fetchCurrentSource();
|
this.fetchCurrentSource();
|
||||||
this.buttonText = this.$t('updateSoftware.startButton');
|
this.buttonText = this.$t('updateSoftware.startButton');
|
||||||
this.refreshLogs();
|
this.refreshLogs();
|
||||||
|
|
||||||
|
console.log('بارگذاری اطلاعات اولیه تکمیل شد');
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
console.log('=== Component unmounting ===');
|
||||||
|
console.log('متوقف کردن polling و streaming...');
|
||||||
this.isPolling = false;
|
this.isPolling = false;
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.isUpdating = false;
|
||||||
|
console.log('Component cleanup completed');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue