初始化版本

This commit is contained in:
LL
2025-11-18 14:30:07 +08:00
commit 78d06a2841
20 changed files with 1346 additions and 0 deletions

149
api.php Normal file
View File

@@ -0,0 +1,149 @@
<?php
// 简易文档APIlist / 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']);