mirror of
https://github.com/pelican-dev/panel.git
synced 2026-02-24 11:20:41 +03:00
Compare commits
16 Commits
chore/shif
...
release/v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3f4efbb03 | ||
|
|
1f56b8e114 | ||
|
|
574e03a986 | ||
|
|
05f3422dda | ||
|
|
dbe4bdd62d | ||
|
|
f6710dbbe4 | ||
|
|
e4f807b297 | ||
|
|
cd965678b7 | ||
|
|
a58ae874f3 | ||
|
|
432fb8a514 | ||
|
|
bb02ec4c6c | ||
|
|
69b669e345 | ||
|
|
80993f38a9 | ||
|
|
19103b16b8 | ||
|
|
246997754e | ||
|
|
df75dbe2ad |
@@ -35,7 +35,7 @@ class DeleteUserCommand extends Command
|
||||
if ($this->input->isInteractive()) {
|
||||
$tableValues = [];
|
||||
foreach ($results as $user) {
|
||||
$tableValues[] = [$user->id, $user->email, $user->name];
|
||||
$tableValues[] = [$user->id, $user->email, $user->username];
|
||||
}
|
||||
|
||||
$this->table(['User ID', 'Email', 'Name'], $tableValues);
|
||||
|
||||
@@ -54,7 +54,7 @@ class GSLTokenSchema implements FeatureSchemaInterface
|
||||
->modalHeading('Invalid GSL token')
|
||||
->modalDescription('It seems like your Gameserver Login Token (GSL token) is invalid or has expired.')
|
||||
->modalSubmitActionLabel('Update GSL Token')
|
||||
->disabledSchema(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->disabledSchema(fn () => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->schema([
|
||||
TextEntry::make('info')
|
||||
->label(new HtmlString(Blade::render('You can either <x-filament::link href="https://steamcommunity.com/dev/managegameservers" target="_blank">generate a new one</x-filament::link> and enter it below or leave the field blank to remove it completely.'))),
|
||||
|
||||
@@ -44,7 +44,7 @@ class JavaVersionSchema implements FeatureSchemaInterface
|
||||
->modalHeading('Unsupported Java Version')
|
||||
->modalDescription('This server is currently running an unsupported version of Java and cannot be started.')
|
||||
->modalSubmitActionLabel('Update Docker Image')
|
||||
->disabledSchema(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
|
||||
->disabledSchema(fn () => !user()?->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
|
||||
->schema([
|
||||
TextEntry::make('java')
|
||||
->label('Please select a supported version from the list below to continue starting the server.'),
|
||||
|
||||
@@ -32,9 +32,9 @@ class PIDLimitSchema implements FeatureSchemaInterface
|
||||
return Action::make($this->getId())
|
||||
->requiresConfirmation()
|
||||
->icon('tabler-alert-triangle')
|
||||
->modalHeading(fn () => auth()->user()->isAdmin() ? 'Memory or process limit reached...' : 'Possible resource limit reached...')
|
||||
->modalHeading(fn () => user()?->isAdmin() ? 'Memory or process limit reached...' : 'Possible resource limit reached...')
|
||||
->modalDescription(new HtmlString(Blade::render(
|
||||
auth()->user()->isAdmin() ? <<<'HTML'
|
||||
user()?->isAdmin() ? <<<'HTML'
|
||||
<p>
|
||||
This server has reached the maximum process or memory limit.
|
||||
</p>
|
||||
|
||||
@@ -29,7 +29,7 @@ class SteamDiskSpaceSchema implements FeatureSchemaInterface
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Out of available disk space...')
|
||||
->modalDescription(new HtmlString(Blade::render(
|
||||
auth()->user()->isAdmin() ? <<<'HTML'
|
||||
user()?->isAdmin() ? <<<'HTML'
|
||||
<p>
|
||||
This server has run out of available disk space and cannot complete the install or update
|
||||
process.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Extensions\OAuth;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Laravel\Socialite\Contracts\User as OAuthUser;
|
||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||
|
||||
class OAuthService
|
||||
@@ -43,4 +45,27 @@ class OAuthService
|
||||
|
||||
$this->schemas[$schema->getId()] = $schema;
|
||||
}
|
||||
|
||||
public function linkUser(User $user, OAuthSchemaInterface $schema, OAuthUser $oauthUser): User
|
||||
{
|
||||
$oauth = $user->oauth ?? [];
|
||||
$oauth[$schema->getId()] = $oauthUser->getId();
|
||||
|
||||
$user->update(['oauth' => $oauth]);
|
||||
|
||||
return $user->refresh();
|
||||
}
|
||||
|
||||
public function unlinkUser(User $user, OAuthSchemaInterface $schema): User
|
||||
{
|
||||
$oauth = $user->oauth ?? [];
|
||||
if (!isset($oauth[$schema->getId()])) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
unset($oauth[$schema->getId()]);
|
||||
$user->update(['oauth' => $oauth]);
|
||||
|
||||
return $user->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class Health extends Page
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()->can('view health');
|
||||
return user()?->can('view health');
|
||||
}
|
||||
|
||||
protected function getActions(): array
|
||||
|
||||
@@ -82,7 +82,7 @@ class Settings extends Page implements HasSchemas
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return auth()->user()->can('view settings');
|
||||
return user()?->can('view settings');
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
@@ -106,7 +106,7 @@ class Settings extends Page implements HasSchemas
|
||||
Tabs::make('Tabs')
|
||||
->columns()
|
||||
->persistTabInQueryString()
|
||||
->disabled(fn () => !auth()->user()->can('update settings'))
|
||||
->disabled(fn () => !user()?->can('update settings'))
|
||||
->tabs([
|
||||
Tab::make('general')
|
||||
->label(trans('admin/setting.navigation.general'))
|
||||
@@ -233,12 +233,12 @@ class Settings extends Page implements HasSchemas
|
||||
->color('danger')
|
||||
->icon('tabler-trash')
|
||||
->requiresConfirmation()
|
||||
->authorize(fn () => auth()->user()->can('update settings'))
|
||||
->authorize(fn () => user()?->can('update settings'))
|
||||
->action(fn (Set $set) => $set('TRUSTED_PROXIES', [])),
|
||||
Action::make('cloudflare')
|
||||
->label(trans('admin/setting.general.set_to_cf'))
|
||||
->icon('tabler-brand-cloudflare')
|
||||
->authorize(fn () => auth()->user()->can('update settings'))
|
||||
->authorize(fn () => user()?->can('update settings'))
|
||||
->action(function (Factory $client, Set $set) {
|
||||
$ips = collect();
|
||||
|
||||
@@ -335,7 +335,7 @@ class Settings extends Page implements HasSchemas
|
||||
->label(trans('admin/setting.mail.test_mail'))
|
||||
->icon('tabler-send')
|
||||
->hidden(fn (Get $get) => $get('MAIL_MAILER') === 'log')
|
||||
->authorize(fn () => auth()->user()->can('update settings'))
|
||||
->authorize(fn () => user()?->can('update settings'))
|
||||
->action(function (Get $get) {
|
||||
// Store original mail configuration
|
||||
$originalConfig = [
|
||||
@@ -368,8 +368,8 @@ class Settings extends Page implements HasSchemas
|
||||
'services.mailgun.endpoint' => $get('MAILGUN_ENDPOINT'),
|
||||
]);
|
||||
|
||||
MailNotification::route('mail', auth()->user()->email)
|
||||
->notify(new MailTested(auth()->user()));
|
||||
MailNotification::route('mail', user()?->email)
|
||||
->notify(new MailTested(user()));
|
||||
|
||||
Notification::make()
|
||||
->title(trans('admin/setting.mail.test_mail_sent'))
|
||||
@@ -822,7 +822,7 @@ class Settings extends Page implements HasSchemas
|
||||
return [
|
||||
Action::make('save')
|
||||
->action('save')
|
||||
->authorize(fn () => auth()->user()->can('update settings'))
|
||||
->authorize(fn () => user()?->can('update settings'))
|
||||
->keyBindings(['mod+s']),
|
||||
];
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class ApiKeyResource extends Resource
|
||||
->sortable(),
|
||||
TextColumn::make('user.username')
|
||||
->label(trans('admin/apikey.table.created_by'))
|
||||
->url(fn (ApiKey $apiKey) => auth()->user()->can('update', $apiKey->user) ? EditUser::getUrl(['record' => $apiKey->user]) : null),
|
||||
->url(fn (ApiKey $apiKey) => user()?->can('update', $apiKey->user) ? EditUser::getUrl(['record' => $apiKey->user]) : null),
|
||||
])
|
||||
->recordActions([
|
||||
DeleteAction::make(),
|
||||
|
||||
@@ -38,7 +38,7 @@ class CreateApiKey extends CreateRecord
|
||||
{
|
||||
$data['identifier'] = ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION);
|
||||
$data['token'] = Str::random(ApiKey::KEY_LENGTH);
|
||||
$data['user_id'] = auth()->user()->id;
|
||||
$data['user_id'] = user()?->id;
|
||||
$data['key_type'] = ApiKey::TYPE_APPLICATION;
|
||||
|
||||
$permissions = [];
|
||||
|
||||
@@ -166,7 +166,7 @@ class DatabaseHostResource extends Resource
|
||||
->preload()
|
||||
->helperText(trans('admin/databasehost.linked_nodes_help'))
|
||||
->label(trans('admin/databasehost.linked_nodes'))
|
||||
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'))),
|
||||
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', user()?->accessibleNodes()->pluck('id'))),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
@@ -196,7 +196,7 @@ class DatabaseHostResource extends Resource
|
||||
|
||||
return $query->where(function (Builder $query) {
|
||||
return $query->whereHas('nodes', function (Builder $query) {
|
||||
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
$query->whereIn('nodes.id', user()?->accessibleNodes()->pluck('id'));
|
||||
})->orDoesntHave('nodes');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ class CreateDatabaseHost extends CreateRecord
|
||||
->preload()
|
||||
->helperText(trans('admin/databasehost.linked_nodes_help'))
|
||||
->label(trans('admin/databasehost.linked_nodes'))
|
||||
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'))),
|
||||
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', user()?->accessibleNodes()->pluck('id'))),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class EggResource extends Resource
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return auth()->user()->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.server');
|
||||
return user()?->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.server');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
|
||||
@@ -83,14 +83,16 @@ class CreateEgg extends CreateRecord
|
||||
->rows(2)
|
||||
->columnSpanFull()
|
||||
->helperText(trans('admin/egg.description_help')),
|
||||
Textarea::make('startup')
|
||||
->label(trans('admin/egg.startup'))
|
||||
->rows(3)
|
||||
KeyValue::make('startup_commands')
|
||||
->label(trans('admin/egg.startup_commands'))
|
||||
->live()
|
||||
->columnSpanFull()
|
||||
->required()
|
||||
->placeholder(implode("\n", [
|
||||
'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}',
|
||||
]))
|
||||
->addActionLabel(trans('admin/egg.add_startup'))
|
||||
->keyLabel(trans('admin/egg.startup_name'))
|
||||
->keyPlaceholder('Default')
|
||||
->valueLabel(trans('admin/egg.startup_command'))
|
||||
->valuePlaceholder('java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}')
|
||||
->helperText(trans('admin/egg.startup_help')),
|
||||
TagsInput::make('file_denylist')
|
||||
->label(trans('admin/egg.file_denylist'))
|
||||
|
||||
@@ -80,11 +80,14 @@ class EditEgg extends EditRecord
|
||||
->disabled()
|
||||
->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2])
|
||||
->helperText(trans('admin/egg.author_help_edit')),
|
||||
Textarea::make('startup')
|
||||
->label(trans('admin/egg.startup'))
|
||||
->rows(3)
|
||||
KeyValue::make('startup_commands')
|
||||
->label(trans('admin/egg.startup_commands'))
|
||||
->live()
|
||||
->columnSpanFull()
|
||||
->required()
|
||||
->addActionLabel(trans('admin/egg.add_startup'))
|
||||
->keyLabel(trans('admin/egg.startup_name'))
|
||||
->valueLabel(trans('admin/egg.startup_command'))
|
||||
->helperText(trans('admin/egg.startup_help')),
|
||||
TagsInput::make('file_denylist')
|
||||
->label(trans('admin/egg.file_denylist'))
|
||||
|
||||
@@ -68,7 +68,7 @@ class ListEggs extends ListRecords
|
||||
->modal(false)
|
||||
->excludeAttributes(['author', 'uuid', 'update_url', 'servers_count', 'created_at', 'updated_at'])
|
||||
->beforeReplicaSaved(function (Egg $replica) {
|
||||
$replica->author = auth()->user()->email;
|
||||
$replica->author = user()?->email;
|
||||
$replica->name .= ' Copy';
|
||||
$replica->uuid = Str::uuid()->toString();
|
||||
})
|
||||
|
||||
@@ -166,7 +166,7 @@ class MountResource extends Resource
|
||||
->preload(),
|
||||
Select::make('nodes')->multiple()
|
||||
->label(trans('admin/mount.nodes'))
|
||||
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||
->relationship('nodes', 'name', fn (Builder $query) => $query->whereIn('nodes.id', user()?->accessibleNodes()->pluck('id')))
|
||||
->searchable(['name', 'fqdn'])
|
||||
->preload(),
|
||||
]),
|
||||
@@ -197,7 +197,7 @@ class MountResource extends Resource
|
||||
|
||||
return $query->where(function (Builder $query) {
|
||||
return $query->whereHas('nodes', function (Builder $query) {
|
||||
$query->whereIn('nodes.id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
$query->whereIn('nodes.id', user()?->accessibleNodes()->pluck('id'));
|
||||
})->orDoesntHave('nodes');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class NodeResource extends Resource
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return auth()->user()->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.server');
|
||||
return user()?->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.server');
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
@@ -75,6 +75,6 @@ class NodeResource extends Resource
|
||||
{
|
||||
$query = parent::getEloquentQuery();
|
||||
|
||||
return $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
return $query->whereIn('id', user()?->accessibleNodes()->pluck('id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\ToggleButtons;
|
||||
use Filament\Infolists\Components\CodeEntry;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
@@ -34,6 +35,7 @@ use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Phiki\Grammar\Grammar;
|
||||
use Throwable;
|
||||
|
||||
class EditNode extends EditRecord
|
||||
@@ -547,11 +549,12 @@ class EditNode extends EditRecord
|
||||
->label(trans('admin/node.instructions'))
|
||||
->columnSpanFull()
|
||||
->state(new HtmlString(trans('admin/node.instructions_help'))),
|
||||
Textarea::make('config')
|
||||
CodeEntry::make('config')
|
||||
->label('/etc/pelican/config.yml')
|
||||
->grammar(Grammar::Yaml)
|
||||
->state(fn (Node $node) => $node->getYamlConfiguration())
|
||||
->copyable()
|
||||
->disabled()
|
||||
->rows(19)
|
||||
->hintCopy()
|
||||
->columnSpanFull(),
|
||||
Grid::make()
|
||||
->columns()
|
||||
@@ -629,8 +632,6 @@ class EditNode extends EditRecord
|
||||
{
|
||||
$node = Node::findOrFail($data['id']);
|
||||
|
||||
$data['config'] = $node->getYamlConfiguration();
|
||||
|
||||
if (!is_ip($node->fqdn)) {
|
||||
$ip = get_ip_from_hostname($node->fqdn);
|
||||
if ($ip) {
|
||||
|
||||
@@ -121,7 +121,7 @@ class AllocationsRelationManager extends RelationManager
|
||||
])
|
||||
->groupedBulkActions([
|
||||
DeleteBulkAction::make()
|
||||
->authorize(fn () => auth()->user()->can('update', $this->getOwnerRecord())),
|
||||
->authorize(fn () => user()?->can('update', $this->getOwnerRecord())),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class NodeCpuChart extends ChartWidget
|
||||
$this->cpuHistory = session("{$sessionKey}.cpu_history", []);
|
||||
$this->cpuHistory[] = [
|
||||
'cpu' => round($data['cpu_percent'] * $this->threads, 2),
|
||||
'timestamp' => now(auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
'timestamp' => now(user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
];
|
||||
|
||||
$this->cpuHistory = array_slice($this->cpuHistory, -60);
|
||||
@@ -50,7 +50,7 @@ class NodeCpuChart extends ChartWidget
|
||||
],
|
||||
],
|
||||
'labels' => array_column($this->cpuHistory, 'timestamp'),
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
'locale' => user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class NodeMemoryChart extends ChartWidget
|
||||
'memory' => round(config('panel.use_binary_prefix')
|
||||
? $data['memory_used'] / 1024 / 1024 / 1024
|
||||
: $data['memory_used'] / 1000 / 1000 / 1000, 2),
|
||||
'timestamp' => now(auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
'timestamp' => now(user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
];
|
||||
|
||||
$this->memoryHistory = array_slice($this->memoryHistory, -60);
|
||||
@@ -52,7 +52,7 @@ class NodeMemoryChart extends ChartWidget
|
||||
],
|
||||
],
|
||||
'labels' => array_column($this->memoryHistory, 'timestamp'),
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
'locale' => user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class NodeStorageChart extends ChartWidget
|
||||
],
|
||||
],
|
||||
'labels' => [trans('admin/node.used'), trans('admin/node.unused')],
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
'locale' => user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class RoleResource extends Resource
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return auth()->user()->getCustomization(CustomizationKey::TopNavigation) ? trans('admin/dashboard.advanced') : trans('admin/dashboard.user');
|
||||
return user()?->getCustomization(CustomizationKey::TopNavigation) ? trans('admin/dashboard.advanced') : trans('admin/dashboard.user');
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
|
||||
@@ -117,7 +117,7 @@ class CreateServer extends CreateRecord
|
||||
->selectablePlaceholder(false)
|
||||
->default(function () {
|
||||
/** @var ?Node $latestNode */
|
||||
$latestNode = auth()->user()->accessibleNodes()->latest()->first();
|
||||
$latestNode = user()?->accessibleNodes()->latest()->first();
|
||||
$this->node = $latestNode;
|
||||
|
||||
return $this->node?->id;
|
||||
@@ -128,7 +128,7 @@ class CreateServer extends CreateRecord
|
||||
'md' => 2,
|
||||
])
|
||||
->live()
|
||||
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', user()?->accessibleNodes()->pluck('id')))
|
||||
->searchable()
|
||||
->required()
|
||||
->preload()
|
||||
@@ -141,7 +141,7 @@ class CreateServer extends CreateRecord
|
||||
->preload()
|
||||
->prefixIcon('tabler-user')
|
||||
->selectablePlaceholder(false)
|
||||
->default(auth()->user()->id)
|
||||
->default(user()?->id)
|
||||
->label(trans('admin/server.owner'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
@@ -151,7 +151,7 @@ class CreateServer extends CreateRecord
|
||||
->relationship('user', 'username')
|
||||
->searchable(['username', 'email'])
|
||||
->getOptionLabelFromRecordUsing(fn (User $user) => "$user->username ($user->email)")
|
||||
->createOptionAction(fn (Action $action) => $action->authorize(fn () => auth()->user()->can('create', User::class)))
|
||||
->createOptionAction(fn (Action $action) => $action->authorize(fn () => user()?->can('create', User::class)))
|
||||
->createOptionForm([
|
||||
TextInput::make('username')
|
||||
->label(trans('admin/user.username'))
|
||||
@@ -212,7 +212,7 @@ class CreateServer extends CreateRecord
|
||||
->where('node_id', $get('node_id'))
|
||||
->whereNull('server_id'),
|
||||
)
|
||||
->createOptionAction(fn (Action $action) => $action->authorize(fn (Get $get) => auth()->user()->can('create', Node::find($get('node_id')))))
|
||||
->createOptionAction(fn (Action $action) => $action->authorize(fn (Get $get) => user()?->can('create', Node::find($get('node_id')))))
|
||||
->createOptionForm(function (Get $get) {
|
||||
$getPage = $get;
|
||||
|
||||
@@ -328,7 +328,7 @@ class CreateServer extends CreateRecord
|
||||
->live()
|
||||
->afterStateUpdated(function ($state, Set $set, Get $get, $old) {
|
||||
$egg = Egg::query()->find($state);
|
||||
$set('startup', $egg->startup ?? '');
|
||||
$set('startup', '');
|
||||
$set('image', '');
|
||||
|
||||
$variables = $egg->variables ?? [];
|
||||
@@ -402,24 +402,45 @@ class CreateServer extends CreateRecord
|
||||
])
|
||||
->inline(),
|
||||
|
||||
Textarea::make('startup')
|
||||
->hintIcon('tabler-code')
|
||||
Select::make('select_startup')
|
||||
->label(trans('admin/server.startup_cmd'))
|
||||
->hidden(fn (Get $get) => $get('egg_id') === null)
|
||||
->live()
|
||||
->afterStateUpdated(fn (Set $set, $state) => $set('startup', $state))
|
||||
->options(function ($state, Get $get, Set $set) {
|
||||
$egg = Egg::query()->find($get('egg_id'));
|
||||
$startups = $egg->startup_commands ?? [];
|
||||
|
||||
$currentStartup = $get('startup');
|
||||
if (!$currentStartup && $startups) {
|
||||
$currentStartup = collect($startups)->first();
|
||||
$set('startup', $currentStartup);
|
||||
$set('select_startup', $currentStartup);
|
||||
}
|
||||
|
||||
return array_flip($startups) + ['' => 'Custom Startup'];
|
||||
})
|
||||
->selectablePlaceholder(false)
|
||||
->columnSpanFull(),
|
||||
|
||||
Textarea::make('startup')
|
||||
->hiddenLabel()
|
||||
->hidden(fn (Get $get) => $get('egg_id') === null)
|
||||
->required()
|
||||
->live()
|
||||
->rows(function ($state) {
|
||||
return str($state)->explode("\n")->reduce(
|
||||
fn (int $carry, $line) => $carry + floor(strlen($line) / 125),
|
||||
1
|
||||
);
|
||||
->autosize()
|
||||
->afterStateUpdated(function ($state, Get $get, Set $set) {
|
||||
$egg = Egg::query()->find($get('egg_id'));
|
||||
$startups = $egg->startup_commands ?? [];
|
||||
|
||||
if (in_array($state, $startups)) {
|
||||
$set('select_startup', $state);
|
||||
} else {
|
||||
$set('select_startup', '');
|
||||
}
|
||||
})
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'sm' => 4,
|
||||
'md' => 4,
|
||||
'lg' => 6,
|
||||
]),
|
||||
->placeholder(trans('admin/server.startup_placeholder'))
|
||||
->columnSpanFull(),
|
||||
|
||||
Hidden::make('environment')->default([]),
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ class EditServer extends EditRecord
|
||||
->maxLength(255),
|
||||
Select::make('node_id')
|
||||
->label(trans('admin/server.node'))
|
||||
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', auth()->user()->accessibleNodes()->pluck('id')))
|
||||
->relationship('node', 'name', fn (Builder $query) => $query->whereIn('id', user()?->accessibleNodes()->pluck('id')))
|
||||
->columnSpan([
|
||||
'default' => 2,
|
||||
'sm' => 1,
|
||||
@@ -609,26 +609,51 @@ class EditServer extends EditRecord
|
||||
1 => 'tabler-code-off',
|
||||
])
|
||||
->required(),
|
||||
|
||||
Hidden::make('previewing')
|
||||
->default(false),
|
||||
Textarea::make('startup')
|
||||
|
||||
Select::make('select_startup')
|
||||
->label(trans('admin/server.startup_cmd'))
|
||||
->required()
|
||||
->columnSpan(6)
|
||||
->autosize()
|
||||
->live()
|
||||
->afterStateUpdated(function (Set $set, $state) {
|
||||
$set('startup', $state);
|
||||
$set('previewing', false);
|
||||
})
|
||||
->options(function ($state, Get $get, Set $set) {
|
||||
$egg = Egg::find($get('egg_id'));
|
||||
$startups = $egg->startup_commands ?? [];
|
||||
|
||||
$currentStartup = $get('startup');
|
||||
if (!$currentStartup && $startups) {
|
||||
$currentStartup = collect($startups)->first();
|
||||
$set('startup', $currentStartup);
|
||||
$set('select_startup', $currentStartup);
|
||||
}
|
||||
|
||||
return array_flip($startups) + ['' => 'Custom Startup'];
|
||||
})
|
||||
->selectablePlaceholder(false)
|
||||
->columnSpanFull()
|
||||
->hintAction(PreviewStartupAction::make('preview')),
|
||||
|
||||
Textarea::make('defaultStartup')
|
||||
->hintCopy()
|
||||
->label(trans('admin/server.default_startup'))
|
||||
->disabled()
|
||||
Textarea::make('startup')
|
||||
->hiddenLabel()
|
||||
->required()
|
||||
->live()
|
||||
->autosize()
|
||||
->columnSpan(6)
|
||||
->formatStateUsing(function ($state, Get $get) {
|
||||
$egg = Egg::query()->find($get('egg_id'));
|
||||
->afterStateUpdated(function ($state, Get $get, Set $set) {
|
||||
$egg = Egg::find($get('egg_id'));
|
||||
$startups = $egg->startup_commands ?? [];
|
||||
|
||||
return $egg->startup;
|
||||
}),
|
||||
if (in_array($state, $startups)) {
|
||||
$set('select_startup', $state);
|
||||
} else {
|
||||
$set('select_startup', '');
|
||||
}
|
||||
})
|
||||
->placeholder(trans('admin/server.startup_placeholder'))
|
||||
->columnSpanFull(),
|
||||
|
||||
Repeater::make('server_variables')
|
||||
->hiddenLabel()
|
||||
@@ -916,7 +941,7 @@ class EditServer extends EditRecord
|
||||
}
|
||||
})
|
||||
->hidden(fn () => $canForceDelete)
|
||||
->authorize(fn (Server $server) => auth()->user()->can('delete server', $server)),
|
||||
->authorize(fn (Server $server) => user()?->can('delete server', $server)),
|
||||
Action::make('ForceDelete')
|
||||
->color('danger')
|
||||
->label(trans('filament-actions::force-delete.single.label'))
|
||||
@@ -933,7 +958,7 @@ class EditServer extends EditRecord
|
||||
}
|
||||
})
|
||||
->visible(fn () => $canForceDelete)
|
||||
->authorize(fn (Server $server) => auth()->user()->can('delete server', $server)),
|
||||
->authorize(fn (Server $server) => user()?->can('delete server', $server)),
|
||||
Action::make('console')
|
||||
->label(trans('admin/server.console'))
|
||||
->icon('tabler-terminal')
|
||||
|
||||
@@ -69,7 +69,7 @@ class ListServers extends ListRecords
|
||||
->searchable(),
|
||||
SelectColumn::make('allocation_id')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->hidden(fn () => !auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->hidden(fn () => !user()?->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->disabled(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
->options(fn (Server $server) => $server->allocations->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
|
||||
->selectablePlaceholder(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
@@ -77,7 +77,7 @@ class ListServers extends ListRecords
|
||||
->sortable(),
|
||||
TextColumn::make('allocation_id_readonly')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->hidden(fn () => auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->hidden(fn () => user()?->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->state(fn (Server $server) => $server->allocation->address ?? trans('admin/server.none')),
|
||||
TextColumn::make('image')->hidden(),
|
||||
TextColumn::make('backups_count')
|
||||
@@ -90,7 +90,7 @@ class ListServers extends ListRecords
|
||||
Action::make('View')
|
||||
->label(trans('admin/server.view'))
|
||||
->url(fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server))
|
||||
->authorize(fn (Server $server) => auth()->user()->canAccessTenant($server)),
|
||||
->authorize(fn (Server $server) => user()?->canAccessTenant($server)),
|
||||
EditAction::make(),
|
||||
])
|
||||
->emptyStateIcon('tabler-brand-docker')
|
||||
|
||||
@@ -47,7 +47,7 @@ class ServerResource extends Resource
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return auth()->user()->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.server');
|
||||
return user()?->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.server');
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
@@ -103,6 +103,6 @@ class ServerResource extends Resource
|
||||
{
|
||||
$query = parent::getEloquentQuery();
|
||||
|
||||
return $query->whereIn('node_id', auth()->user()->accessibleNodes()->pluck('id'));
|
||||
return $query->whereIn('node_id', user()?->accessibleNodes()->pluck('id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class EditUser extends EditRecord
|
||||
{
|
||||
return [
|
||||
DeleteAction::make()
|
||||
->label(fn (User $user) => auth()->user()->id === $user->id ? trans('admin/user.self_delete') : ($user->servers()->count() > 0 ? trans('admin/user.has_servers') : trans('filament-actions::delete.single.modal.actions.delete.label')))
|
||||
->disabled(fn (User $user) => auth()->user()->id === $user->id || $user->servers()->count() > 0),
|
||||
->label(fn (User $user) => user()?->id === $user->id ? trans('admin/user.self_delete') : ($user->servers()->count() > 0 ? trans('admin/user.has_servers') : trans('filament-actions::delete.single.modal.actions.delete.label')))
|
||||
->disabled(fn (User $user) => user()?->id === $user->id || $user->servers()->count() > 0),
|
||||
$this->getSaveFormAction()->formId('form'),
|
||||
];
|
||||
}
|
||||
@@ -48,8 +48,7 @@ class EditUser extends EditRecord
|
||||
if (!$record instanceof User) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
unset($data['roles']);
|
||||
unset($data['roles'], $data['avatar']);
|
||||
|
||||
return $this->service->handle($record, $data);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ class ServersRelationManager extends RelationManager
|
||||
->sortable(),
|
||||
SelectColumn::make('allocation_id')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->hidden(fn () => !auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->hidden(fn () => !user()?->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->disabled(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
->options(fn (Server $server) => $server->allocations->mapWithKeys(fn ($allocation) => [$allocation->id => $allocation->address]))
|
||||
->selectablePlaceholder(fn (Server $server) => $server->allocations->count() <= 1)
|
||||
@@ -75,7 +75,7 @@ class ServersRelationManager extends RelationManager
|
||||
->sortable(),
|
||||
TextColumn::make('allocation_id_readonly')
|
||||
->label(trans('admin/server.primary_allocation'))
|
||||
->hidden(fn () => auth()->user()->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->hidden(fn () => user()?->can('update server')) // TODO: update to policy check (fn (Server $server) --> $server is empty)
|
||||
->state(fn (Server $server) => $server->allocation->address ?? trans('admin/server.none')),
|
||||
TextColumn::make('databases_count')
|
||||
->counts('databases')
|
||||
|
||||
@@ -3,33 +3,56 @@
|
||||
namespace App\Filament\Admin\Resources\Users;
|
||||
|
||||
use App\Enums\CustomizationKey;
|
||||
use App\Extensions\OAuth\OAuthService;
|
||||
use App\Facades\Activity;
|
||||
use App\Filament\Admin\Resources\Users\Pages\CreateUser;
|
||||
use App\Filament\Admin\Resources\Users\Pages\EditUser;
|
||||
use App\Filament\Admin\Resources\Users\Pages\ListUsers;
|
||||
use App\Filament\Admin\Resources\Users\Pages\ViewUser;
|
||||
use App\Filament\Admin\Resources\Users\RelationManagers\ServersRelationManager;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\ApiKey;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Models\UserSSHKey;
|
||||
use App\Services\Helpers\LanguageService;
|
||||
use App\Traits\Filament\CanCustomizePages;
|
||||
use App\Traits\Filament\CanCustomizeRelations;
|
||||
use App\Traits\Filament\CanModifyForm;
|
||||
use App\Traits\Filament\CanModifyTable;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Auth\Notifications\ResetPassword;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\PageRegistration;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Components\Actions;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Auth\Events\PasswordResetLinkSent;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
|
||||
class UserResource extends Resource
|
||||
{
|
||||
@@ -61,7 +84,7 @@ class UserResource extends Resource
|
||||
|
||||
public static function getNavigationGroup(): ?string
|
||||
{
|
||||
return auth()->user()->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.user');
|
||||
return user()?->getCustomization(CustomizationKey::TopNavigation) ? false : trans('admin/dashboard.user');
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
@@ -110,7 +133,7 @@ class UserResource extends Resource
|
||||
->hidden(fn ($record) => static::canEdit($record)),
|
||||
EditAction::make(),
|
||||
])
|
||||
->checkIfRecordIsSelectableUsing(fn (User $user) => auth()->user()->id !== $user->id && !$user->servers_count)
|
||||
->checkIfRecordIsSelectableUsing(fn (User $user) => user()?->id !== $user->id && !$user->servers_count)
|
||||
->groupedBulkActions([
|
||||
DeleteBulkAction::make(),
|
||||
]);
|
||||
@@ -119,44 +142,326 @@ class UserResource extends Resource
|
||||
public static function defaultForm(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->columns(['default' => 1, 'lg' => 3])
|
||||
->columns(['default' => 1, 'lg' => 3, 'md' => 2])
|
||||
->components([
|
||||
TextInput::make('username')
|
||||
->label(trans('admin/user.username'))
|
||||
->required()
|
||||
->unique()
|
||||
->maxLength(255),
|
||||
TextInput::make('email')
|
||||
->label(trans('admin/user.email'))
|
||||
->email()
|
||||
->required()
|
||||
->unique()
|
||||
->maxLength(255),
|
||||
TextInput::make('password')
|
||||
->label(trans('admin/user.password'))
|
||||
->hintIcon(fn ($operation) => $operation === 'create' ? 'tabler-question-mark' : null, fn ($operation) => $operation === 'create' ? trans('admin/user.password_help') : null)
|
||||
->password(),
|
||||
CheckboxList::make('roles')
|
||||
->hidden(fn (?User $user) => $user && $user->isRootAdmin())
|
||||
->relationship('roles', 'name', fn (Builder $query) => $query->whereNot('id', Role::getRootAdmin()->id))
|
||||
->saveRelationshipsUsing(fn (User $user, array $state) => $user->syncRoles(collect($state)->map(fn ($role) => Role::findById($role))))
|
||||
->dehydrated()
|
||||
->label(trans('admin/user.admin_roles'))
|
||||
->columnSpanFull()
|
||||
->bulkToggleable(false),
|
||||
CheckboxList::make('root_admin_role')
|
||||
->visible(fn (?User $user) => $user && $user->isRootAdmin())
|
||||
->disabled()
|
||||
->options([
|
||||
'root_admin' => Role::ROOT_ADMIN,
|
||||
])
|
||||
->descriptions([
|
||||
'root_admin' => trans('admin/role.root_admin', ['role' => Role::ROOT_ADMIN]),
|
||||
])
|
||||
->formatStateUsing(fn () => ['root_admin'])
|
||||
->dehydrated(false)
|
||||
->label(trans('admin/user.admin_roles'))
|
||||
->columnSpanFull(),
|
||||
Tabs::make()
|
||||
->schema([
|
||||
Tab::make('account')
|
||||
->label(trans('profile.tabs.account'))
|
||||
->icon('tabler-user-cog')
|
||||
->columns([
|
||||
'default' => 1,
|
||||
'md' => 3,
|
||||
'lg' => 3,
|
||||
])
|
||||
->schema([
|
||||
TextInput::make('username')
|
||||
->label(trans('admin/user.username'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->required()
|
||||
->unique()
|
||||
->maxLength(255),
|
||||
TextInput::make('email')
|
||||
->label(trans('admin/user.email'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->email()
|
||||
->required()
|
||||
->unique()
|
||||
->maxLength(255),
|
||||
TextInput::make('password')
|
||||
->label(trans('admin/user.password'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->hintIcon(fn ($operation) => $operation === 'create' ? 'tabler-question-mark' : null, fn ($operation) => $operation === 'create' ? trans('admin/user.password_help') : null)
|
||||
->password()
|
||||
->hintAction(
|
||||
Action::make('password_reset')
|
||||
->label(trans('admin/user.password_reset'))
|
||||
->hidden(fn () => config('mail.default', 'log') === 'log')
|
||||
->icon('tabler-send')
|
||||
->action(function (User $user) {
|
||||
$status = Password::broker(Filament::getPanel('app')->getAuthPasswordBroker())->sendResetLink([
|
||||
'email' => $user->email,
|
||||
],
|
||||
function (User $user, string $token) {
|
||||
$notification = new ResetPassword($token);
|
||||
$notification->url = Filament::getPanel('app')->getResetPasswordUrl($token, $user);
|
||||
|
||||
$user->notify($notification);
|
||||
|
||||
event(new PasswordResetLinkSent($user));
|
||||
},
|
||||
);
|
||||
|
||||
if ($status === Password::RESET_LINK_SENT) {
|
||||
Notification::make()
|
||||
->title(trans('admin/user.password_reset_sent'))
|
||||
->success()
|
||||
->send();
|
||||
} else {
|
||||
Notification::make()
|
||||
->title(trans('admin/user.password_reset_failed'))
|
||||
->body($status)
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
})),
|
||||
TextInput::make('external_id')
|
||||
->label(trans('admin/user.external_id'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
]),
|
||||
Select::make('timezone')
|
||||
->label(trans('profile.timezone'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->required()
|
||||
->prefixIcon('tabler-clock-pin')
|
||||
->default(fn () => config('app.timezone', 'UTC'))
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn () => collect(DateTimeZone::listIdentifiers())->mapWithKeys(fn ($tz) => [$tz => $tz]))
|
||||
->searchable()
|
||||
->native(false),
|
||||
Select::make('language')
|
||||
->label(trans('profile.language'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'md' => 1,
|
||||
'lg' => 1,
|
||||
])
|
||||
->required()
|
||||
->prefixIcon('tabler-flag')
|
||||
->live()
|
||||
->default('en')
|
||||
->searchable()
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn (LanguageService $languageService) => $languageService->getAvailableLanguages())
|
||||
->native(false),
|
||||
FileUpload::make('avatar')
|
||||
->visible(fn (?User $user, FileUpload $fileUpload) => $user ? $fileUpload->getDisk()->exists($fileUpload->getDirectory() . '/' . $user->id . '.png') : false)
|
||||
->avatar()
|
||||
->directory('avatars')
|
||||
->disk('public')
|
||||
->formatStateUsing(function (FileUpload $fileUpload, ?User $user) {
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
$path = $fileUpload->getDirectory() . '/' . $user->id . '.png';
|
||||
if ($fileUpload->getDisk()->exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
})
|
||||
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
||||
if ($file instanceof TemporaryUploadedFile) {
|
||||
return $file->delete();
|
||||
}
|
||||
|
||||
if ($fileUpload->getDisk()->exists($file)) {
|
||||
return $fileUpload->getDisk()->delete($file);
|
||||
}
|
||||
}),
|
||||
Section::make(trans('profile.tabs.oauth'))
|
||||
->visible(fn (?User $user) => $user)
|
||||
->collapsible()
|
||||
->columnSpanFull()
|
||||
->schema(function (OAuthService $oauthService, ?User $user) {
|
||||
|
||||
if (!$user) {
|
||||
return;
|
||||
}
|
||||
$actions = [];
|
||||
foreach ($user->oauth as $schema => $_) {
|
||||
$schema = $oauthService->get($schema);
|
||||
if (!$schema) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $schema->getId();
|
||||
$name = $schema->getName();
|
||||
$actions[] = Action::make("oauth_$id")
|
||||
->label(trans('profile.unlink', ['name' => $name]))
|
||||
->icon('tabler-unlink')
|
||||
->requiresConfirmation()
|
||||
->color(Color::hex($schema->getHexColor()))
|
||||
->action(function ($livewire) use ($oauthService, $user, $name, $schema) {
|
||||
$oauthService->unlinkUser($user, $schema);
|
||||
$livewire->form->fill($user->attributesToArray());
|
||||
Notification::make()
|
||||
->title(trans('profile.unlinked', ['name' => $name]))
|
||||
->success()
|
||||
->send();
|
||||
});
|
||||
}
|
||||
|
||||
if (!$actions) {
|
||||
return [
|
||||
TextEntry::make('no_oauth')
|
||||
->state(trans('profile.no_oauth'))
|
||||
->hiddenLabel(),
|
||||
];
|
||||
}
|
||||
|
||||
return [Actions::make($actions)];
|
||||
}),
|
||||
]),
|
||||
Tab::make('roles')
|
||||
->label(trans('admin/user.roles'))
|
||||
->icon('tabler-users-group')
|
||||
->components([
|
||||
CheckboxList::make('roles')
|
||||
->hidden(fn (?User $user) => $user && $user->isRootAdmin())
|
||||
->relationship('roles', 'name', fn (Builder $query) => $query->whereNot('id', Role::getRootAdmin()->id))
|
||||
->saveRelationshipsUsing(fn (User $user, array $state) => $user->syncRoles(collect($state)->map(fn ($role) => Role::findById($role))))
|
||||
->dehydrated()
|
||||
->label(trans('admin/user.admin_roles'))
|
||||
->columnSpanFull()
|
||||
->bulkToggleable(false),
|
||||
CheckboxList::make('root_admin_role')
|
||||
->visible(fn (?User $user) => $user && $user->isRootAdmin())
|
||||
->disabled()
|
||||
->options([
|
||||
'root_admin' => Role::ROOT_ADMIN,
|
||||
])
|
||||
->descriptions([
|
||||
'root_admin' => trans('admin/role.root_admin', ['role' => Role::ROOT_ADMIN]),
|
||||
])
|
||||
->formatStateUsing(fn () => ['root_admin'])
|
||||
->dehydrated(false)
|
||||
->label(trans('admin/user.admin_roles'))
|
||||
->columnSpanFull(),
|
||||
]),
|
||||
Tab::make('keys')
|
||||
->visible(fn (?User $user) => $user)
|
||||
->label(trans('profile.tabs.keys'))
|
||||
->icon('tabler-key')
|
||||
->schema([
|
||||
Section::make(trans('profile.api_keys'))
|
||||
->columnSpan(2)
|
||||
->schema([
|
||||
Repeater::make('api_keys')
|
||||
->hiddenLabel()
|
||||
->inlineLabel(false)
|
||||
->relationship('apiKeys')
|
||||
->addable(false)
|
||||
->itemLabel(fn ($state) => $state['identifier'])
|
||||
->deleteAction(function (Action $action) {
|
||||
$action->requiresConfirmation()->action(function (array $arguments, Repeater $component, ?User $user) {
|
||||
$items = $component->getState();
|
||||
$key = $items[$arguments['item']] ?? null;
|
||||
|
||||
if ($key) {
|
||||
$apiKey = ApiKey::find($key['id']);
|
||||
if ($apiKey?->exists()) {
|
||||
$apiKey->delete();
|
||||
|
||||
Activity::event('user:api-key.delete')
|
||||
->actor(user())
|
||||
->subject($user)
|
||||
->subject($apiKey)
|
||||
->property('identifier', $apiKey->identifier)
|
||||
->log();
|
||||
}
|
||||
|
||||
unset($items[$arguments['item']]);
|
||||
$component->state($items);
|
||||
$component->callAfterStateUpdated();
|
||||
}
|
||||
});
|
||||
})
|
||||
->schema([
|
||||
TextEntry::make('memo')
|
||||
->hiddenLabel()
|
||||
->state(fn (ApiKey $key) => $key->memo),
|
||||
])
|
||||
->visible(fn (User $user) => $user->apiKeys()->exists()),
|
||||
|
||||
TextEntry::make('no_api_keys')
|
||||
->state(trans('profile.no_api_keys'))
|
||||
->hiddenLabel()
|
||||
->visible(fn (User $user) => !$user->apiKeys()->exists()),
|
||||
]),
|
||||
Section::make(trans('profile.ssh_keys'))->columnSpan(2)
|
||||
->schema([
|
||||
Repeater::make('ssh_keys')
|
||||
->hiddenLabel()
|
||||
->inlineLabel(false)
|
||||
->relationship('sshKeys')
|
||||
->addable(false)
|
||||
->itemLabel(fn ($state) => $state['name'])
|
||||
->deleteAction(function (Action $action) {
|
||||
$action->requiresConfirmation()->action(function (array $arguments, Repeater $component, User $user) {
|
||||
$items = $component->getState();
|
||||
$key = $items[$arguments['item']];
|
||||
|
||||
$sshKey = UserSSHKey::find($key['id'] ?? null);
|
||||
if ($sshKey->exists()) {
|
||||
$sshKey->delete();
|
||||
|
||||
Activity::event('user:ssh-key.delete')
|
||||
->actor(auth()->user())
|
||||
->subject($user)
|
||||
->subject($sshKey)
|
||||
->property('fingerprint', $sshKey->fingerprint)
|
||||
->log();
|
||||
}
|
||||
|
||||
unset($items[$arguments['item']]);
|
||||
|
||||
$component->state($items);
|
||||
|
||||
$component->callAfterStateUpdated();
|
||||
});
|
||||
})
|
||||
->schema(fn () => [
|
||||
TextEntry::make('fingerprint')
|
||||
->hiddenLabel()
|
||||
->state(fn (UserSSHKey $key) => "SHA256:{$key->fingerprint}"),
|
||||
])
|
||||
->visible(fn (User $user) => $user->sshKeys()->exists()),
|
||||
|
||||
TextEntry::make('no_ssh_keys')
|
||||
->state(trans('profile.no_ssh_keys'))
|
||||
->hiddenLabel()
|
||||
->visible(fn (User $user) => !$user->sshKeys()->exists()),
|
||||
]),
|
||||
]),
|
||||
Tab::make('activity')
|
||||
->visible(fn (?User $user) => $user)
|
||||
->disabledOn('create')
|
||||
->label(trans('profile.tabs.activity'))
|
||||
->icon('tabler-history')
|
||||
->schema([
|
||||
Repeater::make('activity')
|
||||
->hiddenLabel()
|
||||
->inlineLabel(false)
|
||||
->deletable(false)
|
||||
->addable(false)
|
||||
->relationship(null, function (Builder $query) {
|
||||
$query->orderBy('timestamp', 'desc');
|
||||
})
|
||||
->schema([
|
||||
TextEntry::make('log')
|
||||
->hiddenLabel()
|
||||
->state(fn (ActivityLog $log) => new HtmlString($log->htmlable())),
|
||||
]),
|
||||
]),
|
||||
])->columnSpanFull(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,9 +102,9 @@ class ListServers extends ListRecords
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
$baseQuery = auth()->user()->accessibleServers();
|
||||
$baseQuery = user()?->accessibleServers();
|
||||
|
||||
$usingGrid = auth()->user()->getCustomization(CustomizationKey::DashboardLayout) === 'grid';
|
||||
$usingGrid = user()?->getCustomization(CustomizationKey::DashboardLayout) === 'grid';
|
||||
|
||||
return $table
|
||||
->paginated(false)
|
||||
@@ -139,9 +139,9 @@ class ListServers extends ListRecords
|
||||
|
||||
public function getTabs(): array
|
||||
{
|
||||
$all = auth()->user()->accessibleServers();
|
||||
$my = (clone $all)->where('owner_id', auth()->user()->id);
|
||||
$other = (clone $all)->whereNot('owner_id', auth()->user()->id);
|
||||
$all = user()?->accessibleServers();
|
||||
$my = (clone $all)->where('owner_id', user()?->id);
|
||||
$other = (clone $all)->whereNot('owner_id', user()?->id);
|
||||
|
||||
return [
|
||||
'my' => Tab::make('my')
|
||||
@@ -232,21 +232,21 @@ class ListServers extends ListRecords
|
||||
->label(trans('server/console.power_actions.start'))
|
||||
->color('primary')
|
||||
->icon('tabler-player-play-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isStartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'start']),
|
||||
Action::make('restart')
|
||||
->label(trans('server/console.power_actions.restart'))
|
||||
->color('gray')
|
||||
->icon('tabler-reload')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isRestartable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'restart']),
|
||||
Action::make('stop')
|
||||
->label(trans('server/console.power_actions.stop'))
|
||||
->color('danger')
|
||||
->icon('tabler-player-stop-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isStoppable() && !$server->retrieveStatus()->isKillable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'stop']),
|
||||
Action::make('kill')
|
||||
@@ -254,7 +254,7 @@ class ListServers extends ListRecords
|
||||
->color('danger')
|
||||
->icon('tabler-alert-square')
|
||||
->tooltip(trans('server/console.power_actions.kill_tooltip'))
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn (Server $server) => $server->retrieveStatus()->isKillable())
|
||||
->dispatch('powerAction', fn (Server $server) => ['server' => $server, 'action' => 'kill']),
|
||||
])
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Filament\Components\Actions;
|
||||
|
||||
use App\Enums\EggFormat;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Eggs\Sharing\EggExporterService;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
@@ -24,7 +23,7 @@ class ExportEggAction extends Action
|
||||
|
||||
$this->tableIcon('tabler-download');
|
||||
|
||||
$this->authorize(fn () => auth()->user()->can('export egg'));
|
||||
$this->authorize(fn () => user()?->can('export egg'));
|
||||
|
||||
$this->modalHeading(fn (Egg $egg) => trans('filament-actions::export.modal.actions.export.label') . ' ' . $egg->name);
|
||||
|
||||
@@ -38,17 +37,15 @@ class ExportEggAction extends Action
|
||||
|
||||
$this->modalFooterActionsAlignment(Alignment::Center);
|
||||
|
||||
$this->modalFooterActions([ //TODO: Close modal after clicking ->close() does not allow action to preform before closing modal
|
||||
$this->modalFooterActions([
|
||||
Action::make('json')
|
||||
->label(trans('admin/egg.export.as', ['format' => 'json']))
|
||||
->action(fn (EggExporterService $service, Egg $egg) => response()->streamDownload(function () use ($service, $egg) {
|
||||
echo $service->handle($egg->id, EggFormat::JSON);
|
||||
}, 'egg-' . $egg->getKebabName() . '.json')),
|
||||
->url(fn (Egg $egg) => route('api.application.eggs.eggs.export', ['egg' => $egg, 'format' => EggFormat::JSON->value]), true)
|
||||
->close(),
|
||||
Action::make('yaml')
|
||||
->label(trans('admin/egg.export.as', ['format' => 'yaml']))
|
||||
->action(fn (EggExporterService $service, Egg $egg) => response()->streamDownload(function () use ($service, $egg) {
|
||||
echo $service->handle($egg->id, EggFormat::YAML);
|
||||
}, 'egg-' . $egg->getKebabName() . '.yaml')),
|
||||
->url(fn (Egg $egg) => route('api.application.eggs.eggs.export', ['egg' => $egg, 'format' => EggFormat::YAML->value]), true)
|
||||
->close(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,12 @@ class ExportScheduleAction extends Action
|
||||
|
||||
$this->label(trans('filament-actions::export.modal.actions.export.label'));
|
||||
|
||||
$this->authorize(fn () => auth()->user()->can(Permission::ACTION_SCHEDULE_READ, $server));
|
||||
$this->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_READ, $server));
|
||||
|
||||
$this->action(fn (ScheduleExporterService $service, Schedule $schedule) => response()->streamDownload(function () use ($service, $schedule) {
|
||||
echo $service->handle($schedule);
|
||||
}, 'schedule-' . str($schedule->name)->kebab()->lower()->trim() . '.json'));
|
||||
}, 'schedule-' . str($schedule->name)->kebab()->lower()->trim() . '.json', [
|
||||
'Content-Type' => 'application/json',
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class ImportEggAction extends Action
|
||||
|
||||
$this->label(trans('filament-actions::import.modal.actions.import.label'));
|
||||
|
||||
$this->authorize(fn () => auth()->user()->can('import egg'));
|
||||
$this->authorize(fn () => user()?->can('import egg'));
|
||||
|
||||
$this->action(function (array $data, EggImporterService $eggImportService): void {
|
||||
$eggs = array_merge(collect($data['urls'])->flatten()->whereNotNull()->unique()->all(), Arr::wrap($data['files']));
|
||||
|
||||
@@ -33,7 +33,7 @@ class ImportScheduleAction extends Action
|
||||
|
||||
$this->label(trans('filament-actions::import.modal.actions.import.label'));
|
||||
|
||||
$this->authorize(fn () => auth()->user()->can(Permission::ACTION_SCHEDULE_CREATE, $server));
|
||||
$this->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_CREATE, $server));
|
||||
|
||||
$this->schema([
|
||||
Tabs::make('Tabs')
|
||||
|
||||
@@ -15,19 +15,17 @@ class PreviewStartupAction extends Action
|
||||
return 'preview';
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return trans('server/startup.preview');
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->label(fn (Get $get) => $get('previewing') ? trans('server/startup.disable_preview') : trans('server/startup.enable_preview'));
|
||||
|
||||
$this->action(function (Get $get, Set $set, Server $server) {
|
||||
$active = $get('previewing');
|
||||
$set('previewing', !$active);
|
||||
$set('startup', $active ? $server->startup : fn (Server $server, StartupCommandService $service) => $service->handle($server));
|
||||
$previewing = !$get('previewing');
|
||||
|
||||
$set('previewing', $previewing);
|
||||
$set('startup', !$previewing ? $server->startup : fn (Server $server, StartupCommandService $service) => $service->handle($server, $server->startup));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class RotateDatabasePasswordAction extends Action
|
||||
|
||||
$this->icon('tabler-refresh');
|
||||
|
||||
$this->authorize(fn (Database $database) => auth()->user()->can('update', $database));
|
||||
$this->authorize(fn (Database $database) => user()?->can('update', $database));
|
||||
|
||||
$this->modalHeading(trans('admin/databasehost.rotate_password'));
|
||||
|
||||
@@ -56,7 +56,7 @@ class RotateDatabasePasswordAction extends Action
|
||||
} catch (Exception $exception) {
|
||||
Notification::make()
|
||||
->title(trans('admin/databasehost.rotate_error'))
|
||||
->body(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin')) ? $exception->getMessage() : null)
|
||||
->body(fn () => user()?->canAccessPanel(Filament::getPanel('admin')) ? $exception->getMessage() : null)
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class UpdateEggAction extends Action
|
||||
->send();
|
||||
});
|
||||
|
||||
$this->authorize(fn () => auth()->user()->can('import egg'));
|
||||
$this->authorize(fn () => user()?->can('import egg'));
|
||||
|
||||
$this->visible(fn (Egg $egg) => cache()->get("eggs.$egg->uuid.update", false));
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class UpdateEggBulkAction extends BulkAction
|
||||
->send();
|
||||
});
|
||||
|
||||
$this->authorize(fn () => auth()->user()->can('import egg'));
|
||||
$this->authorize(fn () => user()?->can('import egg'));
|
||||
|
||||
$this->deselectRecordsAfterCompletion();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@ class DateTimeColumn extends TextColumn
|
||||
|
||||
public function getTimezone(): string
|
||||
{
|
||||
return auth()->user()->timezone ?? config('app.timezone', 'UTC');
|
||||
return user()->timezone ?? config('app.timezone', 'UTC');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
|
||||
/**
|
||||
* @method User getUser()
|
||||
@@ -128,7 +129,7 @@ class EditProfile extends BaseEditProfile
|
||||
->label(trans('profile.timezone'))
|
||||
->required()
|
||||
->prefixIcon('tabler-clock-pin')
|
||||
->default('UTC')
|
||||
->default(config('app.timezone', 'UTC'))
|
||||
->selectablePlaceholder(false)
|
||||
->options(fn () => collect(DateTimeZone::listIdentifiers())->mapWithKeys(fn ($tz) => [$tz => $tz]))
|
||||
->searchable()
|
||||
@@ -151,14 +152,20 @@ class EditProfile extends BaseEditProfile
|
||||
->directory('avatars')
|
||||
->disk('public')
|
||||
->getUploadedFileNameForStorageUsing(fn () => $this->getUser()->id . '.png')
|
||||
->hintAction(function (FileUpload $fileUpload) {
|
||||
->formatStateUsing(function (FileUpload $fileUpload) {
|
||||
$path = $fileUpload->getDirectory() . '/' . $this->getUser()->id . '.png';
|
||||
if ($fileUpload->getDisk()->exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
})
|
||||
->deleteUploadedFileUsing(function (FileUpload $fileUpload, $file) {
|
||||
if ($file instanceof TemporaryUploadedFile) {
|
||||
return $file->delete();
|
||||
}
|
||||
|
||||
return Action::make('remove_avatar')
|
||||
->icon('tabler-photo-minus')
|
||||
->iconButton()
|
||||
->hidden(fn () => !$fileUpload->getDisk()->exists($path))
|
||||
->action(fn () => $fileUpload->getDisk()->delete($path));
|
||||
if ($fileUpload->getDisk()->exists($file)) {
|
||||
return $fileUpload->getDisk()->delete($file);
|
||||
}
|
||||
}),
|
||||
]),
|
||||
Tab::make('oauth')
|
||||
@@ -181,10 +188,10 @@ class EditProfile extends BaseEditProfile
|
||||
->color(Color::hex($schema->getHexColor()))
|
||||
->action(function (UserUpdateService $updateService) use ($id, $name, $unlink) {
|
||||
if ($unlink) {
|
||||
$oauth = auth()->user()->oauth;
|
||||
$oauth = user()?->oauth;
|
||||
unset($oauth[$id]);
|
||||
|
||||
$updateService->handle(auth()->user(), ['oauth' => $oauth]);
|
||||
$updateService->handle(user(), ['oauth' => $oauth]);
|
||||
|
||||
$this->fillForm();
|
||||
|
||||
@@ -292,7 +299,13 @@ class EditProfile extends BaseEditProfile
|
||||
TextEntry::make('memo')
|
||||
->hiddenLabel()
|
||||
->state(fn (ApiKey $key) => $key->memo),
|
||||
]),
|
||||
])
|
||||
->visible(fn (User $user) => $user->apiKeys()->exists()),
|
||||
|
||||
TextEntry::make('no_api_keys')
|
||||
->state(trans('profile.no_api_keys'))
|
||||
->hiddenLabel()
|
||||
->visible(fn (User $user) => !$user->apiKeys()->exists()),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
@@ -381,7 +394,13 @@ class EditProfile extends BaseEditProfile
|
||||
TextEntry::make('fingerprint')
|
||||
->hiddenLabel()
|
||||
->state(fn (UserSSHKey $key) => "SHA256:{$key->fingerprint}"),
|
||||
]),
|
||||
])
|
||||
->visible(fn (User $user) => $user->sshKeys()->exists()),
|
||||
|
||||
TextEntry::make('no_ssh_keys')
|
||||
->state(trans('profile.no_ssh_keys'))
|
||||
->hiddenLabel()
|
||||
->visible(fn (User $user) => !$user->sshKeys()->exists()),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
@@ -87,7 +87,7 @@ class Console extends Page
|
||||
{
|
||||
return [
|
||||
'server' => Filament::getTenant(),
|
||||
'user' => auth()->user(),
|
||||
'user' => user(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class Console extends Page
|
||||
->label(trans('server/console.power_actions.start'))
|
||||
->color('primary')
|
||||
->icon('tabler-player-play-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_START, $server))
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStartable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'start'))
|
||||
->size(Size::ExtraLarge),
|
||||
@@ -172,7 +172,7 @@ class Console extends Page
|
||||
->label(trans('server/console.power_actions.restart'))
|
||||
->color('gray')
|
||||
->icon('tabler-reload')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_RESTART, $server))
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isRestartable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'restart'))
|
||||
->size(Size::ExtraLarge),
|
||||
@@ -180,7 +180,7 @@ class Console extends Page
|
||||
->label(trans('server/console.power_actions.stop'))
|
||||
->color('danger')
|
||||
->icon('tabler-player-stop-filled')
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn () => !$this->status->isKillable())
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isStoppable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'stop'))
|
||||
@@ -191,7 +191,7 @@ class Console extends Page
|
||||
->icon('tabler-alert-square')
|
||||
->tooltip(trans('server/console.power_actions.kill_tooltip'))
|
||||
->requiresConfirmation()
|
||||
->authorize(fn (Server $server) => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->authorize(fn (Server $server) => user()?->can(Permission::ACTION_CONTROL_STOP, $server))
|
||||
->visible(fn () => $this->status->isKillable())
|
||||
->disabled(fn (Server $server) => $server->isInConflictState() || !$this->status->isKillable())
|
||||
->action(fn (Server $server) => $this->dispatch('setServerState', uuid: $server->uuid, state: 'kill'))
|
||||
|
||||
@@ -51,7 +51,7 @@ class Settings extends ServerFormPage
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label(trans('server/setting.server_info.name'))
|
||||
->disabled(fn (Server $server) => !auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
||||
->required()
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
@@ -64,7 +64,7 @@ class Settings extends ServerFormPage
|
||||
Textarea::make('description')
|
||||
->label(trans('server/setting.server_info.description'))
|
||||
->hidden(!config('panel.editable_server_descriptions'))
|
||||
->disabled(fn (Server $server) => !auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_RENAME, $server))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'sm' => 2,
|
||||
@@ -153,7 +153,7 @@ class Settings extends ServerFormPage
|
||||
]),
|
||||
Fieldset::make(trans('server/setting.server_info.sftp.title'))
|
||||
->columnSpanFull()
|
||||
->hidden(fn (Server $server) => !auth()->user()->can(Permission::ACTION_FILE_SFTP, $server))
|
||||
->hidden(fn (Server $server) => !user()?->can(Permission::ACTION_FILE_SFTP, $server))
|
||||
->columns([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
@@ -174,20 +174,20 @@ class Settings extends ServerFormPage
|
||||
->url(function (Server $server) {
|
||||
$fqdn = $server->node->daemon_sftp_alias ?? $server->node->fqdn;
|
||||
|
||||
return 'sftp://' . rawurlencode(auth()->user()->username) . '.' . $server->uuid_short . '@' . $fqdn . ':' . $server->node->daemon_sftp;
|
||||
return 'sftp://' . rawurlencode(user()?->username) . '.' . $server->uuid_short . '@' . $fqdn . ':' . $server->node->daemon_sftp;
|
||||
}),
|
||||
)
|
||||
->formatStateUsing(function (Server $server) {
|
||||
$fqdn = $server->node->daemon_sftp_alias ?? $server->node->fqdn;
|
||||
|
||||
return 'sftp://' . rawurlencode(auth()->user()->username) . '.' . $server->uuid_short . '@' . $fqdn . ':' . $server->node->daemon_sftp;
|
||||
return 'sftp://' . rawurlencode(user()?->username) . '.' . $server->uuid_short . '@' . $fqdn . ':' . $server->node->daemon_sftp;
|
||||
}),
|
||||
TextInput::make('username')
|
||||
->label(trans('server/setting.server_info.sftp.username'))
|
||||
->columnSpan(1)
|
||||
->copyable()
|
||||
->disabled()
|
||||
->formatStateUsing(fn (Server $server) => auth()->user()->username . '.' . $server->uuid_short),
|
||||
->formatStateUsing(fn (Server $server) => user()?->username . '.' . $server->uuid_short),
|
||||
TextEntry::make('password')
|
||||
->label(trans('server/setting.server_info.sftp.password'))
|
||||
->columnSpan(1)
|
||||
@@ -195,19 +195,19 @@ class Settings extends ServerFormPage
|
||||
]),
|
||||
]),
|
||||
Section::make(trans('server/setting.reinstall.title'))
|
||||
->hidden(fn (Server $server) => !auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
|
||||
->hidden(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
|
||||
->columnSpanFull()
|
||||
->footerActions([
|
||||
Action::make('reinstall')
|
||||
->label(trans('server/setting.reinstall.action'))
|
||||
->color('danger')
|
||||
->disabled(fn (Server $server) => !auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
|
||||
->requiresConfirmation()
|
||||
->modalHeading(trans('server/setting.reinstall.modal'))
|
||||
->modalDescription(trans('server/setting.reinstall.modal_description'))
|
||||
->modalSubmitActionLabel(trans('server/setting.reinstall.yes'))
|
||||
->action(function (Server $server, ReinstallServerService $reinstallService) {
|
||||
abort_unless(auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server), 403);
|
||||
abort_unless(user()?->can(Permission::ACTION_SETTINGS_REINSTALL, $server), 403);
|
||||
|
||||
try {
|
||||
$reinstallService->handle($server);
|
||||
@@ -246,7 +246,7 @@ class Settings extends ServerFormPage
|
||||
|
||||
public function updateName(string $name, Server $server): void
|
||||
{
|
||||
abort_unless(auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server), 403);
|
||||
abort_unless(user()?->can(Permission::ACTION_SETTINGS_RENAME, $server), 403);
|
||||
|
||||
$original = $server->name;
|
||||
|
||||
@@ -277,7 +277,7 @@ class Settings extends ServerFormPage
|
||||
|
||||
public function updateDescription(string $description, Server $server): void
|
||||
{
|
||||
abort_unless(auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server) && config('panel.editable_server_descriptions'), 403);
|
||||
abort_unless(user()?->can(Permission::ACTION_SETTINGS_RENAME, $server) && config('panel.editable_server_descriptions'), 403);
|
||||
|
||||
$original = $server->description;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Utilities\Set;
|
||||
use Filament\Schemas\Schema;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -35,40 +36,56 @@ class Startup extends ServerFormPage
|
||||
return parent::form($schema)
|
||||
->columns([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
'md' => 4,
|
||||
'lg' => 6,
|
||||
'md' => 2,
|
||||
])
|
||||
->components([
|
||||
Hidden::make('previewing')
|
||||
->default(false),
|
||||
Textarea::make('startup')
|
||||
TextInput::make('custom_startup')
|
||||
->label(trans('server/startup.command'))
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
'md' => 2,
|
||||
'lg' => 4,
|
||||
])
|
||||
->autosize()
|
||||
->hintAction(PreviewStartupAction::make())
|
||||
->readOnly(),
|
||||
->readOnly()
|
||||
->visible(fn (Server $server) => !in_array($server->startup, $server->egg->startup_commands))
|
||||
->formatStateUsing(fn () => 'Custom Startup')
|
||||
->hintAction(PreviewStartupAction::make()),
|
||||
Select::make('startup_select')
|
||||
->label(trans('server/startup.command'))
|
||||
->live()
|
||||
->visible(fn (Server $server) => in_array($server->startup, $server->egg->startup_commands))
|
||||
->disabled(fn (Server $server) => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->formatStateUsing(fn (Server $server) => $server->startup)
|
||||
->afterStateUpdated(function ($state, Server $server, Set $set) {
|
||||
$original = $server->startup;
|
||||
$server->forceFill(['startup' => $state])->saveOrFail();
|
||||
|
||||
$set('startup', $state);
|
||||
$set('previewing', false);
|
||||
|
||||
if ($original !== $server->startup) {
|
||||
$startups = array_flip($server->egg->startup_commands);
|
||||
Activity::event('server:startup.command')
|
||||
->property(['old' => $startups[$original], 'new' => $startups[$state]])
|
||||
->log();
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->title(trans('server/startup.notification_startup'))
|
||||
->body(trans('server/startup.notification_startup_body'))
|
||||
->success()
|
||||
->send();
|
||||
})
|
||||
->options(fn (Server $server) => array_flip($server->egg->startup_commands))
|
||||
->selectablePlaceholder(false)
|
||||
->hintAction(PreviewStartupAction::make()),
|
||||
TextInput::make('custom_image')
|
||||
->label(trans('server/startup.docker_image'))
|
||||
->readOnly()
|
||||
->visible(fn (Server $server) => !in_array($server->image, $server->egg->docker_images))
|
||||
->formatStateUsing(fn (Server $server) => $server->image)
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
'md' => 2,
|
||||
'lg' => 2,
|
||||
]),
|
||||
->formatStateUsing(fn (Server $server) => $server->image),
|
||||
Select::make('image')
|
||||
->label(trans('server/startup.docker_image'))
|
||||
->live()
|
||||
->visible(fn (Server $server) => in_array($server->image, $server->egg->docker_images))
|
||||
->disabled(fn (Server $server) => !auth()->user()->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
|
||||
->afterStateUpdated(function ($state, Server $server) {
|
||||
$original = $server->image;
|
||||
$server->forceFill(['image' => $state])->saveOrFail();
|
||||
@@ -89,14 +106,12 @@ class Startup extends ServerFormPage
|
||||
$images = $server->egg->docker_images;
|
||||
|
||||
return array_flip($images);
|
||||
})
|
||||
->selectablePlaceholder(false)
|
||||
->columnSpan([
|
||||
'default' => 1,
|
||||
'sm' => 1,
|
||||
'md' => 2,
|
||||
'lg' => 2,
|
||||
]),
|
||||
}),
|
||||
Textarea::make('startup')
|
||||
->hiddenLabel()
|
||||
->columnSpanFull()
|
||||
->autosize()
|
||||
->readOnly(),
|
||||
Section::make(trans('server/startup.variables'))
|
||||
->columnSpanFull()
|
||||
->schema([
|
||||
@@ -108,7 +123,7 @@ class Startup extends ServerFormPage
|
||||
return $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort');
|
||||
})
|
||||
->grid()
|
||||
->disabled(fn (Server $server) => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->disabled(fn (Server $server) => !user()?->can(Permission::ACTION_STARTUP_UPDATE, $server))
|
||||
->reorderable(false)->addable(false)->deletable(false)
|
||||
->schema([
|
||||
StartupVariable::make('variable_value')
|
||||
@@ -124,12 +139,12 @@ class Startup extends ServerFormPage
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(auth()->user()->can(Permission::ACTION_STARTUP_READ, Filament::getTenant()), 403);
|
||||
abort_unless(user()?->can(Permission::ACTION_STARTUP_READ, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return parent::canAccess() && auth()->user()->can(Permission::ACTION_STARTUP_READ, Filament::getTenant());
|
||||
return parent::canAccess() && user()?->can(Permission::ACTION_STARTUP_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public function update(?string $state, ServerVariable $serverVariable): null
|
||||
|
||||
@@ -73,14 +73,14 @@ class ActivityResource extends Resource
|
||||
$user = $activityLog->actor->username;
|
||||
|
||||
// Only show the email if the actor is the server owner/ a subuser or if the viewing user is an admin
|
||||
if (auth()->user()->isAdmin() || $server->owner_id === $activityLog->actor->id || $server->subusers->where('user_id', $activityLog->actor->id)->first()) {
|
||||
if (user()?->isAdmin() || $server->owner_id === $activityLog->actor->id || $server->subusers->where('user_id', $activityLog->actor->id)->first()) {
|
||||
$user .= " ({$activityLog->actor->email})";
|
||||
}
|
||||
|
||||
return $user;
|
||||
})
|
||||
->tooltip(fn (ActivityLog $activityLog) => auth()->user()->can('seeIps activityLog') ? $activityLog->ip : '')
|
||||
->url(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update', $activityLog->actor) ? EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin') : '')
|
||||
->tooltip(fn (ActivityLog $activityLog) => user()?->can('seeIps activityLog') ? $activityLog->ip : '')
|
||||
->url(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && user()?->can('update', $activityLog->actor) ? EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin') : '')
|
||||
->grow(false),
|
||||
DateTimeColumn::make('timestamp')
|
||||
->label(trans('server/activity.timestamp'))
|
||||
@@ -106,11 +106,11 @@ class ActivityResource extends Resource
|
||||
$user = $activityLog->actor->username;
|
||||
|
||||
// Only show the email if the actor is the server owner/ a subuser or if the viewing user is an admin
|
||||
if (auth()->user()->isAdmin() || $server->owner_id === $activityLog->actor->id || $server->subusers->where('user_id', $activityLog->actor->id)->first()) {
|
||||
if (user()?->isAdmin() || $server->owner_id === $activityLog->actor->id || $server->subusers->where('user_id', $activityLog->actor->id)->first()) {
|
||||
$user .= " ({$activityLog->actor->email})";
|
||||
}
|
||||
|
||||
if (auth()->user()->can('seeIps activityLog')) {
|
||||
if (user()?->can('seeIps activityLog')) {
|
||||
$user .= " - $activityLog->ip";
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ class ActivityResource extends Resource
|
||||
Action::make('edit')
|
||||
->label(trans('filament-actions::edit.single.label'))
|
||||
->icon('tabler-edit')
|
||||
->visible(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update', $activityLog->actor))
|
||||
->visible(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && user()?->can('update', $activityLog->actor))
|
||||
->url(fn (ActivityLog $activityLog) => EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin'))
|
||||
),
|
||||
DateTimePicker::make('timestamp')
|
||||
@@ -166,7 +166,7 @@ class ActivityResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_ACTIVITY_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_ACTIVITY_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
|
||||
@@ -58,7 +58,7 @@ class AllocationResource extends Resource
|
||||
TextInputColumn::make('notes')
|
||||
->label(trans('server/network.notes'))
|
||||
->visibleFrom('sm')
|
||||
->disabled(fn () => !auth()->user()->can(Permission::ACTION_ALLOCATION_UPDATE, $server))
|
||||
->disabled(fn () => !user()?->can(Permission::ACTION_ALLOCATION_UPDATE, $server))
|
||||
->placeholder(trans('server/network.no_notes')),
|
||||
IconColumn::make('primary')
|
||||
->icon(fn ($state) => match ($state) {
|
||||
@@ -70,13 +70,13 @@ class AllocationResource extends Resource
|
||||
default => 'gray',
|
||||
})
|
||||
->tooltip(fn (Allocation $allocation) => $allocation->id === $server->allocation_id ? trans('server/network.primary') : trans('server/network.make_primary'))
|
||||
->action(fn (Allocation $allocation) => auth()->user()->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
|
||||
->action(fn (Allocation $allocation) => user()?->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
|
||||
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
|
||||
->label(trans('server/network.primary')),
|
||||
])
|
||||
->recordActions([
|
||||
DetachAction::make()
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_DELETE, $server))
|
||||
->label(trans('server/network.delete'))
|
||||
->icon('tabler-trash')
|
||||
->action(function (Allocation $allocation) {
|
||||
@@ -96,7 +96,7 @@ class AllocationResource extends Resource
|
||||
Action::make('addAllocation')
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_ALLOCATION_CREATE, $server))
|
||||
->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add'))
|
||||
->hidden(fn () => !config('panel.client_features.allocations.enabled'))
|
||||
->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit)
|
||||
@@ -118,22 +118,22 @@ class AllocationResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_ALLOCATION_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_ALLOCATION_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_ALLOCATION_CREATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_ALLOCATION_CREATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canEdit(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_ALLOCATION_UPDATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_ALLOCATION_UPDATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canDelete(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_ALLOCATION_DELETE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_ALLOCATION_DELETE, Filament::getTenant());
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
|
||||
@@ -129,7 +129,7 @@ class BackupResource extends Resource
|
||||
ActionGroup::make([
|
||||
Action::make('rename')
|
||||
->icon('tabler-pencil')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DELETE, $server))
|
||||
->label(trans('server/backup.actions.rename.title'))
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
@@ -160,7 +160,7 @@ class BackupResource extends Resource
|
||||
Action::make('lock')
|
||||
->iconSize(IconSize::Large)
|
||||
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DELETE, $server))
|
||||
->label(fn (Backup $backup) => !$backup->is_locked ? trans('server/backup.actions.lock.lock') : trans('server/backup.actions.lock.unlock'))
|
||||
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup))
|
||||
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
|
||||
@@ -169,7 +169,7 @@ class BackupResource extends Resource
|
||||
->iconSize(IconSize::Large)
|
||||
->color('primary')
|
||||
->icon('tabler-download')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_DOWNLOAD, $server))
|
||||
->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true)
|
||||
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
|
||||
Action::make('restore')
|
||||
@@ -177,7 +177,7 @@ class BackupResource extends Resource
|
||||
->iconSize(IconSize::Large)
|
||||
->color('success')
|
||||
->icon('tabler-folder-up')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_RESTORE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_RESTORE, $server))
|
||||
->schema([
|
||||
TextEntry::make('stop_info')
|
||||
->hiddenLabel()
|
||||
@@ -210,7 +210,7 @@ class BackupResource extends Resource
|
||||
// If the backup is for an S3 file we need to generate a unique Download link for
|
||||
// it that will allow daemon to actually access the file.
|
||||
if ($backup->disk === Backup::ADAPTER_AWS_S3) {
|
||||
$url = $downloadLinkService->handle($backup, auth()->user());
|
||||
$url = $downloadLinkService->handle($backup, user());
|
||||
}
|
||||
|
||||
// Update the status right away for the server so that we know not to allow certain
|
||||
@@ -258,7 +258,7 @@ class BackupResource extends Resource
|
||||
])
|
||||
->toolbarActions([
|
||||
CreateAction::make()
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_BACKUP_CREATE, $server))
|
||||
->icon('tabler-file-zip')
|
||||
->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? trans('server/backup.actions.create.limit') : trans('server/backup.actions.create.title'))
|
||||
->disabled(fn () => $server->backups()->count() >= $server->backup_limit)
|
||||
@@ -268,7 +268,7 @@ class BackupResource extends Resource
|
||||
->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) {
|
||||
$action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? ''));
|
||||
|
||||
if (auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) {
|
||||
if (user()?->can(Permission::ACTION_BACKUP_DELETE, $server)) {
|
||||
$action->setIsLocked((bool) $data['is_locked']);
|
||||
}
|
||||
|
||||
@@ -298,17 +298,17 @@ class BackupResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_BACKUP_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_BACKUP_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_BACKUP_CREATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_BACKUP_CREATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canDelete(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_BACKUP_DELETE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_BACKUP_DELETE, Filament::getTenant());
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
|
||||
@@ -88,10 +88,10 @@ class DatabaseResource extends Resource
|
||||
TextInput::make('password')
|
||||
->label(trans('server/database.password'))
|
||||
->password()->revealable()
|
||||
->hidden(fn () => !auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
|
||||
->hidden(fn () => !user()?->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
|
||||
->hintAction(
|
||||
RotateDatabasePasswordAction::make()
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_DATABASE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_DATABASE_UPDATE, $server))
|
||||
)
|
||||
->copyable()
|
||||
->formatStateUsing(fn (Database $database) => $database->password),
|
||||
@@ -103,7 +103,7 @@ class DatabaseResource extends Resource
|
||||
TextInput::make('jdbc')
|
||||
->label(trans('server/database.jdbc'))
|
||||
->password()->revealable()
|
||||
->hidden(!auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
|
||||
->hidden(!user()?->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
|
||||
->copyable()
|
||||
->columnSpanFull()
|
||||
->formatStateUsing(fn (Database $database) => $database->jdbc),
|
||||
@@ -210,27 +210,27 @@ class DatabaseResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_DATABASE_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_DATABASE_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canView(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_DATABASE_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_DATABASE_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_DATABASE_CREATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_DATABASE_CREATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canEdit(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_DATABASE_UPDATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_DATABASE_UPDATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canDelete(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_DATABASE_DELETE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_DATABASE_DELETE, Filament::getTenant());
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
|
||||
@@ -32,22 +32,22 @@ class FileResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_FILE_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_FILE_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_FILE_CREATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_FILE_CREATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canEdit(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_FILE_UPDATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_FILE_UPDATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canDelete(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_FILE_DELETE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_FILE_DELETE, Filament::getTenant());
|
||||
}
|
||||
|
||||
/** @return array<string, PageRegistration> */
|
||||
|
||||
@@ -39,12 +39,12 @@ class DownloadFiles extends Page
|
||||
|
||||
$token = $this->nodeJWTService
|
||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||
->setUser(auth()->user())
|
||||
->setUser(user())
|
||||
->setClaims([
|
||||
'file_path' => rawurldecode($path),
|
||||
'server_uuid' => $server->uuid,
|
||||
])
|
||||
->handle($server->node, auth()->user()->id . $server->uuid);
|
||||
->handle($server->node, user()?->id . $server->uuid);
|
||||
|
||||
Activity::event('server:file.download')
|
||||
->property('file', $path)
|
||||
@@ -55,7 +55,7 @@ class DownloadFiles extends Page
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403);
|
||||
abort_unless(user()?->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
public static function route(string $path): PageRegistration
|
||||
|
||||
@@ -81,7 +81,7 @@ class EditFiles extends Page
|
||||
->footerActions([
|
||||
Action::make('save_and_close')
|
||||
->label(trans('server/file.actions.edit.save_close'))
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->icon('tabler-device-floppy')
|
||||
->keyBindings('mod+shift+s')
|
||||
->action(function () {
|
||||
@@ -101,7 +101,7 @@ class EditFiles extends Page
|
||||
}),
|
||||
Action::make('save')
|
||||
->label(trans('server/file.actions.edit.save'))
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->icon('tabler-device-floppy')
|
||||
->keyBindings('mod+s')
|
||||
->action(function () {
|
||||
@@ -227,7 +227,7 @@ class EditFiles extends Page
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
abort_unless(auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403);
|
||||
abort_unless(user()?->can(Permission::ACTION_FILE_READ_CONTENT, Filament::getTenant()), 403);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -119,7 +119,7 @@ class ListFiles extends ListRecords
|
||||
return self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]);
|
||||
}
|
||||
|
||||
if (!auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server)) {
|
||||
if (!user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -127,19 +127,19 @@ class ListFiles extends ListRecords
|
||||
})
|
||||
->recordActions([
|
||||
Action::make('view')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server))
|
||||
->label(trans('server/file.actions.open'))
|
||||
->icon('tabler-eye')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->is_directory)
|
||||
->url(fn (File $file) => self::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])),
|
||||
EditAction::make('edit')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server))
|
||||
->icon('tabler-edit')
|
||||
->visible(fn (File $file) => $file->canEdit())
|
||||
->url(fn (File $file) => EditFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))])),
|
||||
ActionGroup::make([
|
||||
Action::make('rename')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->label(trans('server/file.actions.rename.title'))
|
||||
->icon('tabler-forms')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -169,7 +169,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('copy')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->label(trans('server/file.actions.copy.title'))
|
||||
->icon('tabler-copy')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->is_file)
|
||||
@@ -188,13 +188,13 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('download')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ_CONTENT, $server))
|
||||
->label(trans('server/file.actions.download'))
|
||||
->icon('tabler-download')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->is_file)
|
||||
->url(fn (File $file) => DownloadFiles::getUrl(['path' => encode_path(join_paths($this->path, $file->name))]), true),
|
||||
Action::make('move')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->label(trans('server/file.actions.move.title'))
|
||||
->icon('tabler-replace')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -231,7 +231,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('permissions')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->label(trans('server/file.actions.permissions.title'))
|
||||
->icon('tabler-license')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -293,7 +293,7 @@ class ListFiles extends ListRecords
|
||||
->send();
|
||||
}),
|
||||
Action::make('archive')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->label(trans('server/file.actions.archive.title'))
|
||||
->icon('tabler-archive')->iconSize(IconSize::Large)
|
||||
->schema([
|
||||
@@ -320,7 +320,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
Action::make('unarchive')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->label(trans('server/file.actions.unarchive.title'))
|
||||
->icon('tabler-archive')->iconSize(IconSize::Large)
|
||||
->visible(fn (File $file) => $file->isArchive())
|
||||
@@ -341,7 +341,7 @@ class ListFiles extends ListRecords
|
||||
}),
|
||||
])->iconSize(IconSize::Large),
|
||||
DeleteAction::make()
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_DELETE, $server))
|
||||
->hiddenLabel()
|
||||
->icon('tabler-trash')->iconSize(IconSize::Large)
|
||||
->requiresConfirmation()
|
||||
@@ -361,7 +361,7 @@ class ListFiles extends ListRecords
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
BulkAction::make('move')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_UPDATE, $server))
|
||||
->schema([
|
||||
TextInput::make('location')
|
||||
->label(trans('server/file.actions.move.directory'))
|
||||
@@ -390,7 +390,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
BulkAction::make('archive')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_ARCHIVE, $server))
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label(trans('server/file.actions.archive.archive_name'))
|
||||
@@ -417,7 +417,7 @@ class ListFiles extends ListRecords
|
||||
$this->refreshPage();
|
||||
}),
|
||||
DeleteBulkAction::make()
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_DELETE, $server))
|
||||
->action(function (Collection $files) {
|
||||
$files = $files->map(fn ($file) => $file['name'])->toArray();
|
||||
$this->getDaemonFileRepository()->deleteFiles($this->path, $files);
|
||||
@@ -437,7 +437,7 @@ class ListFiles extends ListRecords
|
||||
]),
|
||||
|
||||
Action::make('new_file')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->tooltip(trans('server/file.actions.new_file.title'))
|
||||
->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->color('primary')
|
||||
@@ -470,7 +470,7 @@ class ListFiles extends ListRecords
|
||||
->hiddenLabel(),
|
||||
]),
|
||||
Action::make('new_folder')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.new_folder.title'))
|
||||
->color('primary')
|
||||
@@ -501,7 +501,7 @@ class ListFiles extends ListRecords
|
||||
->required(),
|
||||
]),
|
||||
Action::make('upload')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_CREATE, $server))
|
||||
->hiddenLabel()->icon('tabler-upload')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.upload.title'))
|
||||
->color('success')
|
||||
@@ -554,7 +554,7 @@ class ListFiles extends ListRecords
|
||||
]),
|
||||
]),
|
||||
Action::make('search')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_FILE_READ, $server))
|
||||
->hiddenLabel()->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||
->tooltip(trans('server/file.actions.global_search.title'))
|
||||
->color('primary')
|
||||
|
||||
@@ -29,7 +29,7 @@ class ViewSchedule extends ViewRecord
|
||||
{
|
||||
return [
|
||||
Action::make('run_now')
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant()))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant()))
|
||||
->label(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? trans('server/schedule.no_tasks') : ($schedule->status === ScheduleStatus::Processing ? ScheduleStatus::Processing->getLabel() : trans('server/schedule.run_now')))
|
||||
->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing ? 'warning' : 'primary')
|
||||
->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->status === ScheduleStatus::Processing)
|
||||
|
||||
@@ -72,12 +72,12 @@ class TasksRelationManager extends RelationManager
|
||||
->default('restart'),
|
||||
TextInput::make('time_offset')
|
||||
->label(trans('server/schedule.tasks.time_offset'))
|
||||
->hidden(fn (Get $get) => config('queue.default') === 'sync' || $get('sequence_id') === 1)
|
||||
->hidden(fn (Get $get) => config('queue.default') === 'sync' || $get('sequence_id') === 1 || $schedule->tasks->isEmpty())
|
||||
->default(0)
|
||||
->numeric()
|
||||
->minValue(0)
|
||||
->maxValue(900)
|
||||
->suffix(trans('server/schedule.tasks.seconds')),
|
||||
->suffix(trans_choice('server/schedule.tasks.seconds', 2)),
|
||||
Toggle::make('continue_on_failure')
|
||||
->label(trans('server/schedule.tasks.continue_on_failure')),
|
||||
];
|
||||
@@ -108,7 +108,9 @@ class TasksRelationManager extends RelationManager
|
||||
TextColumn::make('time_offset')
|
||||
->label(trans('server/schedule.tasks.time_offset'))
|
||||
->hidden(fn () => config('queue.default') === 'sync')
|
||||
->suffix(' '. trans('server/schedule.tasks.seconds')),
|
||||
->suffix(fn (Task $task) => $task->sequence_id > 1 ? ' '. trans_choice('server/schedule.tasks.seconds', $task->time_offset) : null)
|
||||
->state(fn (Task $task) => $task->sequence_id === 1 ? null : $task->time_offset)
|
||||
->placeholder(trans('server/schedule.tasks.first_task')),
|
||||
IconColumn::make('continue_on_failure')
|
||||
->label(trans('server/schedule.tasks.continue_on_failure'))
|
||||
->boolean(),
|
||||
|
||||
@@ -66,22 +66,22 @@ class ScheduleResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_SCHEDULE_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_SCHEDULE_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_SCHEDULE_CREATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_SCHEDULE_CREATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canEdit(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canDelete(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_SCHEDULE_DELETE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_SCHEDULE_DELETE, Filament::getTenant());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,12 +123,12 @@ class ScheduleResource extends Resource
|
||||
->label(trans('server/schedule.cron'))
|
||||
->description(function (Get $get) {
|
||||
try {
|
||||
$nextRun = Utilities::getScheduleNextRunDate($get('cron_minute'), $get('cron_hour'), $get('cron_day_of_month'), $get('cron_month'), $get('cron_day_of_week'))->timezone(auth()->user()->timezone);
|
||||
$nextRun = Utilities::getScheduleNextRunDate($get('cron_minute'), $get('cron_hour'), $get('cron_day_of_month'), $get('cron_month'), $get('cron_day_of_week'))->timezone(user()->timezone ?? 'UTC');
|
||||
} catch (Exception) {
|
||||
$nextRun = trans('server/schedule.invalid');
|
||||
}
|
||||
|
||||
return new HtmlString(trans('server/schedule.cron_body') . '<br>' . trans('server/schedule.cron_timezone', ['timezone' => auth()->user()->timezone, 'next_run' => $nextRun]));
|
||||
return new HtmlString(trans('server/schedule.cron_body') . '<br>' . trans('server/schedule.cron_timezone', ['timezone' => user()->timezone ?? 'UTC', 'next_run' => $nextRun]));
|
||||
})
|
||||
->schema([
|
||||
Actions::make([
|
||||
|
||||
@@ -65,22 +65,22 @@ class UserResource extends Resource
|
||||
|
||||
public static function canViewAny(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_USER_READ, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_USER_READ, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canCreate(): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_USER_CREATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_USER_CREATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canEdit(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_USER_UPDATE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_USER_UPDATE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function canDelete(Model $record): bool
|
||||
{
|
||||
return auth()->user()->can(Permission::ACTION_USER_DELETE, Filament::getTenant());
|
||||
return user()?->can(Permission::ACTION_USER_DELETE, Filament::getTenant());
|
||||
}
|
||||
|
||||
public static function defaultTable(Table $table): Table
|
||||
@@ -139,7 +139,7 @@ class UserResource extends Resource
|
||||
->recordActions([
|
||||
DeleteAction::make()
|
||||
->label(trans('server/user.delete'))
|
||||
->hidden(fn (User $user) => auth()->user()->id === $user->id)
|
||||
->hidden(fn (User $user) => user()?->id === $user->id)
|
||||
->action(function (User $user, SubuserDeletionService $subuserDeletionService) use ($server) {
|
||||
$subuser = $server->subusers->where('user_id', $user->id)->first();
|
||||
$subuserDeletionService->handle($subuser, $server);
|
||||
@@ -151,8 +151,8 @@ class UserResource extends Resource
|
||||
}),
|
||||
EditAction::make()
|
||||
->label(trans('server/user.edit'))
|
||||
->hidden(fn (User $user) => auth()->user()->id === $user->id)
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_UPDATE, $server))
|
||||
->hidden(fn (User $user) => user()?->id === $user->id)
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_USER_UPDATE, $server))
|
||||
->modalHeading(fn (User $user) => trans('server/user.editing', ['user' => $user->email]))
|
||||
->action(function (array $data, SubuserUpdateService $subuserUpdateService, User $user) use ($server) {
|
||||
$subuser = $server->subusers->where('user_id', $user->id)->first();
|
||||
@@ -237,7 +237,7 @@ class UserResource extends Resource
|
||||
->icon('tabler-user-plus')
|
||||
->tooltip(trans('server/user.invite_user'))
|
||||
->createAnother(false)
|
||||
->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_CREATE, $server))
|
||||
->authorize(fn () => user()?->can(Permission::ACTION_USER_CREATE, $server))
|
||||
->schema([
|
||||
Grid::make()
|
||||
->columnSpanFull()
|
||||
|
||||
@@ -27,12 +27,12 @@ class ServerCpuChart extends ChartWidget
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$period = (int) auth()->user()->getCustomization(CustomizationKey::ConsoleGraphPeriod);
|
||||
$period = (int) user()?->getCustomization(CustomizationKey::ConsoleGraphPeriod);
|
||||
$cpu = collect(cache()->get("servers.{$this->server->id}.cpu_absolute"))
|
||||
->slice(-$period)
|
||||
->map(fn ($value, $key) => [
|
||||
'cpu' => round($value, 2),
|
||||
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
'timestamp' => Carbon::createFromTimestamp($key, user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
])
|
||||
->all();
|
||||
|
||||
@@ -48,7 +48,7 @@ class ServerCpuChart extends ChartWidget
|
||||
],
|
||||
],
|
||||
'labels' => array_column($cpu, 'timestamp'),
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
'locale' => user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@ class ServerMemoryChart extends ChartWidget
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$period = (int) auth()->user()->getCustomization(CustomizationKey::ConsoleGraphPeriod);
|
||||
$period = (int) user()?->getCustomization(CustomizationKey::ConsoleGraphPeriod);
|
||||
$memUsed = collect(cache()->get("servers.{$this->server->id}.memory_bytes"))
|
||||
->slice(-$period)
|
||||
->map(fn ($value, $key) => [
|
||||
'memory' => round(config('panel.use_binary_prefix') ? $value / 1024 / 1024 / 1024 : $value / 1000 / 1000 / 1000, 2),
|
||||
'timestamp' => Carbon::createFromTimestamp($key, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
'timestamp' => Carbon::createFromTimestamp($key, user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
])
|
||||
->all();
|
||||
|
||||
@@ -48,7 +48,7 @@ class ServerMemoryChart extends ChartWidget
|
||||
],
|
||||
],
|
||||
'labels' => array_column($memUsed, 'timestamp'),
|
||||
'locale' => auth()->user()->language ?? 'en',
|
||||
'locale' => user()->language ?? 'en',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class ServerNetworkChart extends ChartWidget
|
||||
{
|
||||
$previous = null;
|
||||
|
||||
$period = (int) auth()->user()->getCustomization(CustomizationKey::ConsoleGraphPeriod);
|
||||
$period = (int) user()?->getCustomization(CustomizationKey::ConsoleGraphPeriod);
|
||||
$net = collect(cache()->get("servers.{$this->server->id}.network"))
|
||||
->slice(-$period)
|
||||
->map(function ($current, $timestamp) use (&$previous) {
|
||||
@@ -39,7 +39,7 @@ class ServerNetworkChart extends ChartWidget
|
||||
$net = [
|
||||
'rx' => max(0, $current->rx_bytes - $previous->rx_bytes),
|
||||
'tx' => max(0, $current->tx_bytes - $previous->tx_bytes),
|
||||
'timestamp' => Carbon::createFromTimestamp($timestamp, auth()->user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
'timestamp' => Carbon::createFromTimestamp($timestamp, user()->timezone ?? 'UTC')->format('H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,24 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\Application\Eggs;
|
||||
|
||||
use App\Enums\EggFormat;
|
||||
use App\Http\Controllers\Api\Application\ApplicationApiController;
|
||||
use App\Http\Requests\Api\Application\Eggs\ExportEggRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\GetEggRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\GetEggsRequest;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Eggs\Sharing\EggExporterService;
|
||||
use App\Transformers\Api\Application\EggTransformer;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class EggController extends ApplicationApiController
|
||||
{
|
||||
public function __construct(
|
||||
private EggExporterService $exporterService,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* List eggs
|
||||
*
|
||||
@@ -37,4 +47,20 @@ class EggController extends ApplicationApiController
|
||||
->transformWith($this->getTransformer(EggTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export egg
|
||||
*
|
||||
* Return a single egg as yaml or json file (defaults to YAML)
|
||||
*/
|
||||
public function export(ExportEggRequest $request, Egg $egg): StreamedResponse
|
||||
{
|
||||
$format = EggFormat::tryFrom($request->input('format')) ?? EggFormat::YAML;
|
||||
|
||||
return response()->streamDownload(function () use ($egg, $format) {
|
||||
echo $this->exporterService->handle($egg->id, $format);
|
||||
}, 'egg-' . $egg->getKebabName() . '.' . $format->value, [
|
||||
'Content-Type' => 'application/' . $format->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class OAuthController extends Controller
|
||||
$oauthUser = Socialite::driver($driver->getId())->user();
|
||||
|
||||
if ($request->user()) {
|
||||
$this->linkUser($request->user(), $driver, $oauthUser);
|
||||
$this->oauthService->linkUser($request->user(), $driver, $oauthUser);
|
||||
|
||||
return redirect(EditProfile::getUrl(['tab' => 'oauth::data::tab'], panel: 'app'));
|
||||
}
|
||||
@@ -69,16 +69,6 @@ class OAuthController extends Controller
|
||||
return $this->handleMissingUser($driver, $oauthUser);
|
||||
}
|
||||
|
||||
private function linkUser(User $user, OAuthSchemaInterface $driver, OAuthUser $oauthUser): User
|
||||
{
|
||||
$oauth = $user->oauth;
|
||||
$oauth[$driver->getId()] = $oauthUser->getId();
|
||||
|
||||
$user->update(['oauth' => $oauth]);
|
||||
|
||||
return $user->refresh();
|
||||
}
|
||||
|
||||
private function handleMissingUser(OAuthSchemaInterface $driver, OAuthUser $oauthUser): RedirectResponse
|
||||
{
|
||||
$email = $oauthUser->getEmail();
|
||||
@@ -93,7 +83,7 @@ class OAuthController extends Controller
|
||||
return $this->errorRedirect();
|
||||
}
|
||||
|
||||
$user = $this->linkUser($user, $driver, $oauthUser);
|
||||
$user = $this->oauthService->linkUser($user, $driver, $oauthUser);
|
||||
} else {
|
||||
if (!$driver->shouldCreateMissingUsers()) {
|
||||
return $this->errorRedirect();
|
||||
|
||||
@@ -17,7 +17,7 @@ class AuthenticateApplicationUser
|
||||
{
|
||||
/** @var User|null $user */
|
||||
$user = $request->user();
|
||||
if (!$user || !$user->isRootAdmin()) {
|
||||
if (!$user || !$user->isAdmin()) {
|
||||
throw new AccessDeniedHttpException('This account does not have permission to access the API.');
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,14 @@ abstract class ApplicationApiRequest extends FormRequest
|
||||
$token = $this->user()->currentAccessToken();
|
||||
|
||||
if ($token instanceof TransientToken) {
|
||||
return true;
|
||||
return match ($this->permission) {
|
||||
default => false,
|
||||
AdminAcl::READ => $this->user()->can('viewList ' . $this->resource) && $this->user()->can('view ' . $this->resource),
|
||||
AdminAcl::WRITE => $this->user()->can('update ' . $this->resource),
|
||||
};
|
||||
}
|
||||
|
||||
if ($token->key_type === ApiKey::TYPE_ACCOUNT) {
|
||||
if ($this->user()->isRootAdmin() && $token->key_type === ApiKey::TYPE_ACCOUNT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
13
app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php
Normal file
13
app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\Application\Eggs;
|
||||
|
||||
class ExportEggRequest extends GetEggRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'format' => 'nullable|string|in:yaml,json',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,14 @@ class ServerRequest extends FormRequest
|
||||
/** @var ?Server $server */
|
||||
$server = $this->route()->parameter('server');
|
||||
|
||||
return $server && $server->node_id === $node->id;
|
||||
if ($server) {
|
||||
if ($server->transfer) {
|
||||
return $server->transfer->old_node === $node->id || $server->transfer->new_node === $node->id;
|
||||
}
|
||||
|
||||
return $server->node_id === $node->id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class QueueStep
|
||||
->label(new HtmlString(trans('installer.queue.fields.crontab')))
|
||||
->disabled()
|
||||
->hintCopy()
|
||||
->default('(crontab -l -u www-data 2>/dev/null; echo "* * * * * php ' . base_path() . '/artisan schedule:run >> /dev/null 2>&1") | crontab -u www-data -')
|
||||
->default('(sudo crontab -l -u www-data 2>/dev/null; echo "* * * * * php ' . base_path() . '/artisan schedule:run >> /dev/null 2>&1") | sudo crontab -u www-data -')
|
||||
->hidden(fn () => @file_exists('/.dockerenv'))
|
||||
->columnSpanFull(),
|
||||
TextInput::make('queueService')
|
||||
|
||||
@@ -163,6 +163,11 @@ class ActivityLog extends Model implements HasIcon, HasLabel
|
||||
return trans_choice('activity.'.str($this->event)->replace(':', '.'), array_key_exists('count', $properties) ? $properties['count'] : 1, $properties);
|
||||
}
|
||||
|
||||
public function getIp(): ?string
|
||||
{
|
||||
return auth()->user()->can('seeIps activityLog') ? $this->ip : null;
|
||||
}
|
||||
|
||||
public function htmlable(): string
|
||||
{
|
||||
$user = $this->actor;
|
||||
@@ -175,6 +180,8 @@ class ActivityLog extends Model implements HasIcon, HasLabel
|
||||
|
||||
$avatarUrl = Filament::getUserAvatarUrl($user);
|
||||
$username = str($user->username)->stripTags();
|
||||
$ip = $this->getIp();
|
||||
$ip = $ip ? $ip . ' — ' : '';
|
||||
|
||||
return "
|
||||
<div style='display: flex; align-items: center;'>
|
||||
@@ -183,7 +190,7 @@ class ActivityLog extends Model implements HasIcon, HasLabel
|
||||
<div>
|
||||
<p>$username — $this->event</p>
|
||||
<p>{$this->getLabel()}</p>
|
||||
<p>$this->ip — <span title='{$this->timestamp->format('M j, Y g:ia')}'>{$this->timestamp->diffForHumans()}</span></p>
|
||||
<p>$ip<span title='{$this->timestamp->format('M j, Y g:ia')}'>{$this->timestamp->diffForHumans()}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
";
|
||||
|
||||
@@ -22,8 +22,7 @@ use Illuminate\Support\Str;
|
||||
* @property string $name
|
||||
* @property string|null $description
|
||||
* @property string[]|null $features
|
||||
* @property string $docker_image -- deprecated, use $docker_images
|
||||
* @property array<array-key, string> $docker_images
|
||||
* @property array<string, string> $docker_images
|
||||
* @property string|null $update_url
|
||||
* @property bool $force_outgoing_ip
|
||||
* @property string[]|null $file_denylist
|
||||
@@ -32,7 +31,7 @@ use Illuminate\Support\Str;
|
||||
* @property string|null $config_logs
|
||||
* @property string|null $config_stop
|
||||
* @property int|null $config_from
|
||||
* @property string|null $startup
|
||||
* @property array<string, string> $startup_commands
|
||||
* @property bool $script_is_privileged
|
||||
* @property string|null $script_install
|
||||
* @property string $script_entry
|
||||
@@ -71,7 +70,7 @@ class Egg extends Model implements Validatable
|
||||
/**
|
||||
* Defines the current egg export version.
|
||||
*/
|
||||
public const EXPORT_VERSION = 'PLCN_v2';
|
||||
public const EXPORT_VERSION = 'PLCN_v3';
|
||||
|
||||
/**
|
||||
* Fields that are not mass assignable.
|
||||
@@ -90,7 +89,7 @@ class Egg extends Model implements Validatable
|
||||
'config_logs',
|
||||
'config_stop',
|
||||
'config_from',
|
||||
'startup',
|
||||
'startup_commands',
|
||||
'update_url',
|
||||
'script_is_privileged',
|
||||
'script_install',
|
||||
@@ -111,7 +110,8 @@ class Egg extends Model implements Validatable
|
||||
'file_denylist.*' => ['string'],
|
||||
'docker_images' => ['required', 'array', 'min:1'],
|
||||
'docker_images.*' => ['required', 'string'],
|
||||
'startup' => ['required', 'nullable', 'string'],
|
||||
'startup_commands' => ['required', 'array', 'min:1'],
|
||||
'startup_commands.*' => ['required', 'string', 'distinct'],
|
||||
'config_from' => ['sometimes', 'bail', 'nullable', 'numeric', 'exists:eggs,id'],
|
||||
'config_stop' => ['required_without:config_from', 'nullable', 'string', 'max:255'],
|
||||
'config_startup' => ['required_without:config_from', 'nullable', 'json'],
|
||||
@@ -143,6 +143,7 @@ class Egg extends Model implements Validatable
|
||||
'features' => 'array',
|
||||
'docker_images' => 'array',
|
||||
'file_denylist' => 'array',
|
||||
'startup_commands' => 'array',
|
||||
'tags' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ class File extends Model
|
||||
'is_directory' => $file['directory'],
|
||||
'is_file' => $file['file'],
|
||||
'is_symlink' => $file['symlink'],
|
||||
'mime_type' => $file['mime'],
|
||||
'mime_type' => $file['file'] && str($file['name'])->lower()->endsWith('.jar') && in_array($file['mime'], self::ARCHIVE_MIMES) ? 'application/jar' : $file['mime'],
|
||||
];
|
||||
}, $contents);
|
||||
|
||||
|
||||
@@ -49,6 +49,9 @@ class Role extends BaseRole
|
||||
'activityLog' => [
|
||||
'seeIps',
|
||||
],
|
||||
'panelLog' => [
|
||||
'view',
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array<string, array<string>> */
|
||||
|
||||
@@ -63,7 +63,6 @@ use Spatie\Permission\Traits\HasRoles;
|
||||
* @property Carbon|null $updated_at
|
||||
* @property \Illuminate\Database\Eloquent\Collection|ApiKey[] $apiKeys
|
||||
* @property int|null $api_keys_count
|
||||
* @property string $name
|
||||
* @property DatabaseNotificationCollection|DatabaseNotification[] $notifications
|
||||
* @property int|null $notifications_count
|
||||
* @property \Illuminate\Database\Eloquent\Collection|Server[] $servers
|
||||
|
||||
@@ -24,7 +24,7 @@ class AccountCreated extends Notification implements ShouldQueue
|
||||
public function toMail(User $notifiable): MailMessage
|
||||
{
|
||||
$message = (new MailMessage())
|
||||
->greeting('Hello ' . $notifiable->name . '!')
|
||||
->greeting('Hello ' . $notifiable->username . '!')
|
||||
->line('You are receiving this email because an account has been created for you on ' . config('app.name') . '.')
|
||||
->line('Username: ' . $notifiable->username)
|
||||
->line('Email: ' . $notifiable->email);
|
||||
|
||||
@@ -22,7 +22,7 @@ class MailTested extends Notification
|
||||
{
|
||||
return (new MailMessage())
|
||||
->subject('Panel Test Message')
|
||||
->greeting('Hello ' . $this->user->name . '!')
|
||||
->greeting('Hello ' . $this->user->username . '!')
|
||||
->line('This is a test of the Panel mail system. You\'re good to go!');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use AchyutN\FilamentLogViewer\FilamentLogViewer;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Navigation\NavigationGroup;
|
||||
@@ -32,6 +33,12 @@ class AdminPanelProvider extends PanelProvider
|
||||
])
|
||||
->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
|
||||
->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets');
|
||||
->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets')
|
||||
->plugins([
|
||||
FilamentLogViewer::make()
|
||||
->authorize(fn () => user()->can('view panelLog'))
|
||||
->navigationGroup(fn () => trans('admin/dashboard.advanced'))
|
||||
->navigationIcon('tabler-file-info'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use AchyutN\FilamentLogViewer\FilamentLogViewer;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Panel;
|
||||
@@ -20,8 +21,12 @@ class AppPanelProvider extends PanelProvider
|
||||
->label(trans('profile.admin'))
|
||||
->url(fn () => Filament::getPanel('admin')->getUrl())
|
||||
->icon('tabler-arrow-forward')
|
||||
->visible(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin'))),
|
||||
->visible(fn () => user()?->canAccessPanel(Filament::getPanel('admin'))),
|
||||
])
|
||||
->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\\Filament\\App\\Resources');
|
||||
->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\\Filament\\App\\Resources')
|
||||
->plugins([
|
||||
FilamentLogViewer::make()
|
||||
->authorize(false),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ abstract class PanelProvider extends BasePanelProvider
|
||||
->brandLogo(config('app.logo'))
|
||||
->brandLogoHeight('2rem')
|
||||
->favicon(config('app.favicon', '/pelican.ico'))
|
||||
->topNavigation(fn () => auth()->user()->getCustomization(CustomizationKey::TopNavigation))
|
||||
->topNavigation(fn () => user()?->getCustomization(CustomizationKey::TopNavigation))
|
||||
->maxContentWidth(config('panel.filament.display-width', 'screen-2xl'))
|
||||
->profile(EditProfile::class, false)
|
||||
->userMenuItems([
|
||||
@@ -48,8 +48,7 @@ abstract class PanelProvider extends BasePanelProvider
|
||||
EmailAuthentication::make(),
|
||||
])
|
||||
->requiresMultiFactorAuthentication(function () {
|
||||
/** @var ?User $user */
|
||||
$user = auth()->user(); // TODO: get user, see https://github.com/filamentphp/filament/discussions/17695
|
||||
$user = user(); // TODO: get user, see https://github.com/filamentphp/filament/discussions/17695
|
||||
if ($user) {
|
||||
$level = (int) config('panel.auth.2fa_required');
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ class ServerPanelProvider extends PanelProvider
|
||||
->label(trans('profile.admin'))
|
||||
->icon('tabler-arrow-forward')
|
||||
->url(fn () => Filament::getPanel('admin')->getUrl())
|
||||
->visible(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin'))),
|
||||
->visible(fn () => user()?->canAccessPanel(Filament::getPanel('admin'))),
|
||||
])
|
||||
->navigationItems([
|
||||
NavigationItem::make(trans('server/console.open_in_admin'))
|
||||
->url(fn () => EditServer::getUrl(['record' => Filament::getTenant()], panel: 'admin'))
|
||||
->visible(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin')) && auth()->user()->can('view server', Filament::getTenant()))
|
||||
->visible(fn () => user()?->canAccessPanel(Filament::getPanel('admin')) && user()->can('view server', Filament::getTenant()))
|
||||
->icon('tabler-arrow-back')
|
||||
->sort(99),
|
||||
])
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Services\Eggs;
|
||||
use App\Models\Egg;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerVariable;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class EggChangerService
|
||||
{
|
||||
@@ -21,8 +22,8 @@ class EggChangerService
|
||||
// Change egg id, default image and startup command
|
||||
$server->forceFill([
|
||||
'egg_id' => $newEgg->id,
|
||||
'image' => array_values($newEgg->docker_images)[0],
|
||||
'startup' => $newEgg->startup,
|
||||
'image' => Arr::first($newEgg->docker_images),
|
||||
'startup' => Arr::first($newEgg->startup_commands),
|
||||
])->saveOrFail();
|
||||
|
||||
$oldVariables = [];
|
||||
|
||||
@@ -33,7 +33,7 @@ class EggExporterService
|
||||
'features' => $egg->features,
|
||||
'docker_images' => $egg->docker_images,
|
||||
'file_denylist' => Collection::make($egg->inherit_file_denylist)->filter(fn ($v) => !empty($v))->values(),
|
||||
'startup' => $egg->startup,
|
||||
'startup_commands' => $egg->startup_commands,
|
||||
'config' => [
|
||||
'files' => $egg->inherit_config_files,
|
||||
'startup' => $egg->inherit_config_startup,
|
||||
|
||||
@@ -133,8 +133,9 @@ class EggImporterService
|
||||
$version = $parsed['meta']['version'] ?? '';
|
||||
|
||||
$parsed = match ($version) {
|
||||
'PTDL_v1' => $this->convertToV2($parsed),
|
||||
'PTDL_v2', 'PLCN_v1', 'PLCN_v2' => $parsed,
|
||||
'PTDL_v1' => $this->convertToV3($this->convertLegacy($parsed)),
|
||||
'PTDL_v2', 'PLCN_v1', 'PLCN_v2' => $this->convertToV3($parsed),
|
||||
Egg::EXPORT_VERSION => $parsed,
|
||||
default => throw new InvalidFileUploadException('The file format is not recognized.'),
|
||||
};
|
||||
|
||||
@@ -180,9 +181,9 @@ class EggImporterService
|
||||
if ($forbidden->count()) {
|
||||
$parsed['variables'] = $allowed->merge($updatedVariables)->all();
|
||||
|
||||
if (!empty($parsed['startup'])) {
|
||||
foreach ($parsed['startup_commands'] ?? [] as $name => $startup) {
|
||||
$pattern = '/\b(' . collect($forbidden)->map(fn ($variable) => preg_quote($variable['env_variable']))->join('|') . ')\b/';
|
||||
$parsed['startup'] = preg_replace($pattern, 'SERVER_$1', $parsed['startup']) ?? $parsed['startup'];
|
||||
$parsed['startup_commands'][$name] = preg_replace($pattern, 'SERVER_$1', $startup) ?? $startup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +207,7 @@ class EggImporterService
|
||||
'config_startup' => json_encode(json_decode(Arr::get($parsed, 'config.startup')), JSON_PRETTY_PRINT),
|
||||
'config_logs' => json_encode(json_decode(Arr::get($parsed, 'config.logs')), JSON_PRETTY_PRINT),
|
||||
'config_stop' => Arr::get($parsed, 'config.stop'),
|
||||
'startup' => Arr::get($parsed, 'startup'),
|
||||
'startup_commands' => Arr::get($parsed, 'startup_commands'),
|
||||
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
|
||||
'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'),
|
||||
'script_container' => Arr::get($parsed, 'scripts.installation.container'),
|
||||
@@ -217,7 +218,7 @@ class EggImporterService
|
||||
* @param array<string, mixed> $parsed
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function convertToV2(array $parsed): array
|
||||
protected function convertLegacy(array $parsed): array
|
||||
{
|
||||
if (!isset($parsed['images'])) {
|
||||
$images = [Arr::get($parsed, 'image') ?? 'nil'];
|
||||
@@ -234,4 +235,21 @@ class EggImporterService
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $parsed
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function convertToV3(array $parsed): array
|
||||
{
|
||||
$startup = $parsed['startup'];
|
||||
|
||||
unset($parsed['startup']);
|
||||
|
||||
$parsed['startup_commands'] = [
|
||||
'Default' => $startup,
|
||||
];
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ class ServerCreationService
|
||||
$egg = Egg::query()->findOrFail($data['egg_id']);
|
||||
|
||||
// Fill missing fields from egg
|
||||
$data['image'] ??= collect($egg->docker_images)->first();
|
||||
$data['startup'] ??= $egg->startup;
|
||||
$data['image'] ??= Arr::first($egg->docker_images);
|
||||
$data['startup'] ??= Arr::first($egg->startup_commands);
|
||||
|
||||
// If a deployment object has been passed we need to get the allocation and node that the server should use.
|
||||
if ($deployment) {
|
||||
|
||||
@@ -9,8 +9,10 @@ class StartupCommandService
|
||||
/**
|
||||
* Generates a startup command for a given server instance.
|
||||
*/
|
||||
public function handle(Server $server, bool $hideAllValues = false): string
|
||||
public function handle(Server $server, ?string $startup = null, bool $hideAllValues = false): string
|
||||
{
|
||||
$startup ??= $server->startup;
|
||||
|
||||
$find = ['{{SERVER_MEMORY}}', '{{SERVER_IP}}', '{{SERVER_PORT}}'];
|
||||
$replace = [
|
||||
(string) $server->memory,
|
||||
@@ -23,6 +25,6 @@ class StartupCommandService
|
||||
$replace[] = ($variable->user_viewable && !$hideAllValues) ? ($variable->server_value ?? $variable->default_value) : '[hidden]';
|
||||
}
|
||||
|
||||
return str_replace($find, $replace, $server->startup);
|
||||
return str_replace($find, $replace, $startup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ class StartupModificationService
|
||||
]);
|
||||
|
||||
// Fill missing fields from egg
|
||||
$data['docker_image'] = $data['docker_image'] ?? collect($egg->docker_images)->first();
|
||||
$data['startup'] = $data['startup'] ?? $egg->startup;
|
||||
$data['docker_image'] ??= Arr::first($egg->docker_images);
|
||||
$data['startup'] ??= Arr::first($egg->startup_commands);
|
||||
}
|
||||
|
||||
$server->fill([
|
||||
|
||||
@@ -62,13 +62,15 @@ class TransferServerService
|
||||
|
||||
$server->validateTransferState();
|
||||
|
||||
$this->connection->transaction(function () use ($server, $node_id, $allocation_id, $additional_allocations) {
|
||||
/** @var ServerTransfer $transfer */
|
||||
$transfer = $this->connection->transaction(function () use ($server, $node_id, $allocation_id, $additional_allocations) {
|
||||
// Create a new ServerTransfer entry.
|
||||
$transfer = new ServerTransfer();
|
||||
$transfer = ServerTransfer::create([
|
||||
'server_id' => $server->id,
|
||||
'old_node' => $server->node_id,
|
||||
'new_node' => $node_id,
|
||||
]);
|
||||
|
||||
$transfer->server_id = $server->id;
|
||||
$transfer->old_node = $server->node_id;
|
||||
$transfer->new_node = $node_id;
|
||||
if ($server->allocation_id) {
|
||||
$transfer->old_allocation = $server->allocation_id;
|
||||
$transfer->new_allocation = $allocation_id;
|
||||
@@ -81,18 +83,18 @@ class TransferServerService
|
||||
|
||||
$transfer->save();
|
||||
|
||||
// Generate a token for the destination node that the source node can use to authenticate with.
|
||||
$token = $this->nodeJWTService
|
||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||
->setSubject($server->uuid)
|
||||
->handle($transfer->newNode, $server->uuid, 'sha256');
|
||||
|
||||
// Notify the source node of the pending outgoing transfer.
|
||||
$this->notify($transfer, $token);
|
||||
|
||||
return $transfer;
|
||||
});
|
||||
|
||||
// Generate a token for the destination node that the source node can use to authenticate with.
|
||||
$token = $this->nodeJWTService
|
||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||
->setSubject($server->uuid)
|
||||
->handle($transfer->newNode, $server->uuid, 'sha256');
|
||||
|
||||
// Notify the source node of the pending outgoing transfer.
|
||||
$this->notify($transfer, $token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,7 +108,7 @@ class TransferServerService
|
||||
$allocations = $additional_allocations;
|
||||
$allocations[] = $allocation_id;
|
||||
|
||||
$node = Node::query()->findOrFail($node_id);
|
||||
$node = Node::findOrFail($node_id);
|
||||
$unassigned = $node->allocations()
|
||||
->whereNull('server_id')
|
||||
->pluck('id')
|
||||
@@ -122,7 +124,7 @@ class TransferServerService
|
||||
}
|
||||
|
||||
if (!empty($updateIds)) {
|
||||
Allocation::query()->whereIn('id', $updateIds)->update(['server_id' => $server->id]);
|
||||
Allocation::whereIn('id', $updateIds)->update(['server_id' => $server->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class SubuserCreationService
|
||||
|
||||
$cleanedPermissions = collect($permissions)
|
||||
->unique()
|
||||
->filter(fn ($permission) => $permission === Permission::ACTION_WEBSOCKET_CONNECT || auth()->user()->can($permission, $server))
|
||||
->filter(fn ($permission) => $permission === Permission::ACTION_WEBSOCKET_CONNECT || user()?->can($permission, $server))
|
||||
->sort()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
@@ -22,7 +22,7 @@ class SubuserUpdateService
|
||||
{
|
||||
$cleanedPermissions = collect($permissions)
|
||||
->unique()
|
||||
->filter(fn ($permission) => $permission === Permission::ACTION_WEBSOCKET_CONNECT || auth()->user()->can($permission, $server))
|
||||
->filter(fn ($permission) => $permission === Permission::ACTION_WEBSOCKET_CONNECT || user()?->can($permission, $server))
|
||||
->sort()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
@@ -48,10 +48,7 @@ class EggTransformer extends BaseTransformer
|
||||
'description' => $model->description,
|
||||
'features' => $model->features,
|
||||
'tags' => $model->tags,
|
||||
// "docker_image" is deprecated, but left here to avoid breaking too many things at once
|
||||
// in external software. We'll remove it down the road once things have gotten the chance
|
||||
// to upgrade to using "docker_images".
|
||||
'docker_image' => count($model->docker_images) > 0 ? Arr::first($model->docker_images) : '',
|
||||
'docker_image' => Arr::first($model->docker_images, default: ''), // docker_images, use startup_commands
|
||||
'docker_images' => $model->docker_images,
|
||||
'config' => [
|
||||
'files' => $files,
|
||||
@@ -61,7 +58,8 @@ class EggTransformer extends BaseTransformer
|
||||
'file_denylist' => $model->inherit_file_denylist,
|
||||
'extends' => $model->config_from,
|
||||
],
|
||||
'startup' => $model->startup,
|
||||
'startup' => Arr::first($model->startup_commands, default: ''), // deprecated, use startup_commands
|
||||
'startup_commands' => $model->startup_commands,
|
||||
'script' => [
|
||||
'privileged' => $model->script_is_privileged,
|
||||
'install' => $model->copy_script_install,
|
||||
|
||||
@@ -60,7 +60,7 @@ class ServerTransformer extends BaseClientTransformer
|
||||
'oom_disabled' => !$server->oom_killer,
|
||||
'oom_killer' => $server->oom_killer,
|
||||
],
|
||||
'invocation' => $service->handle($server, !$user->can(Permission::ACTION_STARTUP_READ, $server)),
|
||||
'invocation' => $service->handle($server, hideAllValues: !$user->can(Permission::ACTION_STARTUP_READ, $server)),
|
||||
'docker_image' => $server->image,
|
||||
'egg_features' => $server->egg->inherit_features,
|
||||
'feature_limits' => [
|
||||
|
||||
@@ -108,7 +108,7 @@ if (!function_exists('format_number')) {
|
||||
function format_number(int|float $number, ?int $precision = null, ?int $maxPrecision = null): false|string
|
||||
{
|
||||
try {
|
||||
return Number::format($number, $precision, $maxPrecision, auth()->user()->language ?? 'en');
|
||||
return Number::format($number, $precision, $maxPrecision, user()->language ?? 'en');
|
||||
} catch (Throwable) {
|
||||
// User language is invalid, so default to english
|
||||
return Number::format($number, $precision, $maxPrecision, 'en');
|
||||
@@ -122,3 +122,10 @@ if (!function_exists('encode_path')) {
|
||||
return implode('/', array_map('rawurlencode', explode('/', $path)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('user')) {
|
||||
function user(): ?App\Models\User
|
||||
{
|
||||
return auth(config('auth.defaults.guard', 'web'))->user();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-zip": "*",
|
||||
"achyutn/filament-log-viewer": "^1.4",
|
||||
"aws/aws-sdk-php": "^3.356",
|
||||
"calebporzio/sushi": "^2.5",
|
||||
"dedoc/scramble": "^0.12.10",
|
||||
@@ -23,6 +24,7 @@
|
||||
"lcobucci/jwt": "^5.5",
|
||||
"league/flysystem-aws-s3-v3": "^3.29",
|
||||
"league/flysystem-memory": "^3.29",
|
||||
"phiki/phiki": "^2.0",
|
||||
"phpseclib/phpseclib": "~3.0.18",
|
||||
"predis/predis": "^2.3",
|
||||
"s1lentium/iptools": "~1.2.0",
|
||||
|
||||
221
composer.lock
generated
221
composer.lock
generated
@@ -4,8 +4,87 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fe426a19ad44b345d4cefbd0bd779be6",
|
||||
"content-hash": "101c2afb1f31acb872b4bed541397cd2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "achyutn/filament-log-viewer",
|
||||
"version": "v1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/achyutkneupane/filament-log-viewer.git",
|
||||
"reference": "4544440b8866f40c96ec33de2ecf675a951437b1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/achyutkneupane/filament-log-viewer/zipball/4544440b8866f40c96ec33de2ecf675a951437b1",
|
||||
"reference": "4544440b8866f40c96ec33de2ecf675a951437b1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"filament/filament": "^4.0",
|
||||
"phiki/phiki": "^2.0",
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.23",
|
||||
"orchestra/testbench": "^10.4",
|
||||
"pestphp/pest": "^3.8",
|
||||
"pestphp/pest-plugin-laravel": "^3.2",
|
||||
"pestphp/pest-plugin-livewire": "^3.0",
|
||||
"rector/rector": "^2.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"AchyutN\\FilamentLogViewer\\LogViewerProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"AchyutN\\FilamentLogViewer\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Achyut Neupane",
|
||||
"email": "achyutkneupane@gmail.com",
|
||||
"homepage": "https://achyut.com.np",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "A Filament package to view and manage Laravel logs.",
|
||||
"keywords": [
|
||||
"Viewer",
|
||||
"filament",
|
||||
"laravel",
|
||||
"log"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/achyutkneupane/filament-log-viewer/issues",
|
||||
"source": "https://github.com/achyutkneupane/filament-log-viewer/tree/v1.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.buymeacoffee.com/achyutn",
|
||||
"type": "buy_me_a_coffee"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/achyutkneupane",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/Achyut",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-07T19:04:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
"version": "1.3.4",
|
||||
@@ -128,16 +207,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.356.27",
|
||||
"version": "3.356.34",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "6f6b8cfdc1a1555b7a749395a79bda51910646e0"
|
||||
"reference": "4dabcc34c34ec3c3ce74373c2ff2fc318099d20b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6f6b8cfdc1a1555b7a749395a79bda51910646e0",
|
||||
"reference": "6f6b8cfdc1a1555b7a749395a79bda51910646e0",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4dabcc34c34ec3c3ce74373c2ff2fc318099d20b",
|
||||
"reference": "4dabcc34c34ec3c3ce74373c2ff2fc318099d20b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -219,9 +298,9 @@
|
||||
"support": {
|
||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.356.27"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.356.34"
|
||||
},
|
||||
"time": "2025-09-26T18:12:38+00:00"
|
||||
"time": "2025-10-07T18:05:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "blade-ui-kit/blade-heroicons",
|
||||
@@ -1324,16 +1403,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/actions",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/actions.git",
|
||||
"reference": "4ec445a337f5d547469896dff3d4f30c5c394d71"
|
||||
"reference": "bd54da3378705c246c4a0c2d7f185853295bbcd9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/actions/zipball/4ec445a337f5d547469896dff3d4f30c5c394d71",
|
||||
"reference": "4ec445a337f5d547469896dff3d4f30c5c394d71",
|
||||
"url": "https://api.github.com/repos/filamentphp/actions/zipball/bd54da3378705c246c4a0c2d7f185853295bbcd9",
|
||||
"reference": "bd54da3378705c246c4a0c2d7f185853295bbcd9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1369,20 +1448,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:20+00:00"
|
||||
"time": "2025-10-07T12:45:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/filament",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/panels.git",
|
||||
"reference": "5aeda3f8bb64e85ec3eb16bc7f5bee65b134f66c"
|
||||
"reference": "d03d2f80f08002e55c9617bb480dc8cd0403892a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/panels/zipball/5aeda3f8bb64e85ec3eb16bc7f5bee65b134f66c",
|
||||
"reference": "5aeda3f8bb64e85ec3eb16bc7f5bee65b134f66c",
|
||||
"url": "https://api.github.com/repos/filamentphp/panels/zipball/d03d2f80f08002e55c9617bb480dc8cd0403892a",
|
||||
"reference": "d03d2f80f08002e55c9617bb480dc8cd0403892a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1426,20 +1505,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:23+00:00"
|
||||
"time": "2025-10-08T11:01:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/forms",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/forms.git",
|
||||
"reference": "d6482a25e5f0a876a24a638df221c52011678363"
|
||||
"reference": "ddfa808665077bccae64242dc763e0434e98183f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/forms/zipball/d6482a25e5f0a876a24a638df221c52011678363",
|
||||
"reference": "d6482a25e5f0a876a24a638df221c52011678363",
|
||||
"url": "https://api.github.com/repos/filamentphp/forms/zipball/ddfa808665077bccae64242dc763e0434e98183f",
|
||||
"reference": "ddfa808665077bccae64242dc763e0434e98183f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1476,11 +1555,11 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:24+00:00"
|
||||
"time": "2025-10-08T10:40:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/infolists",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/infolists.git",
|
||||
@@ -1525,16 +1604,16 @@
|
||||
},
|
||||
{
|
||||
"name": "filament/notifications",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/notifications.git",
|
||||
"reference": "33c1d2291747873838c0eedea73c9f7e9784eee7"
|
||||
"reference": "0930beeea0b2d2e9bdc95a28b65409f960ebf81a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/notifications/zipball/33c1d2291747873838c0eedea73c9f7e9784eee7",
|
||||
"reference": "33c1d2291747873838c0eedea73c9f7e9784eee7",
|
||||
"url": "https://api.github.com/repos/filamentphp/notifications/zipball/0930beeea0b2d2e9bdc95a28b65409f960ebf81a",
|
||||
"reference": "0930beeea0b2d2e9bdc95a28b65409f960ebf81a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1568,20 +1647,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:25+00:00"
|
||||
"time": "2025-10-06T08:11:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/schemas",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/schemas.git",
|
||||
"reference": "d5709fcc269c268436ae74292338f77e4f5a2aa4"
|
||||
"reference": "21f58ba01bd88e1fca2c1fa9a06ddfa949e9270b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/schemas/zipball/d5709fcc269c268436ae74292338f77e4f5a2aa4",
|
||||
"reference": "d5709fcc269c268436ae74292338f77e4f5a2aa4",
|
||||
"url": "https://api.github.com/repos/filamentphp/schemas/zipball/21f58ba01bd88e1fca2c1fa9a06ddfa949e9270b",
|
||||
"reference": "21f58ba01bd88e1fca2c1fa9a06ddfa949e9270b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1613,20 +1692,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:21+00:00"
|
||||
"time": "2025-10-06T08:10:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/support",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/support.git",
|
||||
"reference": "4fc9653075d5be21154da130386826b7d9230ae6"
|
||||
"reference": "e3294c6cd71d0b6c8cba8d20c99d5af651351a86"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/support/zipball/4fc9653075d5be21154da130386826b7d9230ae6",
|
||||
"reference": "4fc9653075d5be21154da130386826b7d9230ae6",
|
||||
"url": "https://api.github.com/repos/filamentphp/support/zipball/e3294c6cd71d0b6c8cba8d20c99d5af651351a86",
|
||||
"reference": "e3294c6cd71d0b6c8cba8d20c99d5af651351a86",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1671,20 +1750,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:19+00:00"
|
||||
"time": "2025-10-08T10:40:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/tables",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/tables.git",
|
||||
"reference": "c3dc3ff22cc76b4ed21353c66b3669d0c5c4cd9c"
|
||||
"reference": "b3bf48fd079d8abdf6dfb0936f1d1c8c4a4479ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/tables/zipball/c3dc3ff22cc76b4ed21353c66b3669d0c5c4cd9c",
|
||||
"reference": "c3dc3ff22cc76b4ed21353c66b3669d0c5c4cd9c",
|
||||
"url": "https://api.github.com/repos/filamentphp/tables/zipball/b3bf48fd079d8abdf6dfb0936f1d1c8c4a4479ab",
|
||||
"reference": "b3bf48fd079d8abdf6dfb0936f1d1c8c4a4479ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1716,20 +1795,20 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-29T08:02:20+00:00"
|
||||
"time": "2025-10-08T11:01:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/widgets",
|
||||
"version": "v4.1.0",
|
||||
"version": "v4.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filamentphp/widgets.git",
|
||||
"reference": "5ad3577cf0d3f485678c617facb009f7cef978ac"
|
||||
"reference": "f0b009e52737a2f973771928ada952ff577704ba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filamentphp/widgets/zipball/5ad3577cf0d3f485678c617facb009f7cef978ac",
|
||||
"reference": "5ad3577cf0d3f485678c617facb009f7cef978ac",
|
||||
"url": "https://api.github.com/repos/filamentphp/widgets/zipball/f0b009e52737a2f973771928ada952ff577704ba",
|
||||
"reference": "f0b009e52737a2f973771928ada952ff577704ba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1760,7 +1839,7 @@
|
||||
"issues": "https://github.com/filamentphp/filament/issues",
|
||||
"source": "https://github.com/filamentphp/filament"
|
||||
},
|
||||
"time": "2025-09-21T21:08:23+00:00"
|
||||
"time": "2025-10-08T10:39:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
@@ -2434,16 +2513,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.31.1",
|
||||
"version": "v12.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "281b711710c245dd8275d73132e92635be3094df"
|
||||
"reference": "124efc5f09d4668a4dc13f94a1018c524a58bcb1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/281b711710c245dd8275d73132e92635be3094df",
|
||||
"reference": "281b711710c245dd8275d73132e92635be3094df",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/124efc5f09d4668a4dc13f94a1018c524a58bcb1",
|
||||
"reference": "124efc5f09d4668a4dc13f94a1018c524a58bcb1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2471,7 +2550,6 @@
|
||||
"monolog/monolog": "^3.0",
|
||||
"nesbot/carbon": "^3.8.4",
|
||||
"nunomaduro/termwind": "^2.0",
|
||||
"phiki/phiki": "^2.0.0",
|
||||
"php": "^8.2",
|
||||
"psr/container": "^1.1.1|^2.0.1",
|
||||
"psr/log": "^1.0|^2.0|^3.0",
|
||||
@@ -2650,7 +2728,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-09-23T15:33:04+00:00"
|
||||
"time": "2025-10-07T14:30:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/helpers",
|
||||
@@ -3358,16 +3436,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
"version": "9.25.0",
|
||||
"version": "9.26.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/csv.git",
|
||||
"reference": "f856f532866369fb1debe4e7c5a1db185f40ef86"
|
||||
"reference": "7fce732754d043f3938899e5183e2d0f3d31b571"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/f856f532866369fb1debe4e7c5a1db185f40ef86",
|
||||
"reference": "f856f532866369fb1debe4e7c5a1db185f40ef86",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/7fce732754d043f3938899e5183e2d0f3d31b571",
|
||||
"reference": "7fce732754d043f3938899e5183e2d0f3d31b571",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3445,7 +3523,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T08:29:08+00:00"
|
||||
"time": "2025-10-01T11:24:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
@@ -5592,16 +5670,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.46",
|
||||
"version": "3.0.47",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
|
||||
"reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
|
||||
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d",
|
||||
"reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5682,7 +5760,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.47"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5698,7 +5776,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-26T16:29:55+00:00"
|
||||
"time": "2025-10-06T01:07:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
@@ -13125,16 +13203,11 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.29",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phar-composer-source.git",
|
||||
"reference": "git"
|
||||
},
|
||||
"version": "2.1.30",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
|
||||
"reference": "d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a4a7f159927983dd4f7c8020ed227d80b7f39d7d",
|
||||
"reference": "a4a7f159927983dd4f7c8020ed227d80b7f39d7d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -13179,7 +13252,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-25T06:58:18+00:00"
|
||||
"time": "2025-10-02T16:07:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
||||
@@ -6,7 +6,7 @@ return [
|
||||
'logo' => env('APP_LOGO'),
|
||||
'favicon' => env('APP_FAVICON', '/pelican.ico'),
|
||||
|
||||
'version' => 'canary',
|
||||
'version' => '1.0.0-beta26',
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class EggFactory extends Factory
|
||||
'config_files' => '{}',
|
||||
'name' => $this->faker->name(),
|
||||
'description' => implode(' ', $this->faker->sentences()),
|
||||
'startup' => 'java -jar test.jar',
|
||||
'startup_commands' => ['java -jar test.jar'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
_comment: 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PANEL'
|
||||
meta:
|
||||
version: PLCN_v2
|
||||
version: PLCN_v3
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/minecraft/egg-bungeecord.yaml'
|
||||
exported_at: '2025-07-25T13:32:34+00:00'
|
||||
exported_at: '2025-09-05T08:54:34+00:00'
|
||||
name: Bungeecord
|
||||
author: panel@example.com
|
||||
uuid: 9e6b409e-4028-4947-aea8-50a2c404c271
|
||||
@@ -26,7 +26,8 @@ docker_images:
|
||||
'Java 11': 'ghcr.io/parkervcp/yolks:java_11'
|
||||
'Java 8': 'ghcr.io/parkervcp/yolks:java_8'
|
||||
file_denylist: { }
|
||||
startup: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}'
|
||||
startup_commands:
|
||||
Default: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}'
|
||||
config:
|
||||
files:
|
||||
config.yml:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
_comment: 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PANEL'
|
||||
meta:
|
||||
version: PLCN_v2
|
||||
version: PLCN_v3
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/minecraft/egg-forge-minecraft.yaml'
|
||||
exported_at: '2025-08-05T21:00:17+00:00'
|
||||
exported_at: '2025-09-05T08:54:45+00:00'
|
||||
name: 'Forge Minecraft'
|
||||
author: panel@example.com
|
||||
uuid: ed072427-f209-4603-875c-f540c6dd5a65
|
||||
@@ -22,7 +22,8 @@ docker_images:
|
||||
'Java 11': 'ghcr.io/parkervcp/yolks:java_11'
|
||||
'Java 8': 'ghcr.io/parkervcp/yolks:java_8'
|
||||
file_denylist: { }
|
||||
startup: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true $( [[ ! -f unix_args.txt ]] && printf %s "-jar {{SERVER_JARFILE}}" || printf %s "@unix_args.txt" )'
|
||||
startup_commands:
|
||||
Default: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true $( [[ ! -f unix_args.txt ]] && printf %s "-jar {{SERVER_JARFILE}}" || printf %s "@unix_args.txt" )'
|
||||
config:
|
||||
files:
|
||||
server.properties:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
_comment: 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PANEL'
|
||||
meta:
|
||||
version: PLCN_v2
|
||||
version: PLCN_v3
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/minecraft/egg-paper.yaml'
|
||||
exported_at: '2025-08-05T21:00:17+00:00'
|
||||
exported_at: '2025-09-05T08:54:10+00:00'
|
||||
name: Paper
|
||||
author: parker@example.com
|
||||
uuid: 5da37ef6-58da-4169-90a6-e683e1721247
|
||||
@@ -20,7 +20,8 @@ docker_images:
|
||||
'Java 11': 'ghcr.io/parkervcp/yolks:java_11'
|
||||
'Java 8': 'ghcr.io/parkervcp/yolks:java_8'
|
||||
file_denylist: { }
|
||||
startup: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}'
|
||||
startup_commands:
|
||||
Default: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}'
|
||||
config:
|
||||
files:
|
||||
server.properties:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
_comment: 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PANEL'
|
||||
meta:
|
||||
version: PLCN_v2
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.yaml'
|
||||
exported_at: '2025-08-05T21:00:17+00:00'
|
||||
version: PLCN_v3
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/minecraft/egg-sponge.yaml'
|
||||
exported_at: '2025-09-12T08:38:42+00:00'
|
||||
name: Sponge
|
||||
author: panel@example.com
|
||||
uuid: f0d2f88f-1ff3-42a0-b03f-ac44c5571e6d
|
||||
@@ -20,7 +20,8 @@ docker_images:
|
||||
'Java 11': 'ghcr.io/parkervcp/yolks:java_11'
|
||||
'Java 8': 'ghcr.io/parkervcp/yolks:java_8'
|
||||
file_denylist: { }
|
||||
startup: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}'
|
||||
startup_commands:
|
||||
Default: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}'
|
||||
config:
|
||||
files:
|
||||
server.properties:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
_comment: 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PANEL'
|
||||
meta:
|
||||
version: PLCN_v2
|
||||
version: PLCN_v3
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.yaml'
|
||||
exported_at: '2025-08-05T20:59:51+00:00'
|
||||
exported_at: '2025-09-05T08:55:10+00:00'
|
||||
name: 'Vanilla Minecraft'
|
||||
author: panel@example.com
|
||||
uuid: 9ac39f3d-0c34-4d93-8174-c52ab9e6c57b
|
||||
@@ -24,7 +24,8 @@ docker_images:
|
||||
'Java 11': 'ghcr.io/parkervcp/yolks:java_11'
|
||||
'Java 8': 'ghcr.io/parkervcp/yolks:java_8'
|
||||
file_denylist: { }
|
||||
startup: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}'
|
||||
startup_commands:
|
||||
Default: 'java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}'
|
||||
config:
|
||||
files:
|
||||
server.properties:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
_comment: 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PANEL'
|
||||
meta:
|
||||
version: PLCN_v2
|
||||
version: PLCN_v3
|
||||
update_url: 'https://github.com/pelican-dev/panel/raw/main/database/Seeders/eggs/rust/egg-rust.yaml'
|
||||
exported_at: '2025-07-25T13:30:56+00:00'
|
||||
exported_at: '2025-09-05T08:56:17+00:00'
|
||||
name: Rust
|
||||
author: panel@example.com
|
||||
uuid: bace2dfb-209c-452a-9459-7d6f340b07ae
|
||||
@@ -17,9 +17,10 @@ tags:
|
||||
features:
|
||||
- steam_disk_space
|
||||
docker_images:
|
||||
'ghcr.io/parkervcp/games:rust': 'ghcr.io/parkervcp/games:rust'
|
||||
Rust: 'ghcr.io/parkervcp/games:rust'
|
||||
file_denylist: { }
|
||||
startup: './RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity "rust" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \"{{SERVER_HOSTNAME}}\" +server.level \"{{LEVEL}}\" +server.description \"{{DESCRIPTION}}\" +server.url \"{{SERVER_URL}}\" +server.headerimage \"{{SERVER_IMG}}\" +server.logoimage \"{{SERVER_LOGO}}\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \"{{RCON_PASS}}\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s "+server.worldsize \"{{WORLD_SIZE}}\" +server.seed \"{{WORLD_SEED}}\"" || printf %s "+server.levelurl {{MAP_URL}}" ) {{ADDITIONAL_ARGS}}'
|
||||
startup_commands:
|
||||
Default: './RustDedicated -batchmode +server.port {{SERVER_PORT}} +server.queryport {{QUERY_PORT}} +server.identity "rust" +rcon.port {{RCON_PORT}} +rcon.web true +server.hostname \"{{SERVER_HOSTNAME}}\" +server.level \"{{LEVEL}}\" +server.description \"{{DESCRIPTION}}\" +server.url \"{{SERVER_URL}}\" +server.headerimage \"{{SERVER_IMG}}\" +server.logoimage \"{{SERVER_LOGO}}\" +server.maxplayers {{MAX_PLAYERS}} +rcon.password \"{{RCON_PASS}}\" +server.saveinterval {{SAVEINTERVAL}} +app.port {{APP_PORT}} $( [ -z ${MAP_URL} ] && printf %s "+server.worldsize \"{{WORLD_SIZE}}\" +server.seed \"{{WORLD_SEED}}\"" || printf %s "+server.levelurl {{MAP_URL}}" ) {{ADDITIONAL_ARGS}}'
|
||||
config:
|
||||
files: { }
|
||||
startup:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user