68 lines
2.7 KiB
PHP
68 lines
2.7 KiB
PHP
<?php
|
||
/**
|
||
* 2D/3D 数模库 - 元数据接口(首页库表用)
|
||
* GET 无参 → 返回 library.json 的全部记录(静态 JSON 也行,这里统一走接口)
|
||
* POST {"records":[...]} → 清洗后整体保存(客户端把编辑后的数组传回)
|
||
*
|
||
* 只允许本站页面调用(Sec-Fetch-Site 优先、Referer 兜底,与 api/token.php 同一策略)。
|
||
* 注意这只是防顺手篡改的弱校验:库表只存名称/备注等元数据,不含文件内容。
|
||
*/
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
header('Cache-Control: no-store');
|
||
|
||
$fetchSite = $_SERVER['HTTP_SEC_FETCH_SITE'] ?? '';
|
||
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
||
$host = $_SERVER['HTTP_HOST'] ?? '';
|
||
if ($fetchSite !== '') {
|
||
if ($fetchSite !== 'same-origin' && $fetchSite !== 'same-site') json_out(403, ['error' => 'forbidden']);
|
||
} elseif (!$host || strpos($referer, $host) === false) {
|
||
json_out(403, ['error' => 'forbidden']);
|
||
}
|
||
|
||
function json_out($code, $obj) {
|
||
http_response_code($code);
|
||
echo json_encode($obj, JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
$file = __DIR__ . '/library.json';
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||
if (is_file($file)) { readfile($file); exit; }
|
||
json_out(200, ['records' => []]);
|
||
}
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') json_out(405, ['error' => 'method not allowed']);
|
||
|
||
$in = json_decode((string)file_get_contents('php://input'), true);
|
||
if (!is_array($in) || !isset($in['records']) || !is_array($in['records'])) {
|
||
json_out(400, ['error' => 'bad body']);
|
||
}
|
||
|
||
// 清洗:path 是链接核心(拒绝 ..、限制字符与长度),name/note 纯文本截断
|
||
$records = [];
|
||
$seen = [];
|
||
foreach ($in['records'] as $r) {
|
||
if (!is_array($r)) continue;
|
||
$path = is_string($r['path'] ?? null) ? trim($r['path']) : '';
|
||
$name = is_string($r['name'] ?? null) ? trim($r['name']) : '';
|
||
$note = is_string($r['note'] ?? null) ? trim($r['note']) : '';
|
||
$type = ($r['type'] ?? '') === '2d' ? '2d' : '3d';
|
||
if ($path === '' || strpos($path, '..') !== false || strlen($path) > 300) continue;
|
||
if (!preg_match('/^[\x20-\x7e\x{4e00}-\x{9fff}\/\\\\]+$/u', $path)) continue;
|
||
if (isset($seen[$path])) continue;
|
||
$seen[$path] = true;
|
||
$records[] = [
|
||
'path' => $path,
|
||
'name' => mb_substr($name, 0, 100),
|
||
'type' => $type,
|
||
'note' => mb_substr($note, 0, 200),
|
||
'time' => is_numeric($r['time'] ?? null) ? (int)$r['time'] : time(),
|
||
];
|
||
}
|
||
|
||
// 带锁写文件,防止两个查看器上传时并发写坏
|
||
$json = json_encode(['records' => $records], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
if (file_put_contents($file, $json, LOCK_EX) === false) json_out(500, ['error' => 'write failed']);
|
||
json_out(200, ['ok' => true, 'count' => count($records)]);
|