MoonAPI 接口文档

自建存储(Cloudflare R2 + KV)驱动的 API 服务。当前提供「每日知识卡片」对外接口,以及配套的后台管理接口。

Base URL 与认证

项值
Base URLhttps://api.lunor.top
认证方式请求头 Authorization: Bearer <API_KEY>(或 X-API-Key: <API_KEY>)
密钥获取登录 管理后台 → 「API 密钥」页创建(形如 mk_live_…)
数据格式JSON(UTF-8),全部接口允许跨域(CORS)
日期口径「每日」按北京时间(UTC+8)自然日计算

每日知识卡片对外 · 需密钥

GET/api/v1/daily-card

返回当日知识卡片。同一天内多次请求返回同一张卡片;日期到达后自动轮换。

查询参数

参数必填说明
date否指定日期,格式 YYYY-MM-DD;缺省为今天

成功响应 200

{
  "code": 0,
  "message": "success",
  "requestId": "8f2c…",
  "data": {
    "date": "2026-09-01",
    "source": "auto",          // pinned=后台排期 | auto=自动轮换 | builtin=内置兜底
    "card": {
      "id": "6f9c…",
      "title": "月亮正以每年约 3.8 厘米的速度远离地球",
      "category": "科学",
      "content": "由于潮汐相互作用……",
      "source": "NASA 月球测距计划",
      "tags": ["天文", "月球"],
      "createdAt": "2026-09-01T05:00:00.000Z",
      "updatedAt": "2026-09-01T05:00:00.000Z"
    }
  }
}
管理后台可为指定日期「排期」固定卡片;未排期的日期按日期哈希在卡片库中自动轮换;卡片库为空时返回内置卡片,接口永不为空。该接口同时接受管理员令牌(后台「试调用」页可直接联调),管理员测试不计入密钥配额。

代理转发对外 · 需密钥

GET/api/v1/proxy/{slug}

把请求转发到后台配置的第三方上游接口(如 api.yujin.cn),JSON 结果包装为 MoonAPI 统一信封(二创转发);二进制结果(图片等)直接透传流。

说明

项值
可用服务在 管理后台 →「代理服务」中查看与配置,无需重新部署
查询参数调用方携带的查询参数会原样透传给上游
管理员调试该接口同时接受管理员令牌,便于后台「测试」按钮直接联调
缓存按服务配置的秒数在 KV 中缓存 JSON 响应(命中时 message: success (cache))

成功响应 200(JSON 上游)

{
  "code": 0,
  "message": "success",
  "requestId": "…",
  "data": {
    "slug": "yujin-root",
    "name": "雨瑾云 · 站点首页",
    "source": "api.yujin.cn",
    "fetchedAt": "2026-09-01T12:00:00.000Z",
    "upstreamStatus": 200,
    "upstream": { /* 上游原始 JSON */ }
  }
}

相关错误

codeHTTP含义
40403404slug 对应的代理服务不存在
40303403代理服务已被管理员停用
50201 / 50401502 / 504上游请求失败 / 超时

媒体文件对外 · 公开

GET/api/v1/media/{id}

分发存储在 R2 中的媒体文件(图片等)。文件由管理后台「媒体库」上传,链接形如 https://api.lunor.top/api/v1/media/<uuid>,可直接用于 <img src>。响应带 24 小时浏览器缓存头。

健康检查公开

GET/api/health

无需认证。返回服务状态及 KV / R2 绑定自检结果(bindings.kv、bindings.r2 均为 true 表示后端存储配置正常)。

管理接口管理员令牌

认证与端点清单

管理接口供后台页面使用,需请求头 Authorization: Bearer <管理员令牌>。令牌在首次访问 /admin 时初始化(仅存哈希)。/api/admin/setup 只能成功调用一次。

方法路径说明
POST/api/admin/setup初始化管理员令牌,请求体 {"token":"…"}(≥8 位)
POST/api/admin/login校验管理员令牌
GET/api/admin/cards卡片列表(索引)。首次使用时自动分批导入内置 342 条资料库(打开后台卡片页即自动完成;完成后每日卡片跨天自动轮换)
POST/api/admin/cards新增卡片(单个对象或 {"cards":[…]} 批量,≤500 条)
GET PUT DELETE/api/admin/cards/{id}读取 / 更新 / 删除单张卡片
POST/api/admin/cards/batch-delete批量删除,请求体 {"ids":[…]}
GET/api/admin/daily排期列表
POST/api/admin/daily设置排期 {"date":"YYYY-MM-DD","cardId":"…"};cardId 为 null 表示清除
GET/api/admin/keysAPI 密钥列表(含每日配额)
POST/api/admin/keys创建密钥 {"name":"备注","dailyQuota":0}(0=不限);吊销 {"action":"revoke","key":"…"}
GET/api/admin/proxy代理服务列表
POST/api/admin/proxy新增/更新代理服务 {slug,name,url,method,cacheTtl,timeoutMs,enabled};删除 {"action":"delete","slug":"…"}
GET/api/admin/media媒体文件列表
POST/api/admin/media上传 {name,contentType,base64}(≤5MB);删除 {"action":"delete","id":"…"}
GET/api/admin/stats?days=7用量统计(近 N 天调用量、按键/接口分布、资源汇总)
GET/api/admin/logs?days=7&limit=100调用日志列表(支持 route / slug / status / requestId 过滤),附 success 汇总;?key= 查询单条明细
POST/api/admin/logs清理调用日志 {"action":"clear","keepDays":30}(keepDays=0 清空;单次最多处理 10 天)
每次对外调用都会在 R2 中写入一条调用明细(含服务端分阶段计时),保留 30 天后自动清理;敏感请求参数(key / token / password 等)写入前自动掩码。
设置过 dailyQuota 的密钥,在每日卡片与代理接口上按北京时间自然日限流,超限返回 42901(HTTP 429)。

卡片字段

字段必填说明
title是标题,≤120 字
content是正文,≤5000 字
category否分类,缺省「未分类」
source否内容来源
tags否字符串数组,≤10 个

响应信封

所有接口返回统一结构:{code, message, data, requestId}。code=0 表示成功,非 0 时 HTTP 状态码同步为非 2xx,message 为可直接展示的错误说明。

错误码表

codeHTTP含义
40000400请求体不是合法 JSON
40001400date 参数格式错误
40002400管理员令牌长度不足
40003 / 40005400批量数量超出 1-500 限制
40004 / 40006400更新内容无效 / 缺少待吊销密钥
40101 / 40102401缺少管理员令牌 / 令牌错误
40103 / 40104401缺少 API Key / Key 无效
40302403API Key 已被吊销
40401404卡片不存在
40901409管理员已初始化,禁止重复 setup
40303403代理服务已停用
40402 / 40403 / 40404404密钥不存在 / 代理服务不存在 / 媒体文件不存在
40007–40013400slug 非法 / 上游地址非法 / method 不支持 / 上传内容缺失或非法 / 清理参数非法(40012)等
41301413媒体文件超过 5MB 上限
42901429API Key 已达每日配额
50201 / 50202 / 50401502上游请求失败 / 上游返回非预期内容(诊断信息随响应返回) / 上游超时
50301503管理员未初始化,需先调用 setup
40405404调用日志条目不存在或已被清理

调用示例

cURL

curl -s https://api.lunor.top/api/v1/daily-card \
  -H "Authorization: Bearer mk_live_xxxxxxxx"

JavaScript(fetch)

const res = await fetch('https://api.lunor.top/api/v1/daily-card', {
  headers: { Authorization: `Bearer ${API_KEY}` },
});
const { code, data } = await res.json();
if (code === 0) {
  console.log(data.date, data.card.title, data.card.content);
}

Python(requests)

import requests

r = requests.get(
    "https://api.lunor.top/api/v1/daily-card",
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=10,
)
r.raise_for_status()
card = r.json()["data"]["card"]
print(card["title"], card["content"], sep="\n")