mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 10:19:51 -06:00
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>
127 lines
4.4 KiB
TypeScript
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,
|
|
};
|
|
};
|