mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 05:56:16 +00:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
e2dd9177f7 | |||
5a9e8d6799 | |||
868b70f530 | |||
d07e9bcad2 | |||
0cd815cce6 | |||
5ab6617b5d | |||
72b37c56fd | |||
8a4ef66946 | |||
4517ca7d2a |
35
app/Actions/Server/CreateServerLog.php
Executable file
35
app/Actions/Server/CreateServerLog.php
Executable file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CreateServerLog
|
||||
{
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function create(Server $server, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$server->logs()->create([
|
||||
'is_remote' => true,
|
||||
'name' => $input['path'],
|
||||
'type' => 'remote',
|
||||
'disk' => 'ssh',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function validate(array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'path' => 'required',
|
||||
])->validate();
|
||||
}
|
||||
}
|
31
app/Console/Commands/GetMetricsCommand.php
Normal file
31
app/Console/Commands/GetMetricsCommand.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class GetMetricsCommand extends Command
|
||||
{
|
||||
protected $signature = 'metrics:get';
|
||||
|
||||
protected $description = 'Get server metrics';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$checkedMetrics = 0;
|
||||
Server::query()->whereHas('services', function (Builder $query) {
|
||||
$query->where('type', 'monitoring')
|
||||
->where('name', 'remote-monitor');
|
||||
})->chunk(10, function ($servers) use (&$checkedMetrics) {
|
||||
/** @var Server $server */
|
||||
foreach ($servers as $server) {
|
||||
$info = $server->os()->resourceInfo();
|
||||
$server->metrics()->create(array_merge($info, ['server_id' => $server->id]));
|
||||
$checkedMetrics++;
|
||||
}
|
||||
});
|
||||
$this->info("Checked $checkedMetrics metrics");
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ protected function schedule(Schedule $schedule): void
|
||||
$schedule->command('backups:run "0 0 * * 0"')->weekly();
|
||||
$schedule->command('backups:run "0 0 1 * *"')->monthly();
|
||||
$schedule->command('metrics:delete-older-metrics')->daily();
|
||||
$schedule->command('metrics:get')->everyMinute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +54,6 @@ public function show(Server $server): View
|
||||
{
|
||||
return view('servers.show', [
|
||||
'server' => $server,
|
||||
'logs' => $server->logs()->latest()->limit(10)->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,13 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Server\CreateServerLog;
|
||||
use App\Facades\Toast;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ServerLogController extends Controller
|
||||
{
|
||||
@ -13,6 +16,7 @@ public function index(Server $server): View
|
||||
{
|
||||
return view('server-logs.index', [
|
||||
'server' => $server,
|
||||
'pageTitle' => __('Vito Logs'),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -26,4 +30,31 @@ public function show(Server $server, ServerLog $serverLog): RedirectResponse
|
||||
'content' => $serverLog->getContent(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function remote(Server $server): View
|
||||
{
|
||||
return view('server-logs.remote-logs', [
|
||||
'server' => $server,
|
||||
'remote' => true,
|
||||
'pageTitle' => __('Remote Logs'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Server $server, Request $request): \App\Helpers\HtmxResponse
|
||||
{
|
||||
app(CreateServerLog::class)->create($server, $request->input());
|
||||
|
||||
Toast::success('Log added successfully.');
|
||||
|
||||
return htmx()->redirect(route('servers.logs.remote', ['server' => $server]));
|
||||
}
|
||||
|
||||
public function destroy(Server $server, ServerLog $serverLog): RedirectResponse
|
||||
{
|
||||
$serverLog->delete();
|
||||
|
||||
Toast::success('Remote log deleted successfully.');
|
||||
|
||||
return redirect()->route('servers.logs.remote', ['server' => $server]);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ public function index(Server $server, Site $site): View
|
||||
return view('site-logs.index', [
|
||||
'server' => $server,
|
||||
'site' => $site,
|
||||
'pageTitle' => __('Vito Logs'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
* @property string $disk
|
||||
* @property Server $server
|
||||
* @property ?Site $site
|
||||
* @property bool $is_remote
|
||||
*/
|
||||
class ServerLog extends AbstractModel
|
||||
{
|
||||
@ -27,11 +28,13 @@ class ServerLog extends AbstractModel
|
||||
'type',
|
||||
'name',
|
||||
'disk',
|
||||
'is_remote',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'site_id' => 'integer',
|
||||
'is_remote' => 'boolean',
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
@ -64,6 +67,17 @@ public function site(): BelongsTo
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public static function getRemote($query, bool $active = true, ?Site $site = null)
|
||||
{
|
||||
$query->where('is_remote', $active);
|
||||
|
||||
if ($site) {
|
||||
$query->where('name', 'like', $site->path.'%');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function write($buf): void
|
||||
{
|
||||
if (Str::contains($buf, 'VITO_SSH_ERROR')) {
|
||||
@ -78,6 +92,10 @@ public function write($buf): void
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
if ($this->is_remote) {
|
||||
return $this->server->os()->tail($this->name, 150);
|
||||
}
|
||||
|
||||
if (Storage::disk($this->disk)->exists($this->name)) {
|
||||
return Storage::disk($this->disk)->get($this->name);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\SiteTypes\SiteType;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@ -185,7 +186,9 @@ public function php(): ?Service
|
||||
|
||||
public function changePHPVersion($version): void
|
||||
{
|
||||
$this->server->webserver()->handler()->changePHPVersion($this, $version);
|
||||
/** @var Webserver $handler */
|
||||
$handler = $this->server->webserver()->handler();
|
||||
$handler->changePHPVersion($this, $version);
|
||||
$this->php_version = $version;
|
||||
$this->save();
|
||||
}
|
||||
|
@ -117,6 +117,16 @@ public function readFile(string $path): string
|
||||
);
|
||||
}
|
||||
|
||||
public function tail(string $path, int $lines): string
|
||||
{
|
||||
return $this->server->ssh()->exec(
|
||||
$this->getScript('tail.sh', [
|
||||
'path' => $path,
|
||||
'lines' => $lines,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function runScript(string $path, string $script, ?int $siteId = null): ServerLog
|
||||
{
|
||||
$ssh = $this->server->ssh();
|
||||
@ -156,4 +166,21 @@ public function cleanup(): void
|
||||
'cleanup'
|
||||
);
|
||||
}
|
||||
|
||||
public function resourceInfo(): array
|
||||
{
|
||||
$info = $this->server->ssh()->exec(
|
||||
$this->getScript('resource-info.sh'),
|
||||
);
|
||||
|
||||
return [
|
||||
'load' => str($info)->after('load:')->before(PHP_EOL)->toString(),
|
||||
'memory_total' => str($info)->after('memory_total:')->before(PHP_EOL)->toString(),
|
||||
'memory_used' => str($info)->after('memory_used:')->before(PHP_EOL)->toString(),
|
||||
'memory_free' => str($info)->after('memory_free:')->before(PHP_EOL)->toString(),
|
||||
'disk_total' => str($info)->after('disk_total:')->before(PHP_EOL)->toString(),
|
||||
'disk_used' => str($info)->after('disk_used:')->before(PHP_EOL)->toString(),
|
||||
'disk_free' => str($info)->after('disk_free:')->before(PHP_EOL)->toString(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
[ -f __path__ ] && cat __path__
|
||||
[ -f __path__ ] && sudo cat __path__
|
||||
|
7
app/SSH/OS/scripts/resource-info.sh
Normal file
7
app/SSH/OS/scripts/resource-info.sh
Normal file
@ -0,0 +1,7 @@
|
||||
echo "load:$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | tr -d ' ')"
|
||||
echo "memory_total:$(free -k | awk 'NR==2{print $2}')"
|
||||
echo "memory_used:$(free -k | awk 'NR==2{print $3}')"
|
||||
echo "memory_free:$(free -k | awk 'NR==2{print $7}')"
|
||||
echo "disk_total:$(df -BM / | awk 'NR==2{print $2}' | sed 's/M//')"
|
||||
echo "disk_used:$(df -BM / | awk 'NR==2{print $3}' | sed 's/M//')"
|
||||
echo "disk_free:$(df -BM / | awk 'NR==2{print $4}' | sed 's/M//')"
|
1
app/SSH/OS/scripts/tail.sh
Normal file
1
app/SSH/OS/scripts/tail.sh
Normal file
@ -0,0 +1 @@
|
||||
sudo tail -n __lines__ __path__
|
53
app/SSH/Services/Monitoring/RemoteMonitor/RemoteMonitor.php
Normal file
53
app/SSH/Services/Monitoring/RemoteMonitor/RemoteMonitor.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSH\Services\Monitoring\RemoteMonitor;
|
||||
|
||||
use App\Models\Metric;
|
||||
use App\SSH\Services\AbstractService;
|
||||
use Closure;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class RemoteMonitor extends AbstractService
|
||||
{
|
||||
public function creationRules(array $input): array
|
||||
{
|
||||
return [
|
||||
'type' => [
|
||||
function (string $attribute, mixed $value, Closure $fail) {
|
||||
$monitoringExists = $this->service->server->monitoring();
|
||||
if ($monitoringExists) {
|
||||
$fail('You already have a monitoring service on the server.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'version' => [
|
||||
'required',
|
||||
Rule::in(['latest']),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function creationData(array $input): array
|
||||
{
|
||||
return [
|
||||
'data_retention' => 10,
|
||||
];
|
||||
}
|
||||
|
||||
public function data(): array
|
||||
{
|
||||
return [
|
||||
'data_retention' => $this->service->type_data['data_retention'] ?? 10,
|
||||
];
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
Metric::where('server_id', $this->service->server_id)->delete();
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\SSH\Services\VitoAgent;
|
||||
namespace App\SSH\Services\Monitoring\VitoAgent;
|
||||
|
||||
use App\Models\Metric;
|
||||
use App\SSH\HasScripts;
|
@ -32,7 +32,12 @@ function htmx(): HtmxResponse
|
||||
|
||||
function vito_version(): string
|
||||
{
|
||||
return exec('git describe --tags');
|
||||
$version = exec('git describe --tags');
|
||||
if (str($version)->contains('-')) {
|
||||
return str($version)->before('-').' (dev)';
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
function convert_time_format($string): string
|
||||
|
@ -142,6 +142,7 @@
|
||||
'ufw' => 'firewall',
|
||||
'supervisor' => 'process_manager',
|
||||
'vito-agent' => 'monitoring',
|
||||
'remote-monitor' => 'monitoring',
|
||||
],
|
||||
'service_handlers' => [
|
||||
'nginx' => \App\SSH\Services\Webserver\Nginx::class,
|
||||
@ -152,7 +153,8 @@
|
||||
'php' => \App\SSH\Services\PHP\PHP::class,
|
||||
'ufw' => \App\SSH\Services\Firewall\Ufw::class,
|
||||
'supervisor' => \App\SSH\Services\ProcessManager\Supervisor::class,
|
||||
'vito-agent' => \App\SSH\Services\VitoAgent\VitoAgent::class,
|
||||
'vito-agent' => \App\SSH\Services\Monitoring\VitoAgent\VitoAgent::class,
|
||||
'remote-monitor' => \App\SSH\Services\Monitoring\RemoteMonitor\RemoteMonitor::class,
|
||||
],
|
||||
'service_units' => [
|
||||
'nginx' => [
|
||||
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('server_logs', function (Blueprint $table) {
|
||||
$table->boolean('is_remote')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('server_logs', function (Blueprint $table) {
|
||||
$table->dropColumn('is_remote');
|
||||
});
|
||||
}
|
||||
};
|
@ -19,7 +19,7 @@ RUN apt-get install -y nginx
|
||||
|
||||
# php
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
|
||||
&& apt-get install -y cron gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
|
||||
python2 dnsutils librsvg2-bin fswatch wget \
|
||||
&& add-apt-repository ppa:ondrej/php -y \
|
||||
&& apt-get update \
|
||||
@ -44,6 +44,8 @@ RUN rm /etc/nginx/sites-enabled/default
|
||||
COPY docker/nginx.conf /etc/nginx/sites-available/default
|
||||
RUN ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
|
||||
|
||||
RUN echo "* * * * * cd /var/www/html && php artisan schedule:run >> /var/log/cron.log 2>&1" | crontab -
|
||||
|
||||
# supervisord
|
||||
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
|
@ -37,6 +37,8 @@ php /var/www/html/artisan view:cache
|
||||
|
||||
php /var/www/html/artisan user:create "$NAME" "$EMAIL" "$PASSWORD"
|
||||
|
||||
cron
|
||||
|
||||
echo "Vito is running! 🚀"
|
||||
|
||||
/usr/bin/supervisord
|
||||
|
File diff suppressed because one or more lines are too long
1
public/build/assets/app-f65997bb.css
Normal file
1
public/build/assets/app-f65997bb.css
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"resources/css/app.css": {
|
||||
"file": "assets/app-53e4d707.css",
|
||||
"file": "assets/app-f65997bb.css",
|
||||
"isEntry": true,
|
||||
"src": "resources/css/app.css"
|
||||
},
|
||||
|
29
public/static/images/remote-monitor.svg
Normal file
29
public/static/images/remote-monitor.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 -5.23 70 70" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work>
|
||||
<dc:subject>
|
||||
Miscellaneous
|
||||
</dc:subject>
|
||||
<dc:identifier>
|
||||
health-monitoring
|
||||
</dc:identifier>
|
||||
<dc:title>
|
||||
Health Monitoring
|
||||
</dc:title>
|
||||
<dc:format>
|
||||
image/svg+xml
|
||||
</dc:format>
|
||||
<dc:publisher>
|
||||
Amido Limited
|
||||
</dc:publisher>
|
||||
<dc:creator>
|
||||
Richard Slater
|
||||
</dc:creator>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path d="m -633.94123,753.25889 c -6.59942,-3.2916 -17.80605,-13.7307 -24.90952,-23.2035 l -0.38611,-0.5149 4.90905,0 c 3.3284,0 5.08031,-0.051 5.44092,-0.1594 0.95525,-0.2862 1.50799,-0.9179 2.58607,-2.9554 1.97619,-3.735 2.24879,-4.2224 2.2879,-4.0904 0.0218,0.074 0.44604,4.3009 0.94276,9.3939 0.87326,8.9538 0.91529,9.2823 1.27154,9.9368 0.62081,1.1407 1.47439,1.6301 2.85312,1.6359 1.01617,0 1.76269,-0.3415 2.41627,-1.1191 0.25355,-0.3016 1.82033,-3.2056 3.48173,-6.4532 l 3.02073,-5.9047 10.36659,-0.039 c 10.32236,-0.039 10.36894,-0.041 10.91581,-0.3356 1.1802,-0.6369 1.77594,-1.6202 1.77528,-2.9304 -6.9e-4,-1.3721 -0.67396,-2.4208 -1.91258,-2.9791 -0.5125,-0.231 -1.30161,-0.2501 -11.80218,-0.2858 -7.69785,-0.026 -11.47959,0.01 -11.97032,0.1108 -1.27206,0.264 -1.77303,0.7868 -3.0106,3.1416 l -1.08999,2.0739 -0.1043,-0.5158 c -0.0574,-0.2837 -0.47667,-4.3775 -0.9318,-9.0974 -0.45513,-4.7199 -0.88563,-8.7992 -0.95668,-9.0652 -0.36496,-1.3662 -1.62876,-2.2659 -3.16688,-2.2544 -1.04822,0.01 -1.94772,0.4395 -2.48617,1.1931 -0.17485,0.2447 -1.92936,3.5346 -3.8989,7.311 l -3.581,6.866 -5.76782,0.036 -5.76783,0.036 -0.83086,-1.6834 c -2.06318,-4.1804 -2.89449,-7.6097 -2.738,-11.2949 0.12425,-2.9261 0.69392,-5.0125 2.04328,-7.4832 1.10812,-2.029 3.06519,-4.3559 4.69277,-5.5795 1.78333,-1.3407 4.15216,-2.2461 6.64618,-2.5403 2.10735,-0.2485 4.60651,0.089 7.37391,0.9964 1.2153,0.3984 4.21499,1.9073 5.62954,2.8318 2.45012,1.6012 5.68511,4.4633 7.84072,6.9369 l 0.80955,0.929 0.94007,-1.2397 c 1.88483,-2.4857 4.78785,-5.1075 7.55221,-6.8208 5.19337,-3.2187 11.05786,-4.2791 15.6703,-2.8335 3.74959,1.1752 6.7744,3.9944 8.98105,8.3706 2.19828,4.3596 2.39398,9.8576 0.53892,15.1404 -1.06649,3.0372 -2.39805,5.6594 -4.46756,8.7979 -2.55838,3.88 -4.87538,6.6471 -9.08862,10.8542 -5.31708,5.3093 -11.00984,9.9038 -16.48777,13.3068 -1.60577,0.9976 -3.84246,2.2037 -4.0818,2.201 -0.0583,0 -0.75536,-0.325 -1.54898,-0.7208 z" fill="#00bcf2" transform="translate(667.003 -694.43)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
@ -6,8 +6,9 @@
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#deploy"
|
||||
>
|
||||
<x-primary-button hx-disable>
|
||||
<x-primary-button class="flex items-center justify-between" :active="true" hx-disable>
|
||||
{{ __("Deploy") }}
|
||||
<x-heroicon name="o-play-circle" class="ml-1 h-5 w-5" />
|
||||
</x-primary-button>
|
||||
</form>
|
||||
@endif
|
||||
|
@ -1,10 +1,3 @@
|
||||
@php
|
||||
$class = "mx-auto px-4 sm:px-6 lg:px-8";
|
||||
if (! str($attributes->get("class"))->contains("max-w-")) {
|
||||
$class .= " max-w-7xl";
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div {!! $attributes->merge(["class" => $class]) !!}>
|
||||
<div {!! $attributes->merge(["class" => "max-w-5xl mx-auto"]) !!}>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
@ -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="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m5.231 13.481L15 17.25m-4.5-15H5.625c-.621 0-1.125.504-1.125 1.125v16.5c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Zm3.75 11.625a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 599 B |
15
resources/views/components/heroicons/o-play-circle.blade.php
Normal file
15
resources/views/components/heroicons/o-play-circle.blade.php
Normal file
@ -0,0 +1,15 @@
|
||||
<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="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 476 B |
@ -1,5 +1,5 @@
|
||||
<td
|
||||
{!! $attributes->merge(["class" => "whitespace-nowrap border-t border-gray-200 px-6 py-4 text-gray-700 dark:border-gray-700 dark:text-gray-300 w-1"]) !!}
|
||||
{!! $attributes->merge(["class" => "whitespace-nowrap px-6 py-4 text-gray-700 dark:text-gray-300 w-1"]) !!}
|
||||
>
|
||||
{{ $slot }}
|
||||
</td>
|
||||
|
@ -56,7 +56,7 @@ class="ml-1"
|
||||
-
|
||||
@endif
|
||||
</x-td>
|
||||
<x-td class="flex w-full justify-end">
|
||||
<x-td class="text-right">
|
||||
@if (in_array($file->status, [\App\Enums\BackupFileStatus::CREATED, \App\Enums\BackupFileStatus::RESTORED, \App\Enums\BackupFileStatus::RESTORE_FAILED]))
|
||||
<x-icon-button
|
||||
x-on:click="restoreAction = '{{ route('servers.databases.backups.files.restore', ['server' => $server, 'backup' => $backup, 'backupFile' => $file]) }}'; $dispatch('open-modal', 'restore-backup')"
|
||||
|
@ -32,7 +32,7 @@
|
||||
@include("databases.partials.backup-status", ["status" => $backup->status])
|
||||
</div>
|
||||
</x-td>
|
||||
<x-td class="flex w-full justify-end">
|
||||
<x-td class="text-right">
|
||||
<x-icon-button
|
||||
:href="route('servers.databases.backups', ['server' => $server, 'backup' => $backup])"
|
||||
>
|
||||
|
@ -33,7 +33,7 @@
|
||||
@include("databases.partials.database-status", ["status" => $database->status])
|
||||
</div>
|
||||
</x-td>
|
||||
<x-td class="flex w-full justify-end">
|
||||
<x-td class="text-right">
|
||||
<x-icon-button
|
||||
x-on:click="deleteAction = '{{ route('servers.databases.destroy', ['server' => $server, 'database' => $database]) }}'; $dispatch('open-modal', 'delete-database')"
|
||||
>
|
||||
|
@ -42,7 +42,7 @@
|
||||
@include("databases.partials.database-user-status", ["status" => $databaseUser->status])
|
||||
</div>
|
||||
</x-td>
|
||||
<x-td class="flex w-full justify-end">
|
||||
<x-td class="text-right">
|
||||
<x-icon-button
|
||||
x-on:click="$dispatch('open-modal', 'database-user-password'); document.getElementById('txt-database-user-password').value = 'Loading...';"
|
||||
hx-post="{{ route('servers.databases.users.password', ['server' => $server, 'databaseUser' => $databaseUser]) }}"
|
||||
|
@ -4,7 +4,16 @@
|
||||
@endif
|
||||
|
||||
<x-slot name="header">
|
||||
<h2 class="text-lg font-semibold">{{ $server->name }}</h2>
|
||||
@if (isset($header))
|
||||
<header class="flex-grow border-b border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
|
||||
<div class="mx-auto flex h-20 w-full max-w-full items-center justify-between">
|
||||
{{ $header }}
|
||||
</div>
|
||||
</header>
|
||||
@else
|
||||
<h2 class="text-lg font-semibold">{{ $server->name }}</h2>
|
||||
@endif
|
||||
|
||||
<div class="flex flex-col items-end">
|
||||
@include("servers.partials.server-status")
|
||||
<x-input-label class="mt-1 cursor-pointer" x-data="{ copied: false }">
|
||||
|
@ -155,7 +155,7 @@ class="fixed left-0 top-0 z-40 h-screen w-64 -translate-x-full border-r border-g
|
||||
<li>
|
||||
<x-sidebar-link
|
||||
:href="route('servers.logs', ['server' => $server])"
|
||||
:active="request()->routeIs('servers.logs')"
|
||||
:active="request()->routeIs('servers.logs*')"
|
||||
>
|
||||
<x-heroicon name="o-square-3-stack-3d" class="h-6 w-6" />
|
||||
<span class="ml-2">
|
||||
|
@ -50,7 +50,7 @@ class="mr-1"
|
||||
<x-tab-item
|
||||
class="mr-1"
|
||||
:href="route('servers.sites.logs', ['server' => $site->server, 'site' => $site])"
|
||||
:active="request()->routeIs('servers.sites.logs')"
|
||||
:active="request()->routeIs('servers.sites.logs*')"
|
||||
>
|
||||
<x-heroicon name="o-square-3-stack-3d" class="h-5 w-5" />
|
||||
<span class="ml-2 hidden xl:block">Logs</span>
|
||||
@ -105,7 +105,7 @@ class="flex w-full cursor-pointer items-center rounded-md border border-gray-300
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link
|
||||
:href="route('servers.sites.logs', ['server' => $site->server, 'site' => $site])"
|
||||
:active="request()->routeIs('servers.sites.logs')"
|
||||
:active="request()->routeIs('servers.sites.logs*')"
|
||||
>
|
||||
<x-heroicon name="o-square-3-stack-3d" class="h-5 w-5" />
|
||||
<span class="ml-2">Logs</span>
|
||||
|
@ -63,19 +63,19 @@ class="h-[200px] !p-0"
|
||||
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<span class="text-center lg:text-left">Total Memory</span>
|
||||
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||
{{ $lastMetric ? $lastMetric->memory_total : "-" }} MB
|
||||
{{ $lastMetric ? number_format((int) ($lastMetric->memory_total / 1024)) : "-" }} MB
|
||||
</div>
|
||||
</x-simple-card>
|
||||
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<span class="text-center lg:text-left">Used Memory</span>
|
||||
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||
{{ $lastMetric ? $lastMetric->memory_used : "-" }} MB
|
||||
{{ $lastMetric ? number_format((int) ($lastMetric->memory_used / 1024)) : "-" }} MB
|
||||
</div>
|
||||
</x-simple-card>
|
||||
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<span class="text-center lg:text-left">Free Memory</span>
|
||||
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||
{{ $lastMetric ? $lastMetric->memory_free : "-" }} MB
|
||||
{{ $lastMetric ? number_format((int) ($lastMetric->memory_free / 1024)) : "-" }} MB
|
||||
</div>
|
||||
</x-simple-card>
|
||||
</div>
|
||||
@ -83,19 +83,19 @@ class="h-[200px] !p-0"
|
||||
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<span class="text-center lg:text-left">Total Space</span>
|
||||
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||
{{ $lastMetric ? $lastMetric->disk_total : "-" }} MB
|
||||
{{ $lastMetric ? number_format($lastMetric->disk_total) : "-" }} MB
|
||||
</div>
|
||||
</x-simple-card>
|
||||
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<span class="text-center lg:text-left">Used Space</span>
|
||||
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||
{{ $lastMetric ? $lastMetric->disk_used : "-" }} MB
|
||||
{{ $lastMetric ? number_format($lastMetric->disk_used) : "-" }} MB
|
||||
</div>
|
||||
</x-simple-card>
|
||||
<x-simple-card class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<span class="text-center lg:text-left">Free Space</span>
|
||||
<div class="text-center text-xl font-bold text-gray-600 dark:text-gray-400 lg:text-right">
|
||||
{{ $lastMetric ? $lastMetric->disk_free : "-" }} MB
|
||||
{{ $lastMetric ? number_format($lastMetric->disk_free) : "-" }} MB
|
||||
</div>
|
||||
</x-simple-card>
|
||||
</div>
|
||||
|
@ -22,7 +22,12 @@ class="p-6"
|
||||
<x-input-label for="data_retention" value="Delete metrics older than" />
|
||||
<x-select-input id="data_retention" name="data_retention" class="mt-1 w-full">
|
||||
@foreach (config("core.metrics_data_retention") as $item)
|
||||
<option value="{{ $item }}">{{ $item }} Days</option>
|
||||
<option
|
||||
value="{{ $item }}"
|
||||
@if($server->monitoring()->handler()->data()['data_retention'] == $item) selected @endif
|
||||
>
|
||||
{{ $item }} Days
|
||||
</option>
|
||||
@endforeach
|
||||
</x-select-input>
|
||||
@error("data_retention")
|
||||
|
@ -1,5 +1,8 @@
|
||||
<x-server-layout :server="$server">
|
||||
<x-slot name="pageTitle">{{ $server->name }} Logs</x-slot>
|
||||
@if (isset($pageTitle))
|
||||
<x-slot name="pageTitle">{{ $pageTitle }}</x-slot>
|
||||
@endif
|
||||
|
||||
@include("server-logs.partials.logs-list")
|
||||
@include("server-logs.partials.header")
|
||||
@include("server-logs.partials.logs-list-live")
|
||||
</x-server-layout>
|
||||
|
59
resources/views/server-logs/partials/add-log.blade.php
Normal file
59
resources/views/server-logs/partials/add-log.blade.php
Normal file
@ -0,0 +1,59 @@
|
||||
<div x-data="">
|
||||
<x-card-header>
|
||||
<x-slot name="title">{{ __("Manage your remote logs") }}</x-slot>
|
||||
<x-slot name="description">
|
||||
{{ __("Here you can add new logs") }}
|
||||
</x-slot>
|
||||
<x-slot name="aside">
|
||||
<div class="flex flex-col items-end lg:flex-row lg:items-center">
|
||||
<x-primary-button class="cursor-pointer" x-on:click="$dispatch('open-modal', 'add-log')">
|
||||
{{ __("Add Remote Log") }}
|
||||
</x-primary-button>
|
||||
|
||||
<x-modal name="add-log">
|
||||
<form
|
||||
id="add-log-form"
|
||||
hx-post="{{ route("servers.logs.remote.store", ["server" => $server]) }}"
|
||||
hx-select="#add-log-form"
|
||||
hx-swap="outerHTML"
|
||||
class="p-6"
|
||||
>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __("Add Remote Log") }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-6">
|
||||
<x-input-label for="path" :value="__('Please introduce the full path to the log file.')" />
|
||||
<x-text-input
|
||||
list="sites"
|
||||
value="{{ old('path') }}"
|
||||
id="path"
|
||||
name="path"
|
||||
type="text"
|
||||
class="mt-1 w-full"
|
||||
/>
|
||||
<datalist id="sites">
|
||||
@foreach ($server->sites as $site)
|
||||
<option>{{ str($site->path)->append("/") }}</option>
|
||||
@endforeach
|
||||
</datalist>
|
||||
@error("path")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center 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>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-card-header>
|
||||
</div>
|
54
resources/views/server-logs/partials/header.blade.php
Normal file
54
resources/views/server-logs/partials/header.blade.php
Normal file
@ -0,0 +1,54 @@
|
||||
@if (isset($pageTitle))
|
||||
<x-slot name="pageTitle">{{ $pageTitle }}</x-slot>
|
||||
@endif
|
||||
|
||||
<x-slot name="header">
|
||||
<div class="hidden md:flex md:items-center md:justify-start">
|
||||
<x-tab-item
|
||||
class="mr-1"
|
||||
:href="route('servers.logs', ['server' => $server])"
|
||||
:active="request()->routeIs('servers.logs')"
|
||||
>
|
||||
<x-heroicon name="o-square-3-stack-3d" class="h-5 w-5" />
|
||||
<span class="ml-2 hidden xl:block">{{ __("Vito Logs") }}</span>
|
||||
</x-tab-item>
|
||||
<x-tab-item
|
||||
class="mr-1"
|
||||
:href="route('servers.logs.remote', ['server' => $server])"
|
||||
:active="request()->routeIs('servers.logs.remote')"
|
||||
>
|
||||
<x-heroicon name="o-document-magnifying-glass" class="h-5 w-5" />
|
||||
<span class="ml-2 hidden xl:block">{{ __("Remote Logs") }}</span>
|
||||
</x-tab-item>
|
||||
</div>
|
||||
<div class="md:hidden">
|
||||
<x-dropdown align="left">
|
||||
<x-slot name="trigger">
|
||||
<div
|
||||
class="flex w-full cursor-pointer items-center rounded-md border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-primary-500 dark:focus:ring-primary-500"
|
||||
>
|
||||
Select
|
||||
<button type="button" class="ml-2">
|
||||
<x-heroicon name="o-chevron-down" class="h-4 w-4 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link
|
||||
:href="route('servers.logs', ['server' => $server])"
|
||||
:active="request()->routeIs('servers.logs')"
|
||||
>
|
||||
<x-heroicon name="o-cog-6-tooth" class="h-5 w-5" />
|
||||
<span class="ml-2">{{ __("Vito Logs") }}</span>
|
||||
</x-dropdown-link>
|
||||
<x-dropdown-link
|
||||
:href="route('servers.logs.remote', ['server' => $server])"
|
||||
:active="request()->routeIs('servers.logs.remote')"
|
||||
>
|
||||
<x-heroicon name="o-document-magnifying-glass" class="h-5 w-5" />
|
||||
<span class="ml-2">{{ __("Remote Logs") }}</span>
|
||||
</x-dropdown-link>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
</x-slot>
|
107
resources/views/server-logs/partials/logs-list-live.blade.php
Normal file
107
resources/views/server-logs/partials/logs-list-live.blade.php
Normal file
@ -0,0 +1,107 @@
|
||||
@php
|
||||
if (isset($site)) {
|
||||
$logs = $site
|
||||
->logs()
|
||||
->latest()
|
||||
->paginate(10);
|
||||
} else {
|
||||
$logs = $server
|
||||
->logs()
|
||||
->where("is_remote", isset($remote) ? 1 : 0)
|
||||
->latest()
|
||||
->paginate(10);
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div x-data="{
|
||||
deleteAction: '',
|
||||
}">
|
||||
<x-card-header>
|
||||
<x-slot name="title">
|
||||
{{ $pageTitle ?? "Logs" }}
|
||||
</x-slot>
|
||||
</x-card-header>
|
||||
<x-live id="live-server-logs">
|
||||
<x-table>
|
||||
<x-thead>
|
||||
<x-tr>
|
||||
<x-th>
|
||||
@isset($remote)
|
||||
{{ __("Path") }}
|
||||
@else
|
||||
{{ __("Event") }}
|
||||
@endisset
|
||||
</x-th>
|
||||
<x-th>{{ __("Date") }}</x-th>
|
||||
<x-th></x-th>
|
||||
</x-tr>
|
||||
</x-thead>
|
||||
<x-tbody>
|
||||
@foreach ($logs as $log)
|
||||
<x-tr>
|
||||
<x-td>
|
||||
@isset($remote)
|
||||
{{ $log->name }}
|
||||
@else
|
||||
{{ $log->type }}
|
||||
@endif
|
||||
</x-td>
|
||||
<x-td>
|
||||
<x-datetime :value="$log->created_at" />
|
||||
</x-td>
|
||||
<x-td class="text-right">
|
||||
<x-icon-button
|
||||
x-on:click="$dispatch('open-modal', 'show-log'); document.getElementById('log-content').firstChild.innerHTML = '';"
|
||||
hx-get="{{ route('servers.logs.show', ['server' => $server, 'serverLog' => $log->id]) }}"
|
||||
hx-target="#log-content"
|
||||
hx-select="#log-content"
|
||||
>
|
||||
<x-heroicon name="o-eye" class="h-5 w-5" />
|
||||
</x-icon-button>
|
||||
|
||||
@if ($log->is_remote)
|
||||
<x-icon-button
|
||||
x-on:click="deleteAction = '{{ route('servers.logs.remote.destroy', ['server' => $server, 'serverLog' => $log->id]) }}'; $dispatch('open-modal', 'delete-remote-log')"
|
||||
>
|
||||
<x-heroicon name="o-trash" class="h-5 w-5" />
|
||||
</x-icon-button>
|
||||
@endif
|
||||
</x-td>
|
||||
</x-tr>
|
||||
@endforeach
|
||||
</x-tbody>
|
||||
</x-table>
|
||||
@if ($logs instanceof \Illuminate\Pagination\LengthAwarePaginator)
|
||||
<div class="mt-5">
|
||||
{{ $logs->withQueryString()->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</x-live>
|
||||
<div id="delete"></div>
|
||||
<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") }}
|
||||
@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>
|
||||
<x-confirmation-modal
|
||||
name="delete-remote-log"
|
||||
title="Confirm"
|
||||
description="Are you sure that you want to delete this log?"
|
||||
method="delete"
|
||||
x-bind:action="deleteAction"
|
||||
/>
|
||||
</div>
|
@ -1,70 +0,0 @@
|
||||
@php
|
||||
if (isset($site)) {
|
||||
$logs = $site
|
||||
->logs()
|
||||
->latest()
|
||||
->paginate(10);
|
||||
} else {
|
||||
$logs = $server
|
||||
->logs()
|
||||
->latest()
|
||||
->paginate(10);
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div x-data="">
|
||||
<x-card-header>
|
||||
<x-slot name="title">{{ __("Logs") }}</x-slot>
|
||||
</x-card-header>
|
||||
<x-live id="live-server-logs">
|
||||
<x-table>
|
||||
<x-tr>
|
||||
<x-th>{{ __("Event") }}</x-th>
|
||||
<x-th>{{ __("Date") }}</x-th>
|
||||
<x-th></x-th>
|
||||
</x-tr>
|
||||
@foreach ($logs as $log)
|
||||
<x-tr>
|
||||
<x-td>{{ $log->type }}</x-td>
|
||||
<x-td>
|
||||
<x-datetime :value="$log->created_at" />
|
||||
</x-td>
|
||||
<x-td>
|
||||
<x-icon-button
|
||||
x-on:click="$dispatch('open-modal', 'show-log'); document.getElementById('log-content').firstChild.innerHTML = '';"
|
||||
hx-get="{{ route('servers.logs.show', ['server' => $server, 'serverLog' => $log->id]) }}"
|
||||
hx-target="#log-content"
|
||||
hx-select="#log-content"
|
||||
>
|
||||
<x-heroicon name="o-eye" class="h-5 w-5" />
|
||||
</x-icon-button>
|
||||
</x-td>
|
||||
</x-tr>
|
||||
@endforeach
|
||||
</x-table>
|
||||
@if ($logs instanceof \Illuminate\Pagination\LengthAwarePaginator)
|
||||
<div class="mt-5">
|
||||
{{ $logs->withQueryString()->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</x-live>
|
||||
<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") }}
|
||||
@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>
|
6
resources/views/server-logs/remote-logs.blade.php
Normal file
6
resources/views/server-logs/remote-logs.blade.php
Normal file
@ -0,0 +1,6 @@
|
||||
<x-server-layout :server="$server">
|
||||
@include("server-logs.partials.header")
|
||||
|
||||
@include("server-logs.partials.add-log")
|
||||
@include("server-logs.partials.logs-list-live")
|
||||
</x-server-layout>
|
@ -15,5 +15,5 @@
|
||||
@endif
|
||||
</x-live>
|
||||
|
||||
@include("server-logs.partials.logs-list")
|
||||
@include("server-logs.partials.logs-list-live", ["pageTitle" => "Logs"])
|
||||
</div>
|
||||
|
@ -0,0 +1,6 @@
|
||||
@include("services.partials.unit-actions.restart", ["disabled" => true])
|
||||
@include("services.partials.unit-actions.start", ["disabled" => true])
|
||||
@include("services.partials.unit-actions.stop", ["disabled" => true])
|
||||
@include("services.partials.unit-actions.enable", ["disabled" => true])
|
||||
@include("services.partials.unit-actions.disable", ["disabled" => true])
|
||||
@include("services.partials.unit-actions.uninstall")
|
@ -0,0 +1,37 @@
|
||||
<x-secondary-button class="!w-full" x-on:click="$dispatch('open-modal', 'install-remote-monitor')">
|
||||
Install
|
||||
</x-secondary-button>
|
||||
@push("modals")
|
||||
<x-modal name="install-remote-monitor">
|
||||
<form
|
||||
id="install-remote-monitor-form"
|
||||
hx-post="{{ route("servers.services.install", ["server" => $server]) }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-select="#install-remote-monitor-form"
|
||||
class="p-6"
|
||||
>
|
||||
@csrf
|
||||
<input type="hidden" name="name" value="remote-monitor" />
|
||||
<input type="hidden" name="type" value="monitoring" />
|
||||
<input type="hidden" name="version" value="latest" />
|
||||
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __("Install Remote Monitor") }}
|
||||
</h2>
|
||||
|
||||
@error("type")
|
||||
<x-input-error class="mt-2" :messages="$message" />
|
||||
@enderror
|
||||
|
||||
<div class="mt-6 flex justify-end">
|
||||
<x-secondary-button type="button" x-on:click="$dispatch('close')">
|
||||
{{ __("Cancel") }}
|
||||
</x-secondary-button>
|
||||
|
||||
<x-primary-button id="btn-remote-monitor" hx-disable class="ml-3">
|
||||
{{ __("Install") }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-modal>
|
||||
@endpush
|
@ -21,7 +21,8 @@ class="p-6"
|
||||
|
||||
<div class="mt-6">
|
||||
<x-alert-warning>
|
||||
Vito Agent is only works if you are running your Vito instance on a cloud not local!
|
||||
Vito Agent is only works if you are running your Vito instance on a cloud not local! Consider
|
||||
installing remote-monitor instead.
|
||||
</x-alert-warning>
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<x-icon-button
|
||||
:disabled="isset($disabled) ? $disabled : false"
|
||||
data-tooltip="Disable Service"
|
||||
class="cursor-pointer"
|
||||
href="{{ route('servers.services.disable', ['server' => $server, 'service' => $service]) }}"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<x-icon-button
|
||||
:disabled="$service->status != \App\Enums\ServiceStatus::DISABLED"
|
||||
:disabled="isset($disabled) ? $disabled : $service->status != \App\Enums\ServiceStatus::DISABLED"
|
||||
data-tooltip="Enable Service"
|
||||
class="cursor-pointer"
|
||||
href="{{ route('servers.services.enable', ['server' => $server, 'service' => $service]) }}"
|
||||
|
@ -1,4 +1,5 @@
|
||||
<x-icon-button
|
||||
:disabled="isset($disabled) ? $disabled : false"
|
||||
data-tooltip="Restart Service"
|
||||
class="cursor-pointer"
|
||||
href="{{ route('servers.services.restart', ['server' => $server, 'service' => $service]) }}"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<x-icon-button
|
||||
:disabled="$service->status != \App\Enums\ServiceStatus::STOPPED"
|
||||
:disabled="isset($disabled) ? $disabled : $service->status != \App\Enums\ServiceStatus::STOPPED"
|
||||
data-tooltip="Start Service"
|
||||
class="cursor-pointer"
|
||||
href="{{ route('servers.services.start', ['server' => $server, 'service' => $service]) }}"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<x-icon-button
|
||||
:disabled="isset($disabled) ? $disabled : $service->status != \App\Enums\ServiceStatus::READY"
|
||||
data-tooltip="Stop Service"
|
||||
:disabled="$service->status != \App\Enums\ServiceStatus::READY"
|
||||
class="cursor-pointer"
|
||||
href="{{ route('servers.services.stop', ['server' => $server, 'service' => $service]) }}"
|
||||
>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<x-site-layout :site="$site">
|
||||
<x-slot name="pageTitle">{{ __("Logs") }}</x-slot>
|
||||
<x-slot name="pageTitle">{{ $pageTitle }}</x-slot>
|
||||
|
||||
@include("server-logs.partials.logs-list", ["server" => $site->server, "site" => $site])
|
||||
@include("server-logs.partials.logs-list-live", ["server" => $site->server, "site" => $site])
|
||||
</x-site-layout>
|
||||
|
@ -11,5 +11,5 @@
|
||||
@endif
|
||||
</x-live>
|
||||
|
||||
@include("server-logs.partials.logs-list", ["server" => $site->server, "site" => $site])
|
||||
@include("server-logs.partials.logs-list-live", ["server" => $site->server, "site" => $site])
|
||||
</x-site-layout>
|
||||
|
@ -150,6 +150,9 @@
|
||||
// logs
|
||||
Route::prefix('/{server}/logs')->group(function () {
|
||||
Route::get('/', [ServerLogController::class, 'index'])->name('servers.logs');
|
||||
Route::get('/remote', [ServerLogController::class, 'remote'])->name('servers.logs.remote');
|
||||
Route::post('/remote', [ServerLogController::class, 'store'])->name('servers.logs.remote.store');
|
||||
Route::delete('/remote/{serverLog}', [ServerLogController::class, 'destroy'])->name('servers.logs.remote.destroy');
|
||||
Route::get('/{serverLog}', [ServerLogController::class, 'show'])->name('servers.logs.show');
|
||||
});
|
||||
});
|
||||
|
@ -23,4 +23,37 @@ public function test_see_logs()
|
||||
->assertSuccessful()
|
||||
->assertSeeText($log->type);
|
||||
}
|
||||
|
||||
public function test_see_logs_remote()
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
/** @var ServerLog $log */
|
||||
$log = ServerLog::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'is_remote' => true,
|
||||
'type' => 'remote',
|
||||
'name' => 'see-remote-log',
|
||||
]);
|
||||
|
||||
$this->get(route('servers.logs.remote', $this->server))
|
||||
->assertSuccessful()
|
||||
->assertSeeText('see-remote-log');
|
||||
}
|
||||
|
||||
public function test_create_remote_log()
|
||||
{
|
||||
$this->actingAs($this->user);
|
||||
|
||||
$this->post(route('servers.logs.remote.store', [
|
||||
'server' => $this->server->id,
|
||||
]), [
|
||||
'path' => 'test-path',
|
||||
])->assertOk();
|
||||
|
||||
$this->assertDatabaseHas('server_logs', [
|
||||
'is_remote' => true,
|
||||
'name' => 'test-path',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -282,6 +282,6 @@ public function test_see_logs(): void
|
||||
'site' => $this->site,
|
||||
]))
|
||||
->assertSuccessful()
|
||||
->assertSee('Logs');
|
||||
->assertSee('Vito Logs');
|
||||
}
|
||||
}
|
||||
|
52
tests/Unit/Commands/GetMetricsCommandTest.php
Normal file
52
tests/Unit/Commands/GetMetricsCommandTest.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Commands;
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Facades\SSH;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GetMetricsCommandTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_get_metrics(): void
|
||||
{
|
||||
SSH::fake(<<<'EOF'
|
||||
load:1
|
||||
memory_total:1
|
||||
memory_used:1
|
||||
memory_free:1
|
||||
disk_total:1
|
||||
disk_used:1
|
||||
disk_free:1
|
||||
EOF);
|
||||
|
||||
Service::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'name' => 'remote-monitor',
|
||||
'type' => 'monitoring',
|
||||
'type_data' => [
|
||||
'data_retention' => 7,
|
||||
],
|
||||
'version' => 'latest',
|
||||
'status' => ServiceStatus::READY,
|
||||
]);
|
||||
|
||||
$this->artisan('metrics:get')
|
||||
->expectsOutput('Checked 1 metrics');
|
||||
|
||||
$this->assertDatabaseHas('metrics', [
|
||||
'server_id' => $this->server->id,
|
||||
'load' => 1,
|
||||
'memory_total' => 1,
|
||||
'memory_used' => 1,
|
||||
'memory_free' => 1,
|
||||
'disk_total' => 1,
|
||||
'disk_used' => 1,
|
||||
'disk_free' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user