99 lines
2.5 KiB
TypeScript
99 lines
2.5 KiB
TypeScript
import { eq, and, sql } from 'drizzle-orm';
|
|
import { db } from '@/db';
|
|
import { promptUrlCache } from '@banatie/database';
|
|
import type { PromptUrlCacheEntry, NewPromptUrlCacheEntry } from '@/types/models';
|
|
import { computeSHA256 } from '@/utils/helpers';
|
|
|
|
export class PromptCacheService {
|
|
/**
|
|
* Compute SHA-256 hash of prompt for cache lookup
|
|
*/
|
|
computePromptHash(prompt: string): string {
|
|
return computeSHA256(prompt);
|
|
}
|
|
|
|
/**
|
|
* Check if prompt exists in cache for a project
|
|
*/
|
|
async getCachedEntry(
|
|
promptHash: string,
|
|
projectId: string
|
|
): Promise<PromptUrlCacheEntry | null> {
|
|
const entry = await db.query.promptUrlCache.findFirst({
|
|
where: and(
|
|
eq(promptUrlCache.promptHash, promptHash),
|
|
eq(promptUrlCache.projectId, projectId)
|
|
),
|
|
});
|
|
|
|
return entry || null;
|
|
}
|
|
|
|
/**
|
|
* Create a new cache entry
|
|
*/
|
|
async createCacheEntry(data: NewPromptUrlCacheEntry): Promise<PromptUrlCacheEntry> {
|
|
const [entry] = await db.insert(promptUrlCache).values(data).returning();
|
|
if (!entry) {
|
|
throw new Error('Failed to create cache entry');
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* Update hit count and last hit time for a cache entry
|
|
*/
|
|
async recordCacheHit(id: string): Promise<void> {
|
|
await db
|
|
.update(promptUrlCache)
|
|
.set({
|
|
hitCount: sql`${promptUrlCache.hitCount} + 1`,
|
|
lastHitAt: new Date(),
|
|
})
|
|
.where(eq(promptUrlCache.id, id));
|
|
}
|
|
|
|
/**
|
|
* Get cache statistics for a project
|
|
*/
|
|
async getCacheStats(projectId: string): Promise<{
|
|
totalEntries: number;
|
|
totalHits: number;
|
|
avgHitCount: number;
|
|
}> {
|
|
const entries = await db.query.promptUrlCache.findMany({
|
|
where: eq(promptUrlCache.projectId, projectId),
|
|
});
|
|
|
|
const totalEntries = entries.length;
|
|
const totalHits = entries.reduce((sum, entry) => sum + entry.hitCount, 0);
|
|
const avgHitCount = totalEntries > 0 ? totalHits / totalEntries : 0;
|
|
|
|
return {
|
|
totalEntries,
|
|
totalHits,
|
|
avgHitCount,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Clear old cache entries (can be called periodically)
|
|
*/
|
|
async clearOldEntries(daysOld: number): Promise<number> {
|
|
const cutoffDate = new Date();
|
|
cutoffDate.setDate(cutoffDate.getDate() - daysOld);
|
|
|
|
const result = await db
|
|
.delete(promptUrlCache)
|
|
.where(
|
|
and(
|
|
eq(promptUrlCache.hitCount, 0),
|
|
// Only delete entries with 0 hits that are old
|
|
)
|
|
)
|
|
.returning();
|
|
|
|
return result.length;
|
|
}
|
|
}
|