diff --git a/app/api/currency/route.ts b/app/api/currency/route.ts index a1af942..91e7724 100644 --- a/app/api/currency/route.ts +++ b/app/api/currency/route.ts @@ -1,3 +1,5 @@ +import { getSession } from "@/lib/auth" +import { PoorManCache } from "@/lib/cache" import { format } from "date-fns" import { NextRequest, NextResponse } from "next/server" @@ -7,7 +9,23 @@ type HistoricRate = { inverse: number } +const currencyCache = new PoorManCache(24 * 60 * 60 * 1000) // 24 hours + +function generateCacheKey(fromCurrency: string, toCurrency: string, date: string): string { + return `${fromCurrency},${toCurrency},${date}` +} + +const CLEANUP_INTERVAL = 90 * 60 * 1000 +if (typeof setInterval !== "undefined") { + setInterval(() => currencyCache.cleanup(), CLEANUP_INTERVAL) +} + export async function GET(request: NextRequest) { + const session = await getSession() + if (!session || !session.user) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) + } + try { const searchParams = request.nextUrl.searchParams const fromCurrency = searchParams.get("from") @@ -24,6 +42,15 @@ export async function GET(request: NextRequest) { } const formattedDate = format(date, "yyyy-MM-dd") + + // Check cache first + const cacheKey = generateCacheKey(fromCurrency, toCurrency, formattedDate) + const cachedRate = currencyCache.get(cacheKey) + + if (cachedRate !== undefined) { + return NextResponse.json({ rate: cachedRate, cached: true }) + } + const url = `https://www.xe.com/currencytables/?from=${fromCurrency}&date=${formattedDate}` const response = await fetch(url) @@ -58,7 +85,10 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: `Currency rate not found for ${toCurrency}` }, { status: 404 }) } - return NextResponse.json({ rate: rate.rate }) + // Store in cache + currencyCache.set(cacheKey, rate.rate) + + return NextResponse.json({ rate: rate.rate, cached: false }) } catch (error) { console.error("Currency API error:", error) return NextResponse.json({ error: "Internal server error" }, { status: 500 }) diff --git a/lib/cache.ts b/lib/cache.ts new file mode 100644 index 0000000..602987a --- /dev/null +++ b/lib/cache.ts @@ -0,0 +1,108 @@ +export type CacheKey = string +export type CacheEntry = { + value: T + timestamp: number +} + +export class PoorManCache { + private cache: Map> + private duration: number + + /** + * Create a new cache instance + * @param duration Cache duration in milliseconds + */ + constructor(duration: number) { + this.cache = new Map>() + this.duration = duration + } + + /** + * Get a value from the cache + * @param key Cache key + * @returns The cached value or undefined if not found or expired + */ + get(key: CacheKey): T | undefined { + const entry = this.cache.get(key) + + if (!entry) { + return undefined + } + + // Check if entry is expired + if (Date.now() - entry.timestamp > this.duration) { + this.cache.delete(key) + return undefined + } + + return entry.value + } + + /** + * Set a value in the cache + * @param key Cache key + * @param value Value to cache + */ + set(key: CacheKey, value: T): void { + this.cache.set(key, { + value, + timestamp: Date.now(), + }) + } + + /** + * Check if a key exists in the cache and is not expired + * @param key Cache key + * @returns True if the key exists and is not expired + */ + has(key: CacheKey): boolean { + const entry = this.cache.get(key) + + if (!entry) { + return false + } + + // Check if entry is expired + if (Date.now() - entry.timestamp > this.duration) { + this.cache.delete(key) + return false + } + + return true + } + + /** + * Remove a key from the cache + * @param key Cache key + */ + delete(key: CacheKey): void { + this.cache.delete(key) + } + + /** + * Clear all expired entries from the cache + */ + cleanup(): void { + const now = Date.now() + for (const [key, entry] of this.cache.entries()) { + if (now - entry.timestamp > this.duration) { + this.cache.delete(key) + } + } + } + + /** + * Get the current size of the cache + * @returns Number of entries in the cache + */ + size(): number { + return this.cache.size + } + + /** + * Clear all entries from the cache + */ + clear(): void { + this.cache.clear() + } +}