297 lines
18 KiB
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 <token>
|
|
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>
|