= clm_html((string)clm_cfg('manager_name')) ?>
Manage licenses and installations for multiple products from one isolated module. This dashboard is intentionally separate from the old in-app license manager so you can move it later.
getMessage(); } if (isset($_GET['logout'])) { unset($_SESSION['clm_admin_auth']); clm_redirect('manage.php'); } $error = $schemaError; if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'login') { if ($schemaError !== '') { $error = $schemaError; } elseif (hash_equals((string)clm_cfg('admin_password'), (string)($_POST['password'] ?? ''))) { $_SESSION['clm_admin_auth'] = true; clm_redirect('manage.php'); } else { $error = 'Wrong admin password.'; } } $isAuthenticated = !empty($_SESSION['clm_admin_auth']); if ($isAuthenticated && $schemaError === '' && $_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') !== 'login') { try { $action = (string)($_POST['action'] ?? ''); $tables = clm_tables(); $activationsTable = $tables['activations']; switch ($action) { case 'create_app': $name = trim((string)($_POST['name'] ?? '')); $slug = trim((string)($_POST['slug'] ?? '')); if ($name === '' && $slug === '') { throw new RuntimeException('App name is required.'); } $app = clm_resolve_app($slug, $name, true); if (!$app) { throw new RuntimeException('App could not be created.'); } clm_set_flash('success', 'App saved: ' . $app['name']); break; case 'save_app': $app = clm_app_by_id((int)($_POST['id'] ?? 0)); if (!$app) { throw new RuntimeException('App not found.'); } $app = clm_update_app_record($app, [ 'name' => $_POST['name'] ?? '', 'slug' => $_POST['slug'] ?? '', 'status' => $_POST['status'] ?? 'active', ]); clm_set_flash('success', 'App updated: ' . $app['name']); break; case 'issue_license': $license = clm_issue_license_record([ 'app_slug' => $_POST['app_slug'] ?? '', 'app_name' => $_POST['app_name'] ?? '', 'prefix' => $_POST['prefix'] ?? 'FLAT', 'max_activations' => $_POST['max_activations'] ?? 1, 'max_counters' => $_POST['max_counters'] ?? 1, 'owner' => $_POST['owner'] ?? '', 'customer_name' => $_POST['customer_name'] ?? '', 'customer_email' => $_POST['customer_email'] ?? '', 'address' => $_POST['address'] ?? '', 'expires_at' => $_POST['expires_at'] ?? '', 'notes' => $_POST['notes'] ?? '', ]); clm_set_flash('success', 'License issued: ' . $license['license_key']); break; case 'save_license': $license = clm_license_with_app_by_id((int)($_POST['id'] ?? 0)); if (!$license) { throw new RuntimeException('License not found.'); } clm_update_license_record($license, [ 'owner' => $_POST['owner'] ?? '', 'customer_name' => $_POST['customer_name'] ?? '', 'customer_email' => $_POST['customer_email'] ?? '', 'app_slug' => $_POST['app_slug'] ?? '', 'max_activations' => $_POST['max_activations'] ?? 1, 'max_counters' => $_POST['max_counters'] ?? 1, 'expires_at' => $_POST['expires_at'] ?? '', 'status' => $_POST['status'] ?? 'active', 'notes' => $_POST['notes'] ?? '', ]); clm_set_flash('success', 'License updated.'); break; case 'toggle_license': $license = clm_license_with_app_by_id((int)($_POST['id'] ?? 0)); if (!$license) { throw new RuntimeException('License not found.'); } $nextStatus = ($license['status'] ?? 'active') === 'active' ? 'suspended' : 'active'; clm_update_license_record($license, ['status' => $nextStatus]); clm_set_flash('success', 'License status changed to ' . $nextStatus . '.'); break; case 'deactivate_activation': $id = (int)($_POST['id'] ?? 0); if ($id <= 0) { throw new RuntimeException('Activation not found.'); } $stmt = clm_db()->prepare("UPDATE `{$activationsTable}` SET status = 'deactivated', deactivated_at = NOW(), last_seen_at = NOW() WHERE id = ?"); $stmt->execute([$id]); clm_set_flash('success', 'Activation deactivated.'); break; } } catch (Throwable $e) { clm_set_flash('error', $e->getMessage()); } clm_redirect('manage.php'); } $flash = clm_get_flash(); if (!$isAuthenticated): ?>
Use the configured admin password to manage apps, licenses, and activations from one place.
Mode: = clm_html(clm_manager_mode_label()) ?>
Manage licenses and installations for multiple products from one isolated module. This dashboard is intentionally separate from the old in-app license manager so you can move it later.
Each app should call this module, not keep its own separate activation database.
= clm_html($baseUrl) ?>Current API stays backward compatible with older clients by returning both max_activations and allowed_activations.
| Name | Slug | Status | Licenses | Active installs | Action |
|---|---|---|---|---|---|
| No apps yet. | |||||
| = (int)$app['licenses_count'] ?> | = (int)$app['active_activations'] ?> | ||||
No licenses yet.
= clm_html($license['license_key']) ?>
| License | App | Domain | Fingerprint | Product | Status | Activated | Last seen | Action |
|---|---|---|---|---|---|---|---|---|
| No activations yet. | ||||||||
= clm_html($activation['license_key']) ?> |
= clm_html($activation['app_name']) ?> | = clm_html($activation['domain']) ?> | = clm_html(substr((string)$activation['fingerprint'], 0, 16)) ?>… |
= clm_html($activation['product']) ?> | = clm_html($activation['status']) ?> | = clm_html((string)$activation['activated_at']) ?> | = clm_html((string)($activation['last_seen_at'] ?: '—')) ?> | |