c371a79f93f8177b9b02cc4af020c79913ed472d
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| c371a79f93 |
v0.26.0 — Channels per-license + tag BÊTA sur versions
DEUX features liées :
1. CHANNELS : chaque license peut être attribuée à un manifest distinct
(« channel »). Permet de servir des versions différentes selon le
client. Le serveur lit ?channel=X et sert manifest/versions-{X}.json
avec fallback transparent sur versions.json. NULL = default.
2. BÊTA : nouveau flag isBeta + betaNotes par version dans le manifest.
Visible uniquement par les licenses avec can_see_betas=1. Affichage
d'une pill orange « BÊTA » sur la row + tooltip avec les notes pour
les testeurs. Les installations locales déjà présentes restent
visibles même si l'accès BÊTA est retiré ensuite (on n'efface pas
le disque du client).
DB :
002_channel_betas.sql ajoute channel + can_see_betas sur licenses.
Idempotent (ALTER TABLE IF NOT EXISTS), zero data migration.
Serveur PHP :
- ValidateLicense.php signe channel + canSeeBetas dans la réponse
(ordre des clés CRITIQUE pour matcher le canonical client).
- Manifest.php : whitelist regex anti-traversal sur ?channel=, fallback
silencieux sur versions.json si channel inconnu (évite leak de la
liste de channels par probing).
- SignManifest.php prend un channel optionnel → l'admin peut signer
chaque manifest indépendamment.
- admin/licenses.php : dropdown channel + checkbox bêta sur create,
bouton détails repliable par-row pour edit.
- admin/versions.php : channel switcher en tête, badge BÊTA sur chaque
row, dialog repliable « Bêta » avec checkbox + notes des testeurs.
Client C# :
- License.Channel + License.CanSeeBetas (dans le canonical signé).
- VersionManifest.IsBeta + BetaNotes.
- ManifestService prend un channelProvider via DI, lu depuis license
cachée à chaque fetch (lazy, pas de circular dep).
- MainViewModel.RebuildList filtre les versions IsBeta si !CanSeeBetas
(mais conserve les installées locales — on ne retire pas l'accès
rétroactivement à ce qui est déjà sur disque).
- VersionRowViewModel : props IsBeta / BetaNotes / BetaTooltip.
- MainWindow.xaml : pill orange à côté du n° version pour le featured
et les rows compactes, tooltip dynamique avec les notes testeurs.
Backward compat signature :
Anciennes licenses cachées (signées sans channel/canSeeBetas) sont
toujours validées via un fallback canonical legacy dans VerifySignature.
Sans ce fallback, le passage à v0.26 invaliderait toutes les caches
hors-ligne et bloquerait les users en mobilité.
Migration côté admin : jouer 002_channel_betas.sql sur la base, déployer
les fichiers PHP, créer manifest/versions-{channel}.json pour les
nouveaux channels (l'admin versions.php propose un input « Créer/utiliser
un nouveau channel »). Les licenses existantes restent en channel=NULL
= default = comportement actuel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 9cea07d6be |
Downloads: parallel multi-segment, sparse pre-alloc, faster SHA-256, URL auto-refresh
DownloadManager - Parallel multi-segment download (default 8 connections, configurable up to 16) with per-segment Range requests. Bypasses OVH/Apache per-connection bandwidth throttling — typical speed-up ×4 to ×8 vs single connection. - Reporter task on a dedicated Task with Interlocked aggregate counter (no lock contention with workers). Reports speed/ETA every 250ms even mid-download, fixes the "speed only shows at install transition" bug. - Sparse file pre-allocation via FSCTL_SET_SPARSE before SetLength: no zero-fill, no SeManageVolumePrivilege, instant on any disk type. Removes 5-30s of preparation lag on HDD. - HEAD probe skipped (trust manifest size, signed Ed25519). Falls back to single-segment if first segment returns 200 instead of 206. - Resume URL comparison fixed: ignores HMAC querystring (?exp=&sig=) which changes per request, compares only host+path. Previously every resume started fresh because the old URL never matched the freshly signed one. - Auto-refresh signed URL on 403/410 mid-DL: SemaphoreSlim with 5s debounce so 8 simultaneous segment expirations trigger a single /api/download-url/ call. Slow-connection users (1 Mbps, 30+ hours for 14 GB) keep downloading transparently across multiple TTL cycles. - Per-version hashAlgorithm:none in manifest skips client SHA-256 verification (still relying on Ed25519 manifest signature + HMAC URL). - DangerButton style (red) added to Theme.xaml for the new cancel-resume action. IntegrityService - 16 MiB buffer (was 1 MiB), FileOptions.SequentialScan + Asynchronous, IncrementalHash (uses SHA-NI hardware extensions on .NET 8), double-buffering to overlap CPU and I/O. Typical 14 GB hash verification: 60-180s → 15-40s. HttpClient - MaxConnectionsPerServer=16, EnableMultipleHttp2Connections, HTTP/2 preferred, AutomaticDecompression=None (ZIPs are already compressed), 5min pooled connection lifetime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 2e320d2ab5 |
Brand: install folder uses uppercase PROSERVE v{version}
The product wordmark is rendered uppercase everywhere in the UI (PROSERVE in Pirulen). Align the install directory naming so it reads "PROSERVE v1.4.7" instead of "Proserve v1.4.7" — same brand, same case on disk and in dialog titles. Changes: - server/manifest/versions.json: installFolderTemplate updated for both existing entries. - server/admin/versions.php: default template for new versions added via the backoffice form. - src/PSLauncher.Models/RemoteManifest.cs: default fallback for the property when missing from JSON. - src/PSLauncher.App/Views/MainWindow.xaml + dialogs + ViewModel toasts: UI strings now read "PROSERVE v..." consistent with the brand. InstallationRegistry's regex was already RegexOptions.IgnoreCase, so existing user installs in "Proserve v..." folders keep working (case-insensitive on Windows filesystems anyway). Re-installing an older version after the change re-creates the folder with the new case — Windows is case-preserving but case-insensitive, so launching remains identical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| b7de228bc9 |
v0.6 + v1.0: HMAC download URLs, launcher self-update, Inno Setup installer
Three deliverables shipped together so the next deployment cycle has a
clean distribution story.
(1) Auto-update of the launcher itself
---------------------------------------
- Models: RemoteManifest gains an optional `launcher` section
(LauncherInfo: version, minRequired, download {url,size,sha256},
releaseNotesUrl). Server-side, sign-manifest.php passes it through
unchanged; admins edit versions.json with the new launcher entry +
upload PSLauncher-X.Y.Z.exe to /builds/launcher/.
- Core: ILauncherSelfUpdater compares assembly version against
manifest.launcher.version using the existing SemVer parser, and
reuses DownloadManager (Range/resume/sha256 already proven on the
game ZIPs) to download the new exe into
%LocalAppData%/PSLauncher/selfupdate/.
- New project PSLauncher.Updater (~34 MB self-contained console exe):
spawned by the main app with --target / --source / --pid / --launch.
Waits for the main process to exit (or for the file lock to release),
backs up the current exe to .bak, copies the new file in place, and
restarts. .bak survives the swap so the user can roll back manually.
- App.csproj now declares Version=0.5.0 — currently shipped baseline.
PSLauncher.App.csproj sets a fixed AssemblyVersion so reflection-based
comparison works deterministically.
- MainViewModel.PromptLauncherUpdate: dialog after CheckForUpdates if
the manifest advertises a newer launcher. Download with progress in
the existing footer, then Application.Shutdown() so the Updater can
do its job.
(2) Inno Setup installer
------------------------
installer/PSLauncher.iss + build-installer.ps1 produce a single
PSLauncher-Setup-X.Y.Z.exe (~80 MB) that installs into
Program Files\ASTERION VR\PSLauncher\, drops both PSLauncher.exe and
PSLauncher.Updater.exe side by side (the updater MUST live next to
the target), creates Start Menu + optional Desktop shortcuts, and
registers a clean uninstall entry. The user's %LocalAppData%
(license, logs, cache) is intentionally untouched on uninstall — same
license survives a reinstall.
build-installer.ps1 chains dotnet publish for both projects and ISCC
in one command. README explains the bump-version workflow.
(3) HMAC-signed download URLs
-----------------------------
- New PHP route GET /api/download-url/{version} (Authorization: Bearer
<licenseKey> or ?key=...). Validates the license, checks
download_entitlement_until >= minLicenseDate of the version, and
returns a HMAC-signed URL (path|exp|licId, hash_hmac SHA-256, valid
1 h) + sha256 + sizeBytes for verification.
- /builds/.htaccess routes every *.zip request to gate.php. gate.php
validates exp, lic, sig (constant-time hash_equals), then streams
the file with Range: support so the launcher's resume keeps working.
Audit log gets a download_url_issued entry per request.
- Client-side wired transparently: LicenseService gains
GetSignedDownloadUrlAsync(version) that GETs the endpoint with the
decrypted license key from DPAPI. MainViewModel calls it before
every download; if the endpoint returns 404/401/network-error, the
client falls back to the manifest's plain download.url (graceful
degradation for setups that haven't deployed gate.php yet).
Note on PHP streaming for 14 GB ZIPs: gate.php uses set_time_limit(0)
+ ignore_user_abort(true) + 1 MiB chunked fread with periodic flush.
Works on OVH mutualisé but holds a PHP-FPM slot for the duration. If
parallel downloads scale past a few clients, switch to
mod_xsendfile or migrate /builds/ to Cloudflare R2 with native
S3-presigned URLs and remove the gate entirely.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 1c8c6803e8 |
Initial scaffolding: PS_Launcher v0.2
Client (C# / .NET 8 / WPF, MVVM via CommunityToolkit.Mvvm):
- PSLauncher.App: WPF UI dark theme (Epic-style sidebar + hero + big play button)
with UpdateAvailableDialog rendering Markdown release notes via Markdig.Wpf.
- PSLauncher.Core: services for installation registry (scans Proserve v{X.Y.Z}/),
process launcher, manifest fetch, SHA-256 integrity, HTTP download with
progress, ZIP install via .tmp + atomic rename, update orchestrator.
- PSLauncher.Models: RemoteManifest, InstalledVersion, LocalConfig DTOs.
Server (PHP 8 for OVH mutualisé, deployed under www/PS_Launcher/):
- Front controller + routes /manifest and /releasenotes/{version}.
- Static signed-manifest workflow with tools/sign-manifest.php CLI to
recompute SHA-256 and sizeBytes after each ZIP upload.
- .htaccess: HTTPS redirect, rewrite, security headers.
- config.example.php template; real config.php is gitignored.
Cohabiting versions: each release lives in its own Proserve v{version}/ folder
under installRoot. Old versions are never deleted automatically.
Roadmap: v0.3 = HTTP Range resume + Polly retry + state.json,
v0.4 = MySQL license + Ed25519 signatures + DPAPI, v0.5 = settings/UX polish.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|