From a4cfe4356f43f05bc2347e243ae0433c427823f5 Mon Sep 17 00:00:00 2001 From: Pontoporeia Date: Tue, 7 Jul 2026 19:11:23 +0200 Subject: [PATCH] chore: add explicit spacing to cleanup page article and h1 --- .../admin/actions/_cleanup-stats-data.php | 197 ++++++++++++++++++ .../admin/actions/_peertube-orphans-data.php | 108 ++++++++++ .../admin/actions/cleanup-stats-fragment.php | 129 +++++------- app/public/admin/actions/cleanup-stats.php | 190 +---------------- .../actions/peertube-orphans-fragment.php | 131 +++++------- app/public/admin/actions/peertube-orphans.php | 104 +-------- app/public/assets/css/admin.css | 3 + app/templates/admin/cleanup.php | 42 ++-- app/templates/admin/index.php | 4 +- 9 files changed, 435 insertions(+), 473 deletions(-) create mode 100644 app/public/admin/actions/_cleanup-stats-data.php create mode 100644 app/public/admin/actions/_peertube-orphans-data.php diff --git a/app/public/admin/actions/_cleanup-stats-data.php b/app/public/admin/actions/_cleanup-stats-data.php new file mode 100644 index 0000000..288eb3c --- /dev/null +++ b/app/public/admin/actions/_cleanup-stats-data.php @@ -0,0 +1,197 @@ + 1073741824) { + return number_format($bytes / 1073741824, 1) . ' GB'; + } + if ($bytes > 1048576) { + return number_format($bytes / 1048576, 1) . ' MB'; + } + return number_format($bytes / 1024, 1) . ' KB'; +} + +function getCleanupStats(): array +{ + $storageRoot = STORAGE_ROOT; + $filepondDir = $storageRoot . '/tmp/filepond'; + $trashDir = $storageRoot . '/tmp/_trash'; + $maxAgeFilepond = 7200; // 2h + $maxAgeTrash = 2592000; // 30d + + $sessionSavePath = session_save_path(); + if (!$sessionSavePath || $sessionSavePath === '') { + $sessionSavePath = sys_get_temp_dir(); + } + + $now = time(); + + // ── FilePond stats ─────────────────────────────────────────────────── + $fpStaleCount = 0; + $fpStaleSize = 0; + $fpStaleFiles = []; + $fpActiveCount = 0; + $fpActiveSize = 0; + + if (is_dir($filepondDir)) { + $items = @scandir($filepondDir); + if ($items !== false) { + foreach ($items as $item) { + if ($item === '.' || $item === '..' || $item === '.gitkeep') { + continue; + } + $dirPath = $filepondDir . '/' . $item; + if (!is_dir($dirPath)) { + continue; + } + + $size = dirSizeRecursive($dirPath); + $mtime = filemtime($dirPath); + $ageMinutes = (int)(($now - $mtime) / 60); + + $stale = false; + + // Session-based detection + $manifestPath = $dirPath . '/manifest.json'; + if (file_exists($manifestPath)) { + $manifest = json_decode(file_get_contents($manifestPath), true); + if (is_array($manifest) && !empty($manifest['session_id'])) { + $sessionFile = $sessionSavePath . '/sess_' . $manifest['session_id']; + if (!file_exists($sessionFile)) { + $stale = true; + } + } + } + + // Time-based fallback + if (!$stale && $ageMinutes > ($maxAgeFilepond / 60)) { + $stale = true; + } + + if ($stale) { + $fpStaleCount++; + $fpStaleSize += $size; + $fpStaleFiles[] = [ + 'name' => $item, + 'size' => $size, + 'human' => humanBytes($size), + 'age_minutes' => $ageMinutes, + ]; + } else { + $fpActiveCount++; + $fpActiveSize += $size; + } + } + } + } + + // ── Trash stats ────────────────────────────────────────────────────── + require_once __DIR__ . '/../../../src/Database.php'; + $db = new Database(); + $pdo = $db->getPDO(); + + $existingFileIds = []; + $stmt = $pdo->query('SELECT id FROM thesis_files'); + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $existingFileIds[(int)$row['id']] = true; + } + + $trStaleCount = 0; + $trStaleSize = 0; + $trStaleFiles = []; + $trActiveCount = 0; + $trActiveSize = 0; + + if (is_dir($trashDir)) { + $items = @scandir($trashDir); + if ($items !== false) { + foreach ($items as $item) { + if ($item === '.' || $item === '..') { + continue; + } + $filePath = $trashDir . '/' . $item; + if (!is_file($filePath)) { + continue; + } + + $size = filesize($filePath); + $mtime = filemtime($filePath); + $ageDays = (int)(($now - $mtime) / 86400); + + $stale = false; + + if (preg_match('/^(\d+)_/', $item, $m)) { + $dbId = (int)$m[1]; + if (!isset($existingFileIds[$dbId])) { + $stale = true; + } + } + + if (!$stale && $ageDays > ($maxAgeTrash / 86400)) { + $stale = true; + } + + if ($stale) { + $trStaleCount++; + $trStaleSize += $size; + $trStaleFiles[] = [ + 'name' => $item, + 'size' => $size, + 'human' => humanBytes($size), + 'age_days' => $ageDays, + ]; + } else { + $trActiveCount++; + $trActiveSize += $size; + } + } + } + } + + return [ + 'filepond_stale_count' => $fpStaleCount, + 'filepond_stale_size' => $fpStaleSize, + 'filepond_stale_human' => humanBytes($fpStaleSize), + 'filepond_stale_files' => $fpStaleFiles, + 'filepond_active_count' => $fpActiveCount, + 'filepond_active_size' => $fpActiveSize, + 'filepond_active_human' => humanBytes($fpActiveSize), + 'trash_stale_count' => $trStaleCount, + 'trash_stale_size' => $trStaleSize, + 'trash_stale_human' => humanBytes($trStaleSize), + 'trash_stale_files' => $trStaleFiles, + 'trash_active_count' => $trActiveCount, + 'trash_active_size' => $trActiveSize, + 'trash_active_human' => humanBytes($trActiveSize), + ]; +} diff --git a/app/public/admin/actions/_peertube-orphans-data.php b/app/public/admin/actions/_peertube-orphans-data.php new file mode 100644 index 0000000..52ac35a --- /dev/null +++ b/app/public/admin/actions/_peertube-orphans-data.php @@ -0,0 +1,108 @@ + false, + 'error' => 'PeerTube non configuré.', + ]; + } + + // ── Collect all Peertube UUIDs linked in the DB ────────────────────── + $pdo = $db->getPDO(); + $dbUuids = []; + $linkedMap = []; // uuid → [thesis_id, thesis_title, thesis_identifier] + + $stmt = $pdo->query( + "SELECT tf.file_path, tf.file_name, t.id AS thesis_id, t.title, t.identifier + FROM thesis_files tf + JOIN theses t ON t.id = tf.thesis_id + WHERE tf.file_path LIKE 'peertube_ids:%' + AND t.deleted_at IS NULL" + ); + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $uuid = substr($row['file_path'], strlen('peertube_ids:')); + $dbUuids[$uuid] = true; + $linkedMap[$uuid][] = [ + 'thesis_id' => (int)$row['thesis_id'], + 'title' => $row['title'], + 'identifier' => $row['identifier'] ?? '', + ]; + } + + // ── List all channel videos ────────────────────────────────────────── + try { + $channelVideos = PeerTubeService::listChannelVideos($db); + } catch (\Throwable $e) { + return [ + 'configured' => true, + 'error' => 'Erreur lors du listage des vidéos : ' . $e->getMessage(), + ]; + } + + // ── Find orphans: on channel but not in DB ─────────────────────────── + $orphans = []; + $linked = []; + foreach ($channelVideos as $v) { + $uuid = $v['shortUUID'] ?: $v['uuid']; + if ($uuid === '') { + continue; + } + if (isset($dbUuids[$uuid])) { + $linked[] = [ + 'uuid' => $uuid, + 'name' => $v['name'], + 'theses' => $linkedMap[$uuid] ?? [], + ]; + } else { + $orphans[] = [ + 'uuid' => $uuid, + 'name' => $v['name'], + 'createdAt' => $v['createdAt'], + ]; + } + } + + // ── Find stale DB entries: in DB but not on channel ────────────────── + $stale = []; + foreach ($dbUuids as $uuid => $_) { + $found = false; + foreach ($channelVideos as $v) { + if (($v['shortUUID'] ?: $v['uuid']) === $uuid) { + $found = true; + break; + } + } + if (!$found) { + $stale[] = [ + 'uuid' => $uuid, + 'theses' => $linkedMap[$uuid] ?? [], + ]; + } + } + + $totalOnChannel = count($channelVideos); + + return [ + 'configured' => true, + 'channel_name' => PeerTubeService::getSettings($db)['channel_name'], + 'total_on_channel' => $totalOnChannel, + 'total_linked' => count($linked), + 'orphan_count' => count($orphans), + 'orphans' => $orphans, + 'stale_count' => count($stale), + 'stale_entries' => $stale, + ]; +} diff --git a/app/public/admin/actions/cleanup-stats-fragment.php b/app/public/admin/actions/cleanup-stats-fragment.php index 461d49c..597d07f 100644 --- a/app/public/admin/actions/cleanup-stats-fragment.php +++ b/app/public/admin/actions/cleanup-stats-fragment.php @@ -10,11 +10,8 @@ require_once __DIR__ . '/../../../bootstrap.php'; require_once __DIR__ . '/../../../src/AdminAuth.php'; AdminAuth::requireLogin(); -// Re-use the existing stats endpoint internally -ob_start(); -require __DIR__ . '/cleanup-stats.php'; -$json = ob_get_clean(); -$d = json_decode($json, true); +require_once __DIR__ . '/_cleanup-stats-data.php'; +$d = getCleanupStats(); $fpStale = $d['filepond_stale_count'] ?? 0; $fpActive = $d['filepond_active_count'] ?? 0; @@ -41,13 +38,7 @@ if ($trStale > 0) { } ?> -
- - - Fichiers temporaires - -

✓ Aucun fichier temporaire.

-
+

✓ Aucun fichier temporaire.

@@ -72,71 +63,61 @@ if ($trStale > 0) { 0): ?> -
- - - Téléversements abandonnés - - - - - - - - - - - - - - -
NomTailleÂge
~ min - -
-
+

Téléversements abandonnés

+ + + + + + + + + + + + + +
NomTailleÂge
~ min + +
0): ?> -
- - - Corbeille - - - - - - - - - - - - - - -
NomTailleÂge
~ j - -
-
+

Corbeille

+ + + + + + + + + + + + + +
NomTailleÂge
~ j + +
0 || $trActive > 0): ?> diff --git a/app/public/admin/actions/cleanup-stats.php b/app/public/admin/actions/cleanup-stats.php index b4d4588..692fe39 100644 --- a/app/public/admin/actions/cleanup-stats.php +++ b/app/public/admin/actions/cleanup-stats.php @@ -15,194 +15,10 @@ AdminAuth::requireLogin(); if ($_SERVER['REQUEST_METHOD'] !== 'GET') { http_response_code(405); - exit; + return; } -$storageRoot = STORAGE_ROOT; -$filepondDir = $storageRoot . '/tmp/filepond'; -$trashDir = $storageRoot . '/tmp/_trash'; -$maxAgeFilepond = 7200; // 2h -$maxAgeTrash = 2592000; // 30d - -$sessionSavePath = session_save_path(); -if (!$sessionSavePath || $sessionSavePath === '') { - $sessionSavePath = sys_get_temp_dir(); -} - -$now = time(); - -// ── FilePond stats ─────────────────────────────────────────────────────── -$fpStaleCount = 0; -$fpStaleSize = 0; -$fpStaleFiles = []; -$fpActiveCount = 0; -$fpActiveSize = 0; - -if (is_dir($filepondDir)) { - $items = @scandir($filepondDir); - if ($items !== false) { - foreach ($items as $item) { - if ($item === '.' || $item === '..' || $item === '.gitkeep') { - continue; - } - $dirPath = $filepondDir . '/' . $item; - if (!is_dir($dirPath)) { - continue; - } - - $size = dirSizeRecursive($dirPath); - $mtime = filemtime($dirPath); - $ageMinutes = (int)(($now - $mtime) / 60); - - $stale = false; - - // Session-based detection - $manifestPath = $dirPath . '/manifest.json'; - if (file_exists($manifestPath)) { - $manifest = json_decode(file_get_contents($manifestPath), true); - if (is_array($manifest) && !empty($manifest['session_id'])) { - $sessionFile = $sessionSavePath . '/sess_' . $manifest['session_id']; - if (!file_exists($sessionFile)) { - $stale = true; - } - } - } - - // Time-based fallback - if (!$stale && $ageMinutes > ($maxAgeFilepond / 60)) { - $stale = true; - } - - if ($stale) { - $fpStaleCount++; - $fpStaleSize += $size; - $fpStaleFiles[] = [ - 'name' => $item, - 'size' => $size, - 'human' => humanBytes($size), - 'age_minutes' => $ageMinutes, - ]; - } else { - $fpActiveCount++; - $fpActiveSize += $size; - } - } - } -} - -// ── Trash stats ────────────────────────────────────────────────────────── -require_once __DIR__ . '/../../../src/Database.php'; -$db = new Database(); -$pdo = $db->getPDO(); - -$existingFileIds = []; -$stmt = $pdo->query('SELECT id FROM thesis_files'); -while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $existingFileIds[(int)$row['id']] = true; -} - -$trStaleCount = 0; -$trStaleSize = 0; -$trStaleFiles = []; -$trActiveCount = 0; -$trActiveSize = 0; - -if (is_dir($trashDir)) { - $items = @scandir($trashDir); - if ($items !== false) { - foreach ($items as $item) { - if ($item === '.' || $item === '..') { - continue; - } - $filePath = $trashDir . '/' . $item; - if (!is_file($filePath)) { - continue; - } - - $size = filesize($filePath); - $mtime = filemtime($filePath); - $ageDays = (int)(($now - $mtime) / 86400); - - $stale = false; - - if (preg_match('/^(\d+)_/', $item, $m)) { - $dbId = (int)$m[1]; - if (!isset($existingFileIds[$dbId])) { - $stale = true; - } - } - - if (!$stale && $ageDays > ($maxAgeTrash / 86400)) { - $stale = true; - } - - if ($stale) { - $trStaleCount++; - $trStaleSize += $size; - $trStaleFiles[] = [ - 'name' => $item, - 'size' => $size, - 'human' => humanBytes($size), - 'age_days' => $ageDays, - ]; - } else { - $trActiveCount++; - $trActiveSize += $size; - } - } - } -} +require_once __DIR__ . '/_cleanup-stats-data.php'; header('Content-Type: application/json; charset=utf-8'); -echo json_encode([ - 'filepond_stale_count' => $fpStaleCount, - 'filepond_stale_size' => $fpStaleSize, - 'filepond_stale_human' => humanBytes($fpStaleSize), - 'filepond_stale_files' => $fpStaleFiles, - 'filepond_active_count' => $fpActiveCount, - 'filepond_active_size' => $fpActiveSize, - 'filepond_active_human' => humanBytes($fpActiveSize), - 'trash_stale_count' => $trStaleCount, - 'trash_stale_size' => $trStaleSize, - 'trash_stale_human' => humanBytes($trStaleSize), - 'trash_stale_files' => $trStaleFiles, - 'trash_active_count' => $trActiveCount, - 'trash_active_size' => $trActiveSize, - 'trash_active_human' => humanBytes($trActiveSize), -]); - -// ── Helpers ─────────────────────────────────────────────────────────────── -function dirSizeRecursive(string $dir): int -{ - $size = 0; - if (!is_dir($dir)) { - return 0; - } - $items = @scandir($dir); - if ($items === false) { - return 0; - } - foreach ($items as $item) { - if ($item === '.' || $item === '..') { - continue; - } - $path = $dir . '/' . $item; - if (is_dir($path)) { - $size += dirSizeRecursive($path); - } else { - $size += filesize($path); - } - } - return $size; -} - -function humanBytes(int $bytes): string -{ - if ($bytes > 1073741824) { - return number_format($bytes / 1073741824, 1) . ' GB'; - } - if ($bytes > 1048576) { - return number_format($bytes / 1048576, 1) . ' MB'; - } - return number_format($bytes / 1024, 1) . ' KB'; -} +echo json_encode(getCleanupStats()); diff --git a/app/public/admin/actions/peertube-orphans-fragment.php b/app/public/admin/actions/peertube-orphans-fragment.php index 29da083..4097d89 100644 --- a/app/public/admin/actions/peertube-orphans-fragment.php +++ b/app/public/admin/actions/peertube-orphans-fragment.php @@ -10,94 +10,69 @@ require_once __DIR__ . '/../../../bootstrap.php'; require_once __DIR__ . '/../../../src/AdminAuth.php'; AdminAuth::requireLogin(); -// Re-use the existing JSON endpoint internally -ob_start(); -require __DIR__ . '/peertube-orphans.php'; -$json = ob_get_clean(); -$d = json_decode($json, true); +require_once __DIR__ . '/_peertube-orphans-data.php'; +$d = getPeerTubeOrphansData(); if (!($d['configured'] ?? false)): ?> -
- - - Vidéos PeerTube orphelines - -

⚠️ PeerTube non configuré.

-
+

⚠️ PeerTube non configuré.

-
- - - Vidéos PeerTube orphelines - -

-
+

-
- - - Vidéos PeerTube orphelines vidéos orphelines - 0): ?> - - - - - - - - - - - -
NomDate
- - - - -
+

Vidéos orphelines vidéos orphelines

+ + + + + + + + + + + +
NomDate
+ + + + +
-

✓ Aucune vidéo orpheline.

+

✓ Aucune vidéo orpheline.

-
0): ?> -
- - - Références DB obsolètes - -

Ces UUID sont référencés en base de données mais n'existent plus sur la chaîne PeerTube. Les TFE liés affichent des liens morts.

- - - - - - - - - - -
UUIDTFE(s)
- - ' . htmlspecialchars($label) . ''; - }, $s['theses'])) ?> - -
-
+

Références DB obsolètes

+

Ces UUID sont référencés en base de données mais n'existent plus sur la chaîne PeerTube. Les TFE liés affichent des liens morts.

+ + + + + + + + + + +
UUIDTFE(s)
+ + ' . htmlspecialchars($label) . ''; + }, $s['theses'])) ?> + +
diff --git a/app/public/admin/actions/peertube-orphans.php b/app/public/admin/actions/peertube-orphans.php index c482697..c38b21d 100644 --- a/app/public/admin/actions/peertube-orphans.php +++ b/app/public/admin/actions/peertube-orphans.php @@ -14,108 +14,10 @@ AdminAuth::requireLogin(); if ($_SERVER['REQUEST_METHOD'] !== 'GET') { http_response_code(405); - exit; + return; } -require_once APP_ROOT . '/src/Database.php'; -require_once APP_ROOT . '/src/PeerTubeService.php'; - -$db = new Database(); - -if (!PeerTubeService::isConfigured($db)) { - header('Content-Type: application/json; charset=utf-8'); - echo json_encode([ - 'configured' => false, - 'error' => 'PeerTube non configuré.', - ]); - exit; -} - -// ── Collect all Peertube UUIDs linked in the DB ────────────────────────── -$pdo = $db->getPDO(); -$dbUuids = []; -$linkedMap = []; // uuid → [thesis_id, thesis_title, thesis_identifier] - -$stmt = $pdo->query( - "SELECT tf.file_path, tf.file_name, t.id AS thesis_id, t.title, t.identifier - FROM thesis_files tf - JOIN theses t ON t.id = tf.thesis_id - WHERE tf.file_path LIKE 'peertube_ids:%' - AND t.deleted_at IS NULL" -); -while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $uuid = substr($row['file_path'], strlen('peertube_ids:')); - $dbUuids[$uuid] = true; - $linkedMap[$uuid][] = [ - 'thesis_id' => (int)$row['thesis_id'], - 'title' => $row['title'], - 'identifier' => $row['identifier'] ?? '', - ]; -} - -// ── List all channel videos ────────────────────────────────────────────── -try { - $channelVideos = PeerTubeService::listChannelVideos($db); -} catch (\Throwable $e) { - header('Content-Type: application/json; charset=utf-8'); - echo json_encode([ - 'configured' => true, - 'error' => 'Erreur lors du listage des vidéos : ' . $e->getMessage(), - ]); - exit; -} - -// ── Find orphans: on channel but not in DB ─────────────────────────────── -$orphans = []; -$linked = []; -foreach ($channelVideos as $v) { - $uuid = $v['shortUUID'] ?: $v['uuid']; - if ($uuid === '') { - continue; - } - if (isset($dbUuids[$uuid])) { - $linked[] = [ - 'uuid' => $uuid, - 'name' => $v['name'], - 'theses' => $linkedMap[$uuid] ?? [], - ]; - } else { - $orphans[] = [ - 'uuid' => $uuid, - 'name' => $v['name'], - 'createdAt' => $v['createdAt'], - ]; - } -} - -// ── Find stale DB entries: in DB but not on channel ────────────────────── -$stale = []; -foreach ($dbUuids as $uuid => $_) { - $found = false; - foreach ($channelVideos as $v) { - if (($v['shortUUID'] ?: $v['uuid']) === $uuid) { - $found = true; - break; - } - } - if (!$found) { - $stale[] = [ - 'uuid' => $uuid, - 'theses' => $linkedMap[$uuid] ?? [], - ]; - } -} - -$totalOnChannel = count($channelVideos); +require_once __DIR__ . '/_peertube-orphans-data.php'; header('Content-Type: application/json; charset=utf-8'); -echo json_encode([ - 'configured' => true, - 'channel_name' => PeerTubeService::getSettings($db)['channel_name'], - 'total_on_channel' => $totalOnChannel, - 'total_linked' => count($linked), - 'orphan_count' => count($orphans), - 'orphans' => $orphans, - 'stale_count' => count($stale), - 'stale_entries' => $stale, -]); +echo json_encode(getPeerTubeOrphansData()); diff --git a/app/public/assets/css/admin.css b/app/public/assets/css/admin.css index 167f6b3..34b2e9f 100644 --- a/app/public/assets/css/admin.css +++ b/app/public/assets/css/admin.css @@ -2235,6 +2235,9 @@ th.admin-ap-col { .admin-main--toc > article > section { margin-bottom: var(--space-xl); + border: none; + border-radius: 0; + padding: 0; } .admin-main--toc > article > section > fieldset { diff --git a/app/templates/admin/cleanup.php b/app/templates/admin/cleanup.php index e1fdefe..d0e76d1 100644 --- a/app/templates/admin/cleanup.php +++ b/app/templates/admin/cleanup.php @@ -1,47 +1,29 @@ -
-
-
-
-

- - Nettoyer les fichiers temporaires -

-
-
-
+
+
+

+ + Nettoyer les fichiers temporaires +

-
-
-

Téléversements et corbeille

+
+

Fichiers temporaires

-
- - - Fichiers temporaires - -

Chargement…

-
+

Chargement…

-
-

Vidéos PeerTube

+
+

Vidéos PeerTube

-
- - - Vidéos PeerTube - -

Chargement…

-
+

Chargement…

diff --git a/app/templates/admin/index.php b/app/templates/admin/index.php index 6b476e3..e6213ff 100644 --- a/app/templates/admin/index.php +++ b/app/templates/admin/index.php @@ -28,11 +28,9 @@ Corbeille () - 0): ?> - Nettoyer () + Nettoyer 0 ? " ($tmpTotalCount)" : '' ?> -