diff --git a/apps/api/src/routers/public.ts b/apps/api/src/routers/public.ts index 15af76d..ca37bd8 100644 --- a/apps/api/src/routers/public.ts +++ b/apps/api/src/routers/public.ts @@ -1,4 +1,5 @@ import type { Database, Tables } from "@xtablo/shared-types"; +import type { S3Client } from "@aws-sdk/client-s3"; import { Hono } from "hono"; import { createFactory } from "hono/factory"; import { @@ -10,6 +11,7 @@ import { type TimeSlot, type WeeklyAvailability, } from "../helpers/slots.js"; +import { getOrgIcon } from "../helpers/orgIcons.js"; import type { BaseEnv } from "../types/app.types.js"; // import { MiddlewareManager } from "../middlewares/middleware.js"; @@ -125,10 +127,52 @@ const getPublicSlots = factory.createHandlers(async (c) => { }); }); +const getOrgIconHandler = factory.createHandlers(async (c) => { + const orgIdStr = c.req.param("orgId"); + const filename = c.req.param("filename"); + const orgId = Number.parseInt(orgIdStr, 10); + + if (Number.isNaN(orgId) || !filename) { + return c.json({ error: "Invalid request" }, 400); + } + + // Parse filename: "icon-192.png" or "icon-512-maskable.png" + const match = filename.match(/^icon-(\d+)(-maskable)?\.png$/); + if (!match) { + return c.json({ error: "Invalid icon filename" }, 400); + } + + const size = Number.parseInt(match[1], 10); + const maskable = !!match[2]; + + const s3Client = c.get("s3_client") as S3Client; + const result = await getOrgIcon(s3Client, orgId, size, maskable); + + if (!result) { + // Fallback: redirect to default PWA icon + const defaultPath = maskable + ? "/pwa-icons/pwa-512x512-maskable.png" + : size <= 32 + ? `/pwa-icons/favicon-${size}x${size}.png` + : size === 180 + ? "/pwa-icons/apple-touch-icon-180x180.png" + : `/pwa-icons/pwa-${size}x${size}.png`; + return c.redirect(defaultPath, 302); + } + + return new Response(result.buffer, { + headers: { + "Content-Type": result.contentType, + "Cache-Control": "public, max-age=86400", + }, + }); +}); + export const getPublicRouter = () => { const publicRouter = new Hono(); publicRouter.get("/slots/:shortUserId/:standardName", ...getPublicSlots); + publicRouter.get("/org-icons/:orgId/:filename", ...getOrgIconHandler); return publicRouter; };