1
0
Fork 0
mirror of synced 2024-06-16 09:25:12 +12:00
budibase/packages/backend-core/src/events/processors/posthog/rateLimiting.ts
2022-08-09 13:31:12 +01:00

107 lines
2.7 KiB
TypeScript

import { Event } from "@budibase/types"
import { CacheKeys, TTL } from "../../../cache/generic"
import * as cache from "../../../cache/generic"
import * as context from "../../../context"
type RateLimitedEvent =
| Event.SERVED_BUILDER
| Event.SERVED_APP_PREVIEW
| Event.SERVED_APP
const isRateLimited = (event: Event): event is RateLimitedEvent => {
return (
event === Event.SERVED_BUILDER ||
event === Event.SERVED_APP_PREVIEW ||
event === Event.SERVED_APP
)
}
const isPerApp = (event: RateLimitedEvent) => {
return event === Event.SERVED_APP_PREVIEW || event === Event.SERVED_APP
}
interface EventProperties {
timestamp: number
}
enum RateLimit {
CALENDAR_DAY = "calendarDay",
}
const RATE_LIMITS = {
[Event.SERVED_APP]: RateLimit.CALENDAR_DAY,
[Event.SERVED_APP_PREVIEW]: RateLimit.CALENDAR_DAY,
[Event.SERVED_BUILDER]: RateLimit.CALENDAR_DAY,
}
/**
* Check if this event should be sent right now
* Return false to signal the event SHOULD be sent
* Return true to signal the event should NOT be sent
*/
export const limited = async (event: Event): Promise<boolean> => {
// not a rate limited event -- send
if (!isRateLimited(event)) {
return false
}
const cachedEvent = await readEvent(event)
if (cachedEvent) {
const timestamp = new Date(cachedEvent.timestamp)
const limit = RATE_LIMITS[event]
switch (limit) {
case RateLimit.CALENDAR_DAY: {
// get midnight at the start of the next day for the timestamp
timestamp.setDate(timestamp.getDate() + 1)
timestamp.setHours(0, 0, 0, 0)
// if we have passed the threshold into the next day
if (Date.now() > timestamp.getTime()) {
// update the timestamp in the event -- send
await recordEvent(event, { timestamp: Date.now() })
return false
} else {
// still within the limited period -- don't send
return true
}
}
}
} else {
// no event present i.e. expired -- send
await recordEvent(event, { timestamp: Date.now() })
return false
}
}
const eventKey = (event: RateLimitedEvent) => {
let key = `${CacheKeys.EVENTS_RATE_LIMIT}:${event}`
if (isPerApp(event)) {
key = key + ":" + context.getAppId()
}
return key
}
const readEvent = async (
event: RateLimitedEvent
): Promise<EventProperties | undefined> => {
const key = eventKey(event)
const result = await cache.get(key)
return result as EventProperties
}
const recordEvent = async (
event: RateLimitedEvent,
properties: EventProperties
) => {
const key = eventKey(event)
const limit = RATE_LIMITS[event]
let ttl
switch (limit) {
case RateLimit.CALENDAR_DAY: {
ttl = TTL.ONE_DAY
}
}
await cache.store(key, properties, ttl)
}