miniflare
Version:
Fun, full-featured, fully-local simulator for Cloudflare Workers
57 lines (55 loc) • 2.5 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
for (var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target, i = decorators.length - 1, decorator; i >= 0; i--)
(decorator = decorators[i]) && (result = (kind ? decorator(target, key, result) : decorator(result)) || result);
return kind && result && __defProp(target, key, result), result;
};
// src/workers/ratelimit/ratelimit-object.worker.ts
import { drain, get, MiniflareDurableObject, POST } from "miniflare:shared";
var SQL_SCHEMA = `
CREATE TABLE IF NOT EXISTS _mf_ratelimit_buckets (
key TEXT NOT NULL,
period INTEGER NOT NULL,
epoch INTEGER NOT NULL,
count INTEGER NOT NULL,
PRIMARY KEY (key, period)
);
`;
function sqlStmts(db) {
return {
getBucket: db.stmt(
`SELECT key, period, epoch, count FROM _mf_ratelimit_buckets
WHERE key = :key AND period = :period`
),
putBucket: db.stmt(
`INSERT OR REPLACE INTO _mf_ratelimit_buckets (key, period, epoch, count)
VALUES (:key, :period, :epoch, :count)`
),
// Windows are aligned to the wall clock, so every key sharing a period
// rolls over at the same instant. Clearing them all together matches the
// previous `#buckets.clear()` and stops the table growing without bound.
// It has to stay scoped to the period, or rolling one period's window over
// would discard the counters belonging to the other.
deleteExpired: db.stmt(
"DELETE FROM _mf_ratelimit_buckets WHERE period = :period AND epoch != :epoch"
)
};
}
var RateLimiterObject = class extends MiniflareDurableObject {
#stmts;
get stmts() {
return this.#stmts === void 0 && (this.db.exec(SQL_SCHEMA), this.#stmts = sqlStmts(this.db)), this.#stmts;
}
limit = async (req) => {
let { key, limit, period } = await req.json(), epoch = Math.floor(Date.now() / (period * 1e3)), bucket = get(this.stmts.getBucket({ key, period })), count = 0;
return bucket !== void 0 && bucket.epoch === epoch ? count = bucket.count : drain(this.stmts.deleteExpired({ period, epoch })), count >= limit ? Response.json({ success: !1 }) : (this.stmts.putBucket({ key, period, epoch, count: count + 1 }), Response.json({ success: !0 }));
};
};
__decorateClass([
POST("/limit")
], RateLimiterObject.prototype, "limit", 2);
export {
RateLimiterObject
};
//# sourceMappingURL=ratelimit-object.worker.js.map