111 lines
4.0 KiB
PHP
111 lines
4.0 KiB
PHP
<?php
|
|
require __DIR__ . '/_bootstrap.php';
|
|
|
|
/**
|
|
* @OA\Post(
|
|
* path="/document-file-replace.php",
|
|
* tags={"Documents"},
|
|
* summary="Replace the file of an existing document",
|
|
* description="Atomic swap: the new file is written first, the row is repointed, then the old file is removed. Metadata (document_id, expiry, note, created_at) is kept and max_documents is not checked.",
|
|
* security={{"bearerAuth":{}}},
|
|
* @OA\RequestBody(required=true, @OA\MediaType(mediaType="multipart/form-data",
|
|
* @OA\Schema(
|
|
* required={"id","file"},
|
|
* @OA\Property(property="id", type="integer", description="doc_storage.id"),
|
|
* @OA\Property(property="file", type="string", format="binary", description="PDF or image"),
|
|
* @OA\Property(property="title", type="string", description="optional new display name")
|
|
* )
|
|
* )),
|
|
* @OA\Response(response=200, description="Replaced", @OA\JsonContent(
|
|
* @OA\Property(property="data", ref="#/components/schemas/UploadedFile"))),
|
|
* @OA\Response(response=401, ref="#/components/responses/Unauthorized"),
|
|
* @OA\Response(response=403, ref="#/components/responses/Forbidden"),
|
|
* @OA\Response(response=404, ref="#/components/responses/NotFound"),
|
|
* @OA\Response(response=422, ref="#/components/responses/ValidationError")
|
|
* )
|
|
*/
|
|
require_method('POST');
|
|
$user = require_auth($pdo);
|
|
$config = require __DIR__ . '/config.php';
|
|
|
|
$id = (int) ($_POST['id'] ?? 0);
|
|
|
|
$fields = [];
|
|
if ($id <= 0) {
|
|
$fields['id'] = ['Required'];
|
|
}
|
|
if (empty($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
|
$fields['file'] = ['Valid file is required'];
|
|
}
|
|
if ($fields) {
|
|
json_error(422, 'Validation failed', $fields);
|
|
}
|
|
|
|
$stmt = $pdo->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()));
|