netgescon-day0/app/Services/AmministratoreArchiveSyncService.php

193 lines
6.4 KiB
PHP
Executable File

<?php
namespace App\Services;
use App\Models\Amministratore;
use App\Models\UserSetting;
class AmministratoreArchiveSyncService
{
/**
* Sincronizza un archivio legacy (cartella codice stabile) all'interno dell'area dati dell'amministratore.
*
* @param Amministratore $amministratore Amministratore destinatario.
* @param string $legacyCode Codice cartella legacy (es. 0024).
* @param string|null $sourceBasePath Radice archivi legacy. Se nullo, usa UserSetting::get('gescon.source_path').
* @param bool $refresh Se true, sostituisce eventuale directory di destinazione esistente.
* @return array Metadati dell'archivio sincronizzato.
*/
public function sync(Amministratore $amministratore, string $legacyCode, ?string $sourceBasePath = null, bool $refresh = false): array
{
$legacyCode = trim($legacyCode);
if ($legacyCode === '') {
throw new \InvalidArgumentException('Legacy code mancante.');
}
$sourceBasePath = $sourceBasePath
? rtrim($sourceBasePath, DIRECTORY_SEPARATOR)
: rtrim((string) UserSetting::get('gescon.source_path', '/mnt/gescon-archives/gescon'), DIRECTORY_SEPARATOR);
$sourceCandidates = $this->buildSourceCandidates($legacyCode, $sourceBasePath);
$sourcePath = $this->resolveExistingDirectory($sourceCandidates);
if (!$sourcePath) {
throw new \RuntimeException('Archivio legacy non trovato per codice ' . $legacyCode);
}
// Assicura struttura cartelle amministratore
$amministratore->provisionArchiveIfMissing();
$baseArchivePath = $amministratore->getArchivePath();
$this->ensureDirectoryExists($baseArchivePath);
$targetPath = $baseArchivePath . DIRECTORY_SEPARATOR . 'legacy' . DIRECTORY_SEPARATOR . $legacyCode;
$this->ensureDirectoryExists(dirname($targetPath));
if ($refresh && $this->isDirectory($targetPath)) {
$this->deleteDirectory($targetPath);
}
// Copia solo se la directory non esiste oppure è stato richiesto refresh
if (!$this->isDirectory($targetPath)) {
$this->copyDirectory($sourcePath, $targetPath);
}
$meta = $this->inspectDirectory($targetPath);
return [
'legacy_code' => $legacyCode,
'source' => $sourcePath,
'target' => $targetPath,
'copied_at' => date('Y-m-d H:i:s'),
'size_bytes' => $meta['size'],
'files_count' => $meta['files_count'],
'directories_count' => $meta['directories_count'],
'latest_file_at' => $meta['latest_file_at'],
];
}
private function buildSourceCandidates(string $legacyCode, string $basePath): array
{
$candidates = [];
if ($basePath !== '') {
$candidates[] = $basePath . DIRECTORY_SEPARATOR . $legacyCode;
$candidates[] = $basePath . DIRECTORY_SEPARATOR . str_pad($legacyCode, 4, '0', STR_PAD_LEFT);
}
// In alcuni casi il legacy code include già l'intero percorso
if ($this->isDirectory($legacyCode)) {
$candidates[] = $legacyCode;
}
return array_values(array_unique(array_filter($candidates)));
}
private function resolveExistingDirectory(array $candidates): ?string
{
foreach ($candidates as $directory) {
if ($directory && $this->isDirectory($directory)) {
return rtrim($directory, DIRECTORY_SEPARATOR);
}
}
return null;
}
private function ensureDirectoryExists(string $path): void
{
if (!$this->isDirectory($path)) {
if (!mkdir($path, 0755, true) && !is_dir($path)) {
throw new \RuntimeException('Impossibile creare directory: ' . $path);
}
}
}
private function deleteDirectory(string $path): void
{
if (!$this->isDirectory($path)) {
return;
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $item) {
if ($item->isDir()) {
rmdir($item->getPathname());
} else {
unlink($item->getPathname());
}
}
rmdir($path);
}
private function copyDirectory(string $source, string $target): void
{
if (!$this->isDirectory($source)) {
throw new \RuntimeException('Sorgente non valida: ' . $source);
}
$this->ensureDirectoryExists($target);
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($source, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$relativePath = ltrim(substr($item->getPathname(), strlen($source)), DIRECTORY_SEPARATOR);
$destinationPath = $relativePath === ''
? $target
: $target . DIRECTORY_SEPARATOR . $relativePath;
if ($item->isDir()) {
$this->ensureDirectoryExists($destinationPath);
} else {
$this->ensureDirectoryExists(dirname($destinationPath));
if (!copy($item->getPathname(), $destinationPath)) {
throw new \RuntimeException('Impossibile copiare file: ' . $item->getPathname());
}
}
}
}
private function isDirectory(string $path): bool
{
return is_dir($path);
}
private function inspectDirectory(string $path): array
{
$size = 0;
$files = 0;
$dirs = 0;
$latest = null;
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS)
);
foreach ($iterator as $item) {
if ($item->isDir()) {
$dirs++;
continue;
}
$files++;
$size += $item->getSize();
$mtime = $item->getMTime();
if (!$latest || $mtime > $latest) {
$latest = $mtime;
}
}
return [
'size' => $size,
'files_count' => $files,
'directories_count' => $dirs,
'latest_file_at' => $latest ? date('Y-m-d H:i:s', $latest) : null,
];
}
}