feat: dynamic manifest in Cloudflare Worker with cookie-based org identification

Adds parseOrgIdFromCookie and buildManifest exports to the worker, intercepting /manifest.webmanifest to serve org-specific PWA icon URLs based on the x-org-id cookie. Removes legacy @ts-nocheck and biome-ignore-file comments.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arthur Belleville 2026-04-02 22:01:57 +02:00
parent 538d6e39e6
commit 595b330741
No known key found for this signature in database
2 changed files with 112 additions and 7 deletions

View file

@ -0,0 +1,37 @@
import { describe, it, expect } from "vitest";
import { buildManifest, parseOrgIdFromCookie } from "./index";
describe("parseOrgIdFromCookie", () => {
it("returns null when no cookie header", () => {
expect(parseOrgIdFromCookie(null)).toBeNull();
});
it("returns null when x-org-id cookie is missing", () => {
expect(parseOrgIdFromCookie("other=value; foo=bar")).toBeNull();
});
it("parses x-org-id from cookie string", () => {
expect(parseOrgIdFromCookie("x-org-id=42; other=value")).toBe(42);
});
it("returns null for non-numeric x-org-id", () => {
expect(parseOrgIdFromCookie("x-org-id=abc")).toBeNull();
});
});
describe("buildManifest", () => {
it("returns default icons when orgId is null", () => {
const manifest = buildManifest(null);
expect(manifest.name).toBe("XTablo");
expect(manifest.icons[0].src).toBe("/pwa-icons/pwa-192x192.png");
});
it("returns org-specific icon URLs when orgId is provided", () => {
const manifest = buildManifest(42);
expect(manifest.name).toBe("XTablo");
expect(manifest.icons[0].src).toBe("/api/v1/public/org-icons/42/icon-192.png");
expect(manifest.icons[1].src).toBe("/api/v1/public/org-icons/42/icon-512.png");
expect(manifest.icons[2].src).toBe("/api/v1/public/org-icons/42/icon-512-maskable.png");
expect(manifest.icons[2].purpose).toBe("maskable");
});
});

View file

@ -1,16 +1,84 @@
// @ts-nocheck
// biome-ignore-file: Worker entry point with dynamic imports
/* eslint-disable */
export function parseOrgIdFromCookie(cookieHeader: string | null): number | null {
if (!cookieHeader) return null;
const match = cookieHeader.match(/(?:^|;\s*)x-org-id=(\d+)/);
if (!match) return null;
const id = Number.parseInt(match[1], 10);
return Number.isNaN(id) ? null : id;
}
interface ManifestIcon {
src: string;
sizes: string;
type: string;
purpose?: string;
}
interface WebAppManifest {
name: string;
short_name: string;
description: string;
start_url: string;
display: string;
orientation: string;
theme_color: string;
background_color: string;
icons: ManifestIcon[];
}
export function buildManifest(orgId: number | null): WebAppManifest {
const base = {
name: "XTablo",
short_name: "XTablo",
description: "Collaborative project management for construction teams",
start_url: "/",
display: "standalone",
orientation: "any",
theme_color: "#1e1b2e",
background_color: "#1e1b2e",
};
if (orgId !== null) {
return {
...base,
icons: [
{ src: `/api/v1/public/org-icons/${orgId}/icon-192.png`, sizes: "192x192", type: "image/png" },
{ src: `/api/v1/public/org-icons/${orgId}/icon-512.png`, sizes: "512x512", type: "image/png" },
{ src: `/api/v1/public/org-icons/${orgId}/icon-512-maskable.png`, sizes: "512x512", type: "image/png", purpose: "maskable" },
],
};
}
return {
...base,
icons: [
{ src: "/pwa-icons/pwa-192x192.png", sizes: "192x192", type: "image/png" },
{ src: "/pwa-icons/pwa-512x512.png", sizes: "512x512", type: "image/png" },
{ src: "/pwa-icons/pwa-512x512-maskable.png", sizes: "512x512", type: "image/png", purpose: "maskable" },
],
};
}
export default {
fetch(request) {
fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname.startsWith("/api/")) {
return Response.json({
name: "Cloudflare",
if (url.pathname === "/manifest.webmanifest") {
const cookieHeader = request.headers.get("cookie");
const orgId = parseOrgIdFromCookie(cookieHeader);
const manifest = buildManifest(orgId);
return new Response(JSON.stringify(manifest), {
headers: {
"Content-Type": "application/manifest+json",
"Cache-Control": "no-cache",
},
});
}
if (url.pathname.startsWith("/api/")) {
return Response.json({ name: "Cloudflare" });
}
return new Response(null, { status: 404 });
},
};