This commit is contained in:
Saeed Vaziry
2024-06-08 19:48:17 +03:30
committed by GitHub
parent 3b42f93654
commit a862a603f2
36 changed files with 1127 additions and 23 deletions

View File

@ -0,0 +1,14 @@
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
{{ $attributes }}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 316 B

View File

@ -42,6 +42,7 @@
{{ $attributes->has("focusable") ? "setTimeout(() => firstFocusable().focus(), 100)" : "" }}
} else {
document.body.classList.remove('overflow-y-hidden')
$dispatch('modal-{{ $name }}-closed')
}
})
"
@ -54,6 +55,7 @@
x-show="show"
class="fixed inset-0 z-50 overflow-y-auto px-4 py-6 sm:px-0"
style="display: {{ $show ? "block" : "none" }}"
{{ $attributes }}
>
<div
x-show="show"

View File

@ -41,23 +41,7 @@ class="mt-1 w-full"
</div>
<div class="mt-6">
@php
$user = old("user", "vito");
@endphp
<x-input-label for="user" :value="__('User')" />
<x-select-input id="user" name="user" class="mt-1 w-full">
<option value="" selected disabled>
{{ __("Select") }}
</option>
<option value="root" @if($user === 'root') selected @endif>root</option>
<option value="{{ $server->getSshUser() }}" @if($user === $server->getSshUser()) selected @endif>
{{ $server->getSshUser() }}
</option>
</x-select-input>
@error("user")
<x-input-error class="mt-2" :messages="$message" />
@enderror
@include("fields.user", ["value" => old("user")])
</div>
<div class="mt-6">

View File

@ -0,0 +1,15 @@
<x-input-label for="user" :value="__('User')" />
<x-select-input id="user" name="user" class="mt-1 w-full">
<option value="" selected disabled>
{{ __("Select") }}
</option>
<option value="root" @if($value === 'root') selected @endif>root</option>
@if (isset($server))
<option value="{{ $server->getSshUser() }}" @if($value === $server->getSshUser()) selected @endif>
{{ $server->getSshUser() }}
</option>
@endif
</x-select-input>
@error("user")
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -161,6 +161,13 @@ class="fixed left-0 top-0 z-40 h-screen w-64 -translate-x-full border-r border-g
<x-hr />
@endif
<li>
<x-sidebar-link :href="route('scripts.index')" :active="request()->routeIs('scripts.*')">
<x-heroicon name="o-bolt" class="h-6 w-6" />
<span class="ml-2">Scripts</span>
</x-sidebar-link>
</li>
<li>
<x-sidebar-link :href="route('profile')" :active="request()->routeIs('profile')">
<x-heroicon name="o-user-circle" class="h-6 w-6" />

View File

@ -0,0 +1,5 @@
<x-app-layout>
<x-slot name="pageTitle">{{ __("Scripts") }}</x-slot>
@include("scripts.partials.scripts-list")
</x-app-layout>

View File

@ -0,0 +1,37 @@
<div>
<x-primary-button x-data="" x-on:click.prevent="$dispatch('open-modal', 'create-script')">
{{ __("Create Script") }}
</x-primary-button>
<x-modal name="create-script">
<form
id="create-script-form"
hx-post="{{ route("scripts.store") }}"
hx-swap="outerHTML"
hx-select="#create-script-form"
class="p-6"
>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __("Create script") }}
</h2>
<div class="mt-6">
@include("scripts.partials.fields.name", ["value" => old("name")])
</div>
<div class="mt-6">
@include("scripts.partials.fields.content", ["value" => old("content")])
</div>
<div class="mt-6 flex justify-end">
<x-secondary-button type="button" x-on:click="$dispatch('close')">
{{ __("Cancel") }}
</x-secondary-button>
<x-primary-button class="ml-3" hx-disable>
{{ __("Create") }}
</x-primary-button>
</div>
</form>
</x-modal>
</div>

View File

@ -0,0 +1,18 @@
<x-modal name="delete-script" :show="$errors->isNotEmpty()">
<form id="delete-script-form" method="post" x-bind:action="deleteAction" class="p-6">
@csrf
@method("delete")
<h2 class="text-lg font-medium">Are you sure that you want to delete this script?</h2>
<div class="mt-6 flex justify-end">
<x-secondary-button type="button" x-on:click="$dispatch('close')">
{{ __("Cancel") }}
</x-secondary-button>
<x-danger-button class="ml-3">
{{ __("Delete") }}
</x-danger-button>
</div>
</form>
</x-modal>

View File

@ -0,0 +1,35 @@
<x-modal
name="edit-script"
:show="true"
x-on:modal-edit-script-closed.window="window.history.pushState('', '', '{{ route('scripts.index') }}');"
>
<form
id="edit-script-form"
hx-post="{{ route("scripts.edit", ["script" => $script]) }}"
hx-swap="outerHTML"
hx-select="#edit-script-form"
class="p-6"
>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __("Edit script") }}
</h2>
<div class="mt-6">
@include("scripts.partials.fields.name", ["value" => old("name", $script->name)])
</div>
<div class="mt-6">
@include("scripts.partials.fields.content", ["value" => old("content", $script->content)])
</div>
<div class="mt-6 flex justify-end">
<x-secondary-button type="button" x-on:click="$dispatch('close')">
{{ __("Cancel") }}
</x-secondary-button>
<x-primary-button class="ml-3" hx-disable>
{{ __("Save") }}
</x-primary-button>
</div>
</form>
</x-modal>

View File

@ -0,0 +1,109 @@
<x-modal
name="execute-script"
:show="true"
x-on:modal-execute-script-closed.window="window.history.pushState('', '', '{{ route('scripts.index') }}');"
>
<div
x-data="{
server: '',
selectServer() {
let url =
'{{ route("scripts.index", ["execute" => $script->id]) }}&server=' +
this.server
window.history.pushState('', '', url)
htmx.ajax('GET', url, {
target: '#select-user',
swap: 'outerHTML',
select: '#select-user',
})
},
}"
>
<form
id="execute-script-form"
hx-post="{{ route("scripts.execute", ["script" => $script]) }}"
hx-swap="outerHTML"
hx-select="#execute-script-form"
class="p-6"
>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __("Execute script") }}
</h2>
<div class="mt-6">
<x-input-label for="server" :value="__('Select a server to execute')" />
<x-select-input
id="server"
name="server"
x-model="server"
x-on:change="selectServer"
class="mt-1 w-full"
>
<option value="" selected disabled>
{{ __("Select") }}
</option>
@php
$executeServers = auth()
->user()
->allServers()
->get();
@endphp
@foreach ($executeServers as $executeServer)
<option value="{{ $executeServer->id }}">
{{ $executeServer->name }} [{{ $executeServer->project->name }}]
</option>
@endforeach
</x-select-input>
@error("server")
<x-input-error class="mt-2" :messages="$message" />
@enderror
</div>
<div class="mt-6" id="select-user">
@php
$s = null;
if (request()->has("server")) {
$s = auth()
->user()
->allServers()
->findOrFail(request("server"));
}
@endphp
@include("fields.user", ["value" => old("user"), "server" => $s])
</div>
@if (count($script->getVariables()) > 0)
<x-input-label class="mt-6" value="Variables" />
<div class="mt-2 space-y-6 border-2 border-dashed border-gray-200 px-2 py-3 dark:border-gray-700">
@foreach ($script->getVariables() as $variable)
<div>
<x-input-label :for="'variable-' . $variable" :value="$variable" />
<x-text-input
id="variable-{{ $variable }}"
name="variables[{{ $variable }}]"
class="mt-1 w-full"
value="{{ old('variables.' . $variable) }}"
/>
</div>
@error("variables." . $variable)
<x-input-error class="mt-2" :messages="$message" />
@enderror
@endforeach
</div>
@endif
<div class="mt-6 flex justify-end">
<x-secondary-button type="button" x-on:click="$dispatch('close')">
{{ __("Cancel") }}
</x-secondary-button>
<x-primary-button class="ml-3" hx-disable>
{{ __("Execute") }}
</x-primary-button>
</div>
</form>
</div>
</x-modal>

View File

@ -0,0 +1,10 @@
<x-input-label for="content" :value="__('Content')" />
<x-textarea id="content" name="content" class="mt-1 min-h-[400px] w-full">
{{ $value }}
</x-textarea>
@error("content")
<x-input-error class="mt-2" :messages="$message" />
@enderror
<x-input-help>You can use variables like ${VARIABLE_NAME} in the script</x-input-help>
<x-input-help>The variables will be asked when executing the script</x-input-help>

View File

@ -0,0 +1,5 @@
<x-input-label for="name" :value="__('Name')" />
<x-text-input value="{{ $value }}" id="name" name="name" type="text" class="mt-1 w-full" />
@error("name")
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -0,0 +1,11 @@
@if ($status == \App\Enums\ScriptExecutionStatus::EXECUTING)
<x-status status="warning">{{ $status }}</x-status>
@endif
@if ($status == \App\Enums\ScriptExecutionStatus::COMPLETED)
<x-status status="success">{{ $status }}</x-status>
@endif
@if ($status == \App\Enums\ScriptExecutionStatus::FAILED)
<x-status status="danger">{{ $status }}</x-status>
@endif

View File

@ -0,0 +1,67 @@
<x-container>
<x-card-header>
<x-slot name="title">Script Executions</x-slot>
<x-slot name="description">Here you can see the list of the latest executions of your script</x-slot>
</x-card-header>
<x-live id="script-executions" interval="5s">
@if (count($executions) > 0)
<div id="scripts-list" x-data="{}">
<x-table>
<x-thead>
<x-tr>
<x-th>Date</x-th>
<x-th>Status</x-th>
<x-th></x-th>
</x-tr>
</x-thead>
<x-tbody>
@foreach ($executions as $execution)
<x-tr>
<x-td>
<x-datetime :value="$execution->created_at" />
</x-td>
<x-td>
@include("scripts.partials.script-execution-status", ["status" => $execution->status])
</x-td>
<x-td class="text-right">
<x-icon-button
x-on:click="$dispatch('open-modal', 'show-log')"
id="show-log-{{ $execution->id }}"
hx-get="{{ route('scripts.log', ['script' => $script, 'execution' => $execution]) }}"
hx-target="#show-log-content"
hx-select="#show-log-content"
hx-swap="outerHTML"
data-tooltip="Logs"
>
<x-heroicon name="o-eye" class="h-5 w-5" />
</x-icon-button>
</x-td>
</x-tr>
@endforeach
</x-tbody>
</x-table>
</div>
@else
<x-simple-card>
<div class="text-center">This script hasn't been executed yet!</div>
</x-simple-card>
@endif
<div class="mt-5">
{{ $executions->withQueryString()->links() }}
</div>
</x-live>
<x-modal name="show-log" max-width="4xl">
<div class="p-6" id="show-log-content">
<h2 class="mb-5 text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __("View Log") }}
</h2>
<x-console-view>{{ session()->get("content") }}</x-console-view>
<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>
</x-container>

View File

@ -0,0 +1,94 @@
<x-container>
<x-card-header>
<x-slot name="title">Scripts</x-slot>
<x-slot name="description">Your scripts are here. Create/Edit/Delete and Execute them on your servers.</x-slot>
<x-slot name="aside">
@include("scripts.partials.create-script")
</x-slot>
</x-card-header>
@if (count($scripts) > 0)
<div id="scripts-list" x-data="{ deleteAction: '' }">
<x-table>
<x-thead>
<x-tr>
<x-th>ID</x-th>
<x-th>Name</x-th>
<x-th>Last Executed At</x-th>
<x-th></x-th>
</x-tr>
</x-thead>
<x-tbody>
@foreach ($scripts as $script)
<x-tr>
<x-td>{{ $script->id }}</x-td>
<x-td>{{ $script->name }}</x-td>
<x-td>
@if ($script->lastExecution)
<x-datetime :value="$script->lastExecution->created_at" />
@else
-
@endif
</x-td>
<x-td class="text-right">
<x-icon-button :href="route('scripts.show', $script)" data-tooltip="Executions">
<x-heroicon name="o-eye" class="h-5 w-5" />
</x-icon-button>
<x-icon-button
data-tooltip="Execute"
id="execute-{{ $script->id }}"
hx-get="{{ route('scripts.index', ['execute' => $script->id]) }}"
hx-replace-url="true"
hx-select="#execute"
hx-target="#execute"
hx-ext="disable-element"
hx-disable-element="#execute-{{ $script->id }}"
>
<x-heroicon name="o-bolt" class="h-5 w-5 text-primary-500" />
</x-icon-button>
<x-icon-button
data-tooltip="Edit"
id="edit-{{ $script->id }}"
hx-get="{{ route('scripts.index', ['edit' => $script->id]) }}"
hx-replace-url="true"
hx-select="#edit"
hx-target="#edit"
hx-ext="disable-element"
hx-disable-element="#edit-{{ $script->id }}"
>
<x-heroicon name="o-pencil" class="h-5 w-5" />
</x-icon-button>
<x-icon-button
data-tooltip="Delete"
x-on:click="deleteAction = '{{ route('scripts.delete', $script->id) }}'; $dispatch('open-modal', 'delete-script')"
>
<x-heroicon name="o-trash" class="h-5 w-5" />
</x-icon-button>
</x-td>
</x-tr>
@endforeach
</x-tbody>
</x-table>
@include("scripts.partials.delete-script")
<div id="edit">
@if (isset($editScript))
@include("scripts.partials.edit-script", ["script" => $editScript])
@endif
</div>
<div id="execute">
@if (isset($executeScript))
@include("scripts.partials.execute-script", ["script" => $executeScript])
@endif
</div>
</div>
@else
<x-simple-card>
<div class="text-center">
{{ __("You don't have any scripts yet!") }}
</div>
</x-simple-card>
@endif
</x-container>

View File

@ -0,0 +1,5 @@
<x-app-layout>
<x-slot name="pageTitle">{{ $script->name }}</x-slot>
@include("scripts.partials.script-executions-list")
</x-app-layout>

View File

@ -1,4 +1,8 @@
<x-modal name="edit-source-control" :show="true">
<x-modal
name="edit-source-control"
:show="true"
x-on:modal-edit-source-control-closed.window="window.history.pushState('', '', '{{ route('settings.source-controls') }}');"
>
<form
id="edit-source-control-form"
hx-post="{{ route("settings.source-controls.update", ["sourceControl" => $sourceControl->id]) }}"