mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
- Move oversized queue payloads from KV to R2 (PAYLOAD binding, webhooks/YYYY/MM/DD/*.json, KV queue:payload:* fallback)
- Persist routes/groups to D1 (d1_routes/d1_groups) with memory -> KV -> D1 three-tier cache, seeded from legacy KV config keys
- Move webhook dedup (dedup_keys), delivery state (delivery_state) and message tracking (message_tracking) to D1 via canUseD1 probe with automatic KV fallback
- Batch send_logs inserts (recordSendBatch) and add group_id/ts index
- Add storage-prune scheduled task for expired dedup/state/tracking rows
- Add TTL to invite:group:{id} index and audit all ephemeral KV keys
- Add D1 indexes for the new tables
- Sync AGENTS.md, README.md/zh and docs/ (en/zh) with the new storage layout
64 lines
No EOL
1.8 KiB
TypeScript
64 lines
No EOL
1.8 KiB
TypeScript
import type { Env } from "../types";
|
|
|
|
const PAYLOAD_PREFIX = "webhooks/";
|
|
const DEFAULT_TTL_SECONDS = 3600;
|
|
|
|
export interface PayloadStore {
|
|
put(payload: string, ttl?: number): Promise<string>;
|
|
get(key: string): Promise<string | null>;
|
|
delete(key: string): Promise<void>;
|
|
}
|
|
|
|
function generateKey(): string {
|
|
const bytes = new Uint8Array(16);
|
|
crypto.getRandomValues(bytes);
|
|
const hex = Array.from(bytes)
|
|
.map((b) => b.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
const now = new Date();
|
|
const y = now.getUTCFullYear();
|
|
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
return `${PAYLOAD_PREFIX}${y}/${m}/${d}/${hex}.json`;
|
|
}
|
|
|
|
export function r2PayloadStore(env: Env): PayloadStore {
|
|
const bucket = env.PAYLOAD;
|
|
if (!bucket) {
|
|
return {
|
|
async put(_payload: string): Promise<string> {
|
|
throw new Error("R2 binding is not configured");
|
|
},
|
|
async get(): Promise<null> {
|
|
throw new Error("R2 binding is not configured");
|
|
},
|
|
async delete(): Promise<void> {
|
|
throw new Error("R2 binding is not configured");
|
|
},
|
|
};
|
|
}
|
|
|
|
return {
|
|
async put(payload: string, ttl?: number): Promise<string> {
|
|
const key = generateKey();
|
|
const expires = new Date(
|
|
Date.now() + (ttl ?? DEFAULT_TTL_SECONDS) * 1000,
|
|
);
|
|
await bucket.put(key, payload, {
|
|
httpMetadata: { contentType: "application/json" },
|
|
customMetadata: { expires: expires.toISOString() },
|
|
});
|
|
return key;
|
|
},
|
|
|
|
async get(key: string): Promise<string | null> {
|
|
const obj = await bucket.get(key);
|
|
if (!obj) return null;
|
|
return obj.text();
|
|
},
|
|
|
|
async delete(key: string): Promise<void> {
|
|
await bucket.delete(key);
|
|
},
|
|
};
|
|
} |