- request_log 新增 user_agent 列(含旧表 ALTER 迁移)及 ip/status 索引,记录解码后的明文 query(含 key,不脱敏) - 请求日志批量写入改为多值 INSERT 单次往返,替代事务逐条 Exec - 新增按本地时区分天聚合查询,状态页展示今日请求量与近 7 天请求趋势(峰值日高亮) - 新增 TZ 时区支持:.env 配置 TZ 后日志时间戳与状态页全部时间统一生效,二进制内嵌 tzdata 保证容器可用 - 五张表统一补齐 created_at/updated_at 字段,启动时自动补列并按业务时间回填旧行 - device_pool 的 device_header 增加唯一约束;audio_cache 写入改为 UPSERT 修复同 key 重复报错 - 状态页"最近请求"新增 UA 列与限流状态显示;config.yaml 暴露 rate_limit_qps 配置并默认启用请求日志 - deploy.sh 同名版本默认拒绝覆盖,新增 --force/-f 参数显式允许覆盖
807 lines
49 KiB
HTML
807 lines
49 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>番茄小说 API · 运行状态</title>
|
||
<style>
|
||
/* finesse · register=product · A=brick-tomato/warm-oat · B=system-sans+mono
|
||
* C=single-col-strip · D=none · E=warm-glass · SOUL=5 SPECTACLE=2 DENSITY=7 */
|
||
:root {
|
||
--page: #f3efe6;
|
||
--bg: #ffffff;
|
||
--panel-2: #f9f6ef;
|
||
--border: rgba(44, 30, 22, 0.07);
|
||
--ink-1: #1e1712;
|
||
--ink-2: #4a3e35;
|
||
--ink-3: #776a5b;
|
||
--accent: #a8402c;
|
||
--accent-soft: rgba(168, 64, 44, 0.10);
|
||
--on-accent: #ffffff;
|
||
--accent-2: #5c5a3a;
|
||
--accent-2-soft: rgba(92, 90, 58, 0.12);
|
||
--up: #35704a;
|
||
--up-soft: rgba(62, 122, 82, 0.12);
|
||
--up-ring: rgba(62, 122, 82, 0.40);
|
||
--down: #c22e22;
|
||
--down-soft: rgba(194, 46, 34, 0.10);
|
||
--term-bg: #1b1511;
|
||
--term-ink: #e6d9c8;
|
||
--shadow: 0 1px 3px rgba(56, 36, 24, 0.05);
|
||
--border-strong: rgba(44, 30, 22, 0.14);
|
||
--accent-line: rgba(168, 64, 44, 0.35);
|
||
--accent-shadow: 0 2px 6px rgba(168, 64, 44, 0.28);
|
||
--btn-hl: inset 0 1px 0 rgba(255, 255, 255, 0.20);
|
||
--glass: rgba(255, 255, 255, 0.62);
|
||
--glass-bar: rgba(243, 239, 230, 0.78);
|
||
--wash-1: rgba(168, 64, 44, 0.07);
|
||
--wash-2: rgba(92, 90, 58, 0.05);
|
||
--r: 18px;
|
||
--r-sm: 12px;
|
||
--mono: "SF Mono", ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--page: #16110d;
|
||
--bg: #211b16;
|
||
--panel-2: #2b241d;
|
||
--border: rgba(255, 255, 255, 0.07);
|
||
--ink-1: #f2ece3;
|
||
--ink-2: #c9bcab;
|
||
--ink-3: #9a8d7c;
|
||
--accent: #e0705a;
|
||
--accent-soft: rgba(224, 112, 90, 0.14);
|
||
--on-accent: #200e06;
|
||
--accent-2: #b5b37a;
|
||
--accent-2-soft: rgba(181, 179, 122, 0.14);
|
||
--up: #5fc489;
|
||
--up-soft: rgba(95, 196, 137, 0.14);
|
||
--up-ring: rgba(95, 196, 137, 0.40);
|
||
--down: #ff6b5a;
|
||
--down-soft: rgba(255, 107, 90, 0.13);
|
||
--term-bg: #120e0a;
|
||
--term-ink: #d9cdbd;
|
||
--shadow: none;
|
||
--border-strong: rgba(255, 255, 255, 0.13);
|
||
--accent-line: rgba(224, 112, 90, 0.42);
|
||
--accent-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
|
||
--btn-hl: inset 0 1px 0 rgba(255, 255, 255, 0.22);
|
||
--glass: rgba(33, 27, 22, 0.60);
|
||
--glass-bar: rgba(22, 17, 13, 0.74);
|
||
--wash-1: rgba(224, 112, 90, 0.08);
|
||
--wash-2: rgba(181, 179, 122, 0.045);
|
||
}
|
||
}
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
html, body { overflow-x: clip; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||
background: var(--page);
|
||
color: var(--ink-1);
|
||
min-height: 100vh;
|
||
padding: 28px 20px 20px;
|
||
font-size: 14px;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
.num { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
|
||
/* 品牌色晕:给玻璃层提供可模糊的内容 */
|
||
body::before {
|
||
content: '';
|
||
position: fixed; inset: 0; z-index: -1; pointer-events: none;
|
||
background:
|
||
radial-gradient(640px 420px at 85% -5%, var(--wash-1), transparent 65%),
|
||
radial-gradient(760px 540px at -10% 105%, var(--wash-2), transparent 65%);
|
||
}
|
||
.page { max-width: 1120px; margin: 0 auto; }
|
||
|
||
/* ── 顶部品牌行 ─────────────────────── */
|
||
.tophead {
|
||
display: flex; align-items: center; gap: 12px;
|
||
position: sticky; top: 0; z-index: 10;
|
||
margin: -28px -20px 16px; padding: 20px 20px 14px;
|
||
background: var(--glass-bar);
|
||
-webkit-backdrop-filter: blur(14px) saturate(1.3);
|
||
backdrop-filter: blur(14px) saturate(1.3);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.glyph {
|
||
width: 38px; height: 38px; border-radius: 12px; flex-shrink: 0;
|
||
background: var(--accent); color: var(--on-accent);
|
||
display: grid; place-items: center;
|
||
font-size: 18px; font-weight: 800; line-height: 1;
|
||
box-shadow: var(--btn-hl), var(--accent-shadow);
|
||
}
|
||
.tophead h1 { font-size: 18px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.3; }
|
||
.tophead .sub { display: block; font-size: 12px; font-weight: 400; color: var(--ink-3); margin-top: 1px; }
|
||
.clock { margin-left: auto; font-size: 12.5px; color: var(--ink-3); }
|
||
|
||
/* ── 卡片基底 ───────────────────────── */
|
||
.card {
|
||
background: var(--glass);
|
||
-webkit-backdrop-filter: blur(14px) saturate(1.35);
|
||
backdrop-filter: blur(14px) saturate(1.35);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--r);
|
||
padding: 18px 20px;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
|
||
.card-title { font-size: 13px; font-weight: 700; }
|
||
.head-side { font-size: 11.5px; color: var(--ink-3); }
|
||
|
||
/* ── 服务状态条 ─────────────────────── */
|
||
.hero { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 28px; padding: 20px 22px; margin-bottom: 14px; }
|
||
.hero-status { min-width: 0; }
|
||
.live-pill {
|
||
display: inline-flex; align-items: center; gap: 7px;
|
||
font-size: 12px; font-weight: 600; color: var(--up);
|
||
background: var(--up-soft); border-radius: 999px; padding: 4px 11px;
|
||
}
|
||
.dot { width: 8px; height: 8px; border-radius: 999px; background: var(--up); animation: ripple 2.2s ease-out infinite; }
|
||
@keyframes ripple {
|
||
0% { box-shadow: 0 0 0 0 var(--up-ring); }
|
||
70%, 100% { box-shadow: 0 0 0 7px transparent; }
|
||
}
|
||
.uptime-label { font-size: 12px; color: var(--ink-3); margin: 10px 0 2px; }
|
||
.uptime { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; }
|
||
.hero-meta { margin-left: auto; min-width: 220px; }
|
||
.hero-meta .kv { padding: 4px 0; }
|
||
|
||
/* ── KPI 卡 ─────────────────────────── */
|
||
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; margin-bottom: 14px; }
|
||
.kpi-top { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
|
||
.chip-ic {
|
||
width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
|
||
background: var(--panel-2); color: var(--ink-2);
|
||
display: grid; place-items: center;
|
||
}
|
||
.chip-ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.8; }
|
||
.kpi-label { font-size: 12px; font-weight: 600; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.05em; line-height: 1.5; }
|
||
.big { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.2; }
|
||
.big-aside { font-size: 14px; font-weight: 600; color: var(--ink-3); letter-spacing: 0; }
|
||
.big-sub { font-size: 12px; color: var(--ink-3); margin-top: 3px; }
|
||
.kpi .kv { border-top: 1px solid var(--border); margin-top: 8px; padding-top: 8px; }
|
||
.kpi .kv:first-of-type { margin-top: 14px; }
|
||
.kv { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; min-width: 0; }
|
||
.kv .k { color: var(--ink-3); flex-shrink: 0; }
|
||
.kv .v { font-weight: 600; text-align: right; overflow-wrap: anywhere; }
|
||
|
||
/* 比例条 */
|
||
.meter { margin: 12px 0 2px; }
|
||
.meter-cap { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--ink-3); margin-bottom: 5px; }
|
||
.track { height: 6px; border-radius: 999px; background: var(--panel-2); overflow: hidden; }
|
||
.pfill { display: block; height: 100%; border-radius: inherit; background: var(--accent); width: 0; transition: width 0.7s cubic-bezier(0.22, 0.61, 0.36, 1); }
|
||
.no-anim .pfill, .no-anim .sfill { transition: none !important; }
|
||
|
||
/* 状态小徽章 */
|
||
.chip { display: inline-flex; align-items: center; font-size: 11.5px; font-weight: 600; border-radius: 999px; padding: 3px 10px; border: 1px solid var(--border); }
|
||
.chip-up { background: var(--up-soft); color: var(--up); }
|
||
.chip-off { background: var(--panel-2); color: var(--ink-3); }
|
||
.chip-row { margin-top: 10px; }
|
||
|
||
/* 请求分类细条 */
|
||
.srows { margin-top: 12px; display: flex; flex-direction: column; gap: 7px; }
|
||
.srow { display: grid; grid-template-columns: 40px 1fr 48px; gap: 8px; align-items: center; }
|
||
.slabel { font-size: 11.5px; color: var(--ink-3); }
|
||
.strack { height: 5px; border-radius: 999px; background: var(--panel-2); overflow: hidden; }
|
||
.sfill { display: block; height: 100%; border-radius: inherit; background: var(--accent); width: 0; transition: width 0.7s cubic-bezier(0.22, 0.61, 0.36, 1); }
|
||
.sval { font-size: 12px; font-weight: 600; color: var(--ink-2); text-align: right; }
|
||
/* 每日请求行:结构同 srow,宽度服务端算好,不参与 .srow 的 JS 归一化 */
|
||
.drows { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); display: flex; flex-direction: column; gap: 7px; }
|
||
.drow { display: grid; grid-template-columns: 44px 1fr 52px; gap: 8px; align-items: center; }
|
||
.dlabel { font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
|
||
.dval { font-size: 12px; font-weight: 600; color: var(--ink-2); text-align: right; }
|
||
.drow.is-max .dlabel, .drow.is-max .dval { color: var(--accent); }
|
||
|
||
/* ── 最近请求表 ─────────────────────── */
|
||
.req-card { margin-bottom: 14px; overflow: hidden; }
|
||
.twrap { overflow-x: auto; margin: 0 -20px; padding: 0 20px; }
|
||
table { width: 100%; min-width: 640px; border-collapse: collapse; font-size: 12.5px; }
|
||
th {
|
||
text-align: left; font-weight: 600; color: var(--ink-3);
|
||
font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; line-height: 1.5;
|
||
padding: 7px 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
|
||
}
|
||
td { padding: 8px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
|
||
tbody tr { transition: background 0.12s; }
|
||
tbody tr:hover { background: var(--panel-2); }
|
||
tbody tr:last-child td { border-bottom: none; }
|
||
td.code { font-family: var(--mono); font-size: 12px; }
|
||
td.r, th.r { text-align: right; }
|
||
.q { color: var(--ink-3); font-size: 11.5px; }
|
||
.path-cell { max-width: 380px; overflow: hidden; text-overflow: ellipsis; }
|
||
.mth {
|
||
font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: 0.03em;
|
||
background: var(--accent-2-soft); color: var(--accent-2);
|
||
padding: 2px 7px; border-radius: 6px;
|
||
border: 1px solid var(--border);
|
||
}
|
||
.stc { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; font-family: var(--mono); font-size: 11.5px; }
|
||
.stc i { width: 7px; height: 7px; border-radius: 999px; }
|
||
.stc-ok { color: var(--up); } .stc-ok i { background: var(--up); }
|
||
.stc-err { color: var(--down); } .stc-err i { background: var(--down); }
|
||
.empty { padding: 30px 10px; text-align: center; }
|
||
.empty .e-title { font-size: 13px; font-weight: 700; margin-bottom: 6px; }
|
||
.empty .e-desc { font-size: 12px; color: var(--ink-3); line-height: 1.6; }
|
||
.empty code { font-family: var(--mono); font-size: 11.5px; background: var(--panel-2); border-radius: 6px; padding: 1px 6px; }
|
||
|
||
/* ── API 文档 ───────────────────────── */
|
||
.docs details { border-top: 1px solid var(--border); }
|
||
.docs details:first-of-type { border-top: none; }
|
||
.docs summary {
|
||
list-style: none; display: flex; align-items: center; gap: 10px;
|
||
padding: 12px 8px; margin: 0 -8px; border-radius: 10px; cursor: pointer;
|
||
}
|
||
.docs summary::-webkit-details-marker { display: none; }
|
||
.docs summary:hover { background: var(--panel-2); }
|
||
.chev { display: grid; place-items: center; color: var(--ink-3); transition: transform 0.2s ease; }
|
||
.chev svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; }
|
||
details[open] .chev { transform: rotate(90deg); }
|
||
.grp-name { font-size: 13.5px; font-weight: 700; }
|
||
.cnt {
|
||
font-size: 11px; font-weight: 600; color: var(--ink-3);
|
||
background: var(--panel-2); border-radius: 999px; padding: 1px 8px;
|
||
border: 1px solid var(--border);
|
||
}
|
||
.eps { padding: 2px 0 10px; }
|
||
.api-endpoint { padding: 10px 10px; margin: 0 -10px 2px; border-top: 1px solid var(--border); border-radius: 10px; }
|
||
.api-endpoint:first-child { border-top: none; }
|
||
.api-endpoint:hover { background: var(--panel-2); }
|
||
.ep-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
|
||
.api-path { font-family: var(--mono); font-size: 13px; font-weight: 700; letter-spacing: -0.01em; }
|
||
.ep-desc { font-size: 12px; color: var(--ink-3); }
|
||
.api-params { display: flex; flex-wrap: wrap; gap: 5px 16px; margin-top: 8px; }
|
||
.param-item { display: inline-flex; align-items: baseline; gap: 6px; font-size: 12px; }
|
||
.param-name { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--accent); }
|
||
.req, .opt { font-size: 10.5px; font-weight: 700; border-radius: 5px; padding: 1px 6px; border: 1px solid var(--border); }
|
||
.req { background: var(--accent-soft); color: var(--accent); }
|
||
.opt { background: var(--panel-2); color: var(--ink-3); }
|
||
.param-desc { color: var(--ink-3); font-size: 11.5px; }
|
||
|
||
/* ── 行内测试 ───────────────────────── */
|
||
.test-btn {
|
||
margin-left: auto; font-size: 11.5px; font-weight: 600; white-space: nowrap;
|
||
color: var(--accent); background: var(--bg);
|
||
border: 1px solid var(--accent-line); border-radius: 999px;
|
||
padding: 4px 13px; cursor: pointer;
|
||
box-shadow: var(--shadow);
|
||
transition: background 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.1s;
|
||
}
|
||
.test-btn:hover, .test-btn.on { background: var(--accent-soft); border-color: var(--accent); }
|
||
.test-btn:active { transform: translateY(1px); }
|
||
.test-box { margin-top: 10px; padding: 12px; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--r-sm); }
|
||
.test-param { display: flex; align-items: center; gap: 8px; margin: 5px 0; }
|
||
.test-param > span {
|
||
width: 110px; flex-shrink: 0; font-size: 11.5px; color: var(--ink-3);
|
||
font-family: var(--mono);
|
||
}
|
||
.test-param input {
|
||
flex: 1; min-width: 0; padding: 7px 10px; font-size: 12.5px; font-family: var(--mono);
|
||
border: 1px solid var(--border); border-radius: 9px;
|
||
background: var(--bg); color: var(--ink-1); outline: none;
|
||
transition: border-color 0.12s, box-shadow 0.12s;
|
||
}
|
||
.test-param input:hover:not(:focus) { border-color: var(--border-strong); }
|
||
.test-param input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
|
||
.test-send {
|
||
margin-top: 9px; padding: 7px 18px; font-size: 12.5px; font-weight: 700;
|
||
background: var(--accent); color: var(--on-accent);
|
||
border: none; border-radius: 9px; cursor: pointer;
|
||
box-shadow: var(--btn-hl), var(--accent-shadow);
|
||
transition: filter 0.15s, box-shadow 0.15s, transform 0.1s;
|
||
}
|
||
.test-send:hover { filter: brightness(1.07); }
|
||
.test-send:active { transform: translateY(1px); box-shadow: var(--btn-hl); }
|
||
.test-result {
|
||
margin-top: 10px; max-height: 300px; overflow: auto;
|
||
background: var(--term-bg); color: var(--term-ink);
|
||
padding: 12px; border-radius: 10px;
|
||
font-family: var(--mono); font-size: 11.5px; line-height: 1.6;
|
||
white-space: pre-wrap; word-break: break-all;
|
||
}
|
||
.twrap, .test-result { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
|
||
.twrap::-webkit-scrollbar, .test-result::-webkit-scrollbar { width: 8px; height: 8px; }
|
||
.twrap::-webkit-scrollbar-track, .test-result::-webkit-scrollbar-track { background: transparent; }
|
||
.twrap::-webkit-scrollbar-thumb, .test-result::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; }
|
||
|
||
/* ── 页脚与焦点 ─────────────────────── */
|
||
.foot { font-size: 11.5px; color: var(--ink-3); text-align: center; margin: 20px 0 4px; }
|
||
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||
|
||
@media (max-width: 640px) {
|
||
body { padding: 20px 14px 14px; }
|
||
.card { padding: 16px; border-radius: 16px; }
|
||
.twrap { margin: 0 -16px; padding: 0 16px; }
|
||
.uptime { font-size: 21px; }
|
||
.hero-meta { margin-left: 0; width: 100%; }
|
||
.ep-desc { display: none; }
|
||
.tophead { margin: -20px -14px 14px; padding: 12px 16px 10px; }
|
||
.kpis { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
|
||
}
|
||
/* 不支持毛玻璃时退回实色卡片 */
|
||
@supports not (backdrop-filter: blur(1px)) {
|
||
:root { --glass: #ffffff; --glass-bar: #f3efe6; }
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
@supports not (backdrop-filter: blur(1px)) {
|
||
:root { --glass: #211b16; --glass-bar: #16110d; }
|
||
}
|
||
}
|
||
@media (prefers-reduced-transparency: reduce) {
|
||
body::before { display: none; }
|
||
.card, .tophead { -webkit-backdrop-filter: none; backdrop-filter: none; }
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
|
||
}
|
||
</style>
|
||
<script type="text/javascript">
|
||
var RM = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
var firstLoad = true;
|
||
|
||
window.onload = function () {
|
||
enhance();
|
||
injectTestButtons();
|
||
setInterval(autoRefresh, 3000);
|
||
};
|
||
|
||
// 自动刷新:只更新数据区(状态条/KPI/最近请求),不清空文档区和正在使用的测试表单
|
||
function autoRefresh() {
|
||
fetch(window.location.href)
|
||
.then(function (response) { return response.text(); })
|
||
.then(function (html) {
|
||
var doc = new DOMParser().parseFromString(html, 'text/html');
|
||
var fresh = doc.getElementById('stats');
|
||
if (!fresh) return;
|
||
var stats = document.getElementById('stats');
|
||
stats.classList.add('no-anim'); // 刷新时条形图直接落位,不重播生长动画
|
||
stats.innerHTML = fresh.innerHTML;
|
||
firstLoad = false;
|
||
enhance();
|
||
requestAnimationFrame(function () {
|
||
requestAnimationFrame(function () { stats.classList.remove('no-anim'); });
|
||
});
|
||
})
|
||
.catch(function (err) { console.error('刷新失败:', err); });
|
||
}
|
||
|
||
// 条形图宽度 + 首次载入的数字滚动
|
||
function enhance() {
|
||
var rows = document.querySelectorAll('.srow');
|
||
var max = 1, i;
|
||
for (i = 0; i < rows.length; i++) max = Math.max(max, +rows[i].getAttribute('data-count') || 0);
|
||
for (i = 0; i < rows.length; i++) {
|
||
var v = +rows[i].getAttribute('data-count') || 0;
|
||
rows[i].querySelector('.sfill').style.width = (v / max * 100) + '%';
|
||
}
|
||
document.querySelectorAll('.pfill').forEach(function (el) {
|
||
var a = +el.getAttribute('data-ratio'), b = +el.getAttribute('data-of') || 1;
|
||
if (isFinite(a)) el.style.width = Math.max(0, Math.min(100, a / b * 100)) + '%';
|
||
});
|
||
if (firstLoad && !RM) {
|
||
document.querySelectorAll('[data-count-num]').forEach(function (el) {
|
||
var target = +el.getAttribute('data-count-num') || 0;
|
||
if (target <= 0) return;
|
||
var t0 = performance.now(), dur = 600;
|
||
(function tick(t) {
|
||
var p = Math.min(1, (t - t0) / dur);
|
||
p = 1 - Math.pow(1 - p, 3);
|
||
el.textContent = String(Math.round(target * p));
|
||
if (p < 1) requestAnimationFrame(tick);
|
||
})(t0);
|
||
});
|
||
}
|
||
}
|
||
|
||
// 为每个业务接口注入"测试"按钮(状态页自身除外)
|
||
function injectTestButtons() {
|
||
document.querySelectorAll('.api-endpoint').forEach(function (ep) {
|
||
var path = ep.querySelector('.api-path').textContent.trim();
|
||
if (path === '/status') return;
|
||
var btn = document.createElement('button');
|
||
btn.type = 'button';
|
||
btn.className = 'test-btn';
|
||
btn.textContent = '测试';
|
||
btn.onclick = function () { toggleTest(ep, path); };
|
||
ep.querySelector('.ep-head').appendChild(btn);
|
||
});
|
||
}
|
||
|
||
// 展开/收起测试表单;参数清单直接读该接口的参数文档 DOM,零重复维护
|
||
function toggleTest(ep, path) {
|
||
var existing = ep.querySelector('.test-box');
|
||
var btn = ep.querySelector('.test-btn');
|
||
if (btn) btn.classList.toggle('on', !existing); // 面板开着时按钮保持点亮
|
||
if (existing) { existing.remove(); return; }
|
||
|
||
var box = document.createElement('div');
|
||
box.className = 'test-box';
|
||
ep.querySelectorAll('.param-item').forEach(function (p) {
|
||
var name = p.querySelector('.param-name').textContent.trim();
|
||
var required = p.querySelector('.req') ? ' *' : '';
|
||
box.insertAdjacentHTML('beforeend',
|
||
'<div class="test-param"><span>' + name + required + '</span>' +
|
||
'<input data-name="' + name + '" placeholder="' + name + '"></div>');
|
||
});
|
||
if (!box.children.length) {
|
||
box.insertAdjacentHTML('beforeend', '<div class="test-param">无参数</div>');
|
||
}
|
||
var send = document.createElement('button');
|
||
send.type = 'button';
|
||
send.className = 'test-send';
|
||
send.textContent = '发送请求';
|
||
send.onclick = function () { sendTest(ep, path); };
|
||
box.appendChild(send);
|
||
var result = document.createElement('pre');
|
||
result.className = 'test-result';
|
||
box.appendChild(result);
|
||
ep.appendChild(box);
|
||
}
|
||
|
||
function sendTest(ep, path) {
|
||
var result = ep.querySelector('.test-result');
|
||
var sp = new URLSearchParams();
|
||
var key = new URLSearchParams(location.search).get('key');
|
||
if (key) sp.set('key', key); // protect_all 开启时业务接口需要密钥
|
||
ep.querySelectorAll('.test-box input[data-name]').forEach(function (i) {
|
||
if (i.value.trim() !== '') sp.set(i.dataset.name, i.value.trim());
|
||
});
|
||
result.textContent = '请求中...';
|
||
fetch(path + '?' + sp.toString())
|
||
.then(function (r) { return r.text(); })
|
||
.then(function (t) {
|
||
try { t = JSON.stringify(JSON.parse(t), null, 2); } catch (e) {}
|
||
result.textContent = t; // textContent 写入,无注入面
|
||
})
|
||
.catch(function (err) { result.textContent = '请求失败: ' + err.message; });
|
||
}
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<div class="page">
|
||
<div id="stats">
|
||
<header class="tophead">
|
||
<span class="glyph" aria-hidden="true">番</span>
|
||
<div>
|
||
<h1>番茄小说 API</h1>
|
||
<span class="sub">自部署实例 · 运行状态</span>
|
||
</div>
|
||
<span class="clock num">{{.NowTime}}</span>
|
||
</header>
|
||
|
||
<section class="hero card">
|
||
<div class="hero-status">
|
||
<span class="live-pill"><i class="dot" aria-hidden="true"></i>服务运行中</span>
|
||
<div class="uptime-label">持续运行</div>
|
||
<div class="uptime num">{{.Uptime}}</div>
|
||
</div>
|
||
<div class="hero-meta">
|
||
<div class="kv"><span class="k">签名算法</span><span class="v num">{{.Signature}}</span></div>
|
||
<div class="kv"><span class="k">启动时间</span><span class="v num">{{.StartTime}}</span></div>
|
||
<div class="kv"><span class="k">限流</span><span class="v num">{{.RateLimit}}</span></div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="kpis">
|
||
<section class="card kpi">
|
||
<div class="kpi-top">
|
||
<span class="chip-ic" aria-hidden="true"><svg viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5"/><path d="M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3"/></svg></span>
|
||
<span class="kpi-label">数据库</span>
|
||
</div>
|
||
<div class="big num">{{.DBTotal}}</div>
|
||
<div class="big-sub">数据库文件大小</div>
|
||
<div class="meter">
|
||
<div class="meter-cap"><span>空间占用</span><span class="num">{{printf "%.1f" .DBUsedPct}}%</span></div>
|
||
<div class="track"><i class="pfill" data-ratio="{{.DBUsedPct}}" data-of="100"></i></div>
|
||
</div>
|
||
<div class="kv"><span class="k">已用空间</span><span class="v num">{{.DBUsed}}</span></div>
|
||
<div class="kv"><span class="k">空闲空间</span><span class="v num">{{.DBFree}}</span></div>
|
||
</section>
|
||
|
||
<section class="card kpi">
|
||
<div class="kpi-top">
|
||
<span class="chip-ic" aria-hidden="true"><svg viewBox="0 0 24 24"><circle cx="8" cy="15" r="4.5"/><path d="M11.5 11.5 21 2m-5 1 3 3m-6 0 3 3"/></svg></span>
|
||
<span class="kpi-label">设备密钥池</span>
|
||
</div>
|
||
<div class="big num"><span data-count-num="{{.PoolValid}}">{{.PoolValid}}</span><span class="big-aside"> / {{.PoolTotal}}</span></div>
|
||
<div class="big-sub">有效设备 / 总设备数</div>
|
||
<div class="chip-row"><span class="chip {{if eq .PoolStatus "已启用"}}chip-up{{else}}chip-off{{end}}">{{.PoolStatus}}</span></div>
|
||
<div class="meter">
|
||
<div class="meter-cap"><span>有效率</span></div>
|
||
<div class="track"><i class="pfill" data-ratio="{{.PoolValid}}" data-of="{{.PoolTotal}}"></i></div>
|
||
</div>
|
||
<div class="kv"><span class="k">无效设备</span><span class="v num">{{.PoolInvalid}}</span></div>
|
||
<div class="kv"><span class="k">当前索引</span><span class="v num">{{.PoolIndex}}</span></div>
|
||
</section>
|
||
|
||
<section class="card kpi">
|
||
<div class="kpi-top">
|
||
<span class="chip-ic" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M3 12h4l3-8 4 16 3-8h4"/></svg></span>
|
||
<span class="kpi-label">请求统计</span>
|
||
</div>
|
||
<div class="big num" data-count-num="{{.StatTotal}}">{{.StatTotal}}</div>
|
||
<div class="big-sub">累计业务请求 · 今日 <span class="num">{{.TodayCount}}</span> 次</div>
|
||
<div class="srows">
|
||
<div class="srow" data-count="{{.StatSearch}}"><span class="slabel">搜索</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatSearch}}</span></div>
|
||
<div class="srow" data-count="{{.StatDetail}}"><span class="slabel">详情</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatDetail}}</span></div>
|
||
<div class="srow" data-count="{{.StatCatalog}}"><span class="slabel">目录</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatCatalog}}</span></div>
|
||
<div class="srow" data-count="{{.StatContent}}"><span class="slabel">正文</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatContent}}</span></div>
|
||
<div class="srow" data-count="{{.StatManga}}"><span class="slabel">漫画</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatManga}}</span></div>
|
||
<div class="srow" data-count="{{.StatAudio}}"><span class="slabel">音频</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatAudio}}</span></div>
|
||
<div class="srow" data-count="{{.StatVideo}}"><span class="slabel">视频</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatVideo}}</span></div>
|
||
<div class="srow" data-count="{{.StatCategory}}"><span class="slabel">分类</span><span class="strack"><i class="sfill"></i></span><span class="sval num">{{.StatCategory}}</span></div>
|
||
</div>
|
||
{{if .DailyStats}}
|
||
<div class="drows">
|
||
{{range .DailyStats}}
|
||
<div class="drow{{if .MaxCount}} is-max{{end}}"><span class="dlabel">{{.Day}}</span><span class="strack"><i class="sfill" style="width: {{.Pct}}%"></i></span><span class="dval num">{{.Count}}</span></div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
</section>
|
||
|
||
<section class="card kpi">
|
||
<div class="kpi-top">
|
||
<span class="chip-ic" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M3 8h18v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V8z"/><path d="M2 4h20v4H2z"/><path d="M10 12h4"/></svg></span>
|
||
<span class="kpi-label">缓存</span>
|
||
</div>
|
||
<div class="big num" data-count-num="{{.CacheCount}}">{{.CacheCount}}</div>
|
||
<div class="big-sub">正文缓存条数</div>
|
||
<div class="chip-row"><span class="chip {{if eq .CacheStatus "已启用"}}chip-up{{else}}chip-off{{end}}">{{.CacheStatus}}</span></div>
|
||
<div class="kv"><span class="k">自动清理</span><span class="v">{{.CleanupStatus}}</span></div>
|
||
<div class="kv"><span class="k">清理间隔</span><span class="v">{{.CleanupInterval}}</span></div>
|
||
</section>
|
||
</div>
|
||
|
||
<section class="card req-card">
|
||
<div class="card-head">
|
||
<span class="card-title">最近请求</span>
|
||
<span class="head-side num">20 条 · 3 秒自动刷新</span>
|
||
</div>
|
||
{{if .RecentRequests}}
|
||
<div class="twrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>时间</th>
|
||
<th>方法</th>
|
||
<th>路径</th>
|
||
<th>IP</th>
|
||
<th>UA</th>
|
||
<th>状态</th>
|
||
<th class="r">耗时</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{range .RecentRequests}}
|
||
<tr>
|
||
<td class="num">{{.Time}}</td>
|
||
<td><span class="mth">{{.Method}}</span></td>
|
||
<td class="code path-cell" title="{{.Path}}">{{.Path}}{{if .Query}} <span class="q">?{{.Query}}</span>{{end}}</td>
|
||
<td class="code">{{.IP}}</td>
|
||
<td class="code" title="{{.UA}}">{{.UA}}</td>
|
||
<td><span class="stc {{if .IsErr}}stc-err{{else}}stc-ok{{end}}"><i aria-hidden="true"></i>{{.Status}}</span></td>
|
||
<td class="r num">{{.Duration}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{{else}}
|
||
<div class="empty">
|
||
<div class="e-title">暂无请求记录</div>
|
||
<div class="e-desc">未开启请求日志,或服务启动后还没有收到业务请求。<br>可在配置文件中将 <code>server.request_log.enabled</code> 设为 <code>true</code> 后重启。</div>
|
||
</div>
|
||
{{end}}
|
||
</section>
|
||
</div>
|
||
|
||
<section class="docs card">
|
||
<div class="card-head">
|
||
<span class="card-title">API 接口文档</span>
|
||
<span class="head-side">分组可折叠 · 点"测试"直接调试</span>
|
||
</div>
|
||
|
||
<details class="grp" open>
|
||
<summary>
|
||
<span class="chev" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></span>
|
||
<span class="grp-name">书籍相关</span>
|
||
<span class="cnt num">6</span>
|
||
</summary>
|
||
<div class="eps">
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/search</span><span class="ep-desc">搜索书籍</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">query</span><span class="req">必需</span><span class="param-desc">搜索关键字</span></div>
|
||
<div class="param-item"><span class="param-name">offset</span><span class="opt">可选</span><span class="param-desc">偏移量,默认0</span></div>
|
||
<div class="param-item"><span class="param-name">tab_type</span><span class="opt">可选</span><span class="param-desc">类型,默认3</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/detail</span><span class="ep-desc">获取书籍详情</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/info</span><span class="ep-desc">获取书籍详情(同 detail)</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/catalog</span><span class="ep-desc">获取书籍目录</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/directory/all_items</span><span class="ep-desc">获取书籍目录(同 catalog)</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/multi-detail</span><span class="ep-desc">批量获取书籍详情</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID列表,逗号分隔</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="grp">
|
||
<summary>
|
||
<span class="chev" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></span>
|
||
<span class="grp-name">内容相关</span>
|
||
<span class="cnt num">3</span>
|
||
</summary>
|
||
<div class="eps">
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/content</span><span class="ep-desc">获取章节正文内容</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">item_id</span><span class="req">必需</span><span class="param-desc">章节ID,支持路径参数或查询参数</span></div>
|
||
<div class="param-item"><span class="param-name">text_mode</span><span class="opt">可选</span><span class="param-desc">纯文本模式:1/true=纯文本,0/false=HTML</span></div>
|
||
<div class="param-item"><span class="param-name">image_mode</span><span class="opt">可选</span><span class="param-desc">图片处理:1/true=保留图片,0/false=移除图片</span></div>
|
||
<div class="param-item"><span class="param-name">review</span><span class="opt">可选</span><span class="param-desc">段评功能:1=启用,0=禁用</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/multi-content</span><span class="ep-desc">批量获取多个章节正文</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
<div class="param-item"><span class="param-name">item_ids</span><span class="req">必需</span><span class="param-desc">章节ID列表,多个ID用逗号分隔,如:123,456,789</span></div>
|
||
<div class="param-item"><span class="param-name">text_mode</span><span class="opt">可选</span><span class="param-desc">纯文本模式:1/true=纯文本,0/false=HTML</span></div>
|
||
<div class="param-item"><span class="param-name">image_mode</span><span class="opt">可选</span><span class="param-desc">图片处理:1/true=保留图片,0/false=移除图片</span></div>
|
||
<div class="param-item"><span class="param-name">ignore_failed</span><span class="opt">可选</span><span class="param-desc">忽略失败章节:1/true=忽略,0/false=不忽略</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/item-summary</span><span class="ep-desc">获取章节摘要信息</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
<div class="param-item"><span class="param-name">item_ids</span><span class="req">必需</span><span class="param-desc">章节ID列表,逗号分隔</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="grp">
|
||
<summary>
|
||
<span class="chev" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></span>
|
||
<span class="grp-name">段评相关</span>
|
||
<span class="cnt num">3</span>
|
||
</summary>
|
||
<div class="eps">
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/chapter_review</span><span class="ep-desc">获取章节评论列表</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
<div class="param-item"><span class="param-name">item_id</span><span class="req">必需</span><span class="param-desc">章节ID</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/review</span><span class="ep-desc">获取段落评论、回复</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">item_id</span><span class="req">必需</span><span class="param-desc">章节ID</span></div>
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
<div class="param-item"><span class="param-name">para</span><span class="req">必需</span><span class="param-desc">段落索引(从0开始)</span></div>
|
||
<div class="param-item"><span class="param-name">comment_id</span><span class="opt">可选</span><span class="param-desc">评论ID(获取回复时需要)</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/review_html</span><span class="ep-desc">获取段落评论HTML页面</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">item_id</span><span class="req">必需</span><span class="param-desc">章节ID</span></div>
|
||
<div class="param-item"><span class="param-name">book_id</span><span class="req">必需</span><span class="param-desc">书籍ID</span></div>
|
||
<div class="param-item"><span class="param-name">para</span><span class="req">必需</span><span class="param-desc">段落索引(从0开始)</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="grp">
|
||
<summary>
|
||
<span class="chev" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></span>
|
||
<span class="grp-name">多媒体</span>
|
||
<span class="cnt num">3</span>
|
||
</summary>
|
||
<div class="eps">
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/manga</span><span class="ep-desc">获取漫画内容</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">item_ids</span><span class="req">必需</span><span class="param-desc">章节ID</span></div>
|
||
<div class="param-item"><span class="param-name">output</span><span class="opt">可选</span><span class="param-desc">输出格式:url/proxy/data_uri,默认url</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/audio</span><span class="ep-desc">获取音频信息</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">item_ids</span><span class="req">必需</span><span class="param-desc">章节ID</span></div>
|
||
<div class="param-item"><span class="param-name">tone_id</span><span class="opt">可选</span><span class="param-desc">音色ID,默认1</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/video</span><span class="ep-desc">获取视频信息</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">item_ids</span><span class="req">必需</span><span class="param-desc">章节ID</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="grp">
|
||
<summary>
|
||
<span class="chev" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></span>
|
||
<span class="grp-name">分类</span>
|
||
<span class="cnt num">2</span>
|
||
</summary>
|
||
<div class="eps">
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/category-front</span><span class="ep-desc">获取分类首页</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">tab</span><span class="opt">可选</span><span class="param-desc">标签页,默认0</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/category-landing</span><span class="ep-desc">获取分类详情页</span></div>
|
||
<div class="api-params">
|
||
<div class="param-item"><span class="param-name">category_id</span><span class="req">必需</span><span class="param-desc">分类ID</span></div>
|
||
<div class="param-item"><span class="param-name">offset</span><span class="opt">可选</span><span class="param-desc">偏移量,默认0</span></div>
|
||
<div class="param-item"><span class="param-name">limit</span><span class="opt">可选</span><span class="param-desc">每页数量,默认20</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="grp">
|
||
<summary>
|
||
<span class="chev" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg></span>
|
||
<span class="grp-name">系统</span>
|
||
<span class="cnt num">1</span>
|
||
</summary>
|
||
<div class="eps">
|
||
<div class="api-endpoint">
|
||
<div class="ep-head"><span class="mth">GET</span><span class="api-path">/status</span><span class="ep-desc">系统状态页面(当前页面)</span></div>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
</section>
|
||
|
||
<footer class="foot">番茄小说 API · 状态每 3 秒自动刷新 · 测试请求直接发往本实例</footer>
|
||
</div>
|
||
</body>
|
||
</html>
|