Allow clipboard.writeText without HTTPS (#1723)

This commit is contained in:
MartinOscar 2025-09-24 01:22:29 +02:00 committed by GitHub
parent e3b3c92dcb
commit a5858a6d9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 45 additions and 55 deletions

View File

@ -79,7 +79,7 @@ class ApiKeyResource extends Resource
->label(trans('admin/apikey.table.key'))
->icon('tabler-clipboard-text')
->state(fn (ApiKey $key) => $key->identifier . $key->token)
->copyable(fn () => request()->isSecure()),
->copyable(),
TextColumn::make('memo')
->label(trans('admin/apikey.table.description'))
->wrap()

View File

@ -97,14 +97,14 @@ class CreateDatabaseHost extends CreateRecord
->default(fn (Get $get) => "CREATE USER '{$get('username')}'@'{$get('panel_ip')}' IDENTIFIED BY '{$get('password')}';")
->disabled()
->dehydrated(false)
->copyable(fn () => request()->isSecure())
->copyable()
->columnSpanFull(),
TextInput::make('assign_permissions')
->label(trans('admin/databasehost.setup.command_assign_permissions'))
->default(fn (Get $get) => "GRANT ALL PRIVILEGES ON *.* TO '{$get('username')}'@'{$get('panel_ip')}' WITH GRANT OPTION;")
->disabled()
->dehydrated(false)
->copyable(fn () => request()->isSecure())
->copyable()
->columnSpanFull(),
TextEntry::make('cli_exit')
->hiddenLabel()

View File

@ -179,7 +179,7 @@ class EditServer extends EditRecord
TextInput::make('uuid')
->label(trans('admin/server.uuid'))
->copyable(fn () => request()->isSecure())
->copyable()
->columnSpan([
'default' => 2,
'sm' => 1,
@ -190,7 +190,7 @@ class EditServer extends EditRecord
->dehydrated(false),
TextInput::make('uuid_short')
->label(trans('admin/server.short_uuid'))
->copyable(fn () => request()->isSecure())
->copyable()
->columnSpan([
'default' => 2,
'sm' => 1,

View File

@ -77,7 +77,7 @@ class ListServers extends ListRecords
->label('')
->badge()
->visibleFrom('md')
->copyable(request()->isSecure())
->copyable()
->state(fn (Server $server) => $server->allocation->address ?? 'None'),
TextColumn::make('cpuUsage')
->label(trans('server/dashboard.resources'))

View File

@ -2,28 +2,12 @@
namespace App\Filament\Server\Components;
use Closure;
use Filament\Support\Concerns\CanBeCopied;
use Filament\Widgets\StatsOverviewWidget\Stat;
use Illuminate\Contracts\View\View;
class SmallStatBlock extends Stat
{
protected bool|Closure $copyOnClick = false;
use CanBeCopied;
public function copyOnClick(bool|Closure $copyOnClick = true): static
{
$this->copyOnClick = $copyOnClick;
return $this;
}
public function shouldCopyOnClick(): bool
{
return $this->evaluate($this->copyOnClick);
}
public function render(): View
{
return view('filament.components.server-small-data-block', array_merge($this->getViewData(), $this->extractPublicMethods()));
}
protected string $view = 'filament.components.server-small-data-block';
}

View File

@ -165,7 +165,7 @@ class Settings extends ServerFormPage
->label(trans('server/setting.server_info.sftp.connection'))
->columnSpan(1)
->disabled()
->copyable(fn () => request()->isSecure())
->copyable()
->hintAction(
Action::make('connect_sftp')
->label(trans('server/setting.server_info.sftp.action'))
@ -185,7 +185,7 @@ class Settings extends ServerFormPage
TextInput::make('username')
->label(trans('server/setting.server_info.sftp.username'))
->columnSpan(1)
->copyable(fn () => request()->isSecure())
->copyable()
->disabled()
->formatStateUsing(fn (Server $server) => auth()->user()->username . '.' . $server->uuid_short),
TextEntry::make('password')

View File

@ -78,13 +78,13 @@ class DatabaseResource extends Resource
TextInput::make('host')
->label(trans('server/database.host'))
->formatStateUsing(fn (Database $database) => $database->address())
->copyable(fn () => request()->isSecure()),
->copyable(),
TextInput::make('database')
->label(trans('server/database.database'))
->copyable(fn () => request()->isSecure()),
->copyable(),
TextInput::make('username')
->label(trans('server/database.username'))
->copyable(fn () => request()->isSecure()),
->copyable(),
TextInput::make('password')
->label(trans('server/database.password'))
->password()->revealable()
@ -93,7 +93,7 @@ class DatabaseResource extends Resource
RotateDatabasePasswordAction::make()
->authorize(fn () => auth()->user()->can(Permission::ACTION_DATABASE_UPDATE, $server))
)
->copyable(fn () => request()->isSecure())
->copyable()
->formatStateUsing(fn (Database $database) => $database->password),
TextInput::make('remote')
->label(trans('server/database.remote')),
@ -104,7 +104,7 @@ class DatabaseResource extends Resource
->label(trans('server/database.jdbc'))
->password()->revealable()
->hidden(!auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
->copyable(fn () => request()->isSecure())
->copyable()
->columnSpanFull()
->formatStateUsing(fn (Database $database) => $database->jdbc),
]);

View File

@ -6,9 +6,7 @@ use App\Enums\ContainerStatus;
use App\Filament\Server\Components\SmallStatBlock;
use App\Models\Server;
use Carbon\CarbonInterface;
use Filament\Notifications\Notification;
use Filament\Widgets\StatsOverviewWidget;
use Livewire\Attributes\On;
class ServerOverview extends StatsOverviewWidget
{
@ -20,10 +18,10 @@ class ServerOverview extends StatsOverviewWidget
{
return [
SmallStatBlock::make(trans('server/console.labels.name'), $this->server->name)
->copyOnClick(fn () => request()->isSecure()),
->copyable(),
SmallStatBlock::make(trans('server/console.labels.status'), $this->status()),
SmallStatBlock::make(trans('server/console.labels.address'), $this->server?->allocation->address ?? 'None')
->copyOnClick(fn () => request()->isSecure()),
->copyable(),
SmallStatBlock::make(trans('server/console.labels.cpu'), $this->cpuUsage()),
SmallStatBlock::make(trans('server/console.labels.memory'), $this->memoryUsage()),
SmallStatBlock::make(trans('server/console.labels.disk'), $this->diskUsage()),
@ -90,16 +88,4 @@ class ServerOverview extends StatsOverviewWidget
return $used . ($this->server->disk > 0 ? ' / ' . $total : ' / ∞');
}
#[On('copyClick')]
public function copyClick(string $value): void
{
$this->js("window.navigator.clipboard.writeText('{$value}');");
Notification::make()
->title(trans('server/dashboard.copied'))
->body($value)
->success()
->send();
}
}

View File

@ -152,7 +152,7 @@ class AppServiceProvider extends ServiceProvider
Field::macro('hintCopy', function () {
/** @var Field $this */
return $this->hintAction(fn () => request()->isSecure() ? CopyAction::make() : null); // @phpstan-ignore varTag.nativeType
return $this->hintAction(CopyAction::make()); // @phpstan-ignore varTag.nativeType
});
// Don't run any health checks during tests

View File

@ -25,6 +25,4 @@ return [
'power_actions' => 'Power Actions',
'power_action_sent' => ':action sent to :name',
'copied' => 'Copied to clipboard',
];

View File

@ -0,0 +1,17 @@
(() => {
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') return;
navigator.clipboard = navigator.clipboard || {};
navigator.clipboard.writeText = async (text) =>
new Promise((resolve, reject) => {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
const success = document.execCommand('copy');
document.body.removeChild(textarea);
success ? resolve() : reject('Fallback copy failed');
});
})();

View File

@ -102,7 +102,7 @@
terminal.attachCustomKeyEventHandler((event) => {
if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
document.execCommand('copy'); // navigator.clipboard.writeText() only works on ssl..
navigator.clipboard.writeText(terminal.getSelection())
return false;
} else if ((event.ctrlKey || event.metaKey) && event.key === 'f') {
event.preventDefault();

View File

@ -1,6 +1,11 @@
<div class="fi-small-stat-block grid grid-flow-row w-full p-3 rounded-lg bg-white shadow-sm overflow-hidden overflow-x-auto ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10">
@if ($shouldCopyOnClick())
<span class="cursor-pointer" wire:click="copyClick('{{ $getValue() }}')">
@if ($isCopyable($value = $getValue()))
<span class="cursor-pointer" x-on:click="
navigator.clipboard.writeText(@js($value));
$tooltip(@js($getCopyMessage($value)), {
theme: $store.theme,
timeout: 2000,
})">
@else
<span>
@endif
@ -8,7 +13,7 @@
{{ $getLabel() }}
</span>
<span class="text-md font-semibold">
{{ $getValue() }}
{{ $value }}
</span>
</span>
</div>