prepare('INSERT INTO licenses (license_key, owner_name, issued_at, download_entitlement_until, max_machines, notes) VALUES (?, ?, NOW(), ?, ?, ?)') ->execute([$newKey, $owner, $until . ' 23:59:59', $maxMachines, $notes]); $message = "License émise avec succès. Note la clé maintenant — elle ne sera plus jamais affichée."; break; } catch (PDOException $e) { if (str_contains($e->getMessage(), 'Duplicate')) { continue; } throw $e; } } } elseif ($action === 'revoke') { $id = (int)($_POST['id'] ?? 0); $db->prepare('UPDATE licenses SET revoked_at = NOW() WHERE id = ?')->execute([$id]); $message = "License #{$id} révoquée."; } elseif ($action === 'unrevoke') { $id = (int)($_POST['id'] ?? 0); $db->prepare('UPDATE licenses SET revoked_at = NULL WHERE id = ?')->execute([$id]); $message = "License #{$id} réactivée."; } elseif ($action === 'extend') { $id = (int)($_POST['id'] ?? 0); $newUntil = trim($_POST['new_until'] ?? ''); if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $newUntil)) { throw new Exception('Date invalide.'); } $db->prepare('UPDATE licenses SET download_entitlement_until = ? WHERE id = ?') ->execute([$newUntil . ' 23:59:59', $id]); $message = "License #{$id} prolongée jusqu'au {$newUntil}."; } elseif ($action === 'reset_machines') { $id = (int)($_POST['id'] ?? 0); $db->prepare('DELETE FROM license_machines WHERE license_id = ?')->execute([$id]); $message = "Toutes les machines libérées pour la license #{$id}."; } elseif ($action === 'remove_machine') { $licenseId = (int)($_POST['license_id'] ?? 0); $machineId = trim((string)($_POST['machine_id'] ?? '')); if ($licenseId <= 0 || $machineId === '') { throw new Exception('license_id et machine_id requis'); } $stmt = $db->prepare('DELETE FROM license_machines WHERE license_id = ? AND machine_id = ?'); $stmt->execute([$licenseId, $machineId]); $count = $stmt->rowCount(); $shortId = substr($machineId, 0, 12); $message = $count > 0 ? "Machine {$shortId}… libérée pour la license #{$licenseId}." : "Aucune machine correspondante trouvée pour license #{$licenseId}."; } } catch (Exception $e) { $message = $e->getMessage(); $messageType = 'error'; } } $licenses = $db->query( 'SELECT l.*, (SELECT COUNT(*) FROM license_machines m WHERE m.license_id = l.id) AS machines_count FROM licenses l ORDER BY l.id DESC' )->fetchAll(); // Pré-fetch toutes les machines de toutes les licenses en une seule requête, // indexées par license_id pour l'affichage par-row sans N+1. $allMachines = $db->query( 'SELECT license_id, machine_id, machine_label, first_seen, last_seen FROM license_machines ORDER BY last_seen DESC' )->fetchAll(); $machinesByLicense = []; foreach ($allMachines as $m) { $machinesByLicense[(int)$m['license_id']][] = $m; } Layout::header('Licenses', 'licenses'); ?>

Licenses

⚡ Nouvelle clé — copie-la dès maintenant

Elle est stockée hashée — la prochaine fois que tu rechargeras cette page elle ne sera plus visible.

Émettre une license

est piloté par le clic sur la cellule "X / Y" ci-dessus. $machines = $machinesByLicense[(int)$l['id']] ?? []; if (!empty($machines)): ?>
ID Client Émise Expire Machines Statut Actions
#
0): ?> / 0 /
Prolonger
0): ?>
Machines actives sur la license # () :
Machine ID (SHA-256) Label 1ère activation Dernière vue Action
stale
Aucune license émise.