mobile app api

This commit is contained in:
2026-07-27 21:04:07 +03:00
parent 9241e177ac
commit 6913024596
54 changed files with 5156 additions and 0 deletions
@@ -0,0 +1,110 @@
<?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()));