Files
paseo/packages/website/src/routes/__root.tsx
T
Mohamed Boudra 1ae2f9280e fix(website): fetch release version at runtime with asset validation
Move release version resolution from build-time (vite.config.ts) to
runtime (server function on Cloudflare Workers). The server function
fetches GitHub releases, filters out prereleases/drafts, and only
returns a version that has all required assets (Mac DMG, Linux AppImage,
Windows exe). Uses Cloudflare's cf.cacheEverything with a 60s TTL to
avoid hitting GitHub on every request.

This fixes a race condition where the website would deploy and show
download links for a new release before the desktop build assets were
actually uploaded, resulting in 404s for users.
2026-04-03 20:41:26 +07:00

64 lines
1.7 KiB
TypeScript

import type { ReactNode } from "react";
import { createContext, useContext } from "react";
import { Outlet, createRootRoute, HeadContent, Scripts } from "@tanstack/react-router";
import { getLatestRelease } from "~/release";
interface ReleaseContext {
version: string;
}
const ReleaseCtx = createContext<ReleaseContext>({ version: "" });
export function useRelease(): ReleaseContext {
return useContext(ReleaseCtx);
}
export const Route = createRootRoute({
loader: async () => {
return getLatestRelease();
},
head: () => ({
meta: [
{ charSet: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{ name: "theme-color", content: "#101615" },
{ property: "og:site_name", content: "Paseo" },
{ property: "og:type", content: "website" },
{ property: "og:image", content: "https://paseo.sh/og-image.png" },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:image", content: "https://paseo.sh/og-image.png" },
],
links: [
{ rel: "icon", href: "/favicon.ico", sizes: "48x48" },
{ rel: "icon", href: "/favicon.svg", type: "image/svg+xml" },
{ rel: "apple-touch-icon", href: "/favicon.svg" },
],
}),
component: RootComponent,
});
function RootComponent() {
const release = Route.useLoaderData();
return (
<ReleaseCtx value={release}>
<RootDocument>
<Outlet />
</RootDocument>
</ReleaseCtx>
);
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body className="antialiased bg-background text-foreground">
{children}
<Scripts />
</body>
</html>
);
}