/** * Desktop-Paket-Info fuer die Web-Oberflaeche (Phase 18, D-10/D-12). * * Quelle fuer den unauffaelligen Download-Link auf der Anmeldeseite * (`DesktopDownloadLinks`) UND fuer die Einstellungsseite * "Desktop-App" (`DesktopAppSettings`): beide lesen `GET /desktop/latest` * (oeffentlich, `@Public()` in `apps/api/src/desktop/desktop.controller.ts`) * und blenden sich aus, wenn der Server keine Pakete traegt (404). * * Wichtig: Next.js ersetzt `process.env.NEXT_PUBLIC_*` nur dann zur Bauzeit * im Browser-Bundle, wenn der Ausdruck woertlich mit vollem Namen im Code * steht — kein Destructuring, kein `process.env[name]` (siehe `app-version.ts`). * Im Betrieb laeuft die Anfrage ueber den Rewrite `/api-proxy` auf die API. */ export type DesktopPlatform = 'windows' | 'linux'; /** * Spiegel von `DesktopLatestFile`/`DesktopLatestResponse` aus * `packages/shared` (gleicher Stand wie in `app-version.ts`: seit * quick-260922-m1h waere ein Import moeglich, der Spiegel wurde aber bewusst * nicht mit aufgeloest). Die API-Wahrheit bleibt in * `apps/api/src/desktop/desktop.service.ts`. */ export interface DesktopFileInfo { name: string; size: number; sha256: string; url: string; } export interface DesktopLatestInfo { version: string; channel: string; commit: string; buildTime: string; files: Partial>; } const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'; let desktopLatestPromise: Promise | null = null; /** * Laedt `GET /desktop/latest` genau einmal je Modulinstanz (memoisiert); * jeder Fehler (Netz, Nicht-2xx — insbesondere 404 ohne Manifest) ist still * und liefert `null`. Kein `credentials: 'include'`: die Anmeldeseite zeigt * den Link, bevor ein Cookie existiert, und der Endpunkt ist oeffentlich. */ export function loadDesktopLatest(): Promise { if (!desktopLatestPromise) { desktopLatestPromise = fetch(`${API_URL}/desktop/latest`) .then((res) => (res.ok ? (res.json() as Promise) : null)) .catch(() => null); } return desktopLatestPromise; } /** Baut die absolute Download-Adresse aus `API_URL` plus dem relativen `url`-Feld (T-18-07). */ export function desktopDownloadUrl(file: DesktopFileInfo): string { return `${API_URL}${file.url}`; } /** Formatiert Dateigroessen in MB, lokalisiert (z. B. `5,8 MB` / `5.8 MB`). */ export function formatFileSize(bytes: number, locale: string): string { const mb = bytes / 1048576; return `${new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(mb)} MB`; }