mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-17 17:01:37 +00:00
parent
bbe3ca802d
commit
fe331fd2b3
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Enums\ServerStatus;
|
||||
use App\Facades\Notifier;
|
||||
use App\Models\Server;
|
||||
use App\Notifications\ServerDisconnected;
|
||||
@ -15,12 +16,12 @@ public function check(Server $server): Server
|
||||
try {
|
||||
$server->ssh()->connect();
|
||||
$server->refresh();
|
||||
if ($status == 'disconnected') {
|
||||
$server->status = 'ready';
|
||||
if (in_array($status, [ServerStatus::DISCONNECTED, ServerStatus::UPDATING])) {
|
||||
$server->status = ServerStatus::READY;
|
||||
$server->save();
|
||||
}
|
||||
} catch (Throwable) {
|
||||
$server->status = 'disconnected';
|
||||
$server->status = ServerStatus::DISCONNECTED;
|
||||
$server->save();
|
||||
Notifier::send($server, new ServerDisconnected($server));
|
||||
}
|
||||
|
25
app/Actions/Server/Update.php
Normal file
25
app/Actions/Server/Update.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Enums\ServerStatus;
|
||||
use App\Facades\Notifier;
|
||||
use App\Models\Server;
|
||||
use App\Notifications\ServerUpdateFailed;
|
||||
|
||||
class Update
|
||||
{
|
||||
public function update(Server $server): void
|
||||
{
|
||||
$server->status = ServerStatus::UPDATING;
|
||||
$server->save();
|
||||
dispatch(function () use ($server) {
|
||||
$server->os()->upgrade();
|
||||
$server->checkConnection();
|
||||
$server->checkForUpdates();
|
||||
})->catch(function () use ($server) {
|
||||
Notifier::send($server, new ServerUpdateFailed($server));
|
||||
$server->checkConnection();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
}
|
@ -11,4 +11,6 @@ final class ServerStatus
|
||||
const INSTALLATION_FAILED = 'installation_failed';
|
||||
|
||||
const DISCONNECTED = 'disconnected';
|
||||
|
||||
const UPDATING = 'updating';
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Actions\Server\EditServer;
|
||||
use App\Actions\Server\RebootServer;
|
||||
use App\Actions\Server\Update;
|
||||
use App\Facades\Toast;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use App\Models\Server;
|
||||
@ -64,4 +65,24 @@ public function edit(Request $request, Server $server): RedirectResponse
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
public function checkUpdates(Server $server): RedirectResponse
|
||||
{
|
||||
$this->authorize('manage', $server);
|
||||
|
||||
$server->checkForUpdates();
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
public function update(Server $server): HtmxResponse
|
||||
{
|
||||
$this->authorize('manage', $server);
|
||||
|
||||
app(Update::class)->update($server);
|
||||
|
||||
Toast::info('Updating server. This may take a few minutes.');
|
||||
|
||||
return htmx()->back();
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
use App\SSH\Cron\Cron;
|
||||
use App\SSH\OS\OS;
|
||||
use App\SSH\Systemd\Systemd;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
@ -55,6 +56,8 @@
|
||||
* @property Queue[] $daemons
|
||||
* @property SshKey[] $sshKeys
|
||||
* @property string $hostname
|
||||
* @property int $updates
|
||||
* @property Carbon $last_update_check
|
||||
*/
|
||||
class Server extends AbstractModel
|
||||
{
|
||||
@ -82,6 +85,8 @@ class Server extends AbstractModel
|
||||
'security_updates',
|
||||
'progress',
|
||||
'progress_step',
|
||||
'updates',
|
||||
'last_update_check',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -95,6 +100,8 @@ class Server extends AbstractModel
|
||||
'available_updates' => 'integer',
|
||||
'security_updates' => 'integer',
|
||||
'progress' => 'integer',
|
||||
'updates' => 'integer',
|
||||
'last_update_check' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
@ -384,4 +391,11 @@ public function cron(): Cron
|
||||
{
|
||||
return new Cron($this);
|
||||
}
|
||||
|
||||
public function checkForUpdates(): void
|
||||
{
|
||||
$this->updates = $this->os()->availableUpdates();
|
||||
$this->last_update_check = now();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
33
app/Notifications/ServerUpdateFailed.php
Normal file
33
app/Notifications/ServerUpdateFailed.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class ServerUpdateFailed extends AbstractNotification
|
||||
{
|
||||
protected Server $server;
|
||||
|
||||
public function __construct(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
public function rawText(): string
|
||||
{
|
||||
return __("Update failed for server [:server] \nCheck your server's logs \n:logs", [
|
||||
'server' => $this->server->name,
|
||||
'logs' => url('/servers/'.$this->server->id.'/logs'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function toEmail(object $notifiable): MailMessage
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject(__('Server update failed!'))
|
||||
->line('Your server ['.$this->server->name.'] update has been failed.')
|
||||
->line('Check your server logs')
|
||||
->action('View Logs', url('/servers/'.$this->server->id.'/logs'));
|
||||
}
|
||||
}
|
@ -30,6 +30,19 @@ public function upgrade(): void
|
||||
);
|
||||
}
|
||||
|
||||
public function availableUpdates(): int
|
||||
{
|
||||
$result = $this->server->ssh()->exec(
|
||||
$this->getScript('available-updates.sh'),
|
||||
'check-available-updates'
|
||||
);
|
||||
|
||||
// -1 because the first line is not a package
|
||||
$availableUpdates = str($result)->after('Available updates:')->trim()->toInteger() - 1;
|
||||
|
||||
return max($availableUpdates, 0);
|
||||
}
|
||||
|
||||
public function createUser(string $user, string $password, string $key): void
|
||||
{
|
||||
$this->server->ssh()->exec(
|
||||
|
5
app/SSH/OS/scripts/available-updates.sh
Normal file
5
app/SSH/OS/scripts/available-updates.sh
Normal file
@ -0,0 +1,5 @@
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
|
||||
AVAILABLE_UPDATES=$(sudo DEBIAN_FRONTEND=noninteractive apt list --upgradable | wc -l)
|
||||
|
||||
echo "Available updates:$AVAILABLE_UPDATES"
|
@ -1,3 +1,8 @@
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common curl zip unzip git gcc openssl
|
||||
git config --global user.email "__email__"
|
||||
git config --global user.name "__name__"
|
||||
|
||||
# Install Node.js
|
||||
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -;
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install nodejs -y
|
||||
|
@ -2,8 +2,3 @@ sudo DEBIAN_FRONTEND=noninteractive apt-get clean
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get autoremove -y
|
||||
|
||||
# Install Node.js
|
||||
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -;
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install nodejs -y
|
||||
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->integer('updates')->default(0);
|
||||
$table->timestamp('last_update_check')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->dropColumn('updates');
|
||||
$table->dropColumn('last_update_check');
|
||||
});
|
||||
}
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
<div class="mx-auto mb-10">
|
||||
<div {{ $attributes->merge(["class" => "mx-auto mb-10"]) }}>
|
||||
<x-card-header>
|
||||
@if (isset($title))
|
||||
<x-slot name="title">{{ $title }}</x-slot>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<x-card>
|
||||
<x-card id="server-details">
|
||||
<x-slot name="title">{{ __("Details") }}</x-slot>
|
||||
<x-slot name="description">
|
||||
{{ __("More details about your server") }}
|
||||
@ -14,6 +14,53 @@
|
||||
<div class="border-t border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>{{ __("Last Update Checked") }}</div>
|
||||
<div>
|
||||
<x-datetime :value="$server->last_update_check" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="py-5">
|
||||
<div class="border-t border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="available-updates" class="flex items-center justify-between">
|
||||
<div>{{ __("Available Updates") }} ({{ $server->updates }})</div>
|
||||
<div class="flex flex-col items-end md:flex-row md:items-center">
|
||||
@if ($server->updates > 0)
|
||||
<x-primary-button
|
||||
id="btn-update-server"
|
||||
hx-post="{{ route('servers.settings.update', $server) }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#server-details"
|
||||
hx-select="#server-details"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-update-server"
|
||||
>
|
||||
{{ __("Update") }}
|
||||
</x-primary-button>
|
||||
@endif
|
||||
|
||||
<x-secondary-button
|
||||
id="btn-check-updates"
|
||||
class="mb-2 md:mb-0 md:ml-2"
|
||||
hx-post="{{ route('servers.settings.check-updates', $server) }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#server-details"
|
||||
hx-select="#server-details"
|
||||
hx-ext="disable-element"
|
||||
hx-disable-element="#btn-check-updates"
|
||||
>
|
||||
{{ __("Check") }}
|
||||
</x-secondary-button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="py-5">
|
||||
<div class="border-t border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>{{ __("Provider") }}</div>
|
||||
<div class="capitalize">{{ $server->provider }}</div>
|
||||
|
@ -14,4 +14,8 @@
|
||||
@if ($server->status == \App\Enums\ServerStatus::INSTALLATION_FAILED)
|
||||
<x-status status="danger">{{ $server->status }}</x-status>
|
||||
@endif
|
||||
|
||||
@if ($server->status == \App\Enums\ServerStatus::UPDATING)
|
||||
<x-status status="warning">{{ $server->status }}</x-status>
|
||||
@endif
|
||||
</div>
|
||||
|
@ -145,6 +145,8 @@
|
||||
Route::post('/check-connection', [ServerSettingController::class, 'checkConnection'])->name('servers.settings.check-connection');
|
||||
Route::post('/reboot', [ServerSettingController::class, 'reboot'])->name('servers.settings.reboot');
|
||||
Route::post('/edit', [ServerSettingController::class, 'edit'])->name('servers.settings.edit');
|
||||
Route::post('/check-updates', [ServerSettingController::class, 'checkUpdates'])->name('servers.settings.check-updates');
|
||||
Route::post('/update', [ServerSettingController::class, 'update'])->name('servers.settings.update');
|
||||
});
|
||||
|
||||
// logs
|
||||
|
@ -280,4 +280,32 @@ public function test_edit_server_ip_address_and_disconnect(): void
|
||||
'status' => ServerStatus::DISCONNECTED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_check_updates(): void
|
||||
{
|
||||
SSH::fake('Available updates:10');
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.settings.check-updates', $this->server))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->server->refresh();
|
||||
$this->assertEquals(9, $this->server->updates);
|
||||
}
|
||||
|
||||
public function test_update_server(): void
|
||||
{
|
||||
SSH::fake('Available updates:0');
|
||||
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.settings.update', $this->server))
|
||||
->assertSessionDoesntHaveErrors();
|
||||
|
||||
$this->server->refresh();
|
||||
|
||||
$this->assertEquals(ServerStatus::READY, $this->server->status);
|
||||
$this->assertEquals(0, $this->server->updates);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user