CluPilotCloud/resources/views/livewire/admin/secrets.blade.php

150 lines
8.9 KiB
PHP

<div class="mx-auto max-w-3xl space-y-6">
<div class="animate-rise">
<h1 class="text-2xl font-semibold tracking-tight text-ink">{{ __('secrets.title') }}</h1>
<p class="mt-1 text-sm text-muted">{{ __('secrets.subtitle') }}</p>
</div>
@if (! $usable)
<x-ui.alert variant="warning">{{ __('secrets.no_key') }}</x-ui.alert>
@endif
@if (! $unlocked)
{{-- The second gate. Being signed in is not enough to read a payment
key: the realistic threat is an unlocked machine, and a session is
exactly what that hands over. --}}
<form wire:submit="confirmPassword" class="rounded-xl border border-line bg-surface p-6 shadow-xs animate-rise">
<h2 class="font-semibold text-ink">{{ __('secrets.locked_title') }}</h2>
<p class="mt-1.5 max-w-xl text-sm text-muted">{{ __('secrets.locked_body') }}</p>
<div class="mt-4 flex flex-wrap items-start gap-2">
<div class="min-w-56 flex-1">
<x-ui.input name="confirmablePassword" type="password" autocomplete="current-password"
:label="__('admin_settings.password_current')" wire:model="confirmablePassword" />
</div>
<x-ui.button type="submit" variant="primary" class="mt-7" wire:loading.attr="disabled" wire:target="confirmPassword">
{{ __('secrets.unlock') }}
</x-ui.button>
</div>
</form>
@else
<div class="flex items-center justify-between rounded-lg border border-accent-border bg-accent-subtle px-4 py-2.5 animate-rise">
<p class="text-sm text-accent-text">{{ __('secrets.unlocked_note') }}</p>
<button type="button" wire:click="forgetPasswordConfirmation"
class="text-xs font-semibold text-accent-text hover:underline">{{ __('secrets.lock_again') }}</button>
</div>
@foreach ($entries as $entry)
<div wire:key="secret-{{ $entry['key'] }}" class="space-y-4 rounded-xl border border-line bg-surface p-6 shadow-xs animate-rise">
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<h2 class="font-semibold text-ink">{{ $entry['label'] }}</h2>
<p class="mt-1 font-mono text-xs text-muted">{{ $entry['key'] }}</p>
</div>
<span class="inline-flex items-center gap-1.5 rounded-pill border px-2.5 py-0.5 text-xs font-medium
{{ $entry['source'] === 'stored' ? 'border-success-border bg-success-bg text-success'
: ($entry['source'] === 'environment' ? 'border-info-border bg-info-bg text-info'
: 'border-warning-border bg-warning-bg text-warning') }}">
<span class="size-1.5 rounded-pill bg-current" aria-hidden="true"></span>
{{ __('secrets.source_'.$entry['source']) }}
</span>
</div>
@if ($entry['outline'])
<dl class="rounded-lg border border-line bg-surface-2 px-4 py-3 text-sm">
<div class="flex justify-between gap-4">
<dt class="text-muted">{{ __('secrets.stored_value') }}</dt>
<dd class="font-mono text-body">{{ $entry['outline'] }}</dd>
</div>
@if ($entry['updated_at'])
<div class="mt-1 flex justify-between gap-4">
<dt class="text-muted">{{ __('secrets.changed') }}</dt>
<dd class="text-body">{{ \Illuminate\Support\Carbon::parse($entry['updated_at'])->diffForHumans() }}</dd>
</div>
@endif
</dl>
@endif
<div>
<x-ui.input name="entered.{{ $entry['field'] }}" type="password" autocomplete="off"
:label="__('secrets.new_value')" :hint="__('secrets.new_value_hint')"
wire:model="entered.{{ $entry['field'] }}" />
</div>
<div class="flex flex-wrap gap-2">
{{-- Test first: a key that is saved and wrong fails later,
somewhere else, usually in front of a customer. Only
where this entry actually has a checker. --}}
@if ($entry['testable'])
<x-ui.button variant="secondary" wire:click="test('{{ $entry['key'] }}')"
wire:loading.attr="disabled" wire:target="test('{{ $entry['key'] }}')">
{{ __('secrets.test') }}
</x-ui.button>
@endif
<x-ui.button variant="primary" wire:click="save('{{ $entry['key'] }}')"
wire:confirm="{{ __('secrets.save_confirm') }}"
wire:loading.attr="disabled" wire:target="save('{{ $entry['key'] }}')">
{{ __('secrets.save') }}
</x-ui.button>
@if ($entry['source'] === 'stored')
<x-ui.button variant="ghost" wire:click="forget('{{ $entry['key'] }}')"
wire:confirm="{{ __('secrets.forget_confirm') }}"
wire:loading.attr="disabled" wire:target="forget('{{ $entry['key'] }}')">
{{ __('secrets.forget') }}
</x-ui.button>
@endif
</div>
</div>
@endforeach
@if ($check !== null)
<div class="rounded-xl border border-line bg-surface p-6 shadow-xs animate-rise">
<h2 class="font-semibold text-ink">{{ __('secrets.check_title') }}</h2>
@if (! $check['ok'])
<x-ui.alert variant="danger" class="mt-3">{{ __('secrets.check_'.$check['reason']) }}</x-ui.alert>
@else
<dl class="mt-3 space-y-1 text-sm">
<div class="flex gap-2"><dt class="text-muted">{{ __('secrets.check_account') }}:</dt>
<dd class="font-mono text-body">{{ $check['account'] }}{{ $check['business'] ? ' · '.$check['business'] : '' }}</dd></div>
<div class="flex gap-2"><dt class="text-muted">{{ __('secrets.check_mode') }}:</dt>
<dd class="{{ $check['live'] ? 'font-medium text-warning' : 'text-body' }}">
{{ $check['live'] ? __('secrets.mode_live') : __('secrets.mode_test') }}{{ $check['restricted'] ? ' · '.__('secrets.mode_restricted') : '' }}
</dd></div>
</dl>
{{-- The part nobody can see from outside: a key can be
perfectly valid while the endpoint listens for the
wrong events, and nothing fails until a payment goes
unrecorded. --}}
<h3 class="mt-5 text-sm font-semibold text-ink">{{ __('secrets.check_webhooks') }}</h3>
@if ($check['webhooks'] === null)
<p class="mt-1 text-sm text-muted">{{ __('secrets.check_webhooks_unknown') }}</p>
@elseif ($check['webhooks'] === [])
<x-ui.alert variant="warning" class="mt-2">{{ __('secrets.check_webhooks_none') }}</x-ui.alert>
@else
<ul class="mt-2 space-y-3">
@foreach ($check['webhooks'] as $hook)
<li class="rounded-lg border border-line bg-surface-2 px-4 py-3">
<div class="flex flex-wrap items-center justify-between gap-2">
<span class="font-mono text-xs text-body">{{ $hook['url'] }}</span>
<span class="text-xs {{ $hook['status'] === 'enabled' ? 'text-success' : 'text-warning' }}">{{ $hook['status'] }}</span>
</div>
<div class="mt-2 flex flex-wrap gap-1.5">
@foreach ($hook['events'] as $event)
<span class="rounded border border-line px-1.5 py-0.5 font-mono text-[11px] text-muted">{{ $event }}</span>
@endforeach
</div>
</li>
@endforeach
</ul>
@endif
@endif
</div>
@endif
<x-ui.alert variant="info">{{ __('secrets.webhook_secret_note') }}</x-ui.alert>
@endif
</div>