import * as fs from "fs"; import { z } from "zod"; import { deleteLinkStmt, getAllLinksStmt, getLinkByIdStmt, getLinkByShortStmt, getStatsByIdStmt, incClickStmt, insertLinkStmt, insertStatsStmt, updateLinkStmt } from "./db"; import type { LinkEntry } from "../common/lib"; const linkSchema = z.object({ target: z.string().url({ message: "target must be a valid URL" }), short: z.string().min(1, { message: "short cannot be empty" }), ogTitle: z.string().max(60).optional(), ogDesc: z.string().max(120).optional(), ogImage: z.union([ z.literal(""), z.string().trim().url({ message: "ogImage must be a valid URL" }) ]) }); function requireAuth(req: Request) { const pwd = req.headers.get("X-Password"); if (pwd !== process.env.PASSWORD) { return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 403 }); } return null; } export class ClientResponse extends Response { constructor(body?: BodyInit, init?: ResponseInit) { super(body, init); this.headers.set("Access-Control-Allow-Origin", process.env.ORIGIN!); this.headers.set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, DELETE"); this.headers.set("Access-Control-Allow-Headers", "Content-Type, X-Password"); } static override json(body: unknown, init?: ResponseInit): Response { return new Response(JSON.stringify(body), { ...init, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": process.env.ORIGIN!, "Access-Control-Allow-Methods": "OPTIONS, GET, POST, PUT, DELETE", "Access-Control-Allow-Headers": "Content-Type, X-Password", ...init?.headers, }, }); } } // --------------------- Server --------------------- const server = Bun.serve({ hostname: process.env.HOST || "127.0.0.1", routes: { "/api/status": () => new ClientResponse("OK"), "/api/login": { OPTIONS: () => new ClientResponse("Departed", { status: 204 }), POST: (req) => { const pwd = req.headers.get("X-Password"); if (pwd !== process.env.PASSWORD) { return ClientResponse.json({ success: false, error: "Invalid password" }, { status: 403 }); } return ClientResponse.json({ success: true }); }, }, "/api/links": { OPTIONS: () => new ClientResponse("Departed", { status: 204 }), GET: (req) => { const authErr = requireAuth(req); if (authErr) return authErr; const rows = getAllLinksStmt.all(); return ClientResponse.json({ success: true, data: rows }); }, POST: async (req) => { const authErr = requireAuth(req); if (authErr) return authErr; const body = await req.json(); const parsed = linkSchema.safeParse(body); if (!parsed.success) { return ClientResponse.json( { success: false, error: Object.values(z.treeifyError(parsed.error).properties!) .flatMap(z => z.errors) .join(", "), }, { status: 400 } ); } const id = Math.random().toString(36).slice(2, 11); const safe = { id, target: parsed.data.target, short: parsed.data.short, ogTitle: parsed.data.ogTitle ?? "", ogDesc: parsed.data.ogDesc ?? "", ogImage: parsed.data.ogImage ?? "", }; insertLinkStmt.run( safe.id, safe.target, safe.short, safe.ogTitle, safe.ogDesc, safe.ogImage ); insertStatsStmt.run(id); const newLink = getLinkByIdStmt.get(id); return ClientResponse.json({ success: true, data: newLink }); }, }, "/api/link/:short": (req) => { if (req.method === "OPTIONS") { return new ClientResponse("Departed", { status: 204 }); } const short = req.params.short; const link = getLinkByShortStmt.get(short) as LinkEntry; if (!link) { return new ClientResponse("Missing", { status: 404 }); } incClickStmt.run(link.id); const ua = req.headers.get("user-agent")?.toLowerCase() || ""; const isBot = ua.includes("facebookexternalhit") || ua.includes("twitterbot") || ua.includes("discordbot") || ua.includes("linkedinbot") || ua.includes("slackbot") || ua.includes("telegrambot");; if (isBot && (link.ogTitle || link.ogDesc || link.ogImage)) { const title = link.ogTitle || link.short; const description = link.ogDesc || ""; const image = link.ogImage || ""; const html = ` ${title} Redirecting to ${link.target} `; return new Response(html, { status: 200, headers: { "Content-Type": "text/html", }, }); } // normal redirect return new Response(null, { status: 302, headers: { Location: link.target }, }); }, // CRUD for 1 link "/api/links/:id": { OPTIONS: () => new ClientResponse("Departed", { status: 204 }), GET: (req) => { const authErr = requireAuth(req); if (authErr) return authErr; const id = req.params.id; const row = getLinkByIdStmt.get(id); if (!row) { return ClientResponse.json({ success: false, error: "Not found" }, { status: 404 }); } return ClientResponse.json({ success: true, data: row }); }, PUT: async (req) => { const authErr = requireAuth(req); if (authErr) return authErr; const id = req.params.id; const row = getLinkByIdStmt.get(id); if (!row) { return ClientResponse.json({ success: false, error: "Not found" }, { status: 404 }); } const body = await req.json(); const parsed = linkSchema.partial().safeParse(body); if (!parsed.success) { return ClientResponse.json( { success: false, error: Object.values(z.treeifyError(parsed.error).properties!) .flatMap(z => z.errors) .join(", "), }, { status: 400 } ); } updateLinkStmt.run( parsed.data.target ?? null, parsed.data.short ?? null, parsed.data.ogTitle ?? null, parsed.data.ogDesc ?? null, parsed.data.ogImage ?? null, id ); const updated = getLinkByIdStmt.get(id); return ClientResponse.json({ success: true, data: updated }); }, DELETE: (req) => { const authErr = requireAuth(req); if (authErr) return authErr; const id = req.params.id; deleteLinkStmt.run(id); deleteLinkStmt.run(id); return ClientResponse.json({ success: true }); }, }, "/api/links/:id/stats": (req) => { if (req.method === "OPTIONS") { return new ClientResponse("Departed", { status: 204 }); } const authErr = requireAuth(req); if (authErr) return authErr; const id = req.params.id; const row = getStatsByIdStmt.get(id); if (!row) { return ClientResponse.json({ success: false, error: "Not found" }, { status: 404 }); } return ClientResponse.json({ success: true, data: row }); }, "/api/*": () => ClientResponse.json({ success: false, error: "Not found" }, { status: 404 }), }, }); console.log("Server running on http://localhost:" + server.port);