一、下载类接口 小程序 / 客户端调用
统一要求:uid 必须在「用户管理」中存在、启用、token 匹配且配额未用完,否则一律 403。此类接口不需要 appKey。
GET/api/stream?url=&uid=&token=&referer=&ua=&download=&filename=鉴权:uid + token
流式中转:服务器即时拉取上游视频并流式转发给调用方,支持 Range 断点续传。小程序把该链接交给 wx.downloadFile 即可下载,无需为各视频平台配置合法域名。推荐日常使用。
| 参数 | 必填 | 说明 |
url | 是 | 视频直链(去水印接口返回的 videoUrl),仅支持 http/https,不支持内网地址 |
uid | 是 | 用户标识(用户管理中添加) |
token | 是 | 该用户的 token(用户管理中查看) |
referer | 否 | 防盗链 Referer。默认不要传;目标平台返回 502「疑似防盗链拦截」时才补上源站 referer 重试 |
ua | 否 | 自定义 User-Agent,默认取平台配置的 UA(已调优)。不要透传解析接口返回的 ua——部分平台(快手等)CDN 会对特定 UA 限速,实测同链路可慢 40 倍 |
download | 否 | 传 1 时强制浏览器下载(加 Content-Disposition) |
filename | 否 | 配合 download=1 指定下载文件名 |
// 小程序
// ★ 不要拼 &ua= / &referer=(快手等平台 CDN 会按 UA 限速);默认 UA 已调优,满速
const relayUrl = base + '/api/stream?url=' + encodeURIComponent(videoUrl) +
'&uid=' + encodeURIComponent(uid) + '&token=' + token + '&download=1';
wx.downloadFile({ url: relayUrl, timeout: 300000, success(res) { /* res.tempFilePath → 保存相册 */ } });
成功:直接返回视频字节流(200/206)。失败返回 JSON:502 上游返回了非媒体内容,疑似防盗链拦截或链接失效、403 token 无效、403 流量配额已用完。
二、APP 对接接口 外部网站后端调用
外部网站通过 appKey 自动同步用户、查询用户,无需手工在后台添加。appKey 三种传法:query ?appKey= / header X-App-Key / body 字段 appKey。
POST/api/app/user/sync鉴权:appKey
同步/创建用户:用户不存在则自动创建并生成下载 token,已存在则返回现有 token(不会重复生成)。注意:这个接口需要 appKey,小程序直接调它必须带上 appKey,否则 403。
| 参数 | 位置 | 必填 | 说明 |
uid | body | 是 | 你们系统的用户唯一标识,建议用微信 openid,≤64 字符 |
name | body | 否 | 用户昵称 |
appKey | body / query / header | 是 | 平台 appKey(X-App-Key 头 或 body 字段 或 query) |
// 请求
POST /api/app/user/sync
{ "uid": "wx_1a2b3c4d5e6f", "name": "张三", "appKey": "你的appKey" }
// 响应
{ "code": 0, "data": { "uid": "wx_1a2b3c4d5e6f", "name": "张三",
"token": "014dc1268f8ad69fb92abe4580d01b14", "enabled": true } }
返回的 token 要保存好,之后调用下载类接口(/api/stream)时要带上。
GET/api/app/user/:uid?appKey=鉴权:appKey
查询用户状态与用量。用户不存在返回 404。
// 响应
{ "code": 0, "data": { "uid": "wx_1a2b3c4d5e6f", "name": "张三",
"token": "014dc126…", "enabled": true,
"usedBytes": 1024000, "quotaBytes": 104857600 } }
quotaBytes 为 0 表示不限量;usedBytes 为已用流量。
三、管理接口 管理员会话 或 appKey
此类接口两种凭证二选一即可:浏览器登录后的会话 Cookie,或带 appKey(供中转平台等可信方直接查询)。响应中 code != 0 即失败,msg 为原因。
GET/api/stats鉴权:管理员 或 appKey
统计信息。
// 响应
{ "code": 0, "data": { "totalDownloads": 12, "todayBytes": 123456, "totalBytes": 987654 } }
GET/api/limits鉴权:管理员 或 appKey
下载限制配置(limits 段全部参数,只读展示)。修改用 PUT /api/limits(下方)。
GET/api/dashboard/trend?days=7鉴权:管理员 或 appKey
仪表台·全局用量趋势:最近 N 天(默认 7,最大 90)每日下载次数与流量,升序。管理后台「仪表台·全局用量趋势」面板数据源。
// 响应 data
{ "trend": [ { "date": "2026-10-14", "downloads": 8, "bytes": 83886080 } ],
"totalUsers": 5,
"stats": { "totalDownloads": 120, "totalBytes": 1073741824, "todayBytes": 10485760 } }
GET/api/dashboard/user/:uid?days=7鉴权:管理员 或 appKey
仪表台·单用户用量曲线 + 汇总。用户不存在返回 404。管理后台「仪表台·用户用量」面板数据源(用户管理表点击 uid 可直接跳转)。
// 响应 data
{ "uid": "wx_123", "trend": [ { "date": "2026-10-14", "downloads": 3, "bytes": 41943040 } ],
"summary": { "todayCount": 2, "todayBytes": 33554432,
"yesterdayCount": 1, "yesterdayBytes": 16777216,
"monthCount": 10, "monthBytes": 201326592,
"totalCount": 50, "totalBytes": 805306368 } }
PUT/api/limits鉴权:管理员会话
保存下载限制配置(管理后台「下载限制」面板「保存配置」按钮调用)。写回 config.json 并热更新限流器,立即生效,无需重启。字段未传则保留原值,传 0 表示不限。
// 请求体(JSON)
{ "maxSizeBytes": 1073741824, "maxConcurrentPerUser": 2,
"maxBytesPerHour": 5368709120, "maxRequestsPerMinute": 30,
"maxUrlLength": 8192, "flagWindowMinutes": 30, "flagThreshold": 5,
"maxGlobalConcurrent": 64, "preferSmallBytes": 20971520, "largeFileSpeedBps": 8388608 }
// 响应:保存后的完整限制配置
{ "code": 0, "data": { "maxSizeBytes": 1073741824, "maxConcurrentPerUser": 2 } }
GET/api/limits/alerts鉴权:管理员会话
被限流标记的用户列表(自动标记规则见下方「限流标记」说明)。
// 响应 data
[ { "id": "…", "uid": "wx_123", "name": "…", "type": "rate", "count": 5,
"firstAt": 1730000000000, "lastAt": 1730000000000, "status": "active", "resolvedAt": null } ]
POST/api/limits/alerts/:id/resolve鉴权:管理员会话
解除某用户的限流标记(触发计数清零,之后仍可能再次被标记)。
GET/api/users鉴权:管理员 或 appKey
用户列表。响应顶层含 flags(uid → 限流标记信息),每个用户含 summary 汇总(今日/昨日/本月/累计的次数与流量)与 daily 按日明细。
// 响应
{ "code": 0,
"flags": { "wx_123": { "type": "rate", "count": 5 } },
"data": [ { "uid": "wx_123", "name": "张三", "enabled": true, "token": "…",
"usedBytes": 805306368, "quotaBytes": 0, "downloadCount": 50,
"summary": { "todayCount": 2, "todayBytes": 33554432, "yesterdayCount": 1,
"yesterdayBytes": 16777216, "monthCount": 10, "monthBytes": 201326592,
"totalCount": 50, "totalBytes": 805306368 },
"daily": { "2026-10-15": { "downloads": 2, "bytes": 33554432 } } } ] }
POST/api/users鉴权:管理员 或 appKey
新增用户。body:{ uid, name?, quotaBytes? },quotaBytes 为流量配额字节数(0=不限)。uid 已存在返回 409。
PUT/api/users/:uid鉴权:管理员 或 appKey
修改用户。body 可含 { name?, enabled?, quotaBytes? }(只改传入的字段)。
DELETE/api/users/:uid鉴权:管理员 或 appKey
删除用户。
POST/api/users/:uid/reset-token鉴权:管理员 或 appKey
重置用户 token,旧 token 立即失效。响应返回新 token:{ "code": 0, "data": { "uid": "…", "token": "新token" } }。
四、管理员会话 / 密码 / appKey 仅管理员
POST/api/login鉴权:无
管理员登录。body:{ username, password }。成功后在响应头 Set-Cookie 返回 relay_sid,后续请求自动携带。失败 401。
POST/api/logout鉴权:无
退出登录,清除会话 Cookie。
GET/api/session鉴权:管理员会话
当前登录用户:{ "code": 0, "data": { "username": "admin" } }。未登录 401。
POST/api/admin/password鉴权:管理员会话
修改管理员密码。body:{ oldPassword, newPassword },新密码至少 6 位。原密码错误 401。
GET/api/app/appkey鉴权:管理员会话
查看当前 appKey:{ "code": 0, "data": { "appKey": "…" } }。
POST/api/app/appkey/reset鉴权:管理员会话
重置 appKey(生成新的 64 位随机密钥并写入 config.json),旧密钥立即失效,需通知对接方更换。
五、小程序接入完整示例
流程:同步用户拿 token → 去水印拿到直链 → 中转域名下载并保存相册。
// ① 小程序后台「开发 - 开发设置」→「downloadFile 合法域名」
// 添加:https://你的中转域名(仅需这一个)
// ② 同步用户(拿 token)。注意:appKey 建议由你们自己的服务器保存代调,
// 这里演示直接用,便于先跑通
const { data } = await wx.request({
url: base + '/api/app/user/sync',
method: 'POST',
header: { 'content-type': 'application/json' },
data: { uid: 'wx_' + openid, name: '昵称', appKey: '你的appKey' }
}).then(r => r.data); // data.data.token 存下来
// ③ 去水印接口拿到视频直链 videoUrl 后:
// ★ 完全适配写法:只带 url + uid + token(+download=1)
// 不要拼 &ua= / &referer=:快手等平台 CDN 会按 UA 限速(实测慢 40 倍);
// referer 仅在返回 502「疑似防盗链拦截」时按需补源站
const relayUrl = base + '/api/stream?url=' + encodeURIComponent(videoUrl) +
'&uid=' + encodeURIComponent(uid) +
'&token=' + token +
'&download=1';
wx.downloadFile({
url: relayUrl,
timeout: 300000, // 给足下载时间,避免 120s 被掐断
success(res) {
if (res.statusCode !== 200) return wx.showToast({ title: '下载失败', icon: 'none' });
wx.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success() { wx.showToast({ title: '已保存到相册' }); },
fail(err) {
if (err.errMsg.includes('auth')) {
wx.showModal({
title: '需要相册权限', content: '请在设置中允许保存到相册',
confirmText: '去设置',
success(r) { if (r.confirm) wx.openSetting(); }
});
}
}
});
}
});
iOS 注意:保存到相册的动作必须放在用户点击事件回调链中(不要在 setTimeout 里),首次需授权相册权限。