Clone
import QRCode from 'qrcode';
import { html } from './pages/home';
import { dashboardHtml } from './pages/dashboard';
import { loginHtml } from './pages/login';
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const path = url.pathname;
// Serve static assets
if (path.endsWith('.js') || path.endsWith('.css')) {
return serveStatic(path);
}
// API Routes
if (path.startsWith('/api/')) {
return handleAPI(request, env, path);
}
// Pages
if (path === '/') {
return new Response(html(), {
headers: { 'Content-Type': 'text/html' }
});
}
if (path === '/login') {
return new Response(loginHtml(), {
headers: { 'Content-Type': 'text/html' }
});
}
if (path === '/dashboard') {
return new Response(dashboardHtml(), {
headers: { 'Content-Type': 'text/html' }
});
}
// Check for short link redirect
const slug = path.slice(1);
if (slug && !slug.includes('/')) {
const link = await env.DB.prepare(
'SELECT url FROM links WHERE slug = ?'
).bind(slug).first();
if (link) {
// Update click count
await env.DB.prepare(
'UPDATE links SET clicks = clicks + 1 WHERE slug = ?'
).bind(slug).run();
return Response.redirect(link.url, 301);
}
}
return new Response('Not Found', { status: 404 });
}
};
async function handleAPI(request, env, path) {
const url = new URL(request.url);
const method = request.method;
// Auth endpoints
if (path === '/api/auth/request' && method === 'POST') {
const { email } = await request.json();
// Check if email is authorized
const authorized = env.AUTHORIZED_EMAILS?.split(',').map(e => e.trim()).includes(email);
if (!authorized) {
return jsonResponse({ error: 'Unauthorized email' }, 403);
}
// Generate token
const token = crypto.randomUUID();
const expires = new Date(Date.now() + 15 * 60 * 1000).toISOString();
await env.DB.prepare(
'INSERT INTO auth_tokens (token, email, expires_at) VALUES (?, ?, ?)'
).bind(token, email, expires).run();
// Send email via Postmark
const emailResponse = await fetch('https://api.postmarkapp.com/email', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-Postmark-Server-Token': env.EMAIL_API_KEY
},
body: JSON.stringify({
From: env.EMAIL_FROM || 'noreply@qrurl.us',
To: email,
Subject: 'Your QRurl Login Link',
HtmlBody: `
<h2>Login to QRurl</h2>
<p>Click the link below to log in:</p>
<a href="${url.origin}/api/auth/verify?token=${token}">Log In</a>
<p>This link expires in 15 minutes.</p>
`,
TextBody: `Login to QRurl\n\nClick here: ${url.origin}/api/auth/verify?token=${token}`,
MessageStream: 'outbound'
})
});
if (!emailResponse.ok) {
return jsonResponse({ error: 'Failed to send email' }, 500);
}
return jsonResponse({ success: true, message: 'Check your email' });
}
if (path === '/api/auth/verify' && method === 'GET') {
const token = url.searchParams.get('token');
const authToken = await env.DB.prepare(
'SELECT * FROM auth_tokens WHERE token = ? AND used = 0 AND expires_at > datetime("now")'
).bind(token).first();
if (!authToken) {
return new Response('Invalid or expired token', { status: 401 });
}
// Mark token as used
await env.DB.prepare(
'UPDATE auth_tokens SET used = 1 WHERE token = ?'
).bind(token).run();
// Create session
const sessionId = crypto.randomUUID();
const sessionExpires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
await env.DB.prepare(
'INSERT INTO sessions (id, email, expires_at) VALUES (?, ?, ?)'
).bind(sessionId, authToken.email, sessionExpires.toISOString()).run();
// Redirect to dashboard with session cookie
return new Response(null, {
status: 302,
headers: {
'Location': '/dashboard',
'Set-Cookie': `session=${sessionId}; Path=/; HttpOnly; Secure; SameSite=Strict; Expires=${sessionExpires.toUTCString()}`
}
});
}
// Link management
if (path === '/api/links' && method === 'GET') {
const session = getSession(request);
const links = await env.DB.prepare(
session
? 'SELECT * FROM links WHERE user_email = ? ORDER BY created_at DESC'
: 'SELECT * FROM links WHERE user_email IS NULL ORDER BY created_at DESC LIMIT 10'
).bind(session?.email).all();
return jsonResponse(links.results || []);
}
if (path === '/api/links' && method === 'POST') {
const { url: targetUrl, name, customSlug } = await request.json();
if (!targetUrl) {
return jsonResponse({ error: 'URL required' }, 400);
}
const session = getSession(request);
const slug = customSlug || generateSlug();
// Check if slug exists
const existing = await env.DB.prepare(
'SELECT slug FROM links WHERE slug = ?'
).bind(slug).first();
if (existing) {
return jsonResponse({ error: 'Slug already exists' }, 409);
}
await env.DB.prepare(
'INSERT INTO links (slug, url, name, user_email) VALUES (?, ?, ?, ?)'
).bind(slug, targetUrl, name || null, session?.email || null).run();
return jsonResponse({ slug, url: targetUrl });
}
// QR Code generation
if (path.startsWith('/api/qr/')) {
const slug = path.split('/')[3];
const shortUrl = `${url.origin}/${slug}`;
const qrDataUrl = await QRCode.toDataURL(shortUrl, {
errorCorrectionLevel: 'H',
width: 400,
margin: 1
});
return new Response(qrDataUrl.split(',')[1], {
headers: {
'Content-Type': 'image/png',
'Content-Encoding': 'base64'
}
});
}
return jsonResponse({ error: 'Not found' }, 404);
}
function jsonResponse(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' }
});
}
function generateSlug(length = 6) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let slug = '';
for (let i = 0; i < length; i++) {
slug += chars[Math.floor(Math.random() * chars.length)];
}
return slug;
}
function getSession(request) {
const cookie = request.headers.get('Cookie');
if (!cookie) return null;
const match = cookie.match(/session=([^;]+)/);
return match ? { id: match[1] } : null;
}
function serveStatic(path) {
// Simple static file serving
const contentType = path.endsWith('.js') ? 'application/javascript' : 'text/css';
return new Response('', {
headers: { 'Content-Type': contentType }
});
}