mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-19 21:04:44 +02:00
Add button to view install logs (#1356)
Co-authored-by: notCharles <charles@pelican.dev>
This commit is contained in:
parent
7971dc13fc
commit
2296e41a8b
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Filament\Admin\Resources\ServerResource\Pages;
|
namespace App\Filament\Admin\Resources\ServerResource\Pages;
|
||||||
|
|
||||||
use App\Enums\ServerState;
|
use AbdelhamidErrahmouni\FilamentMonacoEditor\MonacoEditor;
|
||||||
use App\Enums\SuspendAction;
|
use App\Enums\SuspendAction;
|
||||||
use App\Filament\Admin\Resources\ServerResource;
|
use App\Filament\Admin\Resources\ServerResource;
|
||||||
use App\Filament\Admin\Resources\ServerResource\RelationManagers\AllocationsRelationManager;
|
use App\Filament\Admin\Resources\ServerResource\RelationManagers\AllocationsRelationManager;
|
||||||
@ -51,6 +51,7 @@ use Filament\Forms\Get;
|
|||||||
use Filament\Forms\Set;
|
use Filament\Forms\Set;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Resources\Pages\EditRecord;
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
use Filament\Support\Enums\Alignment;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Http\Client\ConnectionException;
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
@ -135,7 +136,39 @@ class EditServer extends EditRecord
|
|||||||
'sm' => 1,
|
'sm' => 1,
|
||||||
'md' => 1,
|
'md' => 1,
|
||||||
'lg' => 1,
|
'lg' => 1,
|
||||||
]),
|
])
|
||||||
|
->hintAction(
|
||||||
|
Action::make('view_install_log')
|
||||||
|
->label(trans('admin/server.view_install_log'))
|
||||||
|
//->visible(fn (Server $server) => $server->isFailedInstall())
|
||||||
|
->modalHeading('')
|
||||||
|
->modalSubmitAction(false)
|
||||||
|
->modalFooterActionsAlignment(Alignment::Right)
|
||||||
|
->modalCancelActionLabel(trans('filament::components/modal.actions.close.label'))
|
||||||
|
->form([
|
||||||
|
MonacoEditor::make('logs')
|
||||||
|
->hiddenLabel()
|
||||||
|
->placeholderText(trans('admin/server.no_log'))
|
||||||
|
->formatStateUsing(function (Server $server, DaemonServerRepository $serverRepository) {
|
||||||
|
try {
|
||||||
|
return $serverRepository->setServer($server)->getInstallLogs();
|
||||||
|
} catch (ConnectionException) {
|
||||||
|
Notification::make()
|
||||||
|
->title(trans('admin/server.notifications.error_connecting', ['node' => $server->node->name]))
|
||||||
|
->body(trans('admin/server.notifications.log_failed'))
|
||||||
|
->color('warning')
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
} catch (Exception) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
})
|
||||||
|
->language('shell')
|
||||||
|
->view('filament.plugins.monaco-editor-logs'),
|
||||||
|
])
|
||||||
|
),
|
||||||
|
|
||||||
Textarea::make('description')
|
Textarea::make('description')
|
||||||
->label(trans('admin/server.description'))
|
->label(trans('admin/server.description'))
|
||||||
@ -800,12 +833,12 @@ class EditServer extends EditRecord
|
|||||||
Action::make('toggleInstall')
|
Action::make('toggleInstall')
|
||||||
->label(trans('admin/server.toggle_install'))
|
->label(trans('admin/server.toggle_install'))
|
||||||
->disabled(fn (Server $server) => $server->isSuspended())
|
->disabled(fn (Server $server) => $server->isSuspended())
|
||||||
->modal(fn (Server $server) => $server->status === ServerState::InstallFailed)
|
->modal(fn (Server $server) => $server->isFailedInstall())
|
||||||
->modalHeading(trans('admin/server.toggle_install_failed_header'))
|
->modalHeading(trans('admin/server.toggle_install_failed_header'))
|
||||||
->modalDescription(trans('admin/server.toggle_install_failed_desc'))
|
->modalDescription(trans('admin/server.toggle_install_failed_desc'))
|
||||||
->modalSubmitActionLabel(trans('admin/server.reinstall'))
|
->modalSubmitActionLabel(trans('admin/server.reinstall'))
|
||||||
->action(function (ToggleInstallService $toggleService, ReinstallServerService $reinstallService, Server $server) {
|
->action(function (ToggleInstallService $toggleService, ReinstallServerService $reinstallService, Server $server) {
|
||||||
if ($server->status === ServerState::InstallFailed) {
|
if ($server->isFailedInstall()) {
|
||||||
try {
|
try {
|
||||||
$reinstallService->handle($server);
|
$reinstallService->handle($server);
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
@ -231,7 +231,12 @@ class Server extends Model implements Validatable
|
|||||||
|
|
||||||
public function isInstalled(): bool
|
public function isInstalled(): bool
|
||||||
{
|
{
|
||||||
return $this->status !== ServerState::Installing && $this->status !== ServerState::InstallFailed;
|
return $this->status !== ServerState::Installing && !$this->isFailedInstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFailedInstall(): bool
|
||||||
|
{
|
||||||
|
return $this->status === ServerState::InstallFailed || $this->status === ServerState::ReinstallFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isSuspended(): bool
|
public function isSuspended(): bool
|
||||||
|
@ -141,4 +141,12 @@ class DaemonServerRepository extends DaemonRepository
|
|||||||
'jtis' => [md5($id . $this->server->uuid)],
|
'jtis' => [md5($id . $this->server->uuid)],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getInstallLogs(): string
|
||||||
|
{
|
||||||
|
return $this->getHttpClient()
|
||||||
|
->get("/api/servers/{$this->server->uuid}/install-logs")
|
||||||
|
->throw()
|
||||||
|
->json('data');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ class ToggleInstallService
|
|||||||
{
|
{
|
||||||
public function handle(Server $server): void
|
public function handle(Server $server): void
|
||||||
{
|
{
|
||||||
if ($server->status === ServerState::InstallFailed) {
|
if ($server->isFailedInstall()) {
|
||||||
abort(500, trans('exceptions.server.marked_as_failed'));
|
abort(500, trans('exceptions.server.marked_as_failed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ return [
|
|||||||
'reinstall_modal_heading' => 'Are you sure you want to reinstall this server?',
|
'reinstall_modal_heading' => 'Are you sure you want to reinstall this server?',
|
||||||
'reinstall_modal_description' => '!! This can result in unrecoverable data loss !!',
|
'reinstall_modal_description' => '!! This can result in unrecoverable data loss !!',
|
||||||
'server_status' => 'Server Status',
|
'server_status' => 'Server Status',
|
||||||
|
'view_install_log' => 'View install log',
|
||||||
'uuid' => 'UUID',
|
'uuid' => 'UUID',
|
||||||
'node' => 'Node',
|
'node' => 'Node',
|
||||||
'short_uuid' => 'Short UUID',
|
'short_uuid' => 'Short UUID',
|
||||||
@ -100,6 +101,7 @@ return [
|
|||||||
'create_allocation' => 'Create Allocation',
|
'create_allocation' => 'Create Allocation',
|
||||||
'add_allocation' => 'Add Allocation',
|
'add_allocation' => 'Add Allocation',
|
||||||
'view' => 'View',
|
'view' => 'View',
|
||||||
|
'no_log' => 'No Log Available',
|
||||||
'tabs' => [
|
'tabs' => [
|
||||||
'information' => 'Information',
|
'information' => 'Information',
|
||||||
'egg_configuration' => 'Egg Configuration',
|
'egg_configuration' => 'Egg Configuration',
|
||||||
@ -129,5 +131,6 @@ return [
|
|||||||
'install_toggle_failed' => 'Could not toggle install status',
|
'install_toggle_failed' => 'Could not toggle install status',
|
||||||
'reinstall_started' => 'Reinstall started',
|
'reinstall_started' => 'Reinstall started',
|
||||||
'reinstall_failed' => 'Could not start reinstall',
|
'reinstall_failed' => 'Could not start reinstall',
|
||||||
|
'log_failed' => 'Could not connect to Wings to retrieve server install log.',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
184
resources/views/filament/plugins/monaco-editor-logs.blade.php
Normal file
184
resources/views/filament/plugins/monaco-editor-logs.blade.php
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
@script
|
||||||
|
<script>
|
||||||
|
$wire.on('setContent', ({ content }) => {
|
||||||
|
document.getElementById('{{ $getId() }}').editor.getModel().setValue(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
$wire.on('setLanguage', ({ lang }) => {
|
||||||
|
monaco.editor.setModelLanguage(document.getElementById('{{ $getId() }}').editor.getModel(), lang);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endscript
|
||||||
|
|
||||||
|
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field" class="overflow-hidden">
|
||||||
|
|
||||||
|
<div x-data="{
|
||||||
|
monacoContent: $wire.$entangle('{{ $getStatePath() }}'),
|
||||||
|
previewContent: '',
|
||||||
|
fullScreenModeEnabled: false,
|
||||||
|
showPreview: false,
|
||||||
|
monacoLanguage: '{{ $getLanguage() }}',
|
||||||
|
monacoPlaceholder: {{ (int) $getShowPlaceholder() }},
|
||||||
|
monacoPlaceholderText: '{{ $getPlaceholderText() }}',
|
||||||
|
monacoLoader: {{ (int) $getShowLoader() }},
|
||||||
|
monacoFontSize: '{{ $getFontSize() }}',
|
||||||
|
lineNumbersMinChars: {{ $getLineNumbersMinChars() }},
|
||||||
|
automaticLayout: {{ (int) $getAutomaticLayout() }},
|
||||||
|
monacoId: '{{ $getId() }}',
|
||||||
|
|
||||||
|
toggleFullScreenMode() {
|
||||||
|
this.fullScreenModeEnabled = !this.fullScreenModeEnabled;
|
||||||
|
this.fullScreenModeEnabled ? document.body.classList.add('overflow-hidden')
|
||||||
|
: document.body.classList.remove('overflow-hidden');
|
||||||
|
$el.style.width = this.fullScreenModeEnabled ? '100vw'
|
||||||
|
: $el.parentElement.clientWidth + 'px';
|
||||||
|
},
|
||||||
|
|
||||||
|
monacoEditor(editor){
|
||||||
|
editor.onDidChangeModelContent((e) => {
|
||||||
|
this.monacoContent = editor.getValue();
|
||||||
|
this.updatePlaceholder(editor.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.onDidBlurEditorWidget(() => {
|
||||||
|
this.updatePlaceholder(editor.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.onDidFocusEditorWidget(() => {
|
||||||
|
this.updatePlaceholder(editor.getValue());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePlaceholder: function(value) {
|
||||||
|
if (value == '') {
|
||||||
|
this.monacoPlaceholder = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.monacoPlaceholder = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
monacoEditorFocus(){
|
||||||
|
document.getElementById(this.monacoId).dispatchEvent(
|
||||||
|
new CustomEvent('monaco-editor-focused', { monacoId: this.monacoId })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
monacoEditorAddLoaderScriptToHead() {
|
||||||
|
script = document.createElement('script');
|
||||||
|
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.49.0/min/vs/loader.min.js';
|
||||||
|
document.head.appendChild(script);
|
||||||
|
},
|
||||||
|
|
||||||
|
wrapPreview(value){
|
||||||
|
return `<head>{{ $getPreviewHeadEndContent() }}</head>` +
|
||||||
|
`<body {{ $getPreviewBodyAttributes() }}>` +
|
||||||
|
`{{ $getPreviewBodyStartContent() }}` +
|
||||||
|
`${value}` +
|
||||||
|
`{{ $getPreviewBodyEndContent() }}` +
|
||||||
|
`</body>`;
|
||||||
|
},
|
||||||
|
|
||||||
|
}" x-init="
|
||||||
|
previewContent = wrapPreview(monacoContent);
|
||||||
|
$el.style.height = '500px';
|
||||||
|
$watch('fullScreenModeEnabled', value => {
|
||||||
|
if (value) {
|
||||||
|
$el.style.height = '100vh';
|
||||||
|
} else {
|
||||||
|
$el.style.height = '500px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(typeof _amdLoaderGlobal == 'undefined'){
|
||||||
|
monacoEditorAddLoaderScriptToHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
monacoLoaderInterval = setInterval(() => {
|
||||||
|
if(typeof _amdLoaderGlobal !== 'undefined'){
|
||||||
|
|
||||||
|
// Based on https://jsfiddle.net/developit/bwgkr6uq/ which works without needing service worker. Provided by loader.min.js.
|
||||||
|
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.49.0/min/vs' }});
|
||||||
|
let proxy = URL.createObjectURL(new Blob([` self.MonacoEnvironment = { baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.49.0/min' }; importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.49.0/min/vs/base/worker/workerMain.min.js');`], { type: 'text/javascript' }));
|
||||||
|
window.MonacoEnvironment = { getWorkerUrl: () => proxy };
|
||||||
|
|
||||||
|
require(['vs/editor/editor.main'], () => {
|
||||||
|
|
||||||
|
monaco.editor.defineTheme('custom', {{ $editorTheme() }});
|
||||||
|
document.getElementById(monacoId).editor = monaco.editor.create($refs.monacoEditorElement, {
|
||||||
|
value: monacoContent,
|
||||||
|
theme: localStorage.getItem('theme') === 'light' ? 'iPlastic' : 'custom',
|
||||||
|
fontSize: monacoFontSize,
|
||||||
|
lineNumbersMinChars: lineNumbersMinChars,
|
||||||
|
automaticLayout: automaticLayout,
|
||||||
|
language: monacoLanguage,
|
||||||
|
scrollbar: {
|
||||||
|
horizontal: 'auto',
|
||||||
|
horizontalScrollbarSize: 15,
|
||||||
|
vertical: 'auto',
|
||||||
|
verticalScrollbarSize: 15
|
||||||
|
},
|
||||||
|
wordWrap: 'on',
|
||||||
|
WrappingIndent: 'same',
|
||||||
|
readOnly: true,
|
||||||
|
minimap: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$el.style.zIndex = '1';
|
||||||
|
monacoEditor(document.getElementById(monacoId).editor);
|
||||||
|
document.getElementById(monacoId).addEventListener('monaco-editor-focused', (event) => {
|
||||||
|
document.getElementById(monacoId).editor.focus();
|
||||||
|
});
|
||||||
|
updatePlaceholder(document.getElementById(monacoId).editor.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
clearInterval(monacoLoaderInterval);
|
||||||
|
monacoLoader = false;
|
||||||
|
}
|
||||||
|
}, 5); " :id="monacoId"
|
||||||
|
class="fme-wrapper"
|
||||||
|
:class="{ 'fme-full-screen': fullScreenModeEnabled }" x-cloak>
|
||||||
|
<div class="flex items-center ml-auto">
|
||||||
|
@if($getShowFullScreenToggle())
|
||||||
|
<button type="button" aria-label="{{ __("full_screen_btn_label") }}" class="fme-full-screen-btn"
|
||||||
|
@click="toggleFullScreenMode()">
|
||||||
|
<svg class="fme-full-screen-btn-icon" x-show="!fullScreenModeEnabled"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M16 4l4 0l0 4" />
|
||||||
|
<path d="M14 10l6 -6" />
|
||||||
|
<path d="M8 20l-4 0l0 -4" />
|
||||||
|
<path d="M4 20l6 -6" />
|
||||||
|
<path d="M16 20l4 0l0 -4" />
|
||||||
|
<path d="M14 14l6 6" />
|
||||||
|
<path d="M8 4l-4 0l0 4" />
|
||||||
|
<path d="M4 4l6 6" />
|
||||||
|
</svg>
|
||||||
|
<svg class="fme-full-screen-btn-icon" x-show="fullScreenModeEnabled"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M5 9l4 0l0 -4" />
|
||||||
|
<path d="M3 3l6 6" />
|
||||||
|
<path d="M5 15l4 0l0 4" />
|
||||||
|
<path d="M3 21l6 -6" />
|
||||||
|
<path d="M19 9l-4 0l0 -4" />
|
||||||
|
<path d="M15 9l6 -6" />
|
||||||
|
<path d="M19 15l-4 0l0 4" />
|
||||||
|
<path d="M15 15l6 6" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="fme-container" x-show="!showPreview">
|
||||||
|
<!-- Editor -->
|
||||||
|
<div x-show="!monacoLoader" class="fme-element-wrapper">
|
||||||
|
<div x-ref="monacoEditorElement" class="fme-element" wire:ignore style="height: 100%"></div>
|
||||||
|
<div x-ref="monacoPlaceholderElement" x-show="monacoPlaceholder" @click="monacoEditorFocus()"
|
||||||
|
:style="'font-size: ' + monacoFontSize" class="fme-placeholder"
|
||||||
|
x-text="monacoPlaceholderText"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</x-dynamic-component>
|
Loading…
x
Reference in New Issue
Block a user