150 lines
4.7 KiB
PHP
150 lines
4.7 KiB
PHP
<?php
|
||
// 简易文档API:list / get / save / delete
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
require_once __DIR__ . '/auth.php';
|
||
require_api_auth();
|
||
|
||
$baseDir = __DIR__ . DIRECTORY_SEPARATOR . 'data';
|
||
$docsDir = $baseDir . DIRECTORY_SEPARATOR . 'docs';
|
||
$uploadsDir = $baseDir . DIRECTORY_SEPARATOR . 'uploads';
|
||
$indexFile = $baseDir . DIRECTORY_SEPARATOR . 'index.json';
|
||
$sharesFile = $baseDir . DIRECTORY_SEPARATOR . 'shares.json';
|
||
|
||
function ensureDirs($dirs) {
|
||
foreach ($dirs as $d) {
|
||
if (!is_dir($d)) {
|
||
mkdir($d, 0777, true);
|
||
}
|
||
}
|
||
}
|
||
|
||
ensureDirs([$baseDir, $docsDir, $uploadsDir]);
|
||
|
||
function readJson($file) {
|
||
if (!file_exists($file)) return [];
|
||
$s = file_get_contents($file);
|
||
$data = json_decode($s, true);
|
||
return is_array($data) ? $data : [];
|
||
}
|
||
|
||
function writeJson($file, $data) {
|
||
file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||
}
|
||
|
||
function safeId($id) {
|
||
// 只允许字母数字下划线和中划线
|
||
return preg_match('/^[a-zA-Z0-9_-]+$/', $id);
|
||
}
|
||
|
||
$action = $_GET['action'] ?? '';
|
||
|
||
if ($action === 'list') {
|
||
$index = readJson($indexFile);
|
||
echo json_encode(['docs' => array_values($index)], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
if ($action === 'get') {
|
||
$id = $_GET['id'] ?? '';
|
||
if (!$id || !safeId($id)) { http_response_code(400); echo json_encode(['error'=>'bad id']); exit; }
|
||
$index = readJson($indexFile);
|
||
$title = '';
|
||
foreach ($index as $it) { if ($it['id'] === $id) { $title = $it['title'] ?? ''; break; } }
|
||
$file = $docsDir . DIRECTORY_SEPARATOR . $id . '.html';
|
||
$content = file_exists($file) ? file_get_contents($file) : '';
|
||
echo json_encode(['id'=>$id,'title'=>$title,'content'=>$content], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
if ($action === 'save') {
|
||
$body = json_decode(file_get_contents('php://input'), true);
|
||
$id = $body['id'] ?? '';
|
||
$title = trim($body['title'] ?? '');
|
||
$content = $body['content'] ?? '';
|
||
|
||
$index = readJson($indexFile);
|
||
if (!$id) {
|
||
$id = 'doc_' . uniqid();
|
||
$index[] = ['id' => $id, 'title' => $title ?: '未命名'];
|
||
} else {
|
||
if (!safeId($id)) { http_response_code(400); echo json_encode(['error'=>'bad id']); exit; }
|
||
$found = false;
|
||
foreach ($index as &$it) {
|
||
if ($it['id'] === $id) { $it['title'] = $title ?: ($it['title'] ?? '未命名'); $found = true; break; }
|
||
}
|
||
if (!$found) { $index[] = ['id' => $id, 'title' => $title ?: '未命名']; }
|
||
}
|
||
writeJson($indexFile, $index);
|
||
|
||
$file = $docsDir . DIRECTORY_SEPARATOR . $id . '.html';
|
||
file_put_contents($file, $content);
|
||
echo json_encode(['ok'=>true,'id'=>$id], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
if ($action === 'delete') {
|
||
$body = json_decode(file_get_contents('php://input'), true);
|
||
$id = $body['id'] ?? '';
|
||
if (!$id || !safeId($id)) { http_response_code(400); echo json_encode(['error'=>'bad id']); exit; }
|
||
$index = readJson($indexFile);
|
||
$index = array_values(array_filter($index, function($it) use ($id) { return $it['id'] !== $id; }));
|
||
writeJson($indexFile, $index);
|
||
$file = $docsDir . DIRECTORY_SEPARATOR . $id . '.html';
|
||
if (file_exists($file)) unlink($file);
|
||
echo json_encode(['ok'=>true]);
|
||
exit;
|
||
}
|
||
|
||
// 生成分享链接(支持永久、24h、一次性)
|
||
if ($action === 'share_create' || $action === 'share') {
|
||
$id = $_GET['id'] ?? '';
|
||
$mode = $_GET['mode'] ?? 'permanent'; // permanent | 24h | one_time
|
||
if (!$id || !safeId($id)) { http_response_code(400); echo json_encode(['error'=>'bad id']); exit; }
|
||
$shares = readJson($sharesFile);
|
||
if (!is_array($shares)) $shares = [];
|
||
// 生成随机令牌
|
||
try {
|
||
$token = bin2hex(random_bytes(16));
|
||
} catch (Throwable $e) {
|
||
$token = uniqid('t_', true);
|
||
}
|
||
$now = time();
|
||
$expiresAt = null;
|
||
$singleUse = false;
|
||
if ($mode === '24h') { $expiresAt = $now + 24*3600; }
|
||
if ($mode === 'one_time') { $singleUse = true; }
|
||
$record = [
|
||
'id' => $id,
|
||
'token' => $token,
|
||
'created_at' => $now,
|
||
'expires_at' => $expiresAt,
|
||
'single_use' => $singleUse,
|
||
'used' => false,
|
||
'revoked' => false,
|
||
];
|
||
$shares[] = $record;
|
||
writeJson($sharesFile, $shares);
|
||
$url = '/share.php?token=' . urlencode($token);
|
||
echo json_encode(['url' => $url, 'token' => $token, 'expires_at' => $expiresAt, 'single_use' => $singleUse], JSON_UNESCAPED_UNICODE);
|
||
exit;
|
||
}
|
||
|
||
// 取消当前文档的所有分享
|
||
if ($action === 'share_revoke_all') {
|
||
$id = $_GET['id'] ?? '';
|
||
if (!$id || !safeId($id)) { http_response_code(400); echo json_encode(['error'=>'bad id']); exit; }
|
||
$shares = readJson($sharesFile);
|
||
if (!is_array($shares)) $shares = [];
|
||
$new = [];
|
||
foreach ($shares as $rec) {
|
||
if (($rec['id'] ?? '') !== $id) { $new[] = $rec; }
|
||
}
|
||
writeJson($sharesFile, $new);
|
||
echo json_encode(['ok' => true]);
|
||
exit;
|
||
}
|
||
|
||
http_response_code(404);
|
||
echo json_encode(['error' => 'unknown action']);
|