diff --git a/src/utils/rate-limit.ts b/src/utils/rate-limit.ts new file mode 100644 index 0000000..8972827 --- /dev/null +++ b/src/utils/rate-limit.ts @@ -0,0 +1,35 @@ +const RATE_LIMIT_COOKIE = 'pt_rlid' +const RATE_LIMIT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 + +const parseCookieHeader = (cookieHeader?: string): Record => { + if (!cookieHeader) return {} + + return cookieHeader.split(';').reduce>((acc, part) => { + const [rawKey, ...rawValue] = part.trim().split('=') + if (!rawKey || rawValue.length === 0) return acc + + acc[rawKey] = decodeURIComponent(rawValue.join('=')) + return acc + }, {}) +} + +export const getRateLimitCookieName = (): string => RATE_LIMIT_COOKIE + +export const getRateLimitCookie = (cookieHeader?: string): string | undefined => { + return parseCookieHeader(cookieHeader)[RATE_LIMIT_COOKIE] +} + +export const createRateLimitCookieValue = (): string => crypto.randomUUID() + +export const buildRateLimitCookie = (value: string): string => { + return `${RATE_LIMIT_COOKIE}=${encodeURIComponent(value)}; Max-Age=${RATE_LIMIT_COOKIE_MAX_AGE}; Path=/; HttpOnly; SameSite=Lax; Secure` +} + +export const getRateLimitSubjects = (ip: string, visitorId?: string): string[] => { + const subjects = new Set() + + if (ip) subjects.add(`ip:${ip}`) + if (visitorId) subjects.add(`visitor:${visitorId}`) + + return [...subjects] +}