mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-19 18:01:37 +00:00
add log viewer for queues
This commit is contained in:
parent
fd93f3dd47
commit
7a6dcb5654
13
app/Actions/Queue/GetQueueLogs.php
Normal file
13
app/Actions/Queue/GetQueueLogs.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Queue;
|
||||||
|
|
||||||
|
use App\Models\Queue;
|
||||||
|
|
||||||
|
class GetQueueLogs
|
||||||
|
{
|
||||||
|
public function getLogs(Queue $queue): string
|
||||||
|
{
|
||||||
|
return $queue->server->processManager()->handler()->getLogs($queue->getLogFile());
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Actions\Queue\CreateQueue;
|
use App\Actions\Queue\CreateQueue;
|
||||||
use App\Actions\Queue\DeleteQueue;
|
use App\Actions\Queue\DeleteQueue;
|
||||||
|
use App\Actions\Queue\GetQueueLogs;
|
||||||
use App\Actions\Queue\ManageQueue;
|
use App\Actions\Queue\ManageQueue;
|
||||||
use App\Facades\Toast;
|
use App\Facades\Toast;
|
||||||
use App\Helpers\HtmxResponse;
|
use App\Helpers\HtmxResponse;
|
||||||
@ -51,4 +52,9 @@ public function destroy(Server $server, Site $site, Queue $queue): RedirectRespo
|
|||||||
|
|
||||||
return back();
|
return back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function logs(Server $server, Site $site, Queue $queue): RedirectResponse
|
||||||
|
{
|
||||||
|
return back()->with('content', app(GetQueueLogs::class)->getLogs($queue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
35
package-lock.json
generated
35
package-lock.json
generated
@ -20,6 +20,7 @@
|
|||||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||||
"pusher-js": "^4.3.1",
|
"pusher-js": "^4.3.1",
|
||||||
"tailwindcss": "^3.1.0",
|
"tailwindcss": "^3.1.0",
|
||||||
|
"tippy.js": "^6.3.7",
|
||||||
"toastr": "^2.1.4",
|
"toastr": "^2.1.4",
|
||||||
"vite": "^4.5.2"
|
"vite": "^4.5.2"
|
||||||
}
|
}
|
||||||
@ -465,6 +466,16 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ryangjchandler/alpine-clipboard": {
|
"node_modules/@ryangjchandler/alpine-clipboard": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ryangjchandler/alpine-clipboard/-/alpine-clipboard-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ryangjchandler/alpine-clipboard/-/alpine-clipboard-2.2.0.tgz",
|
||||||
@ -1879,6 +1890,15 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tippy.js": {
|
||||||
|
"version": "6.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||||
|
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@ -2294,6 +2314,12 @@
|
|||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@ryangjchandler/alpine-clipboard": {
|
"@ryangjchandler/alpine-clipboard": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ryangjchandler/alpine-clipboard/-/alpine-clipboard-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ryangjchandler/alpine-clipboard/-/alpine-clipboard-2.2.0.tgz",
|
||||||
@ -3225,6 +3251,15 @@
|
|||||||
"thenify": ">= 3.1.0 < 4"
|
"thenify": ">= 3.1.0 < 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tippy.js": {
|
||||||
|
"version": "6.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||||
|
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@popperjs/core": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"to-regex-range": {
|
"to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||||
"pusher-js": "^4.3.1",
|
"pusher-js": "^4.3.1",
|
||||||
"tailwindcss": "^3.1.0",
|
"tailwindcss": "^3.1.0",
|
||||||
|
"tippy.js": "^6.3.7",
|
||||||
"toastr": "^2.1.4",
|
"toastr": "^2.1.4",
|
||||||
"vite": "^4.5.2"
|
"vite": "^4.5.2"
|
||||||
}
|
}
|
||||||
|
21
public/build/assets/app-44a1ce19.js
Normal file
21
public/build/assets/app-44a1ce19.js
Normal file
File diff suppressed because one or more lines are too long
1
public/build/assets/app-a1ae07b3.css
Normal file
1
public/build/assets/app-a1ae07b3.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
File diff suppressed because one or more lines are too long
@ -4,8 +4,15 @@
|
|||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/css/app.css"
|
"src": "resources/css/app.css"
|
||||||
},
|
},
|
||||||
|
"resources/js/app.css": {
|
||||||
|
"file": "assets/app-a1ae07b3.css",
|
||||||
|
"src": "resources/js/app.css"
|
||||||
|
},
|
||||||
"resources/js/app.js": {
|
"resources/js/app.js": {
|
||||||
"file": "assets/app-c41c626e.js",
|
"css": [
|
||||||
|
"assets/app-a1ae07b3.css"
|
||||||
|
],
|
||||||
|
"file": "assets/app-44a1ce19.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/app.js"
|
"src": "resources/js/app.js"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
@import "toastr.css";
|
@import "toastr.css";
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Alpine from 'alpinejs';
|
import tippy from 'tippy.js';
|
||||||
|
|
||||||
Alpine.directive('clipboard', (el) => {
|
Alpine.directive('clipboard', (el) => {
|
||||||
let text = el.textContent
|
let text = el.textContent
|
||||||
@ -54,3 +54,18 @@ window.toastr.options = {
|
|||||||
"positionClass": "toast-bottom-right",
|
"positionClass": "toast-bottom-right",
|
||||||
"preventDuplicates": true,
|
"preventDuplicates": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import 'tippy.js/dist/tippy.css';
|
||||||
|
import Alpine from 'alpinejs';
|
||||||
|
document.body.addEventListener('htmx:afterSettle', (event) => {
|
||||||
|
tippy('[data-tooltip]', {
|
||||||
|
content(reference) {
|
||||||
|
return reference.getAttribute('data-tooltip');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tippy('[data-tooltip]', {
|
||||||
|
content(reference) {
|
||||||
|
return reference.getAttribute('data-tooltip');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -27,27 +27,41 @@
|
|||||||
hx-post="{{ route('servers.sites.queues.action', ['server' => $server, 'site' => $site, 'queue' => $queue, 'action' => 'stop']) }}"
|
hx-post="{{ route('servers.sites.queues.action', ['server' => $server, 'site' => $site, 'queue' => $queue, 'action' => 'stop']) }}"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
hx-select="#queue-actions-{{ $queue->id }}"
|
hx-select="#queue-actions-{{ $queue->id }}"
|
||||||
|
data-tooltip="Stop"
|
||||||
>
|
>
|
||||||
Stop
|
<x-heroicon-o-stop class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
<x-icon-button
|
<x-icon-button
|
||||||
id="resume-{{ $queue->id }}"
|
id="resume-{{ $queue->id }}"
|
||||||
hx-post="{{ route('servers.sites.queues.action', ['server' => $server, 'site' => $site, 'queue' => $queue, 'action' => 'start']) }}"
|
hx-post="{{ route('servers.sites.queues.action', ['server' => $server, 'site' => $site, 'queue' => $queue, 'action' => 'start']) }}"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
hx-select="#queue-actions-{{ $queue->id }}"
|
hx-select="#queue-actions-{{ $queue->id }}"
|
||||||
|
data-tooltip="Start"
|
||||||
>
|
>
|
||||||
Start
|
<x-heroicon-o-play class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
<x-icon-button
|
<x-icon-button
|
||||||
id="restart-{{ $queue->id }}"
|
id="restart-{{ $queue->id }}"
|
||||||
hx-post="{{ route('servers.sites.queues.action', ['server' => $server, 'site' => $site, 'queue' => $queue, 'action' => 'restart']) }}"
|
hx-post="{{ route('servers.sites.queues.action', ['server' => $server, 'site' => $site, 'queue' => $queue, 'action' => 'restart']) }}"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
hx-select="#queue-actions-{{ $queue->id }}"
|
hx-select="#queue-actions-{{ $queue->id }}"
|
||||||
|
data-tooltip="Restart"
|
||||||
>
|
>
|
||||||
Restart
|
<x-heroicon-o-arrow-path class="h-5 w-5" />
|
||||||
|
</x-icon-button>
|
||||||
|
<x-icon-button
|
||||||
|
id="logs-{{ $queue->id }}"
|
||||||
|
x-on:click="$dispatch('open-modal', 'show-log'); document.getElementById('log-content').firstChild.innerHTML = '';"
|
||||||
|
hx-get="{{ route('servers.sites.queues.logs', ['server' => $server, 'site' => $site, 'queue' => $queue]) }}"
|
||||||
|
hx-target="#log-content"
|
||||||
|
hx-select="#log-content"
|
||||||
|
data-tooltip="Logs"
|
||||||
|
>
|
||||||
|
<x-heroicon-o-square-3-stack-3d class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
<x-icon-button
|
<x-icon-button
|
||||||
x-on:click="deleteAction = '{{ route('servers.sites.queues.destroy', ['server' => $server, 'site' => $site, 'queue' => $queue]) }}'; $dispatch('open-modal', 'delete-queue')"
|
x-on:click="deleteAction = '{{ route('servers.sites.queues.destroy', ['server' => $server, 'site' => $site, 'queue' => $queue]) }}'; $dispatch('open-modal', 'delete-queue')"
|
||||||
|
data-tooltip="Delete"
|
||||||
>
|
>
|
||||||
<x-heroicon-o-trash class="h-5 w-5" />
|
<x-heroicon-o-trash class="h-5 w-5" />
|
||||||
</x-icon-button>
|
</x-icon-button>
|
||||||
@ -71,4 +85,25 @@
|
|||||||
method="delete"
|
method="delete"
|
||||||
x-bind:action="deleteAction"
|
x-bind:action="deleteAction"
|
||||||
/>
|
/>
|
||||||
|
<x-modal name="show-log" max-width="4xl">
|
||||||
|
<div class="p-6">
|
||||||
|
<h2 class="mb-5 text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||||
|
{{ __("View Log") }}
|
||||||
|
</h2>
|
||||||
|
<div id="log-content">
|
||||||
|
<x-console-view>
|
||||||
|
@if (session()->has("content"))
|
||||||
|
{{ session("content") }}
|
||||||
|
@else
|
||||||
|
Loading...
|
||||||
|
@endif
|
||||||
|
</x-console-view>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6 flex justify-end">
|
||||||
|
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||||
|
{{ __("Close") }}
|
||||||
|
</x-secondary-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-modal>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
Route::post('/{site}/queues', [QueueController::class, 'store'])->name('servers.sites.queues.store');
|
Route::post('/{site}/queues', [QueueController::class, 'store'])->name('servers.sites.queues.store');
|
||||||
Route::post('/{site}/queues/{queue}/action/{action}', [QueueController::class, 'action'])->name('servers.sites.queues.action');
|
Route::post('/{site}/queues/{queue}/action/{action}', [QueueController::class, 'action'])->name('servers.sites.queues.action');
|
||||||
Route::delete('/{site}/queues/{queue}', [QueueController::class, 'destroy'])->name('servers.sites.queues.destroy');
|
Route::delete('/{site}/queues/{queue}', [QueueController::class, 'destroy'])->name('servers.sites.queues.destroy');
|
||||||
|
Route::get('/{site}/queues/{queue}/logs', [QueueController::class, 'logs'])->name('servers.sites.queues.logs');
|
||||||
|
|
||||||
// site settings
|
// site settings
|
||||||
Route::get('/{site}/settings', [SiteSettingController::class, 'index'])->name('servers.sites.settings');
|
Route::get('/{site}/settings', [SiteSettingController::class, 'index'])->name('servers.sites.settings');
|
||||||
|
@ -167,4 +167,27 @@ public function test_restart_queue(): void
|
|||||||
'status' => QueueStatus::RUNNING,
|
'status' => QueueStatus::RUNNING,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_show_logs(): void
|
||||||
|
{
|
||||||
|
SSH::fake('logs');
|
||||||
|
|
||||||
|
$this->actingAs($this->user);
|
||||||
|
|
||||||
|
$queue = Queue::factory()->create([
|
||||||
|
'server_id' => $this->server->id,
|
||||||
|
'site_id' => $this->site->id,
|
||||||
|
'status' => QueueStatus::RUNNING,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->get(
|
||||||
|
route('servers.sites.queues.logs', [
|
||||||
|
'server' => $this->server,
|
||||||
|
'site' => $this->site,
|
||||||
|
'queue' => $queue,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
->assertSessionDoesntHaveErrors()
|
||||||
|
->assertSessionHas('content', 'logs');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user