mirror of
https://github.com/pelican-dev/panel.git
synced 2025-11-09 18:19:27 +01:00
Add wings diagnostics retrieving to Edit Node page (#1865)
Co-authored-by: Boy132 <mail@boy132.de>
This commit is contained in:
parent
49f24e37b6
commit
6ed84b5584
@ -4,7 +4,7 @@ namespace App\Filament\Admin\Resources\Nodes\Pages;
|
|||||||
|
|
||||||
use App\Filament\Admin\Resources\Nodes\NodeResource;
|
use App\Filament\Admin\Resources\Nodes\NodeResource;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||||
use App\Services\Helpers\SoftwareVersionService;
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
use App\Services\Nodes\NodeAutoDeployService;
|
use App\Services\Nodes\NodeAutoDeployService;
|
||||||
use App\Services\Nodes\NodeUpdateService;
|
use App\Services\Nodes\NodeUpdateService;
|
||||||
@ -14,6 +14,8 @@ use Exception;
|
|||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Actions\DeleteAction;
|
use Filament\Actions\DeleteAction;
|
||||||
use Filament\Forms\Components\Hidden;
|
use Filament\Forms\Components\Hidden;
|
||||||
|
use Filament\Forms\Components\Slider;
|
||||||
|
use Filament\Forms\Components\Slider\Enums\PipsMode;
|
||||||
use Filament\Forms\Components\TagsInput;
|
use Filament\Forms\Components\TagsInput;
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
@ -25,6 +27,7 @@ use Filament\Resources\Pages\EditRecord;
|
|||||||
use Filament\Schemas\Components\Actions;
|
use Filament\Schemas\Components\Actions;
|
||||||
use Filament\Schemas\Components\Fieldset;
|
use Filament\Schemas\Components\Fieldset;
|
||||||
use Filament\Schemas\Components\Grid;
|
use Filament\Schemas\Components\Grid;
|
||||||
|
use Filament\Schemas\Components\Section;
|
||||||
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
|
||||||
use Filament\Schemas\Components\Tabs;
|
use Filament\Schemas\Components\Tabs;
|
||||||
use Filament\Schemas\Components\Tabs\Tab;
|
use Filament\Schemas\Components\Tabs\Tab;
|
||||||
@ -33,7 +36,10 @@ use Filament\Schemas\Components\Utilities\Set;
|
|||||||
use Filament\Schemas\Components\View;
|
use Filament\Schemas\Components\View;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
use Filament\Support\Enums\Alignment;
|
use Filament\Support\Enums\Alignment;
|
||||||
|
use Filament\Support\Enums\IconSize;
|
||||||
|
use Filament\Support\RawJs;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\HtmlString;
|
use Illuminate\Support\HtmlString;
|
||||||
use Phiki\Grammar\Grammar;
|
use Phiki\Grammar\Grammar;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -45,13 +51,13 @@ class EditNode extends EditRecord
|
|||||||
|
|
||||||
protected static string $resource = NodeResource::class;
|
protected static string $resource = NodeResource::class;
|
||||||
|
|
||||||
private DaemonConfigurationRepository $daemonConfigurationRepository;
|
private DaemonSystemRepository $daemonSystemRepository;
|
||||||
|
|
||||||
private NodeUpdateService $nodeUpdateService;
|
private NodeUpdateService $nodeUpdateService;
|
||||||
|
|
||||||
public function boot(DaemonConfigurationRepository $daemonConfigurationRepository, NodeUpdateService $nodeUpdateService): void
|
public function boot(DaemonSystemRepository $daemonSystemRepository, NodeUpdateService $nodeUpdateService): void
|
||||||
{
|
{
|
||||||
$this->daemonConfigurationRepository = $daemonConfigurationRepository;
|
$this->daemonSystemRepository = $daemonSystemRepository;
|
||||||
$this->nodeUpdateService = $nodeUpdateService;
|
$this->nodeUpdateService = $nodeUpdateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,6 +630,154 @@ class EditNode extends EditRecord
|
|||||||
])->fullWidth(),
|
])->fullWidth(),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
Tab::make('diagnostics')
|
||||||
|
->label(trans('admin/node.tabs.diagnostics'))
|
||||||
|
->icon('tabler-heart-search')
|
||||||
|
->schema([
|
||||||
|
Section::make('diag')
|
||||||
|
->heading(trans('admin/node.tabs.diagnostics'))
|
||||||
|
->columnSpanFull()
|
||||||
|
->columns(4)
|
||||||
|
->disabled(fn (Get $get) => $get('pulled'))
|
||||||
|
->headerActions([
|
||||||
|
Action::make('pull')
|
||||||
|
->label(trans('admin/node.diagnostics.pull'))
|
||||||
|
->icon('tabler-cloud-download')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||||
|
->hidden(fn (Get $get) => $get('pulled'))
|
||||||
|
->action(function (Get $get, Set $set, Node $node) {
|
||||||
|
$includeEndpoints = $get('include_endpoints') ?? true;
|
||||||
|
$includeLogs = $get('include_logs') ?? true;
|
||||||
|
$logLines = $get('log_lines') ?? 200;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->daemonSystemRepository->setNode($node)->getDiagnostics($logLines, $includeEndpoints, $includeLogs);
|
||||||
|
|
||||||
|
if ($response->status() === 404) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.404'))
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set('pulled', true);
|
||||||
|
$set('uploaded', false);
|
||||||
|
$set('log', $response->body());
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.logs_pulled'))
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
} catch (ConnectionException $e) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.error_connecting', ['node' => $node->name]))
|
||||||
|
->body($e->getMessage())
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Action::make('upload')
|
||||||
|
->label(trans('admin/node.diagnostics.upload'))
|
||||||
|
->visible(fn (Get $get) => $get('pulled') ?? false)
|
||||||
|
->icon('tabler-cloud-upload')->iconButton()->iconSize(IconSize::ExtraLarge)
|
||||||
|
->action(function (Get $get, Set $set) {
|
||||||
|
try {
|
||||||
|
$response = Http::asMultipart()->post('https://logs.pelican.dev', [
|
||||||
|
[
|
||||||
|
'name' => 'c',
|
||||||
|
'contents' => $get('log'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'e',
|
||||||
|
'contents' => '14d',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response->failed()) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.upload_failed'))
|
||||||
|
->body(fn () => $response->status() . ' - ' . $response->body())
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $response->json();
|
||||||
|
$url = $data['url'];
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.logs_uploaded'))
|
||||||
|
->body("{$url}")
|
||||||
|
->success()
|
||||||
|
->actions([
|
||||||
|
Action::make('viewLogs')
|
||||||
|
->label(trans('admin/node.diagnostics.view_logs'))
|
||||||
|
->url($url)
|
||||||
|
->openUrlInNewTab(true),
|
||||||
|
])
|
||||||
|
->persistent()
|
||||||
|
->send();
|
||||||
|
$set('log', $url);
|
||||||
|
$set('pulled', false);
|
||||||
|
$set('uploaded', true);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/node.diagnostics.upload_failed'))
|
||||||
|
->body($e->getMessage())
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Action::make('clear')
|
||||||
|
->label(trans('admin/node.diagnostics.clear'))
|
||||||
|
->visible(fn (Get $get) => $get('pulled') ?? false)
|
||||||
|
->icon('tabler-trash')->iconButton()->iconSize(IconSize::ExtraLarge)->color('danger')
|
||||||
|
->action(function (Get $get, Set $set) {
|
||||||
|
$set('pulled', false);
|
||||||
|
$set('uploaded', false);
|
||||||
|
$set('log', null);
|
||||||
|
$this->refresh();
|
||||||
|
}
|
||||||
|
),
|
||||||
|
])
|
||||||
|
->schema([
|
||||||
|
ToggleButtons::make('include_endpoints')
|
||||||
|
->hintIcon('tabler-question-mark')->inline()
|
||||||
|
->hintIconTooltip(trans('admin/node.diagnostics.include_endpoints_hint'))
|
||||||
|
->formatStateUsing(fn () => 1)
|
||||||
|
->boolean(),
|
||||||
|
ToggleButtons::make('include_logs')
|
||||||
|
->live()
|
||||||
|
->hintIcon('tabler-question-mark')->inline()
|
||||||
|
->hintIconTooltip(trans('admin/node.diagnostics.include_logs_hint'))
|
||||||
|
->formatStateUsing(fn () => 1)
|
||||||
|
->boolean(),
|
||||||
|
Slider::make('log_lines')
|
||||||
|
->columnSpan(2)
|
||||||
|
->hiddenLabel()
|
||||||
|
->live()
|
||||||
|
->tooltips(RawJs::make(<<<'JS'
|
||||||
|
`${$value} lines`
|
||||||
|
JS))
|
||||||
|
->visible(fn (Get $get) => $get('include_logs'))
|
||||||
|
->range(minValue: 100, maxValue: 500)
|
||||||
|
->pips(PipsMode::Steps, density: 10)
|
||||||
|
->step(50)
|
||||||
|
->formatStateUsing(fn () => 200)
|
||||||
|
->fillTrack(),
|
||||||
|
Hidden::make('pulled'),
|
||||||
|
Hidden::make('uploaded'),
|
||||||
|
]),
|
||||||
|
Textarea::make('log')
|
||||||
|
->hiddenLabel()
|
||||||
|
->columnSpanFull()
|
||||||
|
->rows(35)
|
||||||
|
->visible(fn (Get $get) => ($get('pulled') ?? false) || ($get('uploaded') ?? false)),
|
||||||
|
]),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -681,7 +835,7 @@ class EditNode extends EditRecord
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if ($changed) {
|
if ($changed) {
|
||||||
$this->daemonConfigurationRepository->setNode($node)->update($node);
|
$this->daemonSystemRepository->setNode($node)->update($node);
|
||||||
}
|
}
|
||||||
parent::getSavedNotification()?->send();
|
parent::getSavedNotification()?->send();
|
||||||
} catch (ConnectionException) {
|
} catch (ConnectionException) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Contracts\Validatable;
|
use App\Contracts\Validatable;
|
||||||
use App\Exceptions\Service\HasActiveServersException;
|
use App\Exceptions\Service\HasActiveServersException;
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||||
use App\Traits\HasValidation;
|
use App\Traits\HasValidation;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
@ -316,7 +316,7 @@ class Node extends Model implements Validatable
|
|||||||
{
|
{
|
||||||
return once(function () {
|
return once(function () {
|
||||||
try {
|
try {
|
||||||
return (new DaemonConfigurationRepository())
|
return (new DaemonSystemRepository())
|
||||||
->setNode($this)
|
->setNode($this)
|
||||||
->getSystemInformation();
|
->getSystemInformation();
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use App\Models\Node;
|
|||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Http\Client\Response;
|
use Illuminate\Http\Client\Response;
|
||||||
|
|
||||||
class DaemonConfigurationRepository extends DaemonRepository
|
class DaemonSystemRepository extends DaemonRepository
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns system information from the daemon instance.
|
* Returns system information from the daemon instance.
|
||||||
@ -30,6 +30,23 @@ class DaemonConfigurationRepository extends DaemonRepository
|
|||||||
})->json();
|
})->json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve diagnostics from the daemon for the current node.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @throws ConnectionException
|
||||||
|
*/
|
||||||
|
public function getDiagnostics(int $lines, bool $includeEndpoints, bool $includeLogs): Response
|
||||||
|
{
|
||||||
|
return $this->getHttpClient()
|
||||||
|
->timeout(5)
|
||||||
|
->get('/api/diagnostics', [
|
||||||
|
'log_lines' => $lines,
|
||||||
|
'include_endpoints' => $includeEndpoints ? 'true' : 'false',
|
||||||
|
'include_logs' => $includeLogs ? 'true' : 'false',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the configuration information for a daemon. Updates the information for
|
* Updates the configuration information for a daemon. Updates the information for
|
||||||
* this instance using a passed-in model. This allows us to change plenty of information
|
* this instance using a passed-in model. This allows us to change plenty of information
|
||||||
@ -4,7 +4,7 @@ namespace App\Services\Nodes;
|
|||||||
|
|
||||||
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonSystemRepository;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -17,7 +17,7 @@ class NodeUpdateService
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ConnectionInterface $connection,
|
private ConnectionInterface $connection,
|
||||||
private DaemonConfigurationRepository $configurationRepository,
|
private DaemonSystemRepository $configurationRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,6 +10,7 @@ return [
|
|||||||
'basic_settings' => 'Basic Settings',
|
'basic_settings' => 'Basic Settings',
|
||||||
'advanced_settings' => 'Advanced Settings',
|
'advanced_settings' => 'Advanced Settings',
|
||||||
'config_file' => 'Configuration File',
|
'config_file' => 'Configuration File',
|
||||||
|
'diagnostics' => 'Diagnostics',
|
||||||
],
|
],
|
||||||
'table' => [
|
'table' => [
|
||||||
'health' => 'Health',
|
'health' => 'Health',
|
||||||
@ -117,6 +118,24 @@ return [
|
|||||||
'error_connecting_description' => 'The configuration could not be automatically updated on Wings, you will need to manually update the configuration file.',
|
'error_connecting_description' => 'The configuration could not be automatically updated on Wings, you will need to manually update the configuration file.',
|
||||||
'allocation' => 'Allocation',
|
'allocation' => 'Allocation',
|
||||||
|
|
||||||
|
'diagnostics' => [
|
||||||
|
'header' => 'Node Diagnostics',
|
||||||
|
'include_endpoints' => 'Include Endpoints',
|
||||||
|
'include_endpoints_hint' => 'Including endpoints will show panel urls within the logs and NOT obscure them.',
|
||||||
|
'include_logs' => 'Include Logs',
|
||||||
|
'include_logs_hint' => 'Including logs will show recent logs and help track down possible issues.',
|
||||||
|
'run_diagnostics' => 'Run Diagnostics',
|
||||||
|
'upload_to_pelican' => 'Upload Logs',
|
||||||
|
'logs_pulled' => 'Logs Pulled!',
|
||||||
|
'logs_uploaded' => 'Logs Uploaded',
|
||||||
|
'upload_failed' => 'Logs Upload Failed',
|
||||||
|
'view_logs' => 'View Logs',
|
||||||
|
'pull' => 'Pull',
|
||||||
|
'upload' => 'Upload',
|
||||||
|
'clear' => 'Clear',
|
||||||
|
'404' => 'The requested diagnostic report could not be found. Make sure wings is up to date and try again.',
|
||||||
|
],
|
||||||
|
|
||||||
'cloudflare_issue' => [
|
'cloudflare_issue' => [
|
||||||
'title' => 'Cloudflare Issue',
|
'title' => 'Cloudflare Issue',
|
||||||
'body' => 'Your Node is not accessible by Cloudflare',
|
'body' => 'Your Node is not accessible by Cloudflare',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user