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>
53 lines
1.7 KiB
PHP
53 lines
1.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require __DIR__ . '/lib/Response.php';
|
|
|
|
use PSLauncher\Response;
|
|
|
|
// Charge la config (hors git ; copier config.example.php → config.php au déploiement)
|
|
$configPath = __DIR__ . '/config.php';
|
|
if (!is_file($configPath)) {
|
|
http_response_code(500);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode(['error' => 'config_missing', 'message' => 'config.php absent — copier config.example.php']);
|
|
exit;
|
|
}
|
|
$config = require $configPath;
|
|
|
|
// Le path est passé via REQUEST_URI parce que le RewriteRule utilise PATH_INFO
|
|
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
|
$path = parse_url($uri, PHP_URL_PATH) ?? '/';
|
|
|
|
// Strip prefix /PS_Launcher/api
|
|
if (preg_match('#^.*?/PS_Launcher/api(.*)$#', $path, $m)) {
|
|
$route = trim($m[1], '/');
|
|
} else {
|
|
$route = trim($path, '/');
|
|
}
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
|
|
try {
|
|
if ($method === 'GET' && ($route === '' || $route === 'manifest')) {
|
|
require __DIR__ . '/routes/Manifest.php';
|
|
\PSLauncher\Routes\Manifest::handle($config);
|
|
return;
|
|
}
|
|
|
|
if ($method === 'GET' && preg_match('#^releasenotes/([0-9]+\.[0-9]+\.[0-9]+)$#', $route, $m)) {
|
|
require __DIR__ . '/routes/Releasenotes.php';
|
|
\PSLauncher\Routes\Releasenotes::handle($m[1]);
|
|
return;
|
|
}
|
|
|
|
if ($method === 'GET' && $route === 'health') {
|
|
Response::json(['status' => 'ok', 'serverTime' => gmdate('c')]);
|
|
}
|
|
|
|
Response::error('not_found', "Route not found: {$route}", 404);
|
|
} catch (\Throwable $e) {
|
|
error_log('[PSLauncher] ' . $e->getMessage() . "\n" . $e->getTraceAsString());
|
|
Response::error('server_error', 'Internal server error', 500);
|
|
}
|