mirror of
https://github.com/pelican-dev/panel.git
synced 2026-02-24 03:12:01 +03:00
Compare commits
1 Commits
lance/2163
...
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);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ use App\Traits\Filament\CanCustomizeTabs;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
@@ -1107,13 +1106,9 @@ class EditServer extends EditRecord
|
||||
->modalHeading(trans('filament-actions::delete.single.modal.heading', ['label' => $this->getRecordTitle()]))
|
||||
->modalSubmitActionLabel(trans('filament-actions::delete.single.label'))
|
||||
->requiresConfirmation()
|
||||
->form(fn (Server $server) => $server->backups()->where('is_locked', false)->exists() ? [
|
||||
Checkbox::make('delete_backups')
|
||||
->label(trans('admin/server.delete_backups', ['count' => $server->backups()->where('is_locked', false)->count()])),
|
||||
] : [])
|
||||
->action(function (array $data, Server $server, ServerDeletionService $service) {
|
||||
->action(function (Server $server, ServerDeletionService $service) {
|
||||
try {
|
||||
$service->withDeleteBackups($data['delete_backups'] ?? false)->handle($server);
|
||||
$service->handle($server);
|
||||
|
||||
return redirect(ListServers::getUrl(panel: 'admin'));
|
||||
} catch (ConnectionException) {
|
||||
@@ -1137,13 +1132,9 @@ class EditServer extends EditRecord
|
||||
->modalHeading(trans('filament-actions::force-delete.single.modal.heading', ['label' => $this->getRecordTitle()]))
|
||||
->modalSubmitActionLabel(trans('filament-actions::force-delete.single.label'))
|
||||
->requiresConfirmation()
|
||||
->form(fn (Server $server) => $server->backups()->where('is_locked', false)->exists() ? [
|
||||
Checkbox::make('delete_backups')
|
||||
->label(trans('admin/server.delete_backups', ['count' => $server->backups()->where('is_locked', false)->count()])),
|
||||
] : [])
|
||||
->action(function (array $data, Server $server, ServerDeletionService $service) {
|
||||
->action(function (Server $server, ServerDeletionService $service) {
|
||||
try {
|
||||
$service->withForce()->withDeleteBackups($data['delete_backups'] ?? false)->handle($server);
|
||||
$service->withForce()->handle($server);
|
||||
|
||||
return redirect(ListServers::getUrl(panel: 'admin'));
|
||||
} catch (ConnectionException) {
|
||||
|
||||
@@ -78,15 +78,11 @@ class Console extends Page
|
||||
$feature = data_get($data, 'key');
|
||||
|
||||
$feature = $this->featureService->get($feature);
|
||||
if (!$feature) {
|
||||
if (!$feature || $this->getMountedAction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getMountedAction()) {
|
||||
$this->replaceMountedAction($feature->getId());
|
||||
} else {
|
||||
$this->mountAction($feature->getId());
|
||||
}
|
||||
$this->mountAction($feature->getId());
|
||||
sleep(2); // TODO find a better way
|
||||
}
|
||||
|
||||
public function getWidgetData(): array
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -209,18 +209,16 @@ class ListFiles extends ListRecords
|
||||
->required()
|
||||
->live(),
|
||||
TextEntry::make('new_location')
|
||||
->state(fn (Get $get, File $file) => resolve_path(join_paths($this->path, str_ends_with($get('location') ?? '/', '/') ? join_paths($get('location') ?? '/', $file->name) : $get('location') ?? '/'))),
|
||||
->state(fn (Get $get, File $file) => resolve_path(join_paths($this->path, $get('location') ?? '/', $file->name))),
|
||||
])
|
||||
->action(function ($data, File $file) {
|
||||
$location = $data['location'];
|
||||
$endsWithSlash = str_ends_with($location, '/');
|
||||
$to = $endsWithSlash ? join_paths($location, $file->name) : $location;
|
||||
$files = [['to' => $to, 'from' => $file->name]];
|
||||
$files = [['to' => join_paths($location, $file->name), 'from' => $file->name]];
|
||||
|
||||
$this->getDaemonFileRepository()->renameFiles($this->path, $files);
|
||||
|
||||
$oldLocation = join_paths($this->path, $file->name);
|
||||
$newLocation = resolve_path(join_paths($this->path, $to));
|
||||
$newLocation = resolve_path(join_paths($this->path, $location, $file->name));
|
||||
|
||||
Activity::event('server:file.rename')
|
||||
->property('directory', $this->path)
|
||||
|
||||
@@ -74,7 +74,7 @@ class OAuthController extends Controller
|
||||
$email = $oauthUser->getEmail();
|
||||
|
||||
if (!$email) {
|
||||
return $this->errorRedirect('No email was linked to your account on the OAuth provider.');
|
||||
return $this->errorRedirect();
|
||||
}
|
||||
|
||||
$user = User::whereEmail($email)->first();
|
||||
|
||||
@@ -358,7 +358,7 @@ class Node extends Model implements Validatable
|
||||
'disk_used' => 0,
|
||||
];
|
||||
|
||||
return cache()->flexible("nodes.$this->id.statistics", [5, 30], function () use ($default) {
|
||||
return cache()->remember("nodes.$this->id.statistics", now()->addSeconds(360), function () use ($default) {
|
||||
try {
|
||||
|
||||
$data = Http::daemon($this)
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Services\Servers;
|
||||
use App\Exceptions\DisplayException;
|
||||
use App\Models\Server;
|
||||
use App\Repositories\Daemon\DaemonServerRepository;
|
||||
use App\Services\Backups\DeleteBackupService;
|
||||
use App\Services\Databases\DatabaseManagementService;
|
||||
use Exception;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
@@ -17,16 +16,13 @@ class ServerDeletionService
|
||||
{
|
||||
protected bool $force = false;
|
||||
|
||||
protected bool $deleteBackups = false;
|
||||
|
||||
/**
|
||||
* ServerDeletionService constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
private ConnectionInterface $connection,
|
||||
private DaemonServerRepository $daemonServerRepository,
|
||||
private DatabaseManagementService $databaseManagementService,
|
||||
private DeleteBackupService $deleteBackupService
|
||||
private DatabaseManagementService $databaseManagementService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -39,16 +35,6 @@ class ServerDeletionService
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if unlocked backups should be deleted from storage when the server is deleted.
|
||||
*/
|
||||
public function withDeleteBackups(bool $bool = true): self
|
||||
{
|
||||
$this->deleteBackups = $bool;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a server from the panel and remove any associated databases from hosts.
|
||||
*
|
||||
@@ -92,22 +78,6 @@ class ServerDeletionService
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->deleteBackups) {
|
||||
foreach ($server->backups()->where('is_locked', false)->get() as $backup) {
|
||||
try {
|
||||
$this->deleteBackupService->handle($backup);
|
||||
} catch (Exception $exception) {
|
||||
if (!$this->force) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$backup->delete();
|
||||
|
||||
logger()->warning($exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$server->allocations()->update([
|
||||
'server_id' => null,
|
||||
'notes' => null,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -98,7 +98,6 @@ return [
|
||||
'delete_db' => 'Are you sure you want to delete :name ?',
|
||||
'delete_db_heading' => 'Delete Database?',
|
||||
'backups' => 'Backups',
|
||||
'delete_backups' => 'Also delete :count backup(s) from storage',
|
||||
'egg' => 'Egg',
|
||||
'mounts' => 'Mounts',
|
||||
'no_mounts' => 'No Mounts exist for this Node',
|
||||
|
||||
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.',
|
||||
];
|
||||
@@ -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.',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Handle X-Forwarded-Proto Header
|
||||
RewriteCond %{HTTP:X-Forwarded-Proto} =https [NC]
|
||||
RewriteRule .* - [E=HTTPS:on]
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
Reference in New Issue
Block a user