prepare('SELECT * FROM doc_storage WHERE id = ? LIMIT 1'); $stmt->execute([$id]); $file = $stmt->fetch(); if (!$file) { json_error(404, 'File not found'); } $allowed = false; if (!empty($file['idhome'])) { $allowed = user_owns_home($pdo, $user, (int) $file['idhome']); } elseif (!empty($file['owner_id'])) { $allowed = user_owns_owner($pdo, $user, (int) $file['owner_id']); } if (!$allowed) { json_error(403, 'No access to this file'); } if (!is_allowed_upload($_FILES['file']['tmp_name'], $_FILES['file']['name'])) { json_error(422, 'Validation failed', ['file' => ['Only PDF or image files are allowed']]); } $isPerson = ($file['entity_type'] ?? '') === 'person' || empty($file['idhome']); $dir = $isPerson ? $config['persondocs_dir'] : $config['homedocs_dir']; $prefix = $isPerson ? (int) $file['owner_id'] : (int) $file['idhome']; if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) { json_error(500, 'Storage directory unavailable'); } $oldName = basename((string) $file['filename']); $oldPath = $dir . '/' . $oldName; // Never reuse the current name: writing over it would destroy the original // before the row is repointed. $safe = preg_replace('/[^A-Za-z0-9._-]/', '_', basename($_FILES['file']['name'])); $newName = $prefix . '-' . time() . '-' . $safe; while ($newName === $oldName || is_file($dir . '/' . $newName)) { $newName = $prefix . '-' . time() . '-' . bin2hex(random_bytes(3)) . '-' . $safe; } $newPath = $dir . '/' . $newName; if (!move_uploaded_file($_FILES['file']['tmp_name'], $newPath)) { json_error(500, 'Failed to store file'); } // Repoint the row; on failure drop the new file so the old one stays authoritative. try { if (array_key_exists('title', $_POST)) { $title = $_POST['title'] !== '' ? $_POST['title'] : null; $pdo->prepare('UPDATE doc_storage SET filename = ?, title = ? WHERE id = ?') ->execute([$newName, $title, $id]); } else { $pdo->prepare('UPDATE doc_storage SET filename = ? WHERE id = ?')->execute([$newName, $id]); } } catch (Throwable $e) { @unlink($newPath); json_error(500, 'Failed to update document'); } if ($oldPath !== $newPath && is_file($oldPath)) { @unlink($oldPath); } $row = $pdo->prepare('SELECT * FROM doc_storage WHERE id = ? LIMIT 1'); $row->execute([$id]); json_data(present_file($row->fetch()));