From 6c2d0a2d50f764f25ff92a7d27157e094db631e4 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Sun, 2 Jun 2024 21:59:12 -0400 Subject: [PATCH 01/15] Remove shenanigans --- app/Services/Nodes/NodeUpdateService.php | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php index f948686ed4..708605b191 100644 --- a/app/Services/Nodes/NodeUpdateService.php +++ b/app/Services/Nodes/NodeUpdateService.php @@ -32,21 +32,10 @@ public function handle(Node $node, array $data, bool $resetToken = false): Node $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); } - [$updated, $exception] = $this->connection->transaction(function () use ($data, $node) { - /** @var \App\Models\Node $updated */ - $updated = $node->replicate(); - $updated->forceFill($data)->save(); + [$node, $exception] = $this->connection->transaction(function () use ($data, $node) { + $node->forceFill($data)->save(); try { - // If we're changing the FQDN for the node, use the newly provided FQDN for the connection - // address. This should alleviate issues where the node gets pointed to a "valid" FQDN that - // isn't actually running the daemon software, and therefore you can't actually change it - // back. - // - // This makes more sense anyways, because only the Panel uses the FQDN for connecting, the - // node doesn't actually care about this. - $node->fqdn = $updated->fqdn; - - $this->configurationRepository->setNode($node)->update($updated); + $this->configurationRepository->setNode($node)->update($node); } catch (DaemonConnectionException $exception) { logger()->warning($exception, ['node_id' => $node->id]); @@ -56,16 +45,16 @@ public function handle(Node $node, array $data, bool $resetToken = false): Node // // This avoids issues with proxies such as Cloudflare which will see daemon as offline and then // inject their own response pages, causing this logic to get fucked up. - return [$updated, true]; + return [$node, true]; } - return [$updated, false]; + return [$node, false]; }); if ($exception) { throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); } - return $updated; + return $node; } } From 8f2261f6cdcf4265cbed650aaba92c37b0f417b8 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Tue, 4 Jun 2024 09:17:36 +0200 Subject: [PATCH 02/15] add alias for node sftp address --- app/Console/Commands/Node/MakeNodeCommand.php | 2 ++ .../Resources/NodeResource/Pages/EditNode.php | 12 ++++++++ .../Application/Nodes/StoreNodeRequest.php | 7 +---- app/Models/Node.php | 4 ++- .../Api/Client/ServerTransformer.php | 1 + ..._add_daemon_sftp_alias_column_to_nodes.php | 28 +++++++++++++++++++ lang/en/commands.php | 1 + resources/scripts/api/server/getServer.ts | 2 ++ .../server/settings/SettingsContainer.tsx | 13 +++++++-- 9 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php diff --git a/app/Console/Commands/Node/MakeNodeCommand.php b/app/Console/Commands/Node/MakeNodeCommand.php index b431e8b864..c57d4ee907 100644 --- a/app/Console/Commands/Node/MakeNodeCommand.php +++ b/app/Console/Commands/Node/MakeNodeCommand.php @@ -25,6 +25,7 @@ class MakeNodeCommand extends Command {--uploadSize= : Enter the maximum upload filesize.} {--daemonListeningPort= : Enter the daemon listening port.} {--daemonSFTPPort= : Enter the daemon SFTP listening port.} + {--daemonSFTPAlias= : Enter the daemon SFTP alias.} {--daemonBase= : Enter the base folder.}'; protected $description = 'Creates a new node on the system via the CLI.'; @@ -65,6 +66,7 @@ public function handle(): void $data['upload_size'] = $this->option('uploadSize') ?? $this->ask(__('commands.make_node.upload_size'), '100'); $data['daemon_listen'] = $this->option('daemonListeningPort') ?? $this->ask(__('commands.make_node.daemonListen'), '8080'); $data['daemon_sftp'] = $this->option('daemonSFTPPort') ?? $this->ask(__('commands.make_node.daemonSFTP'), '2022'); + $data['daemon_sftp_alias'] = $this->option('daemonSFTPAlias') ?? $this->ask(__('commands.make_node.daemonSFTPAlias'), ''); $data['daemon_base'] = $this->option('daemonBase') ?? $this->ask(__('commands.make_node.daemonBase'), '/var/lib/pelican/volumes'); $node = $this->creationService->handle($data); diff --git a/app/Filament/Resources/NodeResource/Pages/EditNode.php b/app/Filament/Resources/NodeResource/Pages/EditNode.php index ca0ac657fe..b7236232dd 100644 --- a/app/Filament/Resources/NodeResource/Pages/EditNode.php +++ b/app/Filament/Resources/NodeResource/Pages/EditNode.php @@ -215,6 +215,18 @@ public function form(Forms\Form $form): Forms\Form ->minValue(1) ->maxValue(1024) ->suffix('MiB'), + Forms\Components\TextInput::make('daemon_sftp') + ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) + ->label('SFTP Port') + ->minValue(0) + ->maxValue(65536) + ->default(2022) + ->required() + ->integer(), + Forms\Components\TextInput::make('daemon_sftp_alias') + ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) + ->label('SFTP Alias') + ->helperText('Display alias for the SFTP address. Leave empty to use the Node FQDN.'), Forms\Components\ToggleButtons::make('public') ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) ->label('Automatic Allocation')->inline() diff --git a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php index 36013cc192..c7166e9df2 100644 --- a/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php +++ b/app/Http/Requests/Api/Application/Nodes/StoreNodeRequest.php @@ -33,10 +33,9 @@ public function rules(array $rules = null): array 'upload_size', 'daemon_listen', 'daemon_sftp', + 'daemon_sftp_alias', 'daemon_base', ])->mapWithKeys(function ($value, $key) { - $key = ($key === 'daemon_sftp') ? 'daemon_sftp' : $key; - return [snake_case($key) => $value]; })->toArray(); } @@ -60,12 +59,8 @@ public function attributes(): array public function validated($key = null, $default = null): array { $response = parent::validated(); - $response['daemon_listen'] = $response['daemon_listen']; - $response['daemon_sftp'] = $response['daemon_sftp']; $response['daemon_base'] = $response['daemon_base'] ?? (new Node())->getAttribute('daemon_base'); - unset($response['daemon_base'], $response['daemon_listen'], $response['daemon_sftp']); - return $response; } } diff --git a/app/Models/Node.php b/app/Models/Node.php index 65aeaa0ea5..7fa89f511f 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -33,6 +33,7 @@ * @property string $daemon_token * @property int $daemon_listen * @property int $daemon_sftp + * @property string|null $daemon_sftp_alias * @property string $daemon_base * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at @@ -72,7 +73,7 @@ class Node extends Model 'memory', 'memory_overallocate', 'disk', 'disk_overallocate', 'cpu', 'cpu_overallocate', 'upload_size', 'daemon_base', - 'daemon_sftp', 'daemon_listen', + 'daemon_sftp', 'daemon_sftp_alias', 'daemon_listen', 'description', 'maintenance_mode', ]; @@ -91,6 +92,7 @@ class Node extends Model 'cpu_overallocate' => 'required|numeric|min:-1', 'daemon_base' => 'sometimes|required|regex:/^([\/][\d\w.\-\/]+)$/', 'daemon_sftp' => 'required|numeric|between:1,65535', + 'daemon_sftp_alias' => 'nullable|string', 'daemon_listen' => 'required|numeric|between:1,65535', 'maintenance_mode' => 'boolean', 'upload_size' => 'int|between:1,1024', diff --git a/app/Transformers/Api/Client/ServerTransformer.php b/app/Transformers/Api/Client/ServerTransformer.php index 686a8e237c..07cd64dbd2 100644 --- a/app/Transformers/Api/Client/ServerTransformer.php +++ b/app/Transformers/Api/Client/ServerTransformer.php @@ -46,6 +46,7 @@ public function transform(Server $server): array 'is_node_under_maintenance' => $server->node->isUnderMaintenance(), 'sftp_details' => [ 'ip' => $server->node->fqdn, + 'alias' => $server->node->daemon_sftp_alias, 'port' => $server->node->daemon_sftp, ], 'description' => $server->description, diff --git a/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php b/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php new file mode 100644 index 0000000000..e0e6e1786e --- /dev/null +++ b/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php @@ -0,0 +1,28 @@ +text('daemon_sftp_alias')->nullable()->after('daemon_sftp'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('nodes', function (Blueprint $table) { + $table->dropColumn('daemon_sftp_alias'); + }); + } +}; diff --git a/lang/en/commands.php b/lang/en/commands.php index fe3448310e..ba0cc07235 100644 --- a/lang/en/commands.php +++ b/lang/en/commands.php @@ -37,6 +37,7 @@ 'upload_size' => "'Enter the maximum filesize upload", 'daemonListen' => 'Enter the daemon listening port', 'daemonSFTP' => 'Enter the daemon SFTP listening port', + 'daemonSFTPAlias' => 'Enter the daemon SFTP alias (can be empty)', 'daemonBase' => 'Enter the base folder', 'succes1' => 'Successfully created a new node with the name: ', 'succes2' => 'and has an id of: ', diff --git a/resources/scripts/api/server/getServer.ts b/resources/scripts/api/server/getServer.ts index d2aa2e0543..ff0abc026a 100644 --- a/resources/scripts/api/server/getServer.ts +++ b/resources/scripts/api/server/getServer.ts @@ -21,6 +21,7 @@ export interface Server { status: ServerStatus; sftpDetails: { ip: string; + alias: string; port: number; }; invocation: string; @@ -57,6 +58,7 @@ export const rawDataToServerObject = ({ attributes: data }: FractalResponseData) dockerImage: data.docker_image, sftpDetails: { ip: data.sftp_details.ip, + alias: data.sftp_details.alias, port: data.sftp_details.port, }, description: data.description ? (data.description.length > 0 ? data.description : null) : null, diff --git a/resources/scripts/components/server/settings/SettingsContainer.tsx b/resources/scripts/components/server/settings/SettingsContainer.tsx index bcdc80b8e0..2539727553 100644 --- a/resources/scripts/components/server/settings/SettingsContainer.tsx +++ b/resources/scripts/components/server/settings/SettingsContainer.tsx @@ -31,8 +31,12 @@ export default () => {
- - + +
@@ -50,7 +54,10 @@ export default () => {
- + Launch SFTP
From f9fd426aca095f247b03a7bfaa0c1876c811aee6 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Wed, 5 Jun 2024 08:47:11 +0200 Subject: [PATCH 03/15] change column type to string Co-authored-by: Lance Pioch --- .../2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php b/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php index e0e6e1786e..b4310ac7d1 100644 --- a/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php +++ b/database/migrations/2024_06_04_085042_add_daemon_sftp_alias_column_to_nodes.php @@ -12,7 +12,7 @@ public function up(): void { Schema::table('nodes', function (Blueprint $table) { - $table->text('daemon_sftp_alias')->nullable()->after('daemon_sftp'); + $table->string('daemon_sftp_alias')->nullable()->after('daemon_sftp'); }); } From 6f1de675237d0b31d120cfc6a35c8ff7b6d39684 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Wed, 5 Jun 2024 16:03:04 -0400 Subject: [PATCH 04/15] Remove extraneous parameters --- .../Controllers/Admin/Nodes/NodeController.php | 3 +-- .../Admin/Nodes/NodeViewController.php | 17 +++++------------ .../Admin/Servers/ServerController.php | 3 +-- app/Http/Controllers/Admin/UserController.php | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/Admin/Nodes/NodeController.php b/app/Http/Controllers/Admin/Nodes/NodeController.php index b02ada6680..f4d7982cf9 100644 --- a/app/Http/Controllers/Admin/Nodes/NodeController.php +++ b/app/Http/Controllers/Admin/Nodes/NodeController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\Admin\Nodes; use Illuminate\View\View; -use Illuminate\Http\Request; use App\Models\Node; use Spatie\QueryBuilder\QueryBuilder; use App\Http\Controllers\Controller; @@ -13,7 +12,7 @@ class NodeController extends Controller /** * Returns a listing of nodes on the system. */ - public function index(Request $request): View + public function index(): View { $nodes = QueryBuilder::for( Node::query()->withCount('servers') diff --git a/app/Http/Controllers/Admin/Nodes/NodeViewController.php b/app/Http/Controllers/Admin/Nodes/NodeViewController.php index 9407ef2a7d..95dc7ede0a 100644 --- a/app/Http/Controllers/Admin/Nodes/NodeViewController.php +++ b/app/Http/Controllers/Admin/Nodes/NodeViewController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\Admin\Nodes; use Illuminate\View\View; -use Illuminate\Http\Request; use App\Models\Node; use Illuminate\Support\Collection; use App\Models\Allocation; @@ -29,16 +28,10 @@ public function __construct( /** * Returns index view for a specific node on the system. */ - public function index(Request $request, Node $node): View + public function index(Node $node): View { $node->loadCount('servers'); - $stats = Node::query() - ->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') - ->join('servers', 'servers.node_id', '=', 'nodes.id') - ->where('node_id', '=', $node->id) - ->first(); - return view('admin.nodes.view.index', [ 'node' => $node, 'version' => $this->versionService, @@ -48,7 +41,7 @@ public function index(Request $request, Node $node): View /** * Returns the settings page for a specific node. */ - public function settings(Request $request, Node $node): View + public function settings(Node $node): View { return view('admin.nodes.view.settings', [ 'node' => $node, @@ -58,7 +51,7 @@ public function settings(Request $request, Node $node): View /** * Return the node configuration page for a specific node. */ - public function configuration(Request $request, Node $node): View + public function configuration(Node $node): View { return view('admin.nodes.view.configuration', compact('node')); } @@ -66,7 +59,7 @@ public function configuration(Request $request, Node $node): View /** * Return the node allocation management page. */ - public function allocations(Request $request, Node $node): View + public function allocations(Node $node): View { $node->setRelation( 'allocations', @@ -92,7 +85,7 @@ public function allocations(Request $request, Node $node): View /** * Return a listing of servers that exist for this specific node. */ - public function servers(Request $request, Node $node): View + public function servers(Node $node): View { $this->plainInject([ 'node' => Collection::wrap($node->makeVisible(['daemon_token_id', 'daemon_token'])) diff --git a/app/Http/Controllers/Admin/Servers/ServerController.php b/app/Http/Controllers/Admin/Servers/ServerController.php index 06853e49e5..5e4595225a 100644 --- a/app/Http/Controllers/Admin/Servers/ServerController.php +++ b/app/Http/Controllers/Admin/Servers/ServerController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\Admin\Servers; use Illuminate\View\View; -use Illuminate\Http\Request; use App\Models\Server; use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\AllowedFilter; @@ -16,7 +15,7 @@ class ServerController extends Controller * Returns all the servers that exist on the system using a paginated result set. If * a query is passed along in the request it is also passed to the repository function. */ - public function index(Request $request): View + public function index(): View { $servers = QueryBuilder::for(Server::query()->with('node', 'user', 'allocation')) ->allowedFilters([ diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 33ced4b752..d69dc4301c 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -37,7 +37,7 @@ public function __construct( /** * Display user index page. */ - public function index(Request $request): View + public function index(): View { $users = QueryBuilder::for( User::query()->select('users.*') From df26c4f9f52572a8cc3f25b289940b470cc50d56 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Wed, 5 Jun 2024 21:49:09 -0400 Subject: [PATCH 05/15] Better exception handling --- .../TwoFactorAuthenticationTokenInvalid.php | 6 ++--- .../Resources/UserResource/Pages/EditUser.php | 6 +++++ .../Exceptions/FilamentExceptionHandler.php | 24 +++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 app/Services/Exceptions/FilamentExceptionHandler.php diff --git a/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php b/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php index 38771a4536..d5f09884c4 100644 --- a/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php +++ b/app/Exceptions/Service/User/TwoFactorAuthenticationTokenInvalid.php @@ -6,9 +6,9 @@ class TwoFactorAuthenticationTokenInvalid extends DisplayException { - /** - * TwoFactorAuthenticationTokenInvalid constructor. - */ + public string $title = 'Invalid 2FA Code'; + public string $icon = 'tabler-2fa'; + public function __construct() { parent::__construct('The provided two-factor authentication token was not valid.'); diff --git a/app/Filament/Resources/UserResource/Pages/EditUser.php b/app/Filament/Resources/UserResource/Pages/EditUser.php index e12e4de409..4750ce28f7 100644 --- a/app/Filament/Resources/UserResource/Pages/EditUser.php +++ b/app/Filament/Resources/UserResource/Pages/EditUser.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources\UserResource\Pages; use App\Filament\Resources\UserResource; +use App\Services\Exceptions\FilamentExceptionHandler; use Filament\Actions; use Filament\Resources\Pages\EditRecord; use App\Models\User; @@ -77,4 +78,9 @@ protected function getFormActions(): array { return []; } + + public function exception($exception, $stopPropagation): void + { + (new FilamentExceptionHandler())->handle($exception, $stopPropagation); + } } diff --git a/app/Services/Exceptions/FilamentExceptionHandler.php b/app/Services/Exceptions/FilamentExceptionHandler.php new file mode 100644 index 0000000000..2898f6c2cb --- /dev/null +++ b/app/Services/Exceptions/FilamentExceptionHandler.php @@ -0,0 +1,24 @@ +title($exception->title ?? null) + ->body($exception->body ?? $exception->getMessage()) + ->color($exception->color ?? 'danger') + ->icon($exception->icon ?? 'tabler-x') + ->danger() + ->send(); + + if ($this->stopPropagation ?? true) { + $stopPropagation(); + } + } +} From a4435a7454c8410d6680527232a7c58ebe20d611 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Wed, 5 Jun 2024 22:12:53 -0400 Subject: [PATCH 06/15] Pint fix --- app/Services/Exceptions/FilamentExceptionHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Exceptions/FilamentExceptionHandler.php b/app/Services/Exceptions/FilamentExceptionHandler.php index 2898f6c2cb..328e944b05 100644 --- a/app/Services/Exceptions/FilamentExceptionHandler.php +++ b/app/Services/Exceptions/FilamentExceptionHandler.php @@ -7,7 +7,7 @@ class FilamentExceptionHandler { - public function handle(Exception $exception, Callable $stopPropagation): void + public function handle(Exception $exception, callable $stopPropagation): void { Notification::make() ->title($exception->title ?? null) From 51cd7a8e814837d358728888b6a0803c3a9657bb Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Thu, 6 Jun 2024 16:15:35 -0400 Subject: [PATCH 07/15] Remove unused route files --- bootstrap/app.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/bootstrap/app.php b/bootstrap/app.php index fa0504e38c..18295a393b 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -9,10 +9,7 @@ \Prologue\Alerts\AlertsServiceProvider::class, ]) ->withRouting( - web: __DIR__.'/../routes/web.php', - // api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', - // channels: __DIR__.'/../routes/channels.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { From 643e4168b9498cacfa4e5835bd3ca01ba0bc0828 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Thu, 6 Jun 2024 19:39:46 -0400 Subject: [PATCH 08/15] Add required rule separately --- .../Resources/ServerResource/Pages/CreateServer.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Filament/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Resources/ServerResource/Pages/CreateServer.php index 7973349ebf..1abae3d30e 100644 --- a/app/Filament/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Resources/ServerResource/Pages/CreateServer.php @@ -371,19 +371,20 @@ public function form(Form $form): Form $text = Forms\Components\TextInput::make('variable_value') ->hidden($this->shouldHideComponent(...)) ->maxLength(191) - ->rules([ + ->required(fn (Forms\Get $get) => in_array('required', explode('|', $get('rules')))) + ->rules( fn (Forms\Get $get): Closure => function (string $attribute, $value, Closure $fail) use ($get) { $validator = Validator::make(['validatorkey' => $value], [ 'validatorkey' => $get('rules'), ]); if ($validator->fails()) { - $message = str($validator->errors()->first())->replace('validatorkey', $get('name')); + $message = str($validator->errors()->first())->replace('validatorkey', $get('name'))->toString(); $fail($message); } }, - ]); + ); $select = Forms\Components\Select::make('variable_value') ->hidden($this->shouldHideComponent(...)) From 02d24b8a36f6937328abc3c3bbd73360a39ff7f2 Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 7 Jun 2024 16:23:25 -0400 Subject: [PATCH 09/15] Fix the egg variable disaster... (#331) * Migrations to update existing eggs in db * Update stock eggs * Update Eggs on import * Also update updated versions of eggs that are uploaded * Redo this.. Tests passed locally. * Pint & Update replace * Squash Migrations, simplify logic * Maybe this way... * Swap them over to single call --------- Co-authored-by: Lance Pioch --- app/Services/Eggs/EggConfigurationService.php | 36 +------- app/Services/Eggs/EggParserService.php | 21 ++++- .../Eggs/Sharing/EggImporterService.php | 1 + .../eggs/minecraft/egg-bungeecord.json | 4 +- .../eggs/minecraft/egg-forge-minecraft.json | 4 +- .../Seeders/eggs/minecraft/egg-paper.json | 4 +- .../minecraft/egg-sponge--sponge-vanilla.json | 6 +- .../eggs/minecraft/egg-vanilla-minecraft.json | 4 +- .../eggs/voice-servers/egg-mumble-server.json | 4 +- ..._05_220135_update_egg_config_variables.php | 92 +++++++++++++++++++ 10 files changed, 127 insertions(+), 49 deletions(-) create mode 100644 database/migrations/2024_06_05_220135_update_egg_config_variables.php diff --git a/app/Services/Eggs/EggConfigurationService.php b/app/Services/Eggs/EggConfigurationService.php index 5280688ee8..cfe458403f 100644 --- a/app/Services/Eggs/EggConfigurationService.php +++ b/app/Services/Eggs/EggConfigurationService.php @@ -124,38 +124,6 @@ protected function replacePlaceholders(Server $server, object $configs): array return $response; } - /** - * Replaces the legacy modifies from eggs with their new counterpart. The legacy Daemon would - * set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their respective values on the Daemon - * side. Ensure that anything referencing those properly replaces them with the matching config - * value. - */ - protected function replaceLegacyModifiers(string $key, string $value): string - { - switch ($key) { - case 'config.docker.interface': - $replace = 'config.docker.network.interface'; - break; - case 'server.build.env.SERVER_MEMORY': - case 'env.SERVER_MEMORY': - $replace = 'server.build.memory'; - break; - case 'server.build.env.SERVER_IP': - case 'env.SERVER_IP': - $replace = 'server.build.default.ip'; - break; - case 'server.build.env.SERVER_PORT': - case 'env.SERVER_PORT': - $replace = 'server.build.default.port'; - break; - default: - // By default, we don't need to change anything, only if we ended up matching a specific legacy item. - $replace = $key; - } - - return str_replace("{{{$key}}}", "{{{$replace}}}", $value); - } - protected function matchAndReplaceKeys(mixed $value, array $structure): mixed { preg_match_all('/{{(?[\w.-]*)}}/', $value, $matches); @@ -175,8 +143,6 @@ protected function matchAndReplaceKeys(mixed $value, array $structure): mixed continue; } - $value = $this->replaceLegacyModifiers($key, $value); - // We don't want to do anything with config keys since the Daemon will need to handle // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker // interface to proxy through, but the Panel would be unaware of that. @@ -198,7 +164,7 @@ protected function matchAndReplaceKeys(mixed $value, array $structure): mixed // variable from the server configuration. $plucked = Arr::get( $structure, - preg_replace('/^env\./', 'build.env.', $key), + preg_replace('/^env\./', 'build.environment.', $key), '' ); diff --git a/app/Services/Eggs/EggParserService.php b/app/Services/Eggs/EggParserService.php index dfb2e26314..d780a9f94f 100644 --- a/app/Services/Eggs/EggParserService.php +++ b/app/Services/Eggs/EggParserService.php @@ -10,6 +10,16 @@ class EggParserService { + public const UPGRADE_VARIABLES = [ + 'server.build.env.SERVER_IP' => 'server.allocations.default.ip', + 'server.build.default.ip' => 'server.allocations.default.ip', + 'server.build.env.SERVER_PORT' => 'server.allocations.default.port', + 'server.build.default.port' => 'server.allocations.default.port', + 'server.build.env.SERVER_MEMORY' => 'server.build.memory_limit', + 'server.build.memory' => 'server.build.memory_limit', + 'server.build.env' => 'server.build.environment', + ]; + /** * Takes an uploaded file and parses out the egg configuration from within. * @@ -26,11 +36,20 @@ public function handle(UploadedFile $file): array $version = $parsed['meta']['version'] ?? ''; - return match ($version) { + $parsed = match ($version) { 'PTDL_v1' => $this->convertToV2($parsed), 'PTDL_v2' => $parsed, default => throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.') }; + + // Make sure we only use recent variable format from now on + $parsed['config']['files'] = str_replace( + array_keys(self::UPGRADE_VARIABLES), + array_values(self::UPGRADE_VARIABLES), + $parsed['config']['files'] ?? '', + ); + + return $parsed; } /** diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index 7ed7f010ab..23ef0c8194 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -45,4 +45,5 @@ public function handle(UploadedFile $file): Egg return $egg; }); } + } diff --git a/database/Seeders/eggs/minecraft/egg-bungeecord.json b/database/Seeders/eggs/minecraft/egg-bungeecord.json index 99aeea0368..d6d15c3fc1 100644 --- a/database/Seeders/eggs/minecraft/egg-bungeecord.json +++ b/database/Seeders/eggs/minecraft/egg-bungeecord.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2024-06-02T20:42:01+00:00", + "exported_at": "2024-06-04T22:51:49+00:00", "name": "Bungeecord", "author": "panel@example.com", "uuid": "9e6b409e-4028-4947-aea8-50a2c404c271", @@ -24,7 +24,7 @@ "file_denylist": [], "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}", "config": { - "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.build.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.build.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", + "files": "{\r\n \"config.yml\": {\r\n \"parser\": \"yaml\",\r\n \"find\": {\r\n \"listeners[0].query_port\": \"{{server.allocations.default.port}}\",\r\n \"listeners[0].host\": \"0.0.0.0:{{server.allocations.default.port}}\",\r\n \"servers.*.address\": {\r\n \"regex:^(127\\\\.0\\\\.0\\\\.1|localhost)(:\\\\d{1,5})?$\": \"{{config.docker.interface}}$2\"\r\n }\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \"Listening on \"\r\n}", "logs": "{}", "stop": "end" diff --git a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json index 543fb2dc02..6875e79ae2 100644 --- a/database/Seeders/eggs/minecraft/egg-forge-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-forge-minecraft.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2024-06-02T20:42:02+00:00", + "exported_at": "2024-06-04T22:51:58+00:00", "name": "Forge Minecraft", "author": "panel@example.com", "uuid": "ed072427-f209-4603-875c-f540c6dd5a65", @@ -24,7 +24,7 @@ "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\" )", "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", "logs": "{}", "stop": "stop" diff --git a/database/Seeders/eggs/minecraft/egg-paper.json b/database/Seeders/eggs/minecraft/egg-paper.json index 3337a84cc9..8eb3002ec6 100644 --- a/database/Seeders/eggs/minecraft/egg-paper.json +++ b/database/Seeders/eggs/minecraft/egg-paper.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2024-06-02T20:42:02+00:00", + "exported_at": "2024-06-04T22:51:57+00:00", "name": "Paper", "author": "parker@example.com", "uuid": "5da37ef6-58da-4169-90a6-e683e1721247", @@ -24,7 +24,7 @@ "file_denylist": [], "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", "logs": "{}", "stop": "stop" diff --git a/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json b/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json index 29b52d24a7..a9d0ebeef8 100644 --- a/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json +++ b/database/Seeders/eggs/minecraft/egg-sponge--sponge-vanilla.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2024-06-02T20:42:03+00:00", + "exported_at": "2024-06-04T22:50:55+00:00", "name": "Sponge (SpongeVanilla)", "author": "panel@example.com", "uuid": "f0d2f88f-1ff3-42a0-b03f-ac44c5571e6d", @@ -24,7 +24,7 @@ "file_denylist": [], "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}", "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", "logs": "{}", "stop": "stop" @@ -60,4 +60,4 @@ "field_type": "text" } ] -} \ No newline at end of file +} diff --git a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json index d7bd64abf8..63036eda0a 100644 --- a/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json +++ b/database/Seeders/eggs/minecraft/egg-vanilla-minecraft.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2024-06-02T20:42:03+00:00", + "exported_at": "2024-06-04T22:51:16+00:00", "name": "Vanilla Minecraft", "author": "panel@example.com", "uuid": "9ac39f3d-0c34-4d93-8174-c52ab9e6c57b", @@ -24,7 +24,7 @@ "file_denylist": [], "startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{SERVER_JARFILE}}", "config": { - "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.build.default.port}}\",\r\n \"query.port\": \"{{server.build.default.port}}\"\r\n }\r\n }\r\n}", + "files": "{\r\n \"server.properties\": {\r\n \"parser\": \"properties\",\r\n \"find\": {\r\n \"server-ip\": \"0.0.0.0\",\r\n \"server-port\": \"{{server.allocations.default.port}}\",\r\n \"query.port\": \"{{server.allocations.default.port}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \")! For help, type \"\r\n}", "logs": "{}", "stop": "stop" diff --git a/database/Seeders/eggs/voice-servers/egg-mumble-server.json b/database/Seeders/eggs/voice-servers/egg-mumble-server.json index 1278c26e38..fd7ecf6795 100644 --- a/database/Seeders/eggs/voice-servers/egg-mumble-server.json +++ b/database/Seeders/eggs/voice-servers/egg-mumble-server.json @@ -4,7 +4,7 @@ "version": "PTDL_v2", "update_url": null }, - "exported_at": "2024-06-02T20:42:08+00:00", + "exported_at": "2024-06-04T22:53:03+00:00", "name": "Mumble Server", "author": "panel@example.com", "uuid": "727ee758-7fb2-4979-972b-d3eba4e1e9f0", @@ -16,7 +16,7 @@ "file_denylist": [], "startup": "mumble-server -fg -ini murmur.ini", "config": { - "files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.build.default.port}}\",\r\n \"host\": \"\",\r\n \"users\": \"{{server.build.env.MAX_USERS}}\"\r\n }\r\n }\r\n}", + "files": "{\r\n \"murmur.ini\": {\r\n \"parser\": \"ini\",\r\n \"find\": {\r\n \"database\": \"\/home\/container\/murmur.sqlite\",\r\n \"logfile\": \"\/home\/container\/murmur.log\",\r\n \"port\": \"{{server.allocations.default.port}}\",\r\n \"host\": \"\",\r\n \"users\": \"{{server.environment.MAX_USERS}}\"\r\n }\r\n }\r\n}", "startup": "{\r\n \"done\": \"Server listening on\"\r\n}", "logs": "{}", "stop": "^C" diff --git a/database/migrations/2024_06_05_220135_update_egg_config_variables.php b/database/migrations/2024_06_05_220135_update_egg_config_variables.php new file mode 100644 index 0000000000..d8c60d3f92 --- /dev/null +++ b/database/migrations/2024_06_05_220135_update_egg_config_variables.php @@ -0,0 +1,92 @@ +get(); + + foreach ($eggs as $egg) { + $updatedPort = str_replace( + 'server.build.default.port', + 'server.allocations.default.port', + $egg->config_files + ); + + if ($updatedPort !== $egg->config_files) { + $egg->config_files = $updatedPort; + echo "Processed Port update with ID: {$egg->name}\n"; + } + + $updatedIp = str_replace( + 'server.build.default.ip', + 'server.allocations.default.ip', + $egg->config_files + ); + + if ($updatedIp !== $egg->config_files) { + $egg->config_files = $updatedIp; + echo "Processed IP update with ID: {$egg->name}\n"; + } + + $updatedEnv = str_replace( + 'server.build.env.', + 'server.environment.', + $egg->config_files + ); + + if ($updatedEnv !== $egg->config_files) { + $egg->config_files = $updatedEnv; + echo "Processed ENV update with ID: {$egg->name}\n"; + } + + DB::table('eggs') + ->where('id', $egg->id) + ->update(['config_files' => $egg->config_files]); + } + } + + public function down(): void + { + $eggs = DB::table('eggs')->get(); + + foreach ($eggs as $egg) { + $revertedEnv = str_replace( + 'server.environment.', + 'server.build.env.', + $egg->config_files + ); + + if ($revertedEnv !== $egg->config_files) { + $egg->config_files = $revertedEnv; + } + + $revertedIp = str_replace( + 'server.allocations.default.ip', + 'server.build.default.ip', + $egg->config_files + ); + + if ($revertedIp !== $egg->config_files) { + $egg->config_files = $revertedIp; + } + + $revertedPort = str_replace( + 'server.allocations.default.port', + 'server.build.default.port', + $egg->config_files + ); + + if ($revertedPort !== $egg->config_files) { + $egg->config_files = $revertedPort; + } + + DB::table('eggs') + ->where('id', $egg->id) + ->update(['config_files' => $egg->config_files]); + } + } +}; From cc1ac1eba1c4abe734380c378891b75b289d46d0 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Fri, 7 Jun 2024 23:31:34 +0200 Subject: [PATCH 10/15] Allow importing eggs via url (#344) * allow importing eggs via url * refactor * run pint * turn back into one button * fix empty check * small cleanup * removed container for tabs * Update URL function * Use sys temp --------- Co-authored-by: notCharles --- .../Resources/EggResource/Pages/ListEggs.php | 54 ++++++++++++--- .../Admin/Eggs/EggShareController.php | 4 +- .../Eggs/Sharing/EggImporterService.php | 18 ++++- .../Eggs/Sharing/EggUpdateImporterService.php | 19 +++++- composer.json | 1 + composer.lock | 65 ++++++++++++++++++- database/Seeders/EggSeeder.php | 4 +- 7 files changed, 149 insertions(+), 16 deletions(-) diff --git a/app/Filament/Resources/EggResource/Pages/ListEggs.php b/app/Filament/Resources/EggResource/Pages/ListEggs.php index 5a0d649f87..71adf94d72 100644 --- a/app/Filament/Resources/EggResource/Pages/ListEggs.php +++ b/app/Filament/Resources/EggResource/Pages/ListEggs.php @@ -8,6 +8,7 @@ use Exception; use Filament\Actions; use Filament\Forms; +use Filament\Forms\Components\Tabs; use Filament\Notifications\Notification; use Filament\Resources\Pages\ListRecords; use Filament\Tables\Table; @@ -62,21 +63,58 @@ protected function getHeaderActions(): array Actions\Action::make('import') ->label('Import') ->form([ - Forms\Components\FileUpload::make('egg') - ->acceptedFileTypes(['application/json']) - ->storeFiles(false) - ->multiple(), + Tabs::make('Tabs') + ->tabs([ + Tabs\Tab::make('From File') + ->icon('tabler-file-upload') + ->schema([ + Forms\Components\FileUpload::make('egg') + ->label('Egg') + ->hint('This should be the json file ( egg-minecraft.json )') + ->acceptedFileTypes(['application/json']) + ->storeFiles(false) + ->multiple(), + ]), + Tabs\Tab::make('From URL') + ->icon('tabler-world-upload') + ->schema([ + Forms\Components\TextInput::make('url') + ->label('URL') + ->hint('This URL should point to a single json file') + ->url(), + ]), + ]) + ->contained(false), + ]) ->action(function (array $data): void { - /** @var TemporaryUploadedFile $eggFile */ - $eggFile = $data['egg']; /** @var EggImporterService $eggImportService */ $eggImportService = resolve(EggImporterService::class); - foreach ($eggFile as $file) { + if (!empty($data['egg'])) { + /** @var TemporaryUploadedFile[] $eggFile */ + $eggFile = $data['egg']; + + foreach ($eggFile as $file) { + try { + $eggImportService->fromFile($file); + } catch (Exception $exception) { + Notification::make() + ->title('Import Failed') + ->danger() + ->send(); + + report($exception); + + return; + } + } + } + + if (!empty($data['url'])) { try { - $eggImportService->handle($file); + $eggImportService->fromUrl($data['url']); } catch (Exception $exception) { Notification::make() ->title('Import Failed') diff --git a/app/Http/Controllers/Admin/Eggs/EggShareController.php b/app/Http/Controllers/Admin/Eggs/EggShareController.php index ffec4aad56..e938403dbe 100644 --- a/app/Http/Controllers/Admin/Eggs/EggShareController.php +++ b/app/Http/Controllers/Admin/Eggs/EggShareController.php @@ -46,7 +46,7 @@ public function export(Egg $egg): Response */ public function import(EggImportFormRequest $request): RedirectResponse { - $egg = $this->importerService->handle($request->file('import_file')); + $egg = $this->importerService->fromFile($request->file('import_file')); $this->alert->success(trans('admin/eggs.notices.imported'))->flash(); return redirect()->route('admin.eggs.view', ['egg' => $egg->id]); @@ -61,7 +61,7 @@ public function import(EggImportFormRequest $request): RedirectResponse */ public function update(EggImportFormRequest $request, Egg $egg): RedirectResponse { - $this->updateImporterService->handle($egg, $request->file('import_file')); + $this->updateImporterService->fromFile($egg, $request->file('import_file')); $this->alert->success(trans('admin/eggs.notices.updated_via_import'))->flash(); return redirect()->route('admin.eggs.view', ['egg' => $egg]); diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index 23ef0c8194..5c94ec2fa6 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -9,6 +9,7 @@ use App\Models\EggVariable; use Illuminate\Database\ConnectionInterface; use App\Services\Eggs\EggParserService; +use Spatie\TemporaryDirectory\TemporaryDirectory; class EggImporterService { @@ -21,7 +22,7 @@ public function __construct(protected ConnectionInterface $connection, protected * * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable */ - public function handle(UploadedFile $file): Egg + public function fromFile(UploadedFile $file): Egg { $parsed = $this->parser->handle($file); @@ -46,4 +47,19 @@ public function handle(UploadedFile $file): Egg }); } + /** + * Take an url and parse it into a new egg. + * + * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable + */ + public function fromUrl(string $url): Egg + { + $info = pathinfo($url); + $tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed(); + $tmpPath = $tmpDir->path($info['basename']); + + file_put_contents($tmpPath, file_get_contents($url)); + + return $this->fromFile(new UploadedFile($tmpPath, $info['basename'], 'application/json')); + } } diff --git a/app/Services/Eggs/Sharing/EggUpdateImporterService.php b/app/Services/Eggs/Sharing/EggUpdateImporterService.php index f2123e1f46..a441079e56 100644 --- a/app/Services/Eggs/Sharing/EggUpdateImporterService.php +++ b/app/Services/Eggs/Sharing/EggUpdateImporterService.php @@ -8,6 +8,7 @@ use App\Models\EggVariable; use Illuminate\Database\ConnectionInterface; use App\Services\Eggs\EggParserService; +use Spatie\TemporaryDirectory\TemporaryDirectory; class EggUpdateImporterService { @@ -23,7 +24,7 @@ public function __construct(protected ConnectionInterface $connection, protected * * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable */ - public function handle(Egg $egg, UploadedFile $file): Egg + public function fromFile(Egg $egg, UploadedFile $file): Egg { $parsed = $this->parser->handle($file); @@ -47,4 +48,20 @@ public function handle(Egg $egg, UploadedFile $file): Egg return $egg->refresh(); }); } + + /** + * Update an existing Egg using an url. + * + * @throws \App\Exceptions\Service\InvalidFileUploadException|\Throwable + */ + public function fromUrl(Egg $egg, string $url): Egg + { + $info = pathinfo($url); + $tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed(); + $tmpPath = $tmpDir->path($info['basename']); + + file_put_contents($tmpPath, file_get_contents($url)); + + return $this->fromFile($egg, new UploadedFile($tmpPath, $info['basename'], 'application/json')); + } } diff --git a/composer.json b/composer.json index 45213c015d..399b42278f 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "s1lentium/iptools": "~1.2.0", "spatie/laravel-fractal": "^6.2", "spatie/laravel-query-builder": "^5.8.1", + "spatie/temporary-directory": "^2.2", "symfony/http-client": "^7.1", "symfony/mailgun-mailer": "^7.1", "symfony/postmark-mailer": "^7.0.7", diff --git a/composer.lock b/composer.lock index 5a5c535365..3b1006ea40 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8feeafbeb16044bd6716510a73393fc0", + "content-hash": "bf44faee3aae2b1d4c1b57893c1aba98", "packages": [ { "name": "abdelhamiderrahmouni/filament-monaco-editor", @@ -6990,6 +6990,67 @@ ], "time": "2024-05-10T08:19:35+00:00" }, + { + "name": "spatie/temporary-directory", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/temporary-directory.git", + "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", + "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\TemporaryDirectory\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily create, use and destroy temporary directories", + "homepage": "https://github.com/spatie/temporary-directory", + "keywords": [ + "php", + "spatie", + "temporary-directory" + ], + "support": { + "issues": "https://github.com/spatie/temporary-directory/issues", + "source": "https://github.com/spatie/temporary-directory/tree/2.2.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-12-25T11:46:58+00:00" + }, { "name": "symfony/clock", "version": "v7.0.7", @@ -13096,5 +13157,5 @@ "ext-zip": "*" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/database/Seeders/EggSeeder.php b/database/Seeders/EggSeeder.php index efdfc8f04c..afcb55a6dc 100644 --- a/database/Seeders/EggSeeder.php +++ b/database/Seeders/EggSeeder.php @@ -75,10 +75,10 @@ protected function parseEggFiles($name) ->first(); if ($egg instanceof Egg) { - $this->updateImporterService->handle($egg, $file); + $this->updateImporterService->fromFile($egg, $file); $this->command->info('Updated ' . $decoded['name']); } else { - $this->importerService->handle($file); + $this->importerService->fromFile($file); $this->command->comment('Created ' . $decoded['name']); } } From 7ee52affb2136b62e29a27b8baa53258c061382a Mon Sep 17 00:00:00 2001 From: notCharles Date: Fri, 7 Jun 2024 17:38:58 -0400 Subject: [PATCH 11/15] Update token rotation --- app/Services/Nodes/NodeUpdateService.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php index 708605b191..8377261af9 100644 --- a/app/Services/Nodes/NodeUpdateService.php +++ b/app/Services/Nodes/NodeUpdateService.php @@ -27,15 +27,22 @@ public function __construct( */ public function handle(Node $node, array $data, bool $resetToken = false): Node { + $data['id'] = $node->id; + if ($resetToken) { $data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH); $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); } - [$node, $exception] = $this->connection->transaction(function () use ($data, $node) { - $node->forceFill($data)->save(); + [$updated, $exception] = $this->connection->transaction(function () use ($data, $node) { + /** @var \App\Models\Node $updated */ + $updated = $node->replicate(); + $updated->exists = true; + $updated->forceFill($data)->save(); try { - $this->configurationRepository->setNode($node)->update($node); + $node->fqdn = $updated->fqdn; + + $this->configurationRepository->setNode($node)->update($updated); } catch (DaemonConnectionException $exception) { logger()->warning($exception, ['node_id' => $node->id]); @@ -45,16 +52,16 @@ public function handle(Node $node, array $data, bool $resetToken = false): Node // // This avoids issues with proxies such as Cloudflare which will see daemon as offline and then // inject their own response pages, causing this logic to get fucked up. - return [$node, true]; + return [$updated, true]; } - return [$node, false]; + return [$updated, false]; }); if ($exception) { throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); } - return $node; + return $updated; } } From 93f059025cf2fe10fca54041f8dd4184a781f766 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Sat, 8 Jun 2024 00:38:46 +0200 Subject: [PATCH 12/15] show update info on dashboard, show git commit (when using git) --- app/Console/Commands/InfoCommand.php | 2 +- app/Filament/Pages/Dashboard.php | 15 +++++++- app/Providers/AppServiceProvider.php | 34 +++---------------- .../Helpers/SoftwareVersionService.php | 32 +++++++++++++++-- lang/en/dashboard/index.php | 4 +++ .../views/filament/pages/dashboard.blade.php | 18 +++++++++- 6 files changed, 70 insertions(+), 35 deletions(-) diff --git a/app/Console/Commands/InfoCommand.php b/app/Console/Commands/InfoCommand.php index 2ae8079e93..53b1dd8894 100644 --- a/app/Console/Commands/InfoCommand.php +++ b/app/Console/Commands/InfoCommand.php @@ -26,7 +26,7 @@ public function handle(): void { $this->output->title('Version Information'); $this->table([], [ - ['Panel Version', config('app.version')], + ['Panel Version', $this->versionService->versionData()['version']], ['Latest Version', $this->versionService->getPanel()], ['Up-to-Date', $this->versionService->isLatestPanel() ? 'Yes' : $this->formatText('No', 'bg=red')], ], 'compact'); diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 4b57da696f..f29587f970 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -7,6 +7,7 @@ use App\Models\Node; use App\Models\Server; use App\Models\User; +use App\Services\Helpers\SoftwareVersionService; use Filament\Actions\CreateAction; use Filament\Pages\Page; @@ -29,8 +30,14 @@ public function getTitle(): string public function getViewData(): array { + /** @var SoftwareVersionService $softwareVersionService */ + $softwareVersionService = app(SoftwareVersionService::class); + return [ 'inDevelopment' => config('app.version') === 'canary', + 'version' => $softwareVersionService->versionData()['version'], + 'latestVersion' => $softwareVersionService->getPanel(), + 'isLatest' => $softwareVersionService->isLatestPanel(), 'eggsCount' => Egg::query()->count(), 'nodesList' => ListNodes::getUrl(), 'nodesCount' => Node::query()->count(), @@ -43,6 +50,12 @@ public function getViewData(): array ->icon('tabler-brand-github') ->url('https://github.com/pelican-dev/panel/discussions', true), ], + 'updateActions' => [ + CreateAction::make() + ->label('Read Documentation') + ->icon('tabler-clipboard-text') + ->url('https://pelican.dev/docs/panel/update', true), + ], 'nodeActions' => [ CreateAction::make() ->label(trans('dashboard/index.sections.intro-first-node.button_label')) @@ -53,7 +66,7 @@ public function getViewData(): array CreateAction::make() ->label(trans('dashboard/index.sections.intro-support.button_donate')) ->icon('tabler-cash') - ->url('https://pelican.dev/donate', true) + ->url($softwareVersionService->getDonations(), true) ->color('success'), ], 'helpActions' => [ diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1c0deb6221..5817890b1b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -6,6 +6,7 @@ use App\Models; use App\Models\ApiKey; use App\Models\Node; +use App\Services\Helpers\SoftwareVersionService; use Dedoc\Scramble\Scramble; use Dedoc\Scramble\Support\Generator\OpenApi; use Dedoc\Scramble\Support\Generator\SecurityScheme; @@ -30,8 +31,9 @@ public function boot(): void { Schema::defaultStringLength(191); - View::share('appVersion', $this->versionData()['version'] ?? 'undefined'); - View::share('appIsGit', $this->versionData()['is_git'] ?? false); + $versionData = app(SoftwareVersionService::class)->versionData(); + View::share('appVersion', $versionData['version'] ?? 'undefined'); + View::share('appIsGit', $versionData['is_git'] ?? false); Paginator::useBootstrap(); @@ -96,34 +98,6 @@ public function register(): void Scramble::ignoreDefaultRoutes(); } - /** - * Return version information for the footer. - */ - protected function versionData(): array - { - return cache()->remember('git-version', 5, function () { - if (file_exists(base_path('.git/HEAD'))) { - $head = explode(' ', file_get_contents(base_path('.git/HEAD'))); - - if (array_key_exists(1, $head)) { - $path = base_path('.git/' . trim($head[1])); - } - } - - if (isset($path) && file_exists($path)) { - return [ - 'version' => substr(file_get_contents($path), 0, 8), - 'is_git' => true, - ]; - } - - return [ - 'version' => config('app.version'), - 'is_git' => false, - ]; - }); - } - public function bootAuth(): void { Sanctum::usePersonalAccessTokenModel(ApiKey::class); diff --git a/app/Services/Helpers/SoftwareVersionService.php b/app/Services/Helpers/SoftwareVersionService.php index b5f3a9b128..21402a4907 100644 --- a/app/Services/Helpers/SoftwareVersionService.php +++ b/app/Services/Helpers/SoftwareVersionService.php @@ -49,6 +49,14 @@ public function getDiscord(): string return Arr::get(self::$result, 'discord') ?? 'https://pelican.dev/discord'; } + /** + * Get the donation URL. + */ + public function getDonations(): string + { + return Arr::get(self::$result, 'donate') ?? 'https://pelican.dev/donate'; + } + /** * Determine if the current version of the panel is the latest. */ @@ -93,8 +101,28 @@ protected function cacheVersionData(): array }); } - public function getDonations(): string + public function versionData(): array { - return 'https://github.com'; + return cache()->remember('git-version', 5, function () { + if (file_exists(base_path('.git/HEAD'))) { + $head = explode(' ', file_get_contents(base_path('.git/HEAD'))); + + if (array_key_exists(1, $head)) { + $path = base_path('.git/' . trim($head[1])); + } + } + + if (isset($path) && file_exists($path)) { + return [ + 'version' => 'canary (' . substr(file_get_contents($path), 0, 8) . ')', + 'is_git' => true, + ]; + } + + return [ + 'version' => config('app.version'), + 'is_git' => false, + ]; + }); } } diff --git a/lang/en/dashboard/index.php b/lang/en/dashboard/index.php index 1dff4f01a7..2c201a30b6 100644 --- a/lang/en/dashboard/index.php +++ b/lang/en/dashboard/index.php @@ -19,6 +19,10 @@ 'button_issues' => 'Create Issue', 'button_features' => 'Discuss Features', ], + 'intro-update' => [ + 'heading' => 'Update available', + 'content' => ':latestVersion is available! Read our documentation to update your Panel.', + ], 'intro-first-node' => [ 'heading' => 'No Nodes Detected', 'content' => "It looks like you don't have any Nodes set up yet, but don't worry because you click the action button to create your first one!", diff --git a/resources/views/filament/pages/dashboard.blade.php b/resources/views/filament/pages/dashboard.blade.php index 527a48aba7..c57178c081 100644 --- a/resources/views/filament/pages/dashboard.blade.php +++ b/resources/views/filament/pages/dashboard.blade.php @@ -4,7 +4,7 @@ :actions="$this->getCachedHeaderActions()" :breadcrumbs="filament()->hasBreadcrumbs() ? $this->getBreadcrumbs() : []" :heading=" trans('dashboard/index.heading')" - :subheading="trans('strings.version', ['version' => config('app.version')])" + :subheading="trans('strings.version', ['version' => $version])" >

{{ trans('dashboard/index.expand_sections') }}

@@ -30,6 +30,22 @@ @endif + @if (!$isLatest) + + {{ trans('dashboard/index.sections.intro-update.heading') }} + +

{{ trans('dashboard/index.sections.intro-update.content', ['latestVersion' => $latestVersion]) }}

+ +
+ @endif + {{-- No Nodes Created --}} @if ($nodesCount <= 0) Date: Sat, 8 Jun 2024 00:43:25 +0200 Subject: [PATCH 13/15] update colors --- app/Filament/Pages/Dashboard.php | 3 ++- resources/views/filament/pages/dashboard.blade.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index f29587f970..af76c00f54 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -54,7 +54,8 @@ public function getViewData(): array CreateAction::make() ->label('Read Documentation') ->icon('tabler-clipboard-text') - ->url('https://pelican.dev/docs/panel/update', true), + ->url('https://pelican.dev/docs/panel/update', true) + ->color('warning'), ], 'nodeActions' => [ CreateAction::make() diff --git a/resources/views/filament/pages/dashboard.blade.php b/resources/views/filament/pages/dashboard.blade.php index c57178c081..ef7f539bd9 100644 --- a/resources/views/filament/pages/dashboard.blade.php +++ b/resources/views/filament/pages/dashboard.blade.php @@ -33,7 +33,7 @@ @if (!$isLatest) Date: Fri, 7 Jun 2024 22:18:12 -0400 Subject: [PATCH 14/15] Whoops.... Fix env replacement... Somehow this got copy pasta'd and yeh.... its not right... --- app/Services/Eggs/EggParserService.php | 3 +- .../2024_06_08_020904_refix_egg_variables.php | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2024_06_08_020904_refix_egg_variables.php diff --git a/app/Services/Eggs/EggParserService.php b/app/Services/Eggs/EggParserService.php index d780a9f94f..f351668988 100644 --- a/app/Services/Eggs/EggParserService.php +++ b/app/Services/Eggs/EggParserService.php @@ -17,7 +17,8 @@ class EggParserService 'server.build.default.port' => 'server.allocations.default.port', 'server.build.env.SERVER_MEMORY' => 'server.build.memory_limit', 'server.build.memory' => 'server.build.memory_limit', - 'server.build.env' => 'server.build.environment', + 'server.build.env' => 'server.environment', + 'server.build.environment' => 'server.environment', ]; /** diff --git a/database/migrations/2024_06_08_020904_refix_egg_variables.php b/database/migrations/2024_06_08_020904_refix_egg_variables.php new file mode 100644 index 0000000000..6cc31eca4b --- /dev/null +++ b/database/migrations/2024_06_08_020904_refix_egg_variables.php @@ -0,0 +1,40 @@ +get(); + + foreach ($eggs as $egg) { + $updatedEnv = str_replace( + 'server.build.environment.', + 'server.environment.', + $egg->config_files + ); + + if ($updatedEnv !== $egg->config_files) { + $egg->config_files = $updatedEnv; + echo "Processed ENV update with ID: {$egg->name}\n"; + } + + DB::table('eggs') + ->where('id', $egg->id) + ->update(['config_files' => $egg->config_files]); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // We shouldn't revert this... + } +}; From 8080435eca28c504ff8f1982f32fba70c0845374 Mon Sep 17 00:00:00 2001 From: notCharles Date: Fri, 7 Jun 2024 22:28:53 -0400 Subject: [PATCH 15/15] It's Late... --- app/Services/Eggs/EggParserService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Eggs/EggParserService.php b/app/Services/Eggs/EggParserService.php index f351668988..22025dc5ce 100644 --- a/app/Services/Eggs/EggParserService.php +++ b/app/Services/Eggs/EggParserService.php @@ -17,8 +17,8 @@ class EggParserService 'server.build.default.port' => 'server.allocations.default.port', 'server.build.env.SERVER_MEMORY' => 'server.build.memory_limit', 'server.build.memory' => 'server.build.memory_limit', - 'server.build.env' => 'server.environment', - 'server.build.environment' => 'server.environment', + 'server.build.env.' => 'server.environment.', + 'server.build.environment.' => 'server.environment.', ]; /**