193 lines
6.4 KiB
PHP
193 lines
6.4 KiB
PHP
<?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,
|
|
];
|
|
}
|
|
}
|