fix: use full API URL for org icon references

The API runs on a separate domain from the frontend. Org icon URLs
in the manifest and settings page need the full API base URL.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arthur Belleville 2026-04-02 22:31:48 +02:00
parent b20768e280
commit ec056fbe6c
No known key found for this signature in database
4 changed files with 27 additions and 12 deletions

View file

@ -420,7 +420,7 @@ export default function SettingsPage() {
<div className="flex flex-wrap items-center gap-4">
{organizationData?.organization?.logo_url ? (
<img
src={`/api/v1/public/org-icons/${organizationData.organization.id}/icon-192.png`}
src={`${import.meta.env.VITE_API_URL}/api/v1/public/org-icons/${organizationData.organization.id}/icon-192.png`}
alt="Organization logo"
className="w-16 h-16 rounded-lg object-cover ring-2 ring-gray-100 dark:ring-gray-800 shrink-0"
/>

View file

@ -27,11 +27,16 @@ describe("buildManifest", () => {
});
it("returns org-specific icon URLs when orgId is provided", () => {
const manifest = buildManifest(42);
const manifest = buildManifest(42, "https://api.example.com");
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[0].src).toBe("https://api.example.com/api/v1/public/org-icons/42/icon-192.png");
expect(manifest.icons[1].src).toBe("https://api.example.com/api/v1/public/org-icons/42/icon-512.png");
expect(manifest.icons[2].src).toBe("https://api.example.com/api/v1/public/org-icons/42/icon-512-maskable.png");
expect(manifest.icons[2].purpose).toBe("maskable");
});
it("uses empty string as apiUrl fallback", () => {
const manifest = buildManifest(42);
expect(manifest.icons[0].src).toBe("/api/v1/public/org-icons/42/icon-192.png");
});
});

View file

@ -25,7 +25,7 @@ interface WebAppManifest {
icons: ManifestIcon[];
}
export function buildManifest(orgId: number | null): WebAppManifest {
export function buildManifest(orgId: number | null, apiUrl = ""): WebAppManifest {
const base = {
name: "XTablo",
short_name: "XTablo",
@ -41,9 +41,9 @@ export function buildManifest(orgId: number | null): WebAppManifest {
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" },
{ src: `${apiUrl}/api/v1/public/org-icons/${orgId}/icon-192.png`, sizes: "192x192", type: "image/png" },
{ src: `${apiUrl}/api/v1/public/org-icons/${orgId}/icon-512.png`, sizes: "512x512", type: "image/png" },
{ src: `${apiUrl}/api/v1/public/org-icons/${orgId}/icon-512-maskable.png`, sizes: "512x512", type: "image/png", purpose: "maskable" },
],
};
}
@ -58,14 +58,18 @@ export function buildManifest(orgId: number | null): WebAppManifest {
};
}
interface Env {
API_URL?: string;
}
export default {
fetch(request: Request) {
fetch(request: Request, env: Env) {
const url = new URL(request.url);
if (url.pathname === "/manifest.webmanifest") {
const cookieHeader = request.headers.get("cookie");
const orgId = parseOrgIdFromCookie(cookieHeader);
const manifest = buildManifest(orgId);
const manifest = buildManifest(orgId, env.API_URL || "");
return new Response(JSON.stringify(manifest), {
headers: {

View file

@ -15,5 +15,11 @@ PYTHON_VERSION = "3.11.5"
[env.production]
route = { pattern = "app.xtablo.com", custom_domain = true }
[env.production.vars]
API_URL = "https://xablo-api-636270553187.europe-west1.run.app"
[env.staging]
route = { pattern = "app-staging.xtablo.com", custom_domain = true }
route = { pattern = "app-staging.xtablo.com", custom_domain = true }
[env.staging.vars]
API_URL = "https://xablo-api-staging-636270553187.europe-west1.run.app"