时间胶囊项目 API 调用信息
时间胶囊 是一个用于存储和展示照片回忆的 Web 应用。前端通过 JavaScript (script.js 和 admin.js) 调用后端 API (photos.php 和 admin.php),实现照片的展示、管理、添加和删除功能。本文档详细描述了所有 API 调用的信息,包括端点、方法、参数、响应格式和使用场景。
当前日期:2025年3月14日
作者:鸭鸭
版本:1.0
项目结构
前端文件:
- index.html:照片展示页面,调用 api/photos.php 获取照片列表。
- assets/js/script.js:前端逻辑,处理照片展示和模态框交互。
- admin.html:管理页面,调用 api/admin.php 进行登录、照片管理和删除。
- assets/js/admin.js:管理页面的前端逻辑。
后端文件:
- api/photos.php:提供照片列表 API。
- api/admin.php:提供登录、照片管理(添加、删除、查询)等 API。
- api/config.php:数据库配置和连接。
API 调用详情
- 获取照片列表 (index.html 和 admin.html)
1.1 获取所有照片 (index.html 调用)
端点:/api/photos.php
方法:GET
参数:无
请求示例:
fetch('/api/photos.php')
.then(response => response.json())
.then(data => {
if (data.success) {
renderPhotos(data.data);
} else {
gallery.innerHTML = `<p class="error">加载照片失败: ${data.message}</p>`;
}
});
响应格式:
成功:
{
"success": true,
"data": [
{
"id": 1,
"url": "https://example.com/photo1.jpg",
"title": "回忆1",
"username": "admin",
"created_at": "2025-03-14 10:00:00"
},
{
"id": 2,
"url": "https://example.com/photo2.jpg",
"title": "回忆2",
"username": "admin",
"created_at": "2025-03-14 11:00:00"
}
]
}
失败:
{
"success": false,
"message": "数据库连接失败,请检查配置"
}
使用场景:
在 index.html 页面加载时,获取所有照片并渲染到画廊中。
用户点击照片时,显示模态框,展示照片详情(标题、上传时间和上传者)。
1.2 获取所有照片 (admin.html 调用)
端点:/api/admin.php
方法:GET
参数:无
请求头:
credentials: 'include':携带会话 cookie 以验证登录状态。
请求示例:
fetch('/api/admin.php', { credentials: 'include' })
.then(response => response.json())
.then(data => {
if (data.success) {
renderPhotos(data.data);
} else {
photoList.innerHTML = `<li class="error">${data.message}</li>`;
}
});
响应格式:
成功:与 photos.php 相同。
未登录:
{
"success": false,
"message": "未登录"
}
失败:
{
"success": false,
"message": "数据库连接失败,请检查配置"
}
使用场景:
在 admin.html 登录后,获取所有照片并渲染到管理页面,供管理员查看和删除,显示照片详情(包括上传者)。
- 管理员登录 (admin.html)
端点:/api/admin.php
方法:POST
参数:
action:login(固定值)
password:管理员密码(经过 SHA-256 哈希)
csrf_token:CSRF 令牌
请求头:
credentials: 'include':携带会话 cookie。
请求示例:
const password = document.getElementById('password').value;
const hashedPassword = await hashPassword(password);
const formData = new FormData();
formData.append('action', 'login');
formData.append('password', hashedPassword);
formData.append('csrf_token', csrfToken);
fetch('/api/admin.php', {
method: 'POST',
body: formData,
credentials: 'include'
})
.then(response => response.json())
.then(data => {
if (data.success) {
loginFormDiv.style.display = 'none';
adminContent.style.display = 'block';
loadPhotos();
} else {
loginMessage.textContent = data.message;
loginMessage.className = 'message error';
}
});
响应格式:
成功:
{
"success": true,
"message": "登录成功"
}
失败:
{
"success": false,
"message": "密码错误"
}
速率限制:
{
"success": false,
"message": "登录尝试过于频繁,请稍后再试"
}
CSRF 失败:
{
"success": false,
"message": "CSRF 验证失败"
}
使用场景:
管理员在 admin.html 输入密码登录,成功后显示照片管理界面。
检查登录状态 (admin.html)
端点:/api/admin.php?check_login=true
方法:GET
参数:
check_login:true(固定值)
请求头:
credentials: 'include':携带会话 cookie。
请求示例:
fetch('/api/admin.php?check_login=true', { credentials: 'include' })
.then(response => response.json())
.then(data => {if (data.success && data.logged_in) { loginFormDiv.style.display = 'none'; adminContent.style.display = 'block'; loadPhotos(); } else { loginFormDiv.style.display = 'block'; adminContent.style.display = 'none'; }
});
响应格式:
已登录:
{
"success": true,
"logged_in": true
}
未登录:
{
"success": true,
"logged_in": false
}
使用场景:
页面加载时检查管理员是否已登录,决定显示登录表单还是管理界面。添加照片 (admin.html)
端点:/api/admin.php
方法:POST
参数:
action:add(固定值)
url:照片 URL(字符串,必填)- 支持格式:
- 普通 URL(例如 https://example.com/photo.jpg)
- VRChat fileId(以 file_ 开头,例如 file_12345,会通过 getPhotoUrlFromApi 解析为实际 URL)
title:照片标题(字符串,可选)
csrf_token:CSRF 令牌
请求头:
credentials: 'include':携带会话 cookie。
请求示例:
const formData = new FormData();
formData.append('action', 'add');
formData.append('url', document.getElementById('url').value); // 支持 file_12345 或 https://example.com/photo.jpg
formData.append('title', document.getElementById('title').value);
formData.append('csrf_token', csrfToken);
fetch('/api/admin.php', {
method: 'POST',
body: formData,
credentials: 'include'
})
.then(response => response.json())
.then(data => {
if (data.success) {
message.textContent = '添加成功!';
message.className = 'message success';
document.getElementById('url').value = '';
document.getElementById('title').value = '';
loadPhotos();
} else {
message.textContent = data.message;
message.className = 'message error';
}
});
响应格式:
成功:
{
"success": true,
"message": "添加成功"
}
失败:
{
"success": false,
"message": "URL 不能为空"
}
或
{
"success": false,
"message": "无效的 URL 格式"
}
或
{
"success": false,
"message": "通过 fileId 获取照片 URL 失败"
}
未登录:
{
"success": false,
"message": "未登录"
}
CSRF 失败:
{
"success": false,
"message": "CSRF 验证失败"
}
使用场景:
管理员在 admin.html 提交照片 URL(支持普通 URL 或 VRChat fileId)和标题,添加到数据库并刷新照片列表。
删除照片 (admin.html)
端点:/api/admin.php?id={id}
方法:DELETE
参数:
id:照片 ID(查询参数,必填)
请求头:
credentials: 'include':携带会话 cookie。
X-CSRF-Token:CSRF 令牌
请求示例:
fetch(/api/admin.php?id=${id}
, {
method: 'DELETE',
credentials: 'include',
headers: {'X-CSRF-Token': csrfToken
}
})
.then(response => response.json())
.then(data => {if (data.success) { message.textContent = '删除成功!'; message.className = 'message success'; loadPhotos(); } else { message.textContent = data.message; message.className = 'message error'; }
});
响应格式:
成功:
{
"success": true,
"message": "删除成功"
}
未登录:
{
"success": false,
"message": "未登录"
}
CSRF 失败:
{
"success": false,
"message": "CSRF 验证失败"
}
使用场景:
管理员在 admin.html 点击“删除”按钮,删除指定照片并刷新列表。退出登录 (admin.html)
端点:/api/admin.php
方法:POST
参数:
action:logout(固定值)
csrf_token:CSRF 令牌
请求头:
credentials: 'include':携带会话 cookie。
请求示例:
fetch('/api/admin.php', {
method: 'POST',
body: new URLSearchParams({ action: 'logout', csrf_token: csrfToken }),
credentials: 'include'
})
.then(response => response.json())
.then(data => {if (data.success) { checkLoginStatus(); }
});
响应格式:
成功:
{
"success": true,
"message": "已退出登录"
}
CSRF 失败:
{
"success": false,
"message": "CSRF 验证失败"
}
使用场景:
管理员在 admin.html 点击“退出登录”,销毁会话并返回登录页面。获取 CSRF 令牌 (admin.html)
端点:/api/admin.php?get_csrf=true
方法:GET
参数:
get_csrf:true(固定值)
请求头:
credentials: 'include':携带会话 cookie。
请求示例:
fetch('/api/admin.php?get_csrf=true', { credentials: 'include' })
.then(response => response.json())
.then(data => {if (data.success) { csrfToken = data.csrf_token; }
});
响应格式:
成功:
{
"success": true,
"csrf_token": "abc123..."
}
使用场景:
页面加载时获取 CSRF 令牌,用于后续 POST 和 DELETE 请求的验证。
安全机制
CSRF 保护:
- 所有 POST 和 DELETE 请求需要携带 CSRF 令牌(csrf_token),通过 admin.php?get_csrf=true 获取。
会话安全:
- 会话 cookie 设置了 HttpOnly, Secure, 和 SameSite=Strict。
- 会话超时:30 分钟(1800 秒)。
速率限制:
- 登录接口限制 5 次失败尝试后锁定 15 分钟(900 秒)。
XSS 防护:
- 前端使用 textContent 渲染用户输入(photo.title),防止脚本注入。
CORS 限制:
- Access-Control-Allow-Origin 设置为受信任域名(http://yourdomain.com)。
密码保护:
- 管理员密码在前端通过 SHA-256 哈希后传输,后端验证哈希值。
注意事项
HTTPS 要求:
- 确保服务器使用 HTTPS,否则密码和会话 cookie 可能被拦截。
环境变量:
- 数据库凭据和管理员密码应存储在环境变量中,避免硬编码。
数据库配置:
- 确保 api/config.php 中的 DB_HOST, DB_USER, DB_PASS, DB_NAME 正确。
依赖:
- 后端需要安装 APCu 扩展(用于速率限制)。
- 确保 PHP 支持 PDO 和 MySQL 扩展。
未来改进建议
前后端分离:
- 当前 API 和前端在同一域名下运行,未来可考虑前后端分离,使用更现代的框架(如 React 或 Vue)重构前端。
API 文档:
- 使用工具(如 Swagger)生成更正式的 API 文档。
日志系统:
- 增强日志记录,跟踪所有 API 调用和错误,便于调试和审计。
用户权限:
- 当前仅支持单一管理员,未来可添加多用户支持和权限管理。