Clone
import { Database } from '../lib/db';
import { NotFoundError } from '../utils/errors';
export async function handleRedirect(request, env, ctx) {
const { slug } = request.params;
if (!slug) {
return new Response('Not Found', { status: 404 });
}
try {
const db = new Database(env.DB);
// Try to get from cache first
let entry;
if (env.CACHE) {
const cached = await env.CACHE.get(`entry:${slug}`, 'json');
if (cached) {
entry = cached;
}
}
// If not in cache, get from database
if (!entry) {
entry = await db.getEntryBySlug(slug);
if (!entry) {
return new Response('Not Found', { status: 404 });
}
// Cache for 5 minutes
if (env.CACHE) {
await env.CACHE.put(`entry:${slug}`, JSON.stringify(entry), {
expirationTtl: 300
});
}
}
// Record analytics asynchronously
ctx.waitUntil(recordAnalytics(request, env, entry.id));
// Increment click count asynchronously
ctx.waitUntil(db.incrementClickCount(slug));
// Redirect to the original URL
return Response.redirect(entry.original_url, 301);
} catch (error) {
console.error('Redirect error:', error);
return new Response('Not Found', { status: 404 });
}
}
async function recordAnalytics(request, env, entryId) {
try {
const db = new Database(env.DB);
// Get client info
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
const userAgent = request.headers.get('User-Agent') || 'unknown';
const referer = request.headers.get('Referer') || null;
const country = request.cf?.country || null;
const city = request.cf?.city || null;
// Hash IP for privacy
const encoder = new TextEncoder();
const data = encoder.encode(ip + env.JWT_SECRET);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const ipHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
await db.recordAnalytics({
entryId,
ipHash: ipHash.substring(0, 16), // Use first 16 chars of hash
userAgent: userAgent.substring(0, 255),
referer: referer ? referer.substring(0, 255) : null,
country,
city
});
} catch (error) {
console.error('Analytics error:', error);
// Don't throw - analytics shouldn't break redirects
}
}