93 lines
4.9 KiB
PHP
93 lines
4.9 KiB
PHP
@props(['entry', 'unlocked'])
|
|
{{--
|
|
One vault entry — outline, new-value field, test/save/forget — reusable
|
|
across every integration section on App\Livewire\Admin\Integrations. Kept
|
|
as its own component so a section can sit a secret and a plain setting
|
|
side by side (R20 exception: this is the page, not a table row) without
|
|
repeating this block per section.
|
|
|
|
Locked and unlocked are two genuinely different renders, not one hidden
|
|
behind CSS: while locked, nothing about the stored value — not even
|
|
whether one exists beyond the badge above this component — reaches the
|
|
page beyond a "gesperrt" note.
|
|
--}}
|
|
<div wire:key="secret-{{ $entry['key'] }}" class="space-y-4">
|
|
<div class="flex flex-wrap items-start justify-between gap-3">
|
|
<div>
|
|
<h3 class="text-sm font-semibold text-ink">{{ $entry['label'] }}</h3>
|
|
<p class="mt-0.5 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 (! $unlocked)
|
|
<p class="flex items-center gap-1.5 text-sm text-muted">
|
|
<x-ui.icon name="lock" class="size-4" />
|
|
{{ __('secrets.locked_title') }}
|
|
</p>
|
|
@else
|
|
@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>
|
|
@if ($entry['multiline'])
|
|
{{-- A multi-line PEM key: a single-line password field would
|
|
mangle it on paste. Not masked — a textarea cannot mask
|
|
its content in any browser. --}}
|
|
<label for="entered-{{ $entry['field'] }}" class="block text-sm font-medium text-body">{{ __('secrets.new_value') }}</label>
|
|
<textarea id="entered-{{ $entry['field'] }}" name="entered.{{ $entry['field'] }}" rows="6" autocomplete="off" spellcheck="false"
|
|
wire:model="entered.{{ $entry['field'] }}"
|
|
class="mt-1.5 block w-full rounded border border-line bg-surface px-3.5 py-2.5 font-mono text-xs text-ink placeholder:text-faint transition"
|
|
placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"></textarea>
|
|
<p class="mt-1.5 text-xs text-muted">{{ __('secrets.ssh_private_key_hint') }}</p>
|
|
@error('entered.'.$entry['field'])<p class="mt-1.5 text-xs text-danger">{{ $message }}</p>@enderror
|
|
@else
|
|
<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'] }}" />
|
|
@endif
|
|
</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. --}}
|
|
@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"
|
|
x-on:click="$dispatch('openModal', { component: 'admin.confirm-save-secret', arguments: { key: '{{ $entry['key'] }}' } })">
|
|
{{ __('secrets.save') }}
|
|
</x-ui.button>
|
|
|
|
@if ($entry['source'] === 'stored')
|
|
<x-ui.button variant="ghost"
|
|
x-on:click="$dispatch('openModal', { component: 'admin.confirm-forget-secret', arguments: { key: '{{ $entry['key'] }}' } })">
|
|
{{ __('secrets.forget') }}
|
|
</x-ui.button>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</div>
|