claude-gemini-multimodal-bridge
Version:
Enterprise-grade AI integration bridge connecting Claude Code, Gemini CLI, and Google AI Studio with intelligent routing and advanced multimodal processing capabilities
173 lines (172 loc) • 5.87 kB
JavaScript
import { logger } from '../utils/logger.js';
export class AuthCache {
static instance;
cache = new Map();
failureTracking = new Map();
TTL_SETTINGS = {
gemini: 6 * 60 * 60 * 1000,
aistudio: 24 * 60 * 60 * 1000,
claude: 12 * 60 * 60 * 1000,
};
FAILURE_BACKOFF = {
delays: [30 * 1000, 60 * 1000, 5 * 60 * 1000],
maxDelay: 5 * 60 * 1000,
resetAfter: 24 * 60 * 60 * 1000,
jitterFactor: 0.1
};
constructor() { }
static getInstance() {
if (!AuthCache.instance) {
AuthCache.instance = new AuthCache();
}
return AuthCache.instance;
}
set(service, auth) {
let ttl;
let expiry;
if (auth.success) {
ttl = this.TTL_SETTINGS[service];
expiry = Date.now() + ttl;
this.failureTracking.delete(service);
logger.debug('Authentication success cached', {
service,
ttl: ttl / 1000 / 60,
expiryTime: new Date(expiry).toISOString(),
});
}
else {
const tracking = this.getOrCreateFailureTracking(service);
tracking.count++;
tracking.lastFailureTime = Date.now();
ttl = this.calculateFailureTTL(tracking.count);
expiry = Date.now() + ttl;
logger.warn('Authentication failure cached with backoff', {
service,
failureCount: tracking.count,
ttl: ttl / 1000,
nextRetryTime: new Date(expiry).toLocaleTimeString(),
error: auth.error,
});
}
this.cache.set(service, {
auth,
expiry,
service,
});
}
get(service) {
const cached = this.cache.get(service);
if (!cached) {
logger.debug('No cached auth found', { service });
return null;
}
const now = Date.now();
const isExpired = now >= cached.expiry;
if (isExpired) {
logger.debug('Cached auth expired', {
service,
expiredSince: (now - cached.expiry) / 1000 / 60,
});
this.cache.delete(service);
return null;
}
const timeToExpiry = (cached.expiry - now) / 1000 / 60;
logger.debug('Using cached auth', {
service,
timeToExpiry: timeToExpiry.toFixed(1),
success: cached.auth.success,
});
return cached.auth;
}
invalidate(service) {
const removed = this.cache.delete(service);
logger.debug('Authentication cache invalidated', { service, removed });
}
clear() {
const count = this.cache.size;
this.cache.clear();
logger.debug('All authentication cache cleared', { count });
}
getStats() {
const now = Date.now();
const services = Object.keys(this.TTL_SETTINGS).map(service => {
const cached = this.cache.get(service);
if (!cached) {
return { service, cached: false };
}
const timeToExpiry = (cached.expiry - now) / 1000 / 60;
return {
service,
cached: true,
timeToExpiry: timeToExpiry > 0 ? timeToExpiry : 0,
success: cached.auth.success,
};
});
return {
totalEntries: this.cache.size,
services,
};
}
cleanup() {
const now = Date.now();
let removedCount = 0;
for (const [service, cached] of this.cache.entries()) {
if (now >= cached.expiry) {
this.cache.delete(service);
removedCount++;
logger.debug('Expired auth cache removed', {
service,
expiredSince: (now - cached.expiry) / 1000 / 60,
});
}
}
if (removedCount > 0) {
logger.debug('Auth cache cleanup completed', { removedCount });
}
return removedCount;
}
isCached(service) {
const cached = this.cache.get(service);
return cached ? Date.now() < cached.expiry : false;
}
forceRefresh(service) {
this.invalidate(service);
this.failureTracking.delete(service);
logger.info('Forced authentication refresh', { service });
}
getOrCreateFailureTracking(service) {
if (!this.failureTracking.has(service)) {
this.failureTracking.set(service, {
count: 0,
firstFailureTime: Date.now(),
lastFailureTime: Date.now()
});
}
const tracking = this.failureTracking.get(service);
if (Date.now() - tracking.firstFailureTime > this.FAILURE_BACKOFF.resetAfter) {
tracking.count = 0;
tracking.firstFailureTime = Date.now();
}
return tracking;
}
calculateFailureTTL(failureCount) {
const index = Math.min(failureCount - 1, this.FAILURE_BACKOFF.delays.length - 1);
const baseDelay = this.FAILURE_BACKOFF.delays[index] || this.FAILURE_BACKOFF.maxDelay;
const jitter = baseDelay * this.FAILURE_BACKOFF.jitterFactor;
const randomJitter = (Math.random() - 0.5) * 2 * jitter;
return Math.round(baseDelay + randomJitter);
}
getFailureInfo(service) {
const tracking = this.failureTracking.get(service);
if (!tracking)
return null;
const cached = this.cache.get(service);
if (cached && !cached.auth.success) {
return {
count: tracking.count,
nextRetryTime: new Date(cached.expiry)
};
}
return { count: tracking.count };
}
}