190 lines
11 KiB
PHP
190 lines
11 KiB
PHP
<?php
|
|
$pageTitle = 'API';
|
|
$clients = is_array($clients ?? null) ? $clients : [];
|
|
$createdKey = (string)($created_key ?? '');
|
|
$status = trim((string)($status ?? ''));
|
|
$message = trim((string)($message ?? ''));
|
|
$baseUrl = rtrim((string)($base_url ?? ''), '/');
|
|
|
|
$endpointRows = [
|
|
[
|
|
'title' => 'Verify key',
|
|
'method' => 'GET',
|
|
'path' => $baseUrl . '/api/v1/auth/verify',
|
|
'note' => 'Quick auth check for AMS bootstrapping.',
|
|
],
|
|
[
|
|
'title' => 'Artist sales',
|
|
'method' => 'GET',
|
|
'path' => $baseUrl . '/api/v1/sales?artist_id=123',
|
|
'note' => 'Detailed paid sales rows for one artist.',
|
|
],
|
|
[
|
|
'title' => 'Sales since sync',
|
|
'method' => 'GET',
|
|
'path' => $baseUrl . '/api/v1/sales/since?artist_id=123&after_id=500',
|
|
'note' => 'Incremental sync using after_id or timestamp.',
|
|
],
|
|
[
|
|
'title' => 'Artist tracks',
|
|
'method' => 'GET',
|
|
'path' => $baseUrl . '/api/v1/tracks?artist_id=123',
|
|
'note' => 'Track list tied to one artist account.',
|
|
],
|
|
[
|
|
'title' => 'Order item detail',
|
|
'method' => 'GET',
|
|
'path' => $baseUrl . '/api/v1/order-items?artist_id=123',
|
|
'note' => 'Granular order transparency for AMS reporting.',
|
|
],
|
|
[
|
|
'title' => 'Submit release / tracks',
|
|
'method' => 'POST',
|
|
'path' => $baseUrl . '/api/v1/releases + /api/v1/tracks',
|
|
'note' => 'AMS pushes approved releases and tracks into AudioCore.',
|
|
],
|
|
];
|
|
|
|
ob_start();
|
|
?>
|
|
<section class="admin-card">
|
|
<div class="badge">Integration</div>
|
|
<div style="margin-top:14px; max-width:860px;">
|
|
<h1 style="margin:0; font-size:30px;">API</h1>
|
|
<p style="margin:8px 0 0; color:var(--muted);">AMS integration endpoints, API keys, and sales sync access live here. Keep this page operational rather than decorative: create clients, issue keys, and hand exact endpoints to the AMS.</p>
|
|
</div>
|
|
|
|
<?php if ($message !== ''): ?>
|
|
<div class="admin-card" style="margin-top:16px; padding:14px; border-color:<?= $status === 'ok' ? 'rgba(34,242,165,.25)' : 'rgba(255,120,120,.22)' ?>; color:<?= $status === 'ok' ? '#9ff8d8' : '#ffb0b0' ?>;">
|
|
<?= htmlspecialchars($message, ENT_QUOTES, 'UTF-8') ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($createdKey !== ''): ?>
|
|
<div class="admin-card" style="margin-top:16px; padding:16px;">
|
|
<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap;">
|
|
<div>
|
|
<div class="label">New API Key</div>
|
|
<div style="margin-top:8px; font-size:13px; color:#ffdfad;">Copy this now. It is only shown once.</div>
|
|
</div>
|
|
<button type="button" class="btn outline small" onclick="navigator.clipboard.writeText('<?= htmlspecialchars($createdKey, ENT_QUOTES, 'UTF-8') ?>')">Copy key</button>
|
|
</div>
|
|
<code style="display:block; margin-top:12px; padding:14px 16px; border-radius:14px; border:1px solid rgba(255,255,255,0.12); background:rgba(255,255,255,0.03); font-family:'IBM Plex Mono', monospace; font-size:13px; overflow:auto; white-space:nowrap;"><?= htmlspecialchars($createdKey, ENT_QUOTES, 'UTF-8') ?></code>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div style="display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; margin-top:18px;">
|
|
<article class="admin-card" style="padding:16px;">
|
|
<div class="label">Clients</div>
|
|
<div style="margin-top:10px; font-size:28px; font-weight:700;"><?= count($clients) ?></div>
|
|
<div style="margin-top:6px; color:var(--muted); font-size:13px;">Configured external systems</div>
|
|
</article>
|
|
<article class="admin-card" style="padding:16px;">
|
|
<div class="label">Auth</div>
|
|
<div style="margin-top:10px; font-size:28px; font-weight:700;">Bearer</div>
|
|
<div style="margin-top:6px; color:var(--muted); font-size:13px;">Also accepts <code>X-API-Key</code></div>
|
|
</article>
|
|
<article class="admin-card" style="padding:16px;">
|
|
<div class="label">Webhook</div>
|
|
<div style="margin-top:10px; font-size:28px; font-weight:700;">sale.paid</div>
|
|
<div style="margin-top:6px; color:var(--muted); font-size:13px;">Outbound sale notifications for AMS sync</div>
|
|
</article>
|
|
</div>
|
|
|
|
<article class="admin-card" style="padding:18px; margin-top:16px;">
|
|
<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap;">
|
|
<div>
|
|
<div class="label">Create API Client</div>
|
|
<div style="margin-top:8px; color:var(--muted); font-size:13px; max-width:680px;">Use one client per AMS install or per integration target. That keeps revocation clean and usage attribution obvious.</div>
|
|
</div>
|
|
</div>
|
|
<form method="post" action="/admin/api/clients/create" style="display:grid; gap:14px; margin-top:16px;">
|
|
<div style="display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr) auto; gap:12px; align-items:end;">
|
|
<label style="display:grid; gap:6px;">
|
|
<span class="label">Client Name</span>
|
|
<input class="input" type="text" name="name" placeholder="AudioCore AMS">
|
|
</label>
|
|
<label style="display:grid; gap:6px;">
|
|
<span class="label">Webhook URL (optional)</span>
|
|
<input class="input" type="url" name="webhook_url" placeholder="https://ams.example.com/webhooks/audiocore">
|
|
</label>
|
|
<button class="btn" type="submit">Create Key</button>
|
|
</div>
|
|
</form>
|
|
</article>
|
|
|
|
<article class="admin-card" style="padding:18px; margin-top:16px;">
|
|
<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap;">
|
|
<div>
|
|
<div class="label">Endpoint Reference</div>
|
|
<div style="margin-top:8px; color:var(--muted); font-size:13px;">Keep this as an operator reference. The layout is stacked because this panel needs readability more than compression.</div>
|
|
</div>
|
|
<div style="color:var(--muted); font-size:12px;">Use <strong>Authorization: Bearer <api-key></strong> or <strong>X-API-Key</strong>.</div>
|
|
</div>
|
|
<div style="display:grid; gap:10px; margin-top:16px;">
|
|
<?php foreach ($endpointRows as $row): ?>
|
|
<div class="admin-card" style="padding:14px 16px; display:grid; grid-template-columns:140px 96px minmax(0,1fr); gap:14px; align-items:start;">
|
|
<div>
|
|
<div style="font-weight:700;"><?= htmlspecialchars($row['title'], ENT_QUOTES, 'UTF-8') ?></div>
|
|
<div style="margin-top:5px; color:var(--muted); font-size:12px; line-height:1.45;"><?= htmlspecialchars($row['note'], ENT_QUOTES, 'UTF-8') ?></div>
|
|
</div>
|
|
<div>
|
|
<span class="pill" style="padding:6px 10px; font-size:10px; letter-spacing:0.14em; border-color:rgba(115,255,198,0.25); color:#9ff8d8;"><?= htmlspecialchars($row['method'], ENT_QUOTES, 'UTF-8') ?></span>
|
|
</div>
|
|
<code style="display:block; padding:10px 12px; border-radius:10px; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.1); font-size:12px; overflow:auto; white-space:nowrap;"><?= htmlspecialchars($row['path'], ENT_QUOTES, 'UTF-8') ?></code>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</article>
|
|
|
|
<article class="admin-card" style="padding:18px; margin-top:16px;">
|
|
<div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap;">
|
|
<div>
|
|
<div class="label">Active Clients</div>
|
|
<div style="margin-top:8px; color:var(--muted); font-size:13px;">Disable a client to cut access immediately. Delete only when you do not need audit visibility anymore.</div>
|
|
</div>
|
|
</div>
|
|
<?php if (!$clients): ?>
|
|
<div style="margin-top:14px; color:var(--muted); font-size:13px;">No API clients created yet.</div>
|
|
<?php else: ?>
|
|
<div style="display:grid; gap:10px; margin-top:14px;">
|
|
<?php foreach ($clients as $client): ?>
|
|
<div class="admin-card" style="padding:16px; display:grid; grid-template-columns:minmax(0,1fr) auto; gap:16px; align-items:center;">
|
|
<div>
|
|
<div style="display:flex; gap:10px; align-items:center; flex-wrap:wrap;">
|
|
<strong><?= htmlspecialchars((string)($client['name'] ?? ''), ENT_QUOTES, 'UTF-8') ?></strong>
|
|
<span class="pill" style="padding:5px 10px; font-size:10px; letter-spacing:0.14em; border-color:<?= (int)($client['is_active'] ?? 0) === 1 ? 'rgba(115,255,198,0.35)' : 'rgba(255,255,255,0.16)' ?>; color:<?= (int)($client['is_active'] ?? 0) === 1 ? '#9ff8d8' : '#a7adba' ?>;">
|
|
<?= (int)($client['is_active'] ?? 0) === 1 ? 'Active' : 'Disabled' ?>
|
|
</span>
|
|
</div>
|
|
<div style="display:grid; grid-template-columns:repeat(3,minmax(0,auto)); gap:18px; margin-top:10px; color:var(--muted); font-size:12px;">
|
|
<div><span class="label" style="display:block; margin-bottom:4px;">Key prefix</span><?= htmlspecialchars((string)($client['api_key_prefix'] ?? ''), ENT_QUOTES, 'UTF-8') ?>...</div>
|
|
<div><span class="label" style="display:block; margin-bottom:4px;">Last used</span><?= htmlspecialchars((string)($client['last_used_at'] ?? 'Never'), ENT_QUOTES, 'UTF-8') ?></div>
|
|
<div><span class="label" style="display:block; margin-bottom:4px;">Last IP</span><?= htmlspecialchars((string)($client['last_used_ip'] ?? '—'), ENT_QUOTES, 'UTF-8') ?></div>
|
|
</div>
|
|
<?php if (!empty($client['webhook_url'])): ?>
|
|
<div style="margin-top:8px; color:var(--muted); font-size:12px;">
|
|
Webhook: <?= htmlspecialchars((string)$client['webhook_url'], ENT_QUOTES, 'UTF-8') ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div style="display:flex; gap:8px; align-items:center; justify-content:flex-end;">
|
|
<form method="post" action="/admin/api/clients/toggle">
|
|
<input type="hidden" name="id" value="<?= (int)($client['id'] ?? 0) ?>">
|
|
<button type="submit" class="btn outline small"><?= (int)($client['is_active'] ?? 0) === 1 ? 'Disable' : 'Enable' ?></button>
|
|
</form>
|
|
<form method="post" action="/admin/api/clients/delete" onsubmit="return confirm('Delete this API client?');">
|
|
<input type="hidden" name="id" value="<?= (int)($client['id'] ?? 0) ?>">
|
|
<button type="submit" class="btn outline small" style="border-color:rgba(255,120,120,.35); color:#ffb0b0;">Delete</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</article>
|
|
</section>
|
|
<?php
|
|
$content = ob_get_clean();
|
|
require __DIR__ . '/../../../admin/views/layout.php';
|