namespace PSLauncher.Models; public sealed class LocalConfig { public int SchemaVersion { get; set; } = 1; public string ServerBaseUrl { get; set; } = "https://example.com/PS_Launcher/api"; public string InstallRoot { get; set; } = string.Empty; public string? LastLaunchedVersion { get; set; } /// /// Code de langue de l'UI : "auto" (suit la langue Windows), "fr", "en", /// "zh", "th" ou "ar". Tout changement nécessite un redémarrage du launcher. /// public string Language { get; set; } = "auto"; /// /// Nombre de connexions HTTP parallèles utilisées pour télécharger un build. /// 1 = comportement legacy single-connection. 4-8 contourne le throttling /// per-connection d'Apache/OVH mutualisé. 16 = défaut depuis v0.19 pour /// raccourcir la « queue » en fin de DL : avec 8 segments sur 14 Go, le /// dernier segment doit finir seul ~1.75 Go (visible comme un effondrement /// du débit en fin de course) ; avec 16, c'est ~875 Mo. Push à 24/32 si /// ton serveur l'accepte (OVH mutualisé tient bien jusqu'à 16-24). /// public int ParallelDownloadSegments { get; set; } = 16; /// /// Si false, ignore la vérification SHA-256 même si le manifest l'exige. /// Utile sur les machines lentes ou pour gagner 30 s-3 min en fin de DL d'un gros build. /// La sécurité repose alors uniquement sur la signature Ed25519 du manifest + HTTPS, /// ce qui reste robuste. /// public bool VerifyDownloadHash { get; set; } = true; /// /// URL de l'outil de reporting local (page web embarquée dans le launcher, /// onglet "Report"). Par défaut pointe vers l'install standard ASTERION /// dans XAMPP. Surchargeable dans Settings → Avancés. /// public string ReportUrl { get; set; } = "http://localhost/ProserveReport/"; /// /// URL de la documentation locale (onglet "Documentation"). Pointe par défaut /// vers l'install standard ASTERION dans XAMPP. Surchargeable dans Settings → /// Avancés. Si vide, l'onglet affiche un placeholder. /// public string DocumentationUrl { get; set; } = "http://localhost/ProserveDoc/"; public LicenseConfig License { get; set; } = new(); /// /// Configuration du déploiement de l'outil Report (page web locale embarquée /// dans l'onglet "Statistiques") vers la racine XAMPP htdocs. /// Le launcher copie le dossier _report/ bundled dans chaque ZIP /// PROSERVE vers {HtdocsRoot}\{FolderName} à chaque install. /// public ReportToolConfig ReportTool { get; set; } = new(); /// /// Configuration du déploiement de l'outil Documentation (page web locale /// embarquée dans l'onglet "Documentation"). Même mécanisme que ReportTool : /// copie de _doc/ bundled dans le ZIP PROSERVE vers /// {HtdocsRoot}\{FolderName} avec backups horodatés et revert. /// public DocToolConfig DocTool { get; set; } = new(); /// /// Surveillance d'état système affichée en bandeau sous la top bar /// (pings d'IP, processus requis comme SteamVR / Vive Business Streaming…). /// Chaque check a un statut Vert (OK) / Orange (warn, ex. ping élevé) / Rouge /// (KO). Tooltip détaillé au survol. /// public HealthChecksConfig HealthChecks { get; set; } = new(); /// /// Connexion à la base MySQL locale (XAMPP) utilisée par PROSERVE pour ses /// statistiques. Le launcher s'en sert pour appliquer les migrations bundled /// dans _migrations/ au moment de l'install d'une nouvelle version. /// public DatabaseConfig Database { get; set; } = new(); } /// /// Paramètres de connexion à la base MySQL locale. Les valeurs par défaut /// correspondent à un XAMPP standard fraîchement installé. Override possible /// via Settings → Base de données. /// public sealed class DatabaseConfig { public string Host { get; set; } = "localhost"; public uint Port { get; set; } = 3306; public string User { get; set; } = "root"; /// /// Mot de passe MySQL stocké chiffré DPAPI (CurrentUser scope) en base64. /// Vide = pas de password (config XAMPP par défaut). NE PAS écrire en clair. /// public string? EncryptedPassword { get; set; } /// /// Nom de la base de données qui contient les tables de PROSERVE. /// Default "proserveapi" — c'est le nom utilisé par le script d'install ASTERION /// qui provisionne XAMPP sur les machines clients. À surcharger seulement si la DB /// a été créée sous un autre nom. /// public string Database { get; set; } = "proserveapi"; /// /// Si true, le launcher applique automatiquement les migrations SQL bundled /// dans _migrations/ de chaque ZIP, juste après l'extraction. /// Sécurité : on n'applique JAMAIS sans cette opt-in (au cas où certains users /// auraient une config DB exotique non gérée par le defaut). /// public bool AutoApplyMigrations { get; set; } = true; } /// /// Paramètres de déploiement de l'outil Report local. /// Le launcher cherche un sous-dossier _report/ dans chaque ZIP PROSERVE /// installé et le copie vers {HtdocsRoot}\{FolderName} via un rename /// atomique (deploy en .new puis swap). L'URL de l'onglet Statistiques pointe /// vers http://localhost/{FolderName}/ ; les deux doivent matcher. /// public sealed class ReportToolConfig { public string HtdocsRoot { get; set; } = @"C:\xampp\htdocs"; public string FolderName { get; set; } = "ProserveReport"; public bool AutoDeploy { get; set; } = true; /// /// Nombre de backups horodatés à conserver dans htdocs après chaque deploy. /// Format des dossiers : {FolderName}.backup-yyyyMMdd-HHmmss. /// Permet le revert via Settings si le nouveau deploy a un bug visible. /// 3 par défaut = couvre les dernières releases sans grossir trop le disque. /// 0 = pas de backup (retourne au comportement v0.12.0). /// public int MaxBackups { get; set; } = 3; } /// /// Mêmes paramètres que mais pour la /// documentation locale (sous-dossier _doc/ du ZIP, déployé vers /// {HtdocsRoot}\ProserveDoc par défaut). /// public sealed class DocToolConfig { public string HtdocsRoot { get; set; } = @"C:\xampp\htdocs"; public string FolderName { get; set; } = "ProserveDoc"; public bool AutoDeploy { get; set; } = true; public int MaxBackups { get; set; } = 3; } public sealed class HealthChecksConfig { /// Période de re-vérification, en secondes. 0 = désactivé. public int RefreshIntervalSeconds { get; set; } = 10; /// /// Liste des checks à effectuer. Defaults = exemples typiques setup VR /// (SteamVR + Vive Business Streaming + ping casque). L'utilisateur édite /// la liste via %LocalAppData%\PSLauncher\config.json pour ajuster /// les noms de processus, IPs et seuils ; un éditeur Settings est prévu /// en v2 si le besoin est récurrent. /// public List Checks { get; set; } = new() { new HealthCheckEntry { Name = "SteamVR", Icon = "🎮", Kind = "Process", Target = "vrserver", }, new HealthCheckEntry { Name = "Vive Business Streaming", Icon = "📡", Kind = "Process", Target = "HtcConnectionUtility", }, // PAS de check « casque connecté » par défaut. // // Pourquoi : avec Vive Business Streaming dans la pile, le driver Vive // ment à SteamVR — il prétend qu'un HMD est présent même quand la // session Wi-Fi avec le casque physique est tombée. Donc tous les // signaux qui passent par SteamVR (VR_IsHmdPresent, vtable IVRSystem, // logs vrserver) sont contaminés par ce mensonge et ne reflètent pas // l'état réel du casque. // // À traiter en Phase 2 via un signal authoritative qui ne passe PAS // par SteamVR (pistes brainstormées : connexions TCP/UDP de // HtcConnectionUtility via GetExtendedTcpTable, endpoint local de // Vive Console, compteurs IO du process, ARP table avec OUIs HTC, // logs Vive Streaming directs). Ajout d'un Kind dédié à ce moment-là. }; } public sealed class HealthCheckEntry { /// Libellé affiché dans le bandeau, ex. "SteamVR". public string Name { get; set; } = ""; /// Pictogramme emoji affiché à côté du nom, ex. "🎮", "📡", "🥽". public string Icon { get; set; } = "🔌"; /// "Process" ou "Ping" (case-insensitive). public string Kind { get; set; } = "Process"; /// /// Selon Kind : nom du processus (sans .exe) pour Process, /// IP/hostname pour Ping. Vide = check ignoré (statut Unknown). /// public string Target { get; set; } = ""; /// /// Période entre 2 vérifications de CE check, en millisecondes. Indépendant /// pour chaque entry car un Process check est rapide (~1ms) et peut tourner /// toutes les secondes, alors qu'un Ping coûte une requête réseau et peut /// tourner toutes les 5-10 s. Default 5000 ms. /// public int RefreshIntervalMs { get; set; } = 5000; /// Pour Ping : RTT au-dessus duquel le statut passe en Warning (Orange). Default 50 ms. public int? PingWarnMs { get; set; } = 50; /// Pour Ping : RTT au-dessus duquel le statut passe en Error (Rouge). Default 200 ms. public int? PingErrorMs { get; set; } = 200; /// Pour Ping : timeout de la requête ICMP. Default 1 s. public int? PingTimeoutMs { get; set; } = 1000; } public sealed class LicenseConfig { public string? EncryptedKey { get; set; } /// /// Date UTC de la dernière validation online réussie. Utilise response.ServerTime /// (signé Ed25519) plutôt que l'horloge locale, donc non manipulable par l'utilisateur. /// Sert de point de départ au calcul d'expiration du cache offline. /// public DateTime? LastValidationAt { get; set; } public DateTime? CachedEntitlementUntil { get; set; } public string? CachedOwnerName { get; set; } /// /// Statut tel que renvoyé par le serveur lors de la dernière validation : "valid", /// "expired", "revoked", "invalid", "machine_limit_exceeded". Persisté pour qu'une /// révocation effectuée pendant que le user est offline soit toujours visible /// même après son expiration de cache local. Le calcul "valid → expired" basé /// sur la date est aussi appliqué côté lecture (sanity) si le temps a passé. /// public string? CachedStatus { get; set; } /// /// Compteur monotone : date UTC la plus récente jamais observée par le launcher /// sur cette machine. Mise à jour à chaque démarrage si now est postérieur. /// Sert d'anti-rollback : si l'utilisateur recule l'horloge PC, on détecte /// (now < LastSeenUtc - grace) et on invalide le cache → forçage d'une /// re-validation online la prochaine fois qu'il sera connecté. /// public DateTime? LastSeenUtc { get; set; } }