Compare commits

...

1 Commits

Author SHA1 Message Date
Lance Pioch
380d9900e5 Add user_mountable functionality for Mounts (#2077)
Resolves #2077
2026-02-06 02:42:22 -05:00
7 changed files with 152 additions and 1 deletions

View File

@@ -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,
};
}

View File

@@ -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()

View File

@@ -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);
}

View 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');
}
}

View File

@@ -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
View 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.',
];

View File

@@ -69,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.',
],
];