服务器 钩子、日志与限制
钩子、日志与限制
服务器对自身的报告,以及允许每个人多频繁地操作。每一项都是
createDriveHandler 的选项,你的代码出错只会被记录,不影响请求。本页内容
做了什么、哪里出错、耗时多少
import pino from 'pino';
import { metrics } from '@opentelemetry/api';
import { createDriveHandler, openTelemetry } from '@rebofiles/server';
const log = pino();
createDriveHandler({
// …
// Each thing done: a webhook, an audit of your own, another index, space counted.
onChange: async ({ activity, entry }) => {
await fetch(process.env.WEBHOOK!, { method: 'POST', body: JSON.stringify({ ...activity, size: entry?.kind === 'file' ? entry.versions[0].size : 0 }) });
},
logger: { error: (message, detail) => log.error({ detail }, message), warn: (message, detail) => log.warn({ detail }, message) },
onRequest: openTelemetry(metrics.getMeter('rebofiles')),
});onChange会收到每一次操作,与动态记录一致(created、uploaded、version、renamed、moved、trashed、restored、deleted、shared、commented、held…),并附带操作后的条目,删除后为 null。logger记录服务器未在响应中说明的问题:未保存的动态、失败的钩子、无法检查的文件。默认为console。onRequest会收到每个路由(如GET /entries/:id)、状态、耗时和请求者。openTelemetry(meter)将其记录为http.server.request.duration直方图和 5xx 计数器。
每人可请求多少
| 类型 | 按 | 默认 |
|---|---|---|
| uploads | 个人 | 每分钟 1000 次 |
| zip | 个人,以及链接 | 每分钟 30 次 |
| searchText | 个人 | 每分钟 120 次 |
| linkPassword | 链接和地址 | 15 分钟内错误 10 次,之后连正确的也不接受 |
| links | 链接 | 每分钟 600 次 |
| requests | 个人 | 无 |
import { createDriveHandler } from '@rebofiles/server';
createDriveHandler({
// …
limits: {
uploads: { max: 200, every: 60_000 }, // starting an upload, by person
requests: { max: 1200, every: 60_000 }, // every request, by person
},
// Wrong passwords of a link counted by the address too, behind a proxy you trust.
clientIp: (request) => request.headers.get('x-forwarded-for')?.split(',')[0].trim(),
});超过限制时返回 429 rate-limited 并带有 Retry-After,网盘会用每个人的语言提示。设为 limits: false 则不限制。
Webhook
通过 HTTP 把每次更改告知其他服务,就像 onChange 告诉你的代码一样:从不拖慢触发更改的请求。
import { everyChange, verifyWebhook, webhook } from '@rebofiles/server';
createDriveHandler({
...options,
// Each change POSTed as JSON, signed, tried again three times: only these actions, or all.
onChange: everyChange(
webhook({ url: 'https://crm.example/hooks/files', secret: process.env.HOOK_SECRET!, actions: ['uploaded', 'version', 'deleted'] }),
({ activity }) => audit(activity),
),
});
// Where it arrives: the body as it came, checked before it is trusted.
const ok = verifyWebhook(process.env.HOOK_SECRET!, await request.text(), request.headers);- 请求体:
id(每次重试相同)、action、at、by、简要的entry和activity。请求头X-Rebofiles-Event、X-Rebofiles-Delivery、X-Rebofiles-Timestamp和X-Rebofiles-Signature(时间戳和请求体的 HMAC-SHA256)。 - 一秒后和四秒后重试,但因请求本身被拒绝(4xx)时不重试;未送达时会通知
onFailure。
多个进程
计数默认保存在进程内,除非你提供的 `limiter` 为所有进程保存:基于 Redis 只需几行。
limiter.ts
import { Redis } from 'ioredis';
import type { Limiter } from '@rebofiles/server';
const redis = new Redis(process.env.REDIS_URL!);
/** The same windows for every process: a count per key, gone when its window is. */
export const redisLimiter: Limiter = {
async hit(key, limit, how) {
if (how?.peek) {
const [count, ttl] = await Promise.all([redis.get(key), redis.pttl(key)]);
return Number(count) >= limit.max ? { ok: false, retryAfter: ttl } : { ok: true, retryAfter: 0 };
}
const count = await redis.incr(key);
if (count === 1) await redis.pexpire(key, limit.every);
return count <= limit.max ? { ok: true, retryAfter: 0 } : { ok: false, retryAfter: await redis.pttl(key) };
},
};