mirror of
https://github.com/marcogll/TaxHacker_s23.git
synced 2026-01-13 13:25:18 +00:00
feat: add auth and cache to currency route
This commit is contained in:
@@ -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<number>(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 })
|
||||
|
||||
108
lib/cache.ts
Normal file
108
lib/cache.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
export type CacheKey = string
|
||||
export type CacheEntry<T> = {
|
||||
value: T
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export class PoorManCache<T> {
|
||||
private cache: Map<CacheKey, CacheEntry<T>>
|
||||
private duration: number
|
||||
|
||||
/**
|
||||
* Create a new cache instance
|
||||
* @param duration Cache duration in milliseconds
|
||||
*/
|
||||
constructor(duration: number) {
|
||||
this.cache = new Map<CacheKey, CacheEntry<T>>()
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user