Pošlete dlouhou URL, dostanete krátký kód a přesměrování přes 302.
Pošlete URL na /api/links a dostanete krátký kód; návštěva /<code> odpoví přesměrováním 302 na původní odkaz. Odkazy jsou v libsql databázi, která vznikne a propojí se při nasazení jako LINKS_DB, takže connection string nenese žádný token a v kódu není žádné heslo.
Kódy jsou krátké a náhodné. Když je nějaký už obsazený, vložení selže na primárním klíči a zkracovač prostě zkusí jiný, takže se kolize řeší bez služby na unikátní identifikátory. Přijímají se jen http a https URL; cokoli jiného skončí chybou 422, aby zkracovač nikdy nepřesměroval návštěvníka na javascript: nebo file: URL.
// A URL shortener. POST a long URL to /api/links and get back a short code; // visiting /<code> redirects there with a 302. Links live in a libsql database // that was created and linked for you at deploy time: its connection string // arrives in LINKS_DB as a tokenless URL, with credentials injected by the // platform at the network edge, so no secret ever appears in this code. import { createClient } from "npm:@libsql/client@0.14.0/web"; const db = createClient({ url: Deno.env.get("LINKS_DB")! }); type Link = { code: string; url: string; created_at: string }; const ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789"; export default async (req: Request): Promise<Response> => { await db.execute( `CREATE TABLE IF NOT EXISTS links ( code TEXT PRIMARY KEY, url TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) )`, ); const url = new URL(req.url); const { pathname } = url; // Create a short link. Body: { "url": "https://..." } if (req.method === "POST" && pathname === "/api/links") { const body = await req.json().catch(() => null); const target = typeof body?.url === "string" ? body.url.trim() : ""; // Only http(s) links are allowed. Reject anything else with a 422 so we // never store a "javascript:" or "file:" URL and redirect a visitor to it. if (!isHttpUrl(target)) { return json({ error: "Provide an http or https URL." }, 422); } const code = await createUniqueLink(target); return json({ code, url: target, short: `${url.origin}/${code}` }, 201); } // Follow a short link: GET /<code> -> 302 redirect to the stored URL. const code = pathname.slice(1); if (req.method === "GET" && /^[a-z0-9]+$/.test(code)) { const found = await db.execute({ sql: "SELECT url FROM links WHERE code = ?", args: [code], }); if (found.rows.length === 0) { return json({ error: "Unknown code." }, 404); } return new Response(null, { status: 302, headers: { location: String(found.rows[0].url) }, }); } // Index: a form plus the most recent links. if (req.method === "GET" && pathname === "/") { return render(await recentLinks(), url.origin); } return json({ error: "Not found." }, 404); }; // Insert with a fresh random code, retrying if that code is already taken. // The PRIMARY KEY makes a duplicate insert fail, which we catch and retry. async function createUniqueLink(target: string): Promise<string> { for (let attempt = 0; attempt < 5; attempt++) { const code = randomCode(); try { await db.execute({ sql: "INSERT INTO links (code, url) VALUES (?, ?)", args: [code, target], }); return code; } catch (error) { if (String(error).includes("UNIQUE")) continue; // collision: try again throw error; } } throw new Error("could not allocate a unique code"); } function randomCode(length = 6): string { const bytes = crypto.getRandomValues(new Uint8Array(length)); let code = ""; for (const byte of bytes) code += ALPHABET[byte % ALPHABET.length]; return code; } function isHttpUrl(value: string): boolean { try { const parsed = new URL(value); return parsed.protocol === "http:" || parsed.protocol === "https:"; } catch { return false; } } async function recentLinks(): Promise<Link[]> { const result = await db.execute( "SELECT code, url, created_at FROM links ORDER BY created_at DESC LIMIT 50", ); return result.rows.map((row) => ({ code: String(row.code), url: String(row.url), created_at: String(row.created_at), })); } function render(links: Link[], origin: string): Response { const rows = links.length === 0 ? `<p class="empty">No links yet.</p>` : links .map( (link) => `<li> <a href="/${link.code}">/${link.code}</a> <span>${escapeHtml(link.url)}</span> </li>`, ) .join("\n "); const body = `<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>URL shortener</title> <style> body { max-width: 42rem; margin: 3rem auto; padding: 0 1.25rem; font: 16px/1.6 system-ui, sans-serif; color: #16181d; } h1 { letter-spacing: -0.02em; } code { font-family: ui-monospace, monospace; } ul { list-style: none; padding: 0; display: grid; gap: 0.6rem; } li { display: grid; gap: 0.15rem; border: 1px solid #e6e8ec; border-radius: 8px; padding: 0.6rem 0.9rem; } li a { font-family: ui-monospace, monospace; color: #2f6feb; text-decoration: none; } li span { color: #5c636e; font-size: 0.85rem; word-break: break-all; } .empty { color: #5c636e; } </style> </head> <body> <h1>URL shortener</h1> <p>Create a link with a POST request:</p> <pre><code>curl -X POST ${origin}/api/links \\ -H "content-type: application/json" \\ -d '{"url":"https://frontback.eu"}'</code></pre> <h2>Recent links</h2> <ul> ${rows} </ul> </body> </html> `; return new Response(body, { headers: { "content-type": "text/html; charset=utf-8" }, }); } function json(data: unknown, status = 200): Response { return new Response(JSON.stringify(data), { status, headers: { "content-type": "application/json; charset=utf-8" }, }); } function escapeHtml(value: string): string { return value .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }
Go beyond what seems possible.