// Copyright 2025 OpenObserve Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
/**
* Frontend cache for built-in regex patterns
* Caches patterns in sessionStorage with 1-hour TTL
*/
interface CachedData {
data: T;
timestamp: number;
ttl: number; // in milliseconds
}
const CACHE_KEY_PREFIX = "regex_patterns_cache_";
const DEFAULT_TTL = 60 * 60 * 1000; // 1 hour in milliseconds
export class RegexPatternCache {
/**
* Get data from cache if it exists and is not expired
*/
static get(orgId: string): T | null {
try {
const cacheKey = `${CACHE_KEY_PREFIX}${orgId}`;
const cachedItem = sessionStorage.getItem(cacheKey);
if (!cachedItem) {
console.debug(`[RegexPatternCache] Cache miss for org: ${orgId}`);
return null;
}
const cached: CachedData = JSON.parse(cachedItem);
const now = Date.now();
// Check if expired
if (now - cached.timestamp > cached.ttl) {
console.debug(
`[RegexPatternCache] Cache expired for org: ${orgId} (age: ${Math.round((now - cached.timestamp) / 1000)}s)`
);
this.clear(orgId);
return null;
}
console.debug(
`[RegexPatternCache] Cache hit for org: ${orgId} (age: ${Math.round((now - cached.timestamp) / 1000)}s)`
);
return cached.data;
} catch (error) {
console.error("[RegexPatternCache] Error reading cache:", error);
return null;
}
}
/**
* Store data in cache with TTL
*/
static set(orgId: string, data: T, ttl: number = DEFAULT_TTL): void {
try {
const cacheKey = `${CACHE_KEY_PREFIX}${orgId}`;
const cached: CachedData = {
data,
timestamp: Date.now(),
ttl,
};
sessionStorage.setItem(cacheKey, JSON.stringify(cached));
console.debug(
`[RegexPatternCache] Cached data for org: ${orgId} (TTL: ${ttl / 1000}s)`
);
} catch (error) {
console.error("[RegexPatternCache] Error writing cache:", error);
// Don't fail the request if caching fails (e.g., quota exceeded)
}
}
/**
* Clear cache for specific organization
*/
static clear(orgId: string): void {
try {
const cacheKey = `${CACHE_KEY_PREFIX}${orgId}`;
sessionStorage.removeItem(cacheKey);
console.debug(`[RegexPatternCache] Cleared cache for org: ${orgId}`);
} catch (error) {
console.error("[RegexPatternCache] Error clearing cache:", error);
}
}
/**
* Clear all regex pattern caches
*/
static clearAll(): void {
try {
const keys = Object.keys(sessionStorage);
keys.forEach((key) => {
if (key.startsWith(CACHE_KEY_PREFIX)) {
sessionStorage.removeItem(key);
}
});
console.debug("[RegexPatternCache] Cleared all caches");
} catch (error) {
console.error("[RegexPatternCache] Error clearing all caches:", error);
}
}
/**
* Get cache statistics
*/
static getStats(orgId: string): { exists: boolean; age: number; ttl: number } | null {
try {
const cacheKey = `${CACHE_KEY_PREFIX}${orgId}`;
const cachedItem = sessionStorage.getItem(cacheKey);
if (!cachedItem) {
return null;
}
const cached: CachedData = JSON.parse(cachedItem);
const now = Date.now();
const age = now - cached.timestamp;
return {
exists: true,
age: Math.round(age / 1000), // in seconds
ttl: Math.round(cached.ttl / 1000), // in seconds
};
} catch (error) {
console.error("[RegexPatternCache] Error getting stats:", error);
return null;
}
}
}