mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
209 lines
6.3 KiB
PHP
209 lines
6.3 KiB
PHP
<?php
|
|
/**
|
|
* Temp file statistics endpoint (admin).
|
|
*
|
|
* GET /admin/actions/cleanup-stats.php
|
|
*
|
|
* Returns JSON with counts and sizes of tmp/filepond/ and tmp/_trash/,
|
|
* broken down by stale (will be cleaned) vs active (still session-bound).
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../../bootstrap.php';
|
|
require_once __DIR__ . '/../../../src/AdminAuth.php';
|
|
|
|
AdminAuth::requireLogin();
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
|
http_response_code(405);
|
|
exit;
|
|
}
|
|
|
|
$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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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';
|
|
}
|