Files
formbricks-formbricks/apps/web/modules/cache/lib/cacheKeys.ts
Victor Hugo dos Santos ef973c8995 chore: merge rate limiter epic branch into main (#6236)
Co-authored-by: Harsh Bhat <90265455+harshsbhat@users.noreply.github.com>
Co-authored-by: Johannes <johannes@formbricks.com>
Co-authored-by: Piyush Gupta <56182734+gupta-piyush19@users.noreply.github.com>
Co-authored-by: Aditya <162564995+Naidu-4444@users.noreply.github.com>
Co-authored-by: Piyush Gupta <piyushguptaa2z123@gmail.com>
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
Co-authored-by: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com>
Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com>
Co-authored-by: Jakob Schott <154420406+jakobsitory@users.noreply.github.com>
Co-authored-by: Suraj <surajsuthar0067@gmail.com>
Co-authored-by: Kshitij Sharma <63995641+kshitij-codes@users.noreply.github.com>
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
Co-authored-by: Matti Nannt <mail@matthiasnannt.com>
2025-07-16 12:28:59 +00:00

127 lines
4.4 KiB
TypeScript

import "server-only";
/**
* Enterprise-grade cache key generator following industry best practices
* Pattern: fb:{resource}:{identifier}[:{subresource}]
*
* Benefits:
* - Clear namespace hierarchy (fb = formbricks)
* - Collision-proof across environments
* - Easy debugging and monitoring
* - Predictable invalidation patterns
* - Multi-tenant safe
*/
export const createCacheKey = {
// Environment-related keys
environment: {
state: (environmentId: string) => `fb:env:${environmentId}:state`,
surveys: (environmentId: string) => `fb:env:${environmentId}:surveys`,
actionClasses: (environmentId: string) => `fb:env:${environmentId}:action_classes`,
config: (environmentId: string) => `fb:env:${environmentId}:config`,
segments: (environmentId: string) => `fb:env:${environmentId}:segments`,
},
// Organization-related keys
organization: {
billing: (organizationId: string) => `fb:org:${organizationId}:billing`,
environments: (organizationId: string) => `fb:org:${organizationId}:environments`,
config: (organizationId: string) => `fb:org:${organizationId}:config`,
limits: (organizationId: string) => `fb:org:${organizationId}:limits`,
},
// License and enterprise features
license: {
status: (organizationId: string) => `fb:license:${organizationId}:status`,
features: (organizationId: string) => `fb:license:${organizationId}:features`,
usage: (organizationId: string) => `fb:license:${organizationId}:usage`,
check: (organizationId: string, feature: string) => `fb:license:${organizationId}:check:${feature}`,
previous_result: (organizationId: string) => `fb:license:${organizationId}:previous_result`,
},
// User-related keys
user: {
profile: (userId: string) => `fb:user:${userId}:profile`,
preferences: (userId: string) => `fb:user:${userId}:preferences`,
organizations: (userId: string) => `fb:user:${userId}:organizations`,
permissions: (userId: string, organizationId: string) =>
`fb:user:${userId}:org:${organizationId}:permissions`,
},
// Project-related keys
project: {
config: (projectId: string) => `fb:project:${projectId}:config`,
environments: (projectId: string) => `fb:project:${projectId}:environments`,
surveys: (projectId: string) => `fb:project:${projectId}:surveys`,
},
// Survey-related keys
survey: {
metadata: (surveyId: string) => `fb:survey:${surveyId}:metadata`,
responses: (surveyId: string) => `fb:survey:${surveyId}:responses`,
stats: (surveyId: string) => `fb:survey:${surveyId}:stats`,
},
// Session and authentication
session: {
data: (sessionId: string) => `fb:session:${sessionId}:data`,
permissions: (sessionId: string) => `fb:session:${sessionId}:permissions`,
},
// Rate limiting and security
rateLimit: {
api: (identifier: string, endpoint: string) => `fb:rate_limit:api:${identifier}:${endpoint}`,
login: (identifier: string) => `fb:rate_limit:login:${identifier}`,
core: (namespace: string, identifier: string, windowStart: number) =>
`fb:rate_limit:${namespace}:${identifier}:${windowStart}`,
},
// Custom keys with validation
custom: (namespace: string, identifier: string, subResource?: string) => {
// Validate namespace to prevent collisions
const validNamespaces = ["temp", "analytics", "webhook", "integration", "backup"];
if (!validNamespaces.includes(namespace)) {
throw new Error(`Invalid cache namespace: ${namespace}. Use: ${validNamespaces.join(", ")}`);
}
const base = `fb:${namespace}:${identifier}`;
return subResource ? `${base}:${subResource}` : base;
},
};
/**
* Cache key validation helpers
*/
export const validateCacheKey = (key: string): boolean => {
// Must start with fb: prefix
if (!key.startsWith("fb:")) return false;
// Must have at least 3 parts (fb:resource:identifier)
const parts = key.split(":");
if (parts.length < 3) return false;
// No empty parts
if (parts.some((part) => part.length === 0)) return false;
return true;
};
/**
* Extract cache key components for debugging/monitoring
*/
export const parseCacheKey = (key: string) => {
if (!validateCacheKey(key)) {
throw new Error(`Invalid cache key format: ${key}`);
}
const [prefix, resource, identifier, ...subResources] = key.split(":");
return {
prefix,
resource,
identifier,
subResource: subResources.length > 0 ? subResources.join(":") : undefined,
full: key,
};
};