mailwolt/resources/views/livewire/ui/system/api-key-table.blade.php

297 lines
18 KiB
PHP

<x-slot:breadcrumbParent>System</x-slot:breadcrumbParent>
<x-slot:breadcrumb>API Keys</x-slot:breadcrumb>
<div x-data="{activeGroup: 'mailboxes'}"
x-on:token-created.window="$dispatch('openModal', {component: 'ui.system.modal.api-key-show-modal', arguments: {plainText: $event.detail.plainText}})">
<div class="mbx-page-header">
<div class="mbx-page-title">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M6 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" stroke="currentColor" stroke-width="1.3"/>
<path d="M9.5 8.5L14 13" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
<circle cx="6" cy="6" r="1.5" fill="currentColor" opacity=".4"/>
</svg>
API Keys
<span class="mbx-total-badge">{{ $tokens->count() }}</span>
</div>
<div class="mbx-page-actions">
<button wire:click="$dispatch('openModal',{component:'ui.system.modal.api-key-create-modal'})"
class="mbx-btn-primary">
<svg width="11" height="11" viewBox="0 0 11 11" fill="none"><path d="M5.5 1v9M1 5.5h9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
API Key
</button>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 340px;gap:14px;align-items:start">
{{-- ═══ Left: Keys + Endpoint-Docs ═══ --}}
<div class="mbx-sections">
{{-- Keys --}}
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info">
<span class="mbx-badge-mute">Aktive Keys</span>
</div>
</div>
@if($tokens->isEmpty())
<div style="padding:36px 16px;text-align:center">
<svg width="30" height="30" viewBox="0 0 16 16" fill="none" style="color:var(--mw-t4);margin:0 auto 10px;display:block">
<path d="M6 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" stroke="currentColor" stroke-width="1.3"/>
<path d="M9.5 8.5L14 13" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
</svg>
<div style="font-size:12.5px;color:var(--mw-t2);font-weight:500;margin-bottom:4px">Keine API Keys vorhanden</div>
<div style="font-size:11.5px;color:var(--mw-t4)">Erstelle deinen ersten Key um externe Anwendungen zu verbinden.</div>
</div>
@else
<div class="mbx-table-wrap">
<table class="mbx-table">
<thead>
<tr>
<th class="mbx-th">Name</th>
<th class="mbx-th">Scopes</th>
<th class="mbx-th" style="width:86px">Modus</th>
<th class="mbx-th" style="width:120px">Zuletzt genutzt</th>
<th class="mbx-th" style="width:110px">Erstellt</th>
<th class="mbx-th mbx-th-right" style="width:50px"></th>
</tr>
</thead>
<tbody>
@foreach($tokens as $token)
<tr class="mbx-tr">
<td class="mbx-td">
<div style="display:flex;align-items:center;gap:8px">
<div style="width:26px;height:26px;border-radius:7px;background:var(--mw-bg4);border:1px solid var(--mw-b2);display:flex;align-items:center;justify-content:center;flex-shrink:0">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none">
<path d="M6 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" stroke="var(--mw-t3)" stroke-width="1.3"/>
<path d="M9.5 8.5L14 13" stroke="var(--mw-t3)" stroke-width="1.3" stroke-linecap="round"/>
</svg>
</div>
<span style="font-size:12.5px;font-weight:500;color:var(--mw-t1)">{{ $token->name }}</span>
</div>
</td>
<td class="mbx-td">
<div style="display:flex;flex-wrap:wrap;gap:3px">
@foreach($token->abilities as $scope)
<span style="font-family:monospace;font-size:9.5px;padding:1px 5px;background:var(--mw-bg4);border:1px solid var(--mw-b2);border-radius:4px;color:var(--mw-t4)">{{ $scope }}</span>
@endforeach
</div>
</td>
<td class="mbx-td">
@if($token->sandbox)
<span style="font-size:10px;padding:2px 7px;border-radius:5px;background:rgba(251,191,36,.1);border:1px solid rgba(251,191,36,.25);color:#f59e0b;font-weight:500">Sandbox</span>
@else
<span style="font-size:10px;padding:2px 7px;border-radius:5px;background:rgba(16,185,129,.08);border:1px solid rgba(16,185,129,.2);color:#34d399;font-weight:500">Live</span>
@endif
</td>
<td class="mbx-td mbx-td-muted" style="font-size:11px">{{ $token->last_used_at?->diffForHumans() ?? '—' }}</td>
<td class="mbx-td mbx-td-muted" style="font-size:11px">{{ $token->created_at->format('d.m.Y') }}</td>
<td class="mbx-td">
<div class="mbx-actions">
<button wire:click="deleteToken({{ $token->id }})"
wire:confirm="API Key '{{ $token->name }}' wirklich löschen?"
class="mbx-act-btn mbx-act-danger" title="Löschen">
<svg width="12" height="12" viewBox="0 0 13 13" fill="none"><path d="M2 3h9M5 3V2h3v1M3.5 3l.5 8h5l.5-8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
{{-- Endpoint Reference --}}
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info">
<span class="mbx-badge-mute">Endpunkte</span>
<span style="font-size:10.5px;color:var(--mw-t4);margin-left:4px">Basis-URL: <code style="font-family:monospace;background:var(--mw-bg3);padding:1px 4px;border-radius:3px">/api/v1</code></span>
</div>
{{-- Tab switcher --}}
<div style="display:flex;gap:4px">
@foreach([
['mailboxes', 'Mailboxen'],
['aliases', 'Aliases'],
['domains', 'Domains'],
] as [$key, $label])
<button @click="activeGroup = '{{ $key }}'"
:style="activeGroup === '{{ $key }}'
? 'background:var(--mw-vbg);border-color:var(--mw-vbd);color:var(--mw-v2);font-weight:600'
: 'background:transparent;border-color:var(--mw-b2);color:var(--mw-t4)'"
style="padding:3px 10px;border-radius:5px;font-size:10.5px;border:1px solid;cursor:pointer;transition:all .1s">
{{ $label }}
</button>
@endforeach
</div>
</div>
<div style="padding:0">
{{-- Mailboxen --}}
<div x-show="activeGroup === 'mailboxes'" x-cloak>
@php $mbRoutes = [
['GET', '/mailboxes', 'mailboxes:read', 'Alle Mailboxen auflisten', '?domain=example.com&active=true'],
['GET', '/mailboxes/{id}', 'mailboxes:read', 'Einzelne Mailbox abrufen', null],
['POST', '/mailboxes', 'mailboxes:write', 'Neue Mailbox erstellen', null],
['PATCH', '/mailboxes/{id}', 'mailboxes:write', 'Mailbox aktualisieren', null],
['DELETE', '/mailboxes/{id}', 'mailboxes:write', 'Mailbox löschen', null],
]; @endphp
@include('livewire.ui.system.partials.api-endpoint-list', ['routes' => $mbRoutes])
<div style="padding:14px 16px;border-top:1px solid var(--mw-b1)">
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:8px;font-weight:500">POST-Body (Erstellen)</div>
<pre style="font-family:monospace;font-size:10.5px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.6">{
"email": "user@example.com",
"password": "sicher123",
"display_name": "Max Mustermann",
"quota_mb": 1024,
"is_active": true
}</pre>
</div>
</div>
{{-- Aliases --}}
<div x-show="activeGroup === 'aliases'" x-cloak>
@php $alRoutes = [
['GET', '/aliases', 'aliases:read', 'Alle Aliases auflisten', '?domain=example.com'],
['GET', '/aliases/{id}', 'aliases:read', 'Einzelnen Alias abrufen', null],
['POST', '/aliases', 'aliases:write', 'Neuen Alias erstellen', null],
['DELETE', '/aliases/{id}', 'aliases:write', 'Alias löschen', null],
]; @endphp
@include('livewire.ui.system.partials.api-endpoint-list', ['routes' => $alRoutes])
<div style="padding:14px 16px;border-top:1px solid var(--mw-b1)">
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:8px;font-weight:500">POST-Body (Erstellen)</div>
<pre style="font-family:monospace;font-size:10.5px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.6">{
"local": "info",
"domain": "example.com",
"recipients": ["admin@example.com"],
"is_active": true
}</pre>
</div>
</div>
{{-- Domains --}}
<div x-show="activeGroup === 'domains'" x-cloak>
@php $doRoutes = [
['GET', '/domains', 'domains:read', 'Alle Domains auflisten', null],
['GET', '/domains/{id}', 'domains:read', 'Einzelne Domain abrufen', null],
['POST', '/domains', 'domains:write', 'Neue Domain erstellen', null],
['DELETE', '/domains/{id}', 'domains:write', 'Domain löschen', null],
]; @endphp
@include('livewire.ui.system.partials.api-endpoint-list', ['routes' => $doRoutes])
<div style="padding:14px 16px;border-top:1px solid var(--mw-b1)">
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:8px;font-weight:500">POST-Body (Erstellen)</div>
<pre style="font-family:monospace;font-size:10.5px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.6">{
"domain": "example.com",
"description": "Hauptdomain",
"max_mailboxes": 100,
"max_aliases": 200,
"default_quota_mb": 1024
}</pre>
</div>
</div>
</div>
</div>
</div>
{{-- ═══ Right: Sidebar ═══ --}}
<div class="mbx-sections">
{{-- Auth-Info --}}
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info">
<span class="mbx-badge-mute">Authentifizierung</span>
</div>
</div>
<div style="padding:14px 16px;display:flex;flex-direction:column;gap:12px">
<div style="font-size:11.5px;color:var(--mw-t3)">Sende den Token als HTTP-Header mit jeder Anfrage:</div>
<pre style="font-family:monospace;font-size:10.5px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.6">Authorization: Bearer &lt;token&gt;
Content-Type: application/json</pre>
<div>
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:6px">Token prüfen:</div>
<pre style="font-family:monospace;font-size:10px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.6">GET /api/v1/me</pre>
</div>
</div>
</div>
{{-- Sandbox --}}
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info">
<span style="font-size:10px;padding:2px 7px;border-radius:5px;background:rgba(251,191,36,.1);border:1px solid rgba(251,191,36,.25);color:#f59e0b;font-weight:600">Sandbox</span>
</div>
</div>
<div style="padding:14px 16px;font-size:11.5px;color:var(--mw-t3);display:flex;flex-direction:column;gap:8px">
<div>Sandbox-Keys simulieren alle Schreiboperationen. Die API antwortet realistisch aber es werden <strong style="color:var(--mw-t2)">keine Änderungen</strong> gespeichert.</div>
<div style="padding:8px 10px;background:rgba(251,191,36,.05);border:1px solid rgba(251,191,36,.15);border-radius:6px;font-family:monospace;font-size:10.5px;color:#f59e0b">
"sandbox": true
</div>
<div style="font-size:11px;color:var(--mw-t4)">Jede Write-Response enthält dieses Feld wenn der Key im Sandbox-Modus ist.</div>
</div>
</div>
{{-- Scopes --}}
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info">
<span class="mbx-badge-mute">Scopes</span>
</div>
</div>
<div style="padding:14px 16px;display:flex;flex-direction:column;gap:6px">
@foreach([
['mailboxes:read', 'Mailboxen lesen'],
['mailboxes:write', 'Mailboxen erstellen / ändern / löschen'],
['aliases:read', 'Aliases lesen'],
['aliases:write', 'Aliases erstellen / löschen'],
['domains:read', 'Domains lesen'],
['domains:write', 'Domains erstellen / löschen'],
] as [$scope, $desc])
<div style="display:flex;align-items:baseline;gap:8px">
<code style="font-family:monospace;font-size:10px;padding:1px 6px;background:var(--mw-bg4);border:1px solid var(--mw-b2);border-radius:4px;color:var(--mw-t3);flex-shrink:0;white-space:nowrap">{{ $scope }}</code>
<span style="font-size:11px;color:var(--mw-t4)">{{ $desc }}</span>
</div>
@endforeach
</div>
</div>
{{-- Response codes --}}
<div class="mbx-section">
<div class="mbx-domain-head">
<div class="mbx-domain-info">
<span class="mbx-badge-mute">Status-Codes</span>
</div>
</div>
<div style="padding:14px 16px;display:flex;flex-direction:column;gap:6px">
@foreach([
['200', 'var(--mw-t3)', 'OK — Abfrage erfolgreich'],
['201', '#34d399', 'Created — Ressource erstellt'],
['204', 'var(--mw-t3)', 'No Content — Gelöscht'],
['401', '#f87171', 'Unauthorized — Token fehlt'],
['403', '#f87171', 'Forbidden — Scope fehlt'],
['404', '#fb923c', 'Not Found — nicht gefunden'],
['422', '#fb923c', 'Unprocessable — Validierung'],
] as [$code, $color, $desc])
<div style="display:flex;align-items:baseline;gap:8px">
<code style="font-family:monospace;font-size:10px;padding:1px 6px;background:var(--mw-bg4);border:1px solid var(--mw-b2);border-radius:4px;color:{{ $color }};flex-shrink:0;font-weight:600">{{ $code }}</code>
<span style="font-size:11px;color:var(--mw-t4)">{{ $desc }}</span>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>