mirror of
https://github.com/pelican-dev/panel.git
synced 2026-02-24 03:12:01 +03:00
Compare commits
1 Commits
lance/2069
...
lance/2077
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
380d9900e5 |
@@ -58,6 +58,9 @@ enum SubuserPermission: string
|
||||
case SettingsDescription = 'settings.description';
|
||||
case SettingsReinstall = 'settings.reinstall';
|
||||
|
||||
case MountRead = 'mount.read';
|
||||
case MountUpdate = 'mount.update';
|
||||
|
||||
/** @return string[] */
|
||||
public function split(): array
|
||||
{
|
||||
@@ -84,6 +87,7 @@ enum SubuserPermission: string
|
||||
'schedule' => TablerIcon::Clock,
|
||||
'settings' => TablerIcon::Settings,
|
||||
'activity' => TablerIcon::Stack,
|
||||
'mount' => TablerIcon::LayersLinked,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,6 +143,24 @@ class MountResource extends Resource
|
||||
])
|
||||
->inline()
|
||||
->default(false),
|
||||
ToggleButtons::make('user_mountable')
|
||||
->label(trans('admin/mount.user_mountable'))
|
||||
->helperText(trans('admin/mount.user_mountable_help'))
|
||||
->stateCast(new BooleanStateCast(false, true))
|
||||
->options([
|
||||
false => trans('admin/mount.toggles.not_user_mountable'),
|
||||
true => trans('admin/mount.toggles.user_mountable'),
|
||||
])
|
||||
->icons([
|
||||
false => TablerIcon::Users,
|
||||
true => TablerIcon::Users,
|
||||
])
|
||||
->colors([
|
||||
false => 'warning',
|
||||
true => 'success',
|
||||
])
|
||||
->inline()
|
||||
->default(true),
|
||||
TextInput::make('source')
|
||||
->label(trans('admin/mount.source'))
|
||||
->required()
|
||||
|
||||
@@ -42,7 +42,6 @@ class CreateMount extends CreateRecord
|
||||
protected function handleRecordCreation(array $data): Model
|
||||
{
|
||||
$data['uuid'] ??= Str::uuid()->toString();
|
||||
$data['user_mountable'] = 1;
|
||||
|
||||
return parent::handleRecordCreation($data);
|
||||
}
|
||||
|
||||
114
app/Filament/Server/Pages/Mounts.php
Normal file
114
app/Filament/Server/Pages/Mounts.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Server\Pages;
|
||||
|
||||
use App\Enums\SubuserPermission;
|
||||
use App\Enums\TablerIcon;
|
||||
use App\Facades\Activity;
|
||||
use App\Models\Mount;
|
||||
use App\Models\Server;
|
||||
use BackedEnum;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class Mounts extends ServerFormPage
|
||||
{
|
||||
protected static string|BackedEnum|null $navigationIcon = TablerIcon::LayersLinked;
|
||||
|
||||
protected static ?int $navigationSort = 11;
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return parent::canAccess() && user()?->can(SubuserPermission::MountRead, Filament::getTenant());
|
||||
}
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(user()?->can(SubuserPermission::MountRead, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
protected function fillForm(): void
|
||||
{
|
||||
$this->form->fill([
|
||||
'mounts' => $this->getRecord()->mounts->pluck('id')->toArray(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
/** @var Server $server */
|
||||
$server = $this->getRecord();
|
||||
|
||||
$allowedMounts = Mount::query()
|
||||
->where('user_mountable', true)
|
||||
->where(function ($query) use ($server) {
|
||||
$query->whereDoesntHave('nodes')
|
||||
->orWhereHas('nodes', fn ($q) => $q->where('nodes.id', $server->node_id));
|
||||
})
|
||||
->where(function ($query) use ($server) {
|
||||
$query->whereDoesntHave('eggs')
|
||||
->orWhereHas('eggs', fn ($q) => $q->where('eggs.id', $server->egg_id));
|
||||
})
|
||||
->get();
|
||||
|
||||
return parent::form($schema)
|
||||
->components([
|
||||
Section::make(trans('server/mount.description'))
|
||||
->schema([
|
||||
CheckboxList::make('mounts')
|
||||
->hiddenLabel()
|
||||
->relationship('mounts')
|
||||
->options(fn () => $allowedMounts->mapWithKeys(fn (Mount $mount) => [$mount->id => $mount->name]))
|
||||
->descriptions(fn () => $allowedMounts->mapWithKeys(fn (Mount $mount) => [$mount->id => "$mount->source -> $mount->target"]))
|
||||
->helperText(fn () => $allowedMounts->isEmpty() ? trans('server/mount.no_mounts') : null)
|
||||
->disabled(fn (Server $server) => !user()?->can(SubuserPermission::MountUpdate, $server))
|
||||
->bulkToggleable()
|
||||
->live()
|
||||
->afterStateUpdated(function ($state) {
|
||||
$this->save();
|
||||
})
|
||||
->columnSpanFull(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
/** @var Server $server */
|
||||
$server = $this->getRecord();
|
||||
|
||||
abort_unless(user()?->can(SubuserPermission::MountUpdate, $server), 403);
|
||||
|
||||
try {
|
||||
$this->form->getState();
|
||||
$this->form->saveRelationships();
|
||||
|
||||
Activity::event('server:mount.update')
|
||||
->log();
|
||||
|
||||
Notification::make()
|
||||
->title(trans('server/mount.notification_updated'))
|
||||
->success()
|
||||
->send();
|
||||
} catch (\Exception $exception) {
|
||||
Notification::make()
|
||||
->title(trans('server/mount.notification_failed'))
|
||||
->body($exception->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return trans('server/mount.title');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return trans('server/mount.title');
|
||||
}
|
||||
}
|
||||
@@ -77,30 +77,17 @@ class SubuserResource extends Resource
|
||||
$options = [];
|
||||
$descriptions = [];
|
||||
|
||||
$translationPrefix = $data['translationPrefix'] ?? 'server/user.permissions.';
|
||||
$customDescriptions = $data['descriptions'] ?? [];
|
||||
|
||||
foreach ($data['permissions'] as $permission) {
|
||||
$options[$permission] = str($permission)->headline();
|
||||
|
||||
if (isset($customDescriptions[$permission])) {
|
||||
$descriptions[$permission] = $customDescriptions[$permission];
|
||||
} else {
|
||||
$descKey = $translationPrefix . $data['name'] . '_' . str($permission)->replace('-', '_');
|
||||
$descriptions[$permission] = trans()->has($descKey) ? trans($descKey) : null;
|
||||
}
|
||||
|
||||
$descriptions[$permission] = trans('server/user.permissions.' . $data['name'] . '_' . str($permission)->replace('-', '_'));
|
||||
$permissionsArray[$data['name']][] = $permission;
|
||||
}
|
||||
|
||||
$tabLabelKey = $translationPrefix . $data['name'];
|
||||
$groupDescKey = $translationPrefix . $data['name'] . '_desc';
|
||||
|
||||
$tabs[] = Tab::make($data['name'])
|
||||
->label($data['label'] ?? (trans()->has($tabLabelKey) ? trans($tabLabelKey) : str($data['name'])->headline()))
|
||||
->label(str($data['name'])->headline())
|
||||
->schema([
|
||||
Section::make()
|
||||
->description($data['description'] ?? (trans()->has($groupDescKey) ? trans($groupDescKey) : null))
|
||||
->description(trans('server/user.permissions.' . $data['name'] . '_desc'))
|
||||
->icon($data['icon'])
|
||||
->contained(false)
|
||||
->schema([
|
||||
|
||||
@@ -33,17 +33,11 @@ class Subuser extends Model implements Validatable
|
||||
*/
|
||||
public const RESOURCE_NAME = 'server_subuser';
|
||||
|
||||
/** @var array<string, array{name: string, hidden?: ?bool, icon?: ?string, permissions: string[], translationPrefix?: string, label?: string, description?: string, descriptions?: array<string, string>}> */
|
||||
/** @var array<string, array{name: string, hidden: ?bool, icon: ?string, permissions: string[]}> */
|
||||
protected static array $customPermissions = [];
|
||||
|
||||
/**
|
||||
* @param string[] $permissions
|
||||
* @param ?string $label Custom label for the permission tab (overrides translation lookup)
|
||||
* @param ?string $description Custom description for the permission group (overrides translation lookup)
|
||||
* @param ?array<string, string> $descriptions Custom descriptions keyed by permission name (overrides translation lookup)
|
||||
* @param ?string $translationPrefix Translation prefix for looking up labels/descriptions (e.g. 'my-plugin::permissions.')
|
||||
*/
|
||||
public static function registerCustomPermissions(string $name, array $permissions, ?string $icon = null, ?bool $hidden = null, ?string $translationPrefix = null, ?string $label = null, ?string $description = null, ?array $descriptions = null): void
|
||||
/** @param string[] $permissions */
|
||||
public static function registerCustomPermissions(string $name, array $permissions, ?string $icon = null, ?bool $hidden = null): void
|
||||
{
|
||||
$customPermission = static::$customPermissions[$name] ?? [];
|
||||
|
||||
@@ -58,22 +52,6 @@ class Subuser extends Model implements Validatable
|
||||
$customPermission['hidden'] = $hidden;
|
||||
}
|
||||
|
||||
if (!is_null($translationPrefix)) {
|
||||
$customPermission['translationPrefix'] = $translationPrefix;
|
||||
}
|
||||
|
||||
if (!is_null($label)) {
|
||||
$customPermission['label'] = $label;
|
||||
}
|
||||
|
||||
if (!is_null($description)) {
|
||||
$customPermission['description'] = $description;
|
||||
}
|
||||
|
||||
if (!is_null($descriptions)) {
|
||||
$customPermission['descriptions'] = array_merge($customPermission['descriptions'] ?? [], $descriptions);
|
||||
}
|
||||
|
||||
static::$customPermissions[$name] = $customPermission;
|
||||
}
|
||||
|
||||
@@ -115,7 +93,7 @@ class Subuser extends Model implements Validatable
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/** @return array<array{name: string, hidden: bool, icon: string, permissions: string[], translationPrefix?: ?string, label?: ?string, description?: ?string, descriptions?: array<string, string>}> */
|
||||
/** @return array<array{name: string, hidden: bool, icon: string, permissions: string[]}> */
|
||||
public static function allPermissionData(): array
|
||||
{
|
||||
$allPermissions = [];
|
||||
@@ -128,28 +106,22 @@ class Subuser extends Model implements Validatable
|
||||
'hidden' => $subuserPermission->isHidden(),
|
||||
'icon' => $subuserPermission->getIcon(),
|
||||
'permissions' => array_merge($allPermissions[$group]['permissions'] ?? [], [$permission]),
|
||||
'translationPrefix' => null,
|
||||
'label' => null,
|
||||
'description' => null,
|
||||
'descriptions' => [],
|
||||
];
|
||||
}
|
||||
|
||||
foreach (static::$customPermissions as $customPermission) {
|
||||
$name = $customPermission['name'];
|
||||
|
||||
$existing = $allPermissions[$name] ?? null;
|
||||
$groupData = $allPermissions[$name] ?? [];
|
||||
|
||||
$allPermissions[$name] = [
|
||||
$groupData = [
|
||||
'name' => $name,
|
||||
'hidden' => $customPermission['hidden'] ?? ($existing !== null ? $existing['hidden'] : false),
|
||||
'icon' => $customPermission['icon'] ?? ($existing !== null ? $existing['icon'] : null),
|
||||
'permissions' => array_unique(array_merge($existing !== null ? $existing['permissions'] : [], $customPermission['permissions'])),
|
||||
'translationPrefix' => ($customPermission['translationPrefix'] ?? null) ?? ($existing !== null ? $existing['translationPrefix'] : null),
|
||||
'label' => ($customPermission['label'] ?? null) ?? ($existing !== null ? $existing['label'] : null),
|
||||
'description' => ($customPermission['description'] ?? null) ?? ($existing !== null ? $existing['description'] : null),
|
||||
'descriptions' => array_merge($existing !== null ? $existing['descriptions'] : [], $customPermission['descriptions'] ?? []),
|
||||
'hidden' => $customPermission['hidden'] ?? $groupData['hidden'] ?? false,
|
||||
'icon' => $customPermission['icon'] ?? $groupData['icon'],
|
||||
'permissions' => array_unique(array_merge($groupData['permissions'] ?? [], $customPermission['permissions'])),
|
||||
];
|
||||
|
||||
$allPermissions[$name] = $groupData;
|
||||
}
|
||||
|
||||
return array_values($allPermissions);
|
||||
|
||||
@@ -17,9 +17,13 @@ return [
|
||||
'no_mounts' => 'No Mounts',
|
||||
'eggs' => 'Eggs',
|
||||
'nodes' => 'Nodes',
|
||||
'user_mountable' => 'User Mountable?',
|
||||
'user_mountable_help' => 'Allow users to toggle this mount on or off for their servers.',
|
||||
'toggles' => [
|
||||
'writable' => 'Writable',
|
||||
'read_only' => 'Read Only',
|
||||
'user_mountable' => 'User Mountable',
|
||||
'not_user_mountable' => 'Admin Only',
|
||||
],
|
||||
'table' => [
|
||||
'name' => 'Name',
|
||||
|
||||
9
lang/en/server/mount.php
Normal file
9
lang/en/server/mount.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Mounts',
|
||||
'description' => 'Manage the mounts attached to your server.',
|
||||
'no_mounts' => 'There are no user-mountable mounts available for this server.',
|
||||
'notification_updated' => 'Mounts updated successfully.',
|
||||
'notification_failed' => 'Failed to update mounts.',
|
||||
];
|
||||
@@ -17,16 +17,6 @@ return [
|
||||
'notification_failed' => 'Failed to invite user!',
|
||||
'permissions' => [
|
||||
'title' => 'Permissions',
|
||||
'control' => 'Control',
|
||||
'user' => 'User',
|
||||
'file' => 'File',
|
||||
'backup' => 'Backup',
|
||||
'schedule' => 'Schedule',
|
||||
'database' => 'Database',
|
||||
'allocation' => 'Allocation',
|
||||
'startup' => 'Startup',
|
||||
'settings' => 'Settings',
|
||||
'activity' => 'Activity',
|
||||
'activity_desc' => 'Permissions that control a user\'s access to the server activity logs.',
|
||||
'startup_desc' => 'Permissions that control a user\'s ability to view this server\'s startup parameters.',
|
||||
'settings_desc' => 'Permissions that control a user\'s ability to modify this server\'s settings.',
|
||||
@@ -79,5 +69,8 @@ return [
|
||||
'backup_delete' => 'Allows a user to remove backups from the system.',
|
||||
'backup_download' => 'Allows a user to download a backup for the server. Danger: this allows a user to access all files for the server in the backup.',
|
||||
'backup_restore' => 'Allows a user to restore a backup for the server. Danger: this allows the user to delete all of the server files in the process.',
|
||||
'mount_desc' => 'Permissions that control a user\'s ability to manage mounts for this server.',
|
||||
'mount_read' => 'Allows a user to view the mounts page and see available mounts.',
|
||||
'mount_update' => 'Allows a user to toggle mounts on or off for the server.',
|
||||
],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user