Files
SoonDesign/backend-web/src/Controllers/FileController.php
T
24kycj 877fd278c2 Web 端数据交互与 design2 布局修复
统一门户 JSON 契约:新增 GET /templates/{id},模板/云文件/保存/打开走虚拟 key;
layer 保存弹层替代 prompt,本地 .soon 已登录后台 POST 登记;修复保存静默失败与 design2 全宽布局。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 01:20:34 +08:00

112 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Soon\Api\Controllers;
use Soon\Api\Core\Json;
use Soon\Api\Middleware\Auth;
use Soon\Api\Services\FileService;
final class FileController
{
public function index(): void
{
$u = Auth::require();
$page = max(1, (int)($_GET['page'] ?? 0));
$size = max(1, min(200, (int)($_GET['size'] ?? 0)));
$limit = $size > 0 ? $size : max(1, min(200, (int)($_GET['limit'] ?? 50)));
$offset = $page > 0 ? ($page - 1) * $limit : max(0, (int)($_GET['offset'] ?? 0));
if ($page > 0) {
$list = FileService::list($u['id'], $limit, $offset);
Json::ok([
'items' => $list,
'total' => FileService::fileCount($u['id']),
'page' => $page,
'size' => $limit,
]);
return;
}
$list = FileService::list($u['id'], $limit, $offset);
Json::ok([
'items' => $list,
'total' => FileService::fileCount($u['id']),
'limit' => $limit,
'offset' => $offset,
]);
}
public function create(): void
{
$u = Auth::require();
$body = Json::readBody();
$name = (string)($body['name'] ?? 'untitled.soon');
$json = (string)($body['json'] ?? '{}');
Json::ok(FileService::create($u['id'], $name, $json));
}
public function show(int $id): void
{
$u = Auth::require();
$row = FileService::fetch($u['id'], $id);
Json::ok([
'id' => (int)$row['id'],
'name' => (string)$row['name'],
'version' => (int)$row['version'],
'size' => (int)$row['size'],
'updated_at' => $row['updated_at'],
'json' => (string)$row['json'],
]);
}
public function update(int $id): void
{
$u = Auth::require();
$body = Json::readBody();
$name = (string)($body['name'] ?? 'untitled.soon');
$json = (string)($body['json'] ?? '{}');
$version = isset($body['version']) ? (int)$body['version'] : null;
Json::ok(FileService::update($u['id'], $id, $name, $json, $version));
}
public function delete(int $id): void
{
$u = Auth::require();
FileService::softDelete($u['id'], $id);
Json::ok(['id' => $id]);
}
public function download(int $id): void
{
$u = Auth::require();
$row = FileService::fetch($u['id'], $id);
$name = (string)$row['name'];
$json = (string)$row['json'];
$size = strlen($json);
$etag = '"' . md5($json) . '"';
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) {
http_response_code(304);
exit;
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . rawurlencode($name) . '"');
header('Content-Length: ' . $size);
header('ETag: ' . $etag);
header('Accept-Ranges: bytes');
header('Cache-Control: private, max-age=0, must-revalidate');
$start = 0;
$end = $size - 1;
if (isset($_SERVER['HTTP_RANGE']) && preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $m)) {
$start = (int)$m[1];
if ($m[2] !== '') $end = (int)$m[2];
if ($end >= $size) $end = $size - 1;
http_response_code(206);
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
header('Content-Length: ' . ($end - $start + 1));
}
$out = fopen('php://output', 'wb');
fwrite($out, substr($json, $start, $end - $start + 1));
fclose($out);
exit;
}
}