All files / api/routes trusted-senders.ts

97.05% Statements 33/34
91.66% Branches 11/12
100% Functions 5/5
96.55% Lines 28/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66                357x     357x 5x         5x       357x 3x 3x   2x 2x     2x       357x 9x 9x 9x   8x 8x   7x 7x     7x   1x 1x             357x 3x 3x 2x 2x 1x     357x    
import type Database from "better-sqlite3-multiple-ciphers";
import { Hono } from "hono";
import { parseIntParam } from "../validation.js";
 
/** Routes for managing image trusted senders (global — not identity-scoped).
 *  Trusted senders have their remote images loaded automatically —
 *  tracking pixels are still stripped regardless of trust status. */
export function trustedSenderRoutes(getDb: () => Database.Database): Hono {
	const api = new Hono();
 
	// List all trusted senders
	api.get("/trusted-senders", (c) => {
		const rows = getDb()
			.prepare(
				"SELECT id, sender_address, created_at FROM image_trusted_senders ORDER BY sender_address",
			)
			.all();
		return c.json(rows);
	});
 
	// Check if a specific sender is trusted
	api.get("/trusted-senders/check", (c) => {
		const sender = c.req.query("sender");
		if (!sender) return c.json({ error: "sender query param required" }, 400);
 
		const normalized = sender.toLowerCase().trim();
		const row = getDb()
			.prepare("SELECT id FROM image_trusted_senders WHERE sender_address = ?")
			.get(normalized);
		return c.json({ trusted: !!row });
	});
 
	// Add a trusted sender
	api.post("/trusted-senders", async (c) => {
		const db = getDb();
		const body = await c.req.json();
		if (!body.sender_address) return c.json({ error: "sender_address is required" }, 400);
 
		const normalized = String(body.sender_address).toLowerCase().trim();
		if (!normalized.includes("@")) return c.json({ error: "Invalid email address" }, 400);
 
		try {
			const result = db
				.prepare("INSERT INTO image_trusted_senders (sender_address) VALUES (?)")
				.run(normalized);
			return c.json({ id: Number(result.lastInsertRowid) }, 201);
		} catch (err) {
			Eif (String(err).includes("UNIQUE constraint")) {
				return c.json({ error: "Sender already trusted" }, 409);
			}
			throw err;
		}
	});
 
	// Remove a trusted sender by ID
	api.delete("/trusted-senders/:id", (c) => {
		const id = parseIntParam(c, "id", c.req.param("id"));
		if (id instanceof Response) return id;
		const result = getDb().prepare("DELETE FROM image_trusted_senders WHERE id = ?").run(id);
		if (result.changes === 0) return c.json({ error: "Trusted sender not found" }, 404);
		return c.json({ ok: true });
	});
 
	return api;
}