feat(api): GET /desktop/update — signierte Desktop-Pakete im Format des Tauri-Updaters ausliefern

- Neuer oeffentlicher Endpunkt (vor download/:platform): base-Origin wird
  per safeOrigin validiert (nur http/https, kein Pfad/Query/Fragment/
  Userinfo, sonst 400) und nur zum Bau der absoluten Download-URL genutzt,
  nie serverseitig abgerufen
- 204 ohne Body bei fremder Plattform/Architektur, fehlendem Manifest,
  fehlender Signatur oder fehlendem/ungueltigem updateVersion; sonst
  { version, pub_date (nur RFC 3339), url, signature, notes }
- Manifest-Felder signature (je Plattform) und updateVersion optional in
  @tessera/shared, Eintragspruefung akzeptiert nur String-Signaturen
- 10 neue Spec-Tests, Test 11 erweitert (23 gesamt); latest/download
  unveraendert

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 15:32:52 +02:00
parent 678ba51725
commit de81c74e53
4 changed files with 351 additions and 7 deletions
+25
View File
@@ -30,6 +30,12 @@ export interface DesktopManifestFile {
name: string;
size: number;
sha256: string;
/**
* Base64-Inhalt der `.sig`-Datei des Tauri-Bundlers (minisign), geschrieben
* von desktop-collect.sh, gelesen nur von `GET /desktop/update`. Fehlt bei
* Bauten mit `--no-sign` -- dann gibt es kein Update in der App.
*/
signature?: string;
}
export interface DesktopManifest {
@@ -37,9 +43,28 @@ export interface DesktopManifest {
channel: string;
commit: string;
buildTime: string;
/**
* SemVer-Form, die der Updater im Client vergleicht: `X.Y.Z` bei live,
* `X.Y.Z-beta.g<sha7>` bei beta (Praefix `g` Pflicht -- ein rein numerischer
* SHA mit fuehrender Null waere kein gueltiger SemVer-Identifier).
*/
updateVersion?: string;
files: Partial<Record<DesktopPlatform, DesktopManifestFile>>;
}
/**
* Antwort von `GET /desktop/update` -- das dynamische Antwortformat von
* `tauri-plugin-updater`. Die Feldnamen sind vom Plugin vorgegeben, darum
* snake_case `pub_date`. `url` muss absolut sein, `signature` ist Pflicht.
*/
export interface DesktopUpdateResponse {
version: string;
pub_date?: string;
url: string;
signature: string;
notes?: string;
}
export interface DesktopLatestFile extends DesktopManifestFile {
url: string;
}