6d6f5d571a
- Admin panel page at #Admin/dataMigration with two-column layout - Export: filter by case status, skip attachments option, progress + log - Import: select backup, preserve IDs / skip duplicates, dry run support - List/delete existing backups with full manifest details - 5 API endpoints (status, export, import, listBackups, deleteBackup) - BufferedIO adapter to capture CLI output for API responses - Admin-only access on all endpoints - i18n: English + Hebrew (fa_IR) translations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
123 lines
3.0 KiB
PHP
123 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace Espo\Modules\DataMigration\Api;
|
|
|
|
use Espo\Core\Api\Action;
|
|
use Espo\Core\Api\Request;
|
|
use Espo\Core\Api\Response;
|
|
use Espo\Core\Api\ResponseComposer;
|
|
use Espo\Core\Exceptions\Forbidden;
|
|
use Espo\Entities\User;
|
|
use Espo\ORM\EntityManager;
|
|
use Espo\Modules\DataMigration\Services\ManifestBuilder;
|
|
|
|
class Status implements Action
|
|
{
|
|
private const ENTITY_TYPES = [
|
|
'Case',
|
|
'Account',
|
|
'Contact',
|
|
'CaseActivity',
|
|
'Charge',
|
|
'Invoice',
|
|
'Document',
|
|
'NhMeeting',
|
|
'NhDecision',
|
|
'NhProcess',
|
|
'NhPlea',
|
|
'NhActivity',
|
|
'NhDocument',
|
|
'NhSyncLog',
|
|
'CaseMemory',
|
|
'SignatureRequest',
|
|
'SignatureRequestSigner',
|
|
'SmsLog',
|
|
'PricingAgreement',
|
|
'PricingAgreementRate',
|
|
'FixedActivityRate',
|
|
'ClientRate',
|
|
];
|
|
|
|
public function __construct(
|
|
private EntityManager $entityManager,
|
|
private ManifestBuilder $manifestBuilder,
|
|
private User $user
|
|
) {}
|
|
|
|
public function process(Request $request): Response
|
|
{
|
|
if (!$this->user->isAdmin()) {
|
|
throw new Forbidden();
|
|
}
|
|
|
|
$entityCounts = [];
|
|
$total = 0;
|
|
|
|
foreach (self::ENTITY_TYPES as $entityType) {
|
|
try {
|
|
$count = $this->entityManager
|
|
->getRDBRepository($entityType)
|
|
->count();
|
|
|
|
$entityCounts[$entityType] = $count;
|
|
$total += $count;
|
|
} catch (\Exception $e) {
|
|
// Entity type doesn't exist
|
|
}
|
|
}
|
|
|
|
$attachmentInfo = $this->getAttachmentInfo();
|
|
|
|
return ResponseComposer::json([
|
|
'entityCounts' => $entityCounts,
|
|
'totalRecords' => $total,
|
|
'attachmentFiles' => $attachmentInfo['count'],
|
|
'attachmentSize' => $attachmentInfo['size'],
|
|
'attachmentSizeFormatted' => $this->formatBytes($attachmentInfo['size']),
|
|
]);
|
|
}
|
|
|
|
private function getAttachmentInfo(): array
|
|
{
|
|
$uploadDir = 'data/upload/';
|
|
$count = 0;
|
|
$size = 0;
|
|
|
|
if (is_dir($uploadDir)) {
|
|
$files = scandir($uploadDir);
|
|
|
|
foreach ($files as $file) {
|
|
if ($file === '.' || $file === '..') {
|
|
continue;
|
|
}
|
|
|
|
$path = $uploadDir . $file;
|
|
|
|
if (is_file($path)) {
|
|
$count++;
|
|
$size += filesize($path);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ['count' => $count, 'size' => $size];
|
|
}
|
|
|
|
private function formatBytes(int $bytes): string
|
|
{
|
|
if ($bytes >= 1073741824) {
|
|
return round($bytes / 1073741824, 2) . ' GB';
|
|
}
|
|
|
|
if ($bytes >= 1048576) {
|
|
return round($bytes / 1048576, 2) . ' MB';
|
|
}
|
|
|
|
if ($bytes >= 1024) {
|
|
return round($bytes / 1024, 2) . ' KB';
|
|
}
|
|
|
|
return $bytes . ' B';
|
|
}
|
|
}
|