/
opt
/
canhelp
/
node_modules
/
better-auth
/
dist
/
api
/
rate-limiter
/
/opt/canhelp/node_modules/better-auth/dist/api/rate-limiter
mkdir
upload
Name
Size
Mode
Actions
index.mjs
6274
0644
edit
dl
rm
index.mjs.map
12888
0644
edit
dl
rm
Edit:
/opt/canhelp/node_modules/better-auth/dist/api/rate-limiter/index.mjs
(6274B)
import { wildcardMatch } from "../../utils/wildcard.mjs"; import { getIp } from "../../utils/get-request-ip.mjs"; import { safeJSONParse } from "@better-auth/core/utils/json"; import { normalizePathname } from "@better-auth/core/utils/url"; import { createRateLimitKey } from "@better-auth/core/utils/ip"; //#region src/api/rate-limiter/index.ts const memory = /* @__PURE__ */ new Map(); function shouldRateLimit(max, window, rateLimitData) { const now = Date.now(); const windowInMs = window * 1e3; return now - rateLimitData.lastRequest < windowInMs && rateLimitData.count >= max; } function rateLimitResponse(retryAfter) { return new Response(JSON.stringify({ message: "Too many requests. Please try again later." }), { status: 429, statusText: "Too Many Requests", headers: { "X-Retry-After": retryAfter.toString() } }); } function getRetryAfter(lastRequest, window) { const now = Date.now(); const windowInMs = window * 1e3; return Math.ceil((lastRequest + windowInMs - now) / 1e3); } function createDatabaseStorageWrapper(ctx) { const model = "rateLimit"; const db = ctx.adapter; return { get: async (key) => { const data = (await db.findMany({ model, where: [{ field: "key", value: key }] }))[0]; if (typeof data?.lastRequest === "bigint") data.lastRequest = Number(data.lastRequest); return data; }, set: async (key, value, _update) => { try { if (_update) await db.updateMany({ model, where: [{ field: "key", value: key }], update: { count: value.count, lastRequest: value.lastRequest } }); else await db.create({ model, data: { key, count: value.count, lastRequest: value.lastRequest } }); } catch (e) { ctx.logger.error("Error setting rate limit", e); } } }; } function getRateLimitStorage(ctx, rateLimitSettings) { if (ctx.options.rateLimit?.customStorage) return ctx.options.rateLimit.customStorage; const storage = ctx.rateLimit.storage; if (storage === "secondary-storage") return { get: async (key) => { const data = await ctx.options.secondaryStorage?.get(key); return data ? safeJSONParse(data) : null; }, set: async (key, value, _update) => { const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10; await ctx.options.secondaryStorage?.set?.(key, JSON.stringify(value), ttl); } }; else if (storage === "memory") return { async get(key) { const entry = memory.get(key); if (!entry) return null; if (Date.now() >= entry.expiresAt) { memory.delete(key); return null; } return entry.data; }, async set(key, value, _update) { const ttl = rateLimitSettings?.window ?? ctx.options.rateLimit?.window ?? 10; const expiresAt = Date.now() + ttl * 1e3; memory.set(key, { data: value, expiresAt }); } }; return createDatabaseStorageWrapper(ctx); } let ipWarningLogged = false; async function resolveRateLimitConfig(req, ctx) { const basePath = new URL(ctx.baseURL).pathname; const path = normalizePathname(req.url, basePath); let currentWindow = ctx.rateLimit.window; let currentMax = ctx.rateLimit.max; const ip = getIp(req, ctx.options); if (!ip) { if (!ipWarningLogged) { ctx.logger.warn("Rate limiting skipped: could not determine client IP address. If you're behind a reverse proxy, make sure to configure `trustedProxies` in your auth config."); ipWarningLogged = true; } return null; } const key = createRateLimitKey(ip, path); const specialRule = getDefaultSpecialRules().find((rule) => rule.pathMatcher(path)); if (specialRule) { currentWindow = specialRule.window; currentMax = specialRule.max; } for (const plugin of ctx.options.plugins || []) if (plugin.rateLimit) { const matchedRule = plugin.rateLimit.find((rule) => rule.pathMatcher(path)); if (matchedRule) { currentWindow = matchedRule.window; currentMax = matchedRule.max; break; } } if (ctx.rateLimit.customRules) { const _path = Object.keys(ctx.rateLimit.customRules).find((p) => { if (p.includes("*")) return wildcardMatch(p)(path); return p === path; }); if (_path) { const customRule = ctx.rateLimit.customRules[_path]; const resolved = typeof customRule === "function" ? await customRule(req, { window: currentWindow, max: currentMax }) : customRule; if (resolved) { currentWindow = resolved.window; currentMax = resolved.max; } if (resolved === false) return null; } } return { key, currentWindow, currentMax }; } async function onRequestRateLimit(req, ctx) { if (!ctx.rateLimit.enabled) return; const config = await resolveRateLimitConfig(req, ctx); if (!config) return; const { key, currentWindow, currentMax } = config; const data = await getRateLimitStorage(ctx, { window: currentWindow }).get(key); if (data && shouldRateLimit(currentMax, currentWindow, data)) return rateLimitResponse(getRetryAfter(data.lastRequest, currentWindow)); } async function onResponseRateLimit(req, ctx) { if (!ctx.rateLimit.enabled) return; const config = await resolveRateLimitConfig(req, ctx); if (!config) return; const { key, currentWindow } = config; const storage = getRateLimitStorage(ctx, { window: currentWindow }); const data = await storage.get(key); const now = Date.now(); if (!data) await storage.set(key, { key, count: 1, lastRequest: now }); else if (now - data.lastRequest > currentWindow * 1e3) await storage.set(key, { ...data, count: 1, lastRequest: now }, true); else await storage.set(key, { ...data, count: data.count + 1, lastRequest: now }, true); } function getDefaultSpecialRules() { return [{ pathMatcher(path) { return path.startsWith("/sign-in") || path.startsWith("/sign-up") || path.startsWith("/change-password") || path.startsWith("/change-email"); }, window: 10, max: 3 }, { pathMatcher(path) { return path === "/request-password-reset" || path === "/send-verification-email" || path.startsWith("/forget-password") || path === "/email-otp/send-verification-otp" || path === "/email-otp/request-password-reset"; }, window: 60, max: 3 }]; } //#endregion export { onRequestRateLimit, onResponseRateLimit }; //# sourceMappingURL=index.mjs.map
Save
cmd:
run