ff05edbe7efef7f1513ddd37ef15e69ce3b9b9e0
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 500f7d12e6 |
v0.29.10 — Admin UI refonte (modals + onglets), emails de release, 4-digit versions
Admin backoffice — UI redesign : - Pages versions.php + licenses.php : remplace les <details> inline qui débordaient horizontalement par UN bouton « ✎ Modifier » par row qui ouvre une modal <dialog> avec onglets. 5 tabs versions (Méta, Notes, BÊTA, Channels, Avancé) + 6 tabs licenses (Prolonger, Slots, Channel, BÊTA, Lock, Contacts, Machines). Délégation JS unique pour les onglets. - Bouton 📋 Copier la clé license dans chaque row (Clipboard API + feedback visuel ✓ vert 1.5s). Évite le détour par phpMyAdmin pour transmettre la clé aux clients. - Overlay « hashing en cours » plein écran sur tous les boutons de hash (3-5 min sur OVH pour 13 Go ZIP). Spinner CSS + message contextualisé par scope (bulk vs single). - Date de release passée de datetime-local à date (l'heure n'a pas de sens UX), avec défaut = aujourd'hui pour release_at et aujourd'hui-1an pour min_license_date (= license standard couvre les releases sur 1 an). Emails de notification release : - Migration 004 : colonne contact_emails TEXT NULL sur licenses (CSV) - Onglet « Contacts » sur la modal licenses pour saisir les emails par license (parsing tolérant : CSV, ligne par ligne, point-virgule) - Bouton « ✉ Notifier » par version : POST notify_release filtre les licenses éligibles (channel match + min_license_date + can_see_betas pour BÊTA) et envoie un email HTML à chaque contact (dédup global) - Template email table-based + bgcolor (compat Outlook/Word engine), navy foncé #0F172A, logo Asterion en CID embed (= affichage direct sans demande de permission Outlook), bouton download installer centré (align="center" + margin auto), release notes en <pre> - Mailer.php helper : parse emails, multipart/related avec attachments inline, fallback execCommand pour clipboard 4-digit version support (X.Y.Z.B) : - SemVer Parse/CompareTo/ToString gèrent 3 ou 4 digits ; Build absent = 0 implicite (1.5.4 == 1.5.4.0 < 1.5.4.13). HasExplicitBuild préserve le format d'origine au round-trip. - Regex InstallationRegistry étendue avec (?:\.\d+)? → reconnaît « PROSERVE v1.5.4.13 » côte-à-côte avec « PROSERVE v1.5.4 » sur disque - Server-side : versions.php, launcher.php, DownloadUrl.php, api/index.php, Releasenotes.php — toutes les regex de validation acceptent le 4ᵉ digit - Use case : dev/test iterations cohabitant avec leur release stable Bugs fixes : - migrate.php : strip ligne par ligne les commentaires SQL avant le check is-empty. Sans ça, le PREMIER chunk d'un fichier migration (= header + premier ALTER) commençait par `--` et était silencieusement skip → ALTER jamais appliqué. Affectait migrations 003, 004. - SignManifest::getOrComputeSha256 ignorait son cache interne quand force demandé par le caller, retournant l'ancien hash en 0 ms même après re-upload SFTP (avec mtime préservé). Propage maintenant le flag $force. Bouton « 🔁 Hash » per-row force maintenant un re-calcul systématique. - DownloadManager : 416 (Range Not Satisfiable) ajouté aux URL-refresh triggers, avec HEAD probe pour comparer taille serveur vs manifest → message d'erreur explicite si ZIP tronqué. Bps display lissé sur une fenêtre glissante de 12 samples (3 s) → plus de clignotement quand un segment finit / Polly retry. SHA mismatch popup enrichi avec les deux SHAs (attendu vs calculé) extraits via regex de l'exception. - DownloadUrl.php : signature de l'URL utilisait un template hardcodé /builds/proserve-{version}.zip, ignorant tout rename serveur. Lit maintenant download.url du manifest et signe le filename réel. Strings i18n (5 langues) : - ~15 nouveaux : SHA mismatch enrichi avec sources, 416 size mismatch, stale manifest, manifest refreshed auto-retry, force fresh menu Bumps : 0.29.7 → 0.29.10 (4-digit support + accumulated UI fixes). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 49a3b855af |
v0.27.0 — Channels comme TAGS sur versions (refactor du modèle)
Changement majeur du modèle de channels en réponse au feedback :
« c'est trop compliqué. Il faudrait juste pouvoir tout laisser dans
builds, et taguer une version dans un canal ou un autre voir plusieurs
canaux. ». Le précédent design (1 fichier manifest par channel +
sous-dossier builds/{channel}/) imposait une duplication de structure
et empêchait une version d'être visible sur plusieurs channels à la fois.
NOUVEAU MODÈLE :
- 1 seul fichier manifest/versions.json
- 1 seul dossier builds/ flat (les ZIPs ont des noms libres pour éviter
les collisions homonymes — proserve-1.5.3.zip, proserve-1.5.3-police.zip…)
- Chaque entry de version a un champ `channels: ["default", "police"]`
qui dit qui peut la voir
- Sémantique additive : un client sur channel "police" voit les versions
taggées "default" + celles taggées "police". "default" = public.
- Une version peut être taggée sur plusieurs channels en une fois.
NOUVEAU REGISTRE channels.json :
Le registre des channels (avec name + label + description) vit dans
manifest/channels.json. Le channel "default" est implicite et ne peut
pas être supprimé. Nouvelle page admin Channels (CRUD) pour gérer la
liste, avec garde-fou anti-suppression : compte les versions taggées
et les licenses attribuées avant d'autoriser la suppression.
SERVEUR :
- api/routes/Manifest.php : reçoit ?channel=X depuis la license,
filtre versions où channels[] contient X ou "default", re-signe avec
la clé privée Ed25519 à la volée. Coût ~1ms par requête.
- admin/versions.php : refactor — plus de session "channel actif", plus
de switcher en haut, plus de prefix builds/{channel}/. Add form a
des checkboxes channels[]. Nouveau bouton "Channels" par-row pour
retager.
- admin/licenses.php : dropdown channel alimenté depuis channels.json
au lieu de scanner les fichiers manifest.
- tools/SignManifest.php : revert du constructeur channel-aware,
toujours sur versions.json + builds/ flat.
CLIENT :
- VersionManifest.Channels (List<string>) ajouté pour debug, mais le
filtrage est server-side donc le client n'a rien à faire de ce champ
côté UX.
- L'envoi du ?channel=X depuis la license signée fonctionne déjà
depuis v0.26.0, pas de changement nécessaire.
MIGRATION :
Les fichiers manifest/versions-{X}.json créés en v0.26.0 deviennent
inutiles. Si tu en avais (test "police" par exemple), copie les
versions concernées dans versions.json et tague-les avec les bons
channels via la nouvelle UI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 7ff5e56102 |
Fix : SignManifest et listing ZIPs supportent les sous-dossiers de channel
v0.26.0 générait des URLs builds/{channel}/proserve-X.Y.Z.zip dans le
manifest pour les channels non-default, mais SignManifest faisait juste
basename(url) → cherchait builds/proserve-X.Y.Z.zip à plat → ZIP introuvable
au moment du Sync.
Fixes :
1. SignManifest::resolveZipPath() : extrait tout le chemin après /builds/
dans l'URL et le mappe sur {$buildsDir}/. Strip ../ pour anti-traversal
même si le manifest est de toute façon signé Ed25519.
2. SignManifest fallback fuzzy étendu à 1 niveau de sous-dossier (glob */*.zip)
pour le cas "admin a renommé le ZIP".
3. admin/versions.php $zips inclut maintenant builds/ + builds/*/ et stocke
les chemins relatifs (asterion-vr/proserve-X.Y.Z.zip) au lieu du basename.
Le check "référencé ?" matche aussi sur le chemin relatif.
4. Workflow text en haut de la page indique le bon chemin SFTP selon le
channel actif (avec rappel "crée le sous-dossier si absent").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 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>
|
|||
| 962f5a8ce0 |
Server: faster releases (cache hashes), per-version skip, longer signed-URL TTL
- gate.php : ETag now derived from size+mtime instead of md5_file($zip). Previously the gate hashed the entire 14 GB ZIP on every HEAD/GET call (including each of 8 parallel segments) → 30s-5min preparation lag for clients before the first byte. Now <1ms per request. - SignManifest : disk-backed cache for SHA-256 keyed by (path,size,mtime). Re-signing 5×14 GB versions used to take ~25 min, now ~1s when nothing changed. New "Force re-hash" toggle in admin to ignore the cache. - versions.php : per-row "🔁 Hash" button to sign a single version, plus a "⚙ Hash" dropdown to toggle hashAlgorithm:none for builds where the user accepts skipping client-side verification (manifest stays signed Ed25519, only the per-ZIP SHA-256 verification is bypassed). - DownloadUrl.php : signed-URL TTL bumped 1h → 6h to cover slow ADSL users who need >1h to finish a 14 GB download. - .gitignore : track server/builds/.htaccess + gate.php (still ignore the actual ZIP/exe binaries). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 56069f606d |
backoffice: dedicated Launcher page, scope-aware Sync
The launcher and the game versions are different release cadences managed by the same operator. Mixing them on one page muddied the workflow. Split them into two top-level nav entries. SignManifest::run(string $scope) -------------------------------- - 'all' (default, used by CLI tools/sign-manifest.php) — unchanged. - 'versions' — only re-hashes the Proserve ZIPs and bumps `latest`. - 'launcher' — only re-hashes the launcher EXE. The Ed25519 sign step always runs at the end so the manifest stays verifiable. Selectively hashing avoids unrelated noise (e.g. mass-hash 14 GB ZIPs when all you wanted was to update the launcher exe). admin/launcher.php (new page) ----------------------------- Self-contained page with the launcher state, Set/Remove forms, the blue "🔁 Hasher le launcher + signer" button, and a list of the .exe files present in builds/launcher/. Workflow doc inline. admin/versions.php ------------------ Cleaned up: launcher card and its set_launcher / remove_launcher / sync_launcher actions removed. The remaining global Sync button is relabeled and now triggers scope='versions' (only Proserve ZIPs). Layout::navHtml gains a "Launcher" item between Versions and Audit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 197b07966c |
backoffice: manage launcher auto-update from the Versions page
Two things change so the operator never has to SSH for a launcher release. 1. SignManifest::run() now also re-hashes the manifest's `launcher` section. It looks for builds/launcher/<basename of launcher.url>; falls back to a tolerant glob *<version>*.exe if the exact name isn't found. Updates sizeBytes + sha256 in place. The Ed25519 sign step at the end already covered this branch — it was just blank data before. 2. admin/versions.php has a new "Auto-update du launcher" card above the manifest table. Shows the announced version + minRequired, the exe presence badge and the hash status, and a small form to set or update the launcher entry (version + minRequired only — URL is derived from base_url + version automatically). A "Retirer la section" button disables the auto-update by deleting the launcher key from versions.json. Lists the .exe files present in builds/launcher/ for visibility. Workflow now: edit the version in the form → SFTP-upload PSLauncher-X.Y.Z.exe to builds/launcher/ → click "🔁 Sync" once → manifest is hashed and signed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| b10a3fbabf |
UI overhaul: minimal top bar, license-first settings, footer-pinned check
Top bar (3-column layout) ------------------------- - Col 1: PROSERVE Launcher wordmark. - Col 2: license badge centered, the badge IS the click target now (a Border with an InputBindings MouseBinding LeftClick to OpenLicense). No more separate "🔑 Activer / changer" button cluttering the right. - Col 3: ⚙ Paramètres + window chrome (min/max/close). - "📁 Dossier" button removed from the top bar — install root is still reachable from Settings. Footer (always visible) ----------------------- - Row 1: "🔄 Vérifier les MAJ" pinned bottom-left, always shown. The optional "Annuler" button stays bottom-right while a download runs. - Row 2: status text + progress bar, only shown when busy or after a status message — the previous "footer entirely hidden when idle" hid the check button too. License flow split in two ------------------------- Click on the license badge: - License is active (valid / expired / revoked) → LicenseDetailsDialog opens. Header pill in matching status color (green/amber/red), shows owner, validity, issued date, machine ID with copy-to-clipboard. Two buttons: "🗑 Désactiver la license" (with confirmation) and Close. - No license OR after deactivation → falls through to the existing OnboardingDialog for re-keying. Settings rework --------------- LICENSE section is now first in SettingsDialog with the same green/amber/red colored chrome as the top bar — at a glance the user sees the same status everywhere. Machine ID copy moved into this card. Sign-manifest no longer needs exec() ------------------------------------ The "🔁 Sync" button in admin/versions.php previously shelled out to `php tools/sign-manifest.php` via exec(). OVH mutualisé often disables exec(), causing silent no-ops and the symptom the user just hit: the ZIP changed (new size 469,657,770 vs manifest's stale 469,831,428) and the launcher rejected it as size mismatch. Refactor: - New PSLauncher\Tools\SignManifest class with ->run() that does the hashing, latest-bump and Ed25519 signing in-process. - tools/sign-manifest.php is now a 6-line wrapper for the class. - admin/versions.php's 'sync' action calls the class directly via require_once + new — works on any host, no exec dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |