'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)]);