feat: container metrics
This commit is contained in:
parent
439bee1203
commit
c81ad5cd03
@ -21,6 +21,6 @@ public function handle(Server $server, $version = 'latest', bool $restart = fals
|
|||||||
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
||||||
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
|
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
|
||||||
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
||||||
], $server, false);
|
], $server, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ private function pull_images($schedule)
|
|||||||
{
|
{
|
||||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if ($server->isMetricsEnabled()) {
|
if ($server->isSentinelEnabled()) {
|
||||||
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||||
|
@ -22,7 +22,9 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public ?int $usageBefore = null;
|
public ?int $usageBefore = null;
|
||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,9 @@ public function uniqueId(): string
|
|||||||
return $this->server->uuid;
|
return $this->server->uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
@ -50,7 +52,7 @@ public function handle(): void
|
|||||||
}
|
}
|
||||||
ray('Sentinel image is up to date');
|
ray('Sentinel image is up to date');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
|
// send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,9 @@ public function backoff(): int
|
|||||||
return isDev() ? 1 : 3;
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
@ -46,7 +48,7 @@ public function handle()
|
|||||||
if ($this->server->isFunctional()) {
|
if ($this->server->isFunctional()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
$this->remove_unnecessary_coolify_yaml();
|
$this->remove_unnecessary_coolify_yaml();
|
||||||
if ($this->server->isMetricsEnabled()) {
|
if ($this->server->isSentinelEnabled()) {
|
||||||
$this->server->checkSentinel();
|
$this->server->checkSentinel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
app/Livewire/Project/Shared/Metrics.php
Normal file
64
app/Livewire/Project/Shared/Metrics.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Metrics extends Component
|
||||||
|
{
|
||||||
|
public $resource;
|
||||||
|
|
||||||
|
public $chartId = 'container-cpu';
|
||||||
|
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
public $categories;
|
||||||
|
|
||||||
|
public int $interval = 5;
|
||||||
|
|
||||||
|
public bool $poll = true;
|
||||||
|
|
||||||
|
public function pollData()
|
||||||
|
{
|
||||||
|
if ($this->poll || $this->interval <= 10) {
|
||||||
|
$this->loadData();
|
||||||
|
if ($this->interval > 10) {
|
||||||
|
$this->poll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadData()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$metrics = $this->resource->getMetrics($this->interval);
|
||||||
|
$cpuMetrics = collect($metrics)->map(function ($metric) {
|
||||||
|
return [$metric[0], $metric[1]];
|
||||||
|
});
|
||||||
|
$memoryMetrics = collect($metrics)->map(function ($metric) {
|
||||||
|
return [$metric[0], $metric[2]];
|
||||||
|
});
|
||||||
|
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
|
||||||
|
'seriesData' => $cpuMetrics,
|
||||||
|
]);
|
||||||
|
$this->dispatch("refreshChartData-{$this->chartId}-memory", [
|
||||||
|
'seriesData' => $memoryMetrics,
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInterval()
|
||||||
|
{
|
||||||
|
if ($this->interval <= 10) {
|
||||||
|
$this->poll = true;
|
||||||
|
}
|
||||||
|
$this->loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.metrics');
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,7 @@ class Form extends Component
|
|||||||
'server.settings.metrics_refresh_rate_seconds' => 'required|integer|min:1',
|
'server.settings.metrics_refresh_rate_seconds' => 'required|integer|min:1',
|
||||||
'server.settings.metrics_history_days' => 'required|integer|min:1',
|
'server.settings.metrics_history_days' => 'required|integer|min:1',
|
||||||
'wildcard_domain' => 'nullable|url',
|
'wildcard_domain' => 'nullable|url',
|
||||||
|
'server.settings.is_server_api_enabled' => 'required|boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@ -63,7 +64,7 @@ class Form extends Component
|
|||||||
'server.settings.metrics_token' => 'Metrics Token',
|
'server.settings.metrics_token' => 'Metrics Token',
|
||||||
'server.settings.metrics_refresh_rate_seconds' => 'Metrics Interval',
|
'server.settings.metrics_refresh_rate_seconds' => 'Metrics Interval',
|
||||||
'server.settings.metrics_history_days' => 'Metrics History',
|
'server.settings.metrics_history_days' => 'Metrics History',
|
||||||
|
'server.settings.is_server_api_enabled' => 'Server API',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@ -85,6 +86,18 @@ public function updatedServerSettingsIsBuildServer()
|
|||||||
$this->dispatch('proxyStatusUpdated');
|
$this->dispatch('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkPortForServerApi()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->server->settings->is_server_api_enabled === true) {
|
||||||
|
$this->server->checkServerApi();
|
||||||
|
$this->dispatch('success', 'Server API is reachable.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@ -94,12 +107,22 @@ public function instantSave()
|
|||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->dispatch('success', 'Server updated.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
$this->dispatch('refreshServerShow');
|
$this->dispatch('refreshServerShow');
|
||||||
if ($this->server->isMetricsEnabled()) {
|
if ($this->server->isSentinelEnabled()) {
|
||||||
PullSentinelImageJob::dispatchSync($this->server);
|
PullSentinelImageJob::dispatchSync($this->server);
|
||||||
$this->dispatch('reloadWindow');
|
ray('Sentinel is enabled');
|
||||||
|
if ($this->server->settings->isDirty('is_metrics_enabled')) {
|
||||||
|
$this->dispatch('reloadWindow');
|
||||||
|
}
|
||||||
|
if ($this->server->settings->isDirty('is_server_api_enabled') && $this->server->settings->is_server_api_enabled === true) {
|
||||||
|
ray('Starting sentinel');
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ray('Sentinel is not enabled');
|
||||||
StopSentinel::dispatch($this->server);
|
StopSentinel::dispatch($this->server);
|
||||||
}
|
}
|
||||||
|
// $this->checkPortForServerApi();
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
@ -1167,4 +1167,33 @@ public function generate_preview_fqdn(int $pull_request_id)
|
|||||||
|
|
||||||
return $preview;
|
return $preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = generateApplicationContainerName($this);
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Stringable;
|
use Illuminate\Support\Stringable;
|
||||||
@ -460,15 +461,44 @@ public function forceDisableServer()
|
|||||||
Storage::disk('ssh-mux')->delete($this->muxFilename());
|
Storage::disk('ssh-mux')->delete($this->muxFilename());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isSentinelEnabled()
|
||||||
|
{
|
||||||
|
return $this->isMetricsEnabled() || $this->isServerApiEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
public function isMetricsEnabled()
|
public function isMetricsEnabled()
|
||||||
{
|
{
|
||||||
return $this->settings->is_metrics_enabled;
|
return $this->settings->is_metrics_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isServerApiEnabled()
|
||||||
|
{
|
||||||
|
return $this->settings->is_server_api_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkServerApi()
|
||||||
|
{
|
||||||
|
if ($this->isServerApiEnabled()) {
|
||||||
|
$server_ip = $this->ip;
|
||||||
|
if (isDev()) {
|
||||||
|
if ($this->id === 0) {
|
||||||
|
$server_ip = 'localhost';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$command = "curl -s http://{$server_ip}:12172/api/health";
|
||||||
|
$process = Process::timeout(5)->run($command);
|
||||||
|
if ($process->failed()) {
|
||||||
|
ray($process->exitCode(), $process->output(), $process->errorOutput());
|
||||||
|
throw new \Exception("Server API is not reachable on http://{$server_ip}:12172");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function checkSentinel()
|
public function checkSentinel()
|
||||||
{
|
{
|
||||||
ray("Checking sentinel on server: {$this->name}");
|
ray("Checking sentinel on server: {$this->name}");
|
||||||
if ($this->isMetricsEnabled()) {
|
if ($this->isSentinelEnabled()) {
|
||||||
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
|
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
|
||||||
$sentinel_found = json_decode($sentinel_found, true);
|
$sentinel_found = json_decode($sentinel_found, true);
|
||||||
$status = data_get($sentinel_found, '0.State.Status', 'exited');
|
$status = data_get($sentinel_found, '0.State.Status', 'exited');
|
||||||
@ -497,10 +527,10 @@ public function getCpuMetrics(int $mins = 5)
|
|||||||
$cpu = str($cpu)->explode("\n")->skip(1)->all();
|
$cpu = str($cpu)->explode("\n")->skip(1)->all();
|
||||||
$parsedCollection = collect($cpu)->flatMap(function ($item) {
|
$parsedCollection = collect($cpu)->flatMap(function ($item) {
|
||||||
return collect(explode("\n", trim($item)))->map(function ($line) {
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
[$time, $value] = explode(',', trim($line));
|
[$time, $cpu_usage_percent] = explode(',', trim($line));
|
||||||
$value = number_format($value, 0);
|
$cpu_usage_percent = number_format($cpu_usage_percent, 0);
|
||||||
|
|
||||||
return [(int) $time, (float) $value];
|
return [(int) $time, (float) $cpu_usage_percent];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -226,4 +226,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,4 +246,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,4 +227,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,4 +227,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,4 +222,33 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,9 +161,6 @@ function get_route_parameters(): array
|
|||||||
|
|
||||||
function get_latest_sentinel_version(): string
|
function get_latest_sentinel_version(): string
|
||||||
{
|
{
|
||||||
if (isDev()) {
|
|
||||||
return '0.0.8';
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
$response = Http::get('https://cdn.coollabs.io/sentinel/versions.json');
|
$response = Http::get('https://cdn.coollabs.io/sentinel/versions.json');
|
||||||
$versions = $response->json();
|
$versions = $response->json();
|
||||||
|
@ -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_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_server_api_enabled')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('server_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_server_api_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -60,6 +60,20 @@
|
|||||||
document.documentElement.classList.remove('dark')
|
document.documentElement.classList.remove('dark')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let theme = localStorage.theme
|
||||||
|
let baseColor = '#FCD452'
|
||||||
|
let textColor = '#ffffff'
|
||||||
|
|
||||||
|
function checkTheme() {
|
||||||
|
theme = localStorage.theme
|
||||||
|
if (theme == 'dark') {
|
||||||
|
baseColor = '#FCD452'
|
||||||
|
textColor = '#ffffff'
|
||||||
|
} else {
|
||||||
|
baseColor = 'black'
|
||||||
|
textColor = '#000000'
|
||||||
|
}
|
||||||
|
}
|
||||||
@auth
|
@auth
|
||||||
window.Pusher = Pusher;
|
window.Pusher = Pusher;
|
||||||
window.Echo = new Echo({
|
window.Echo = new Echo({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
|
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
|
||||||
<h3>CPU</h3>
|
<h3>CPU (%)</h3>
|
||||||
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
|
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
|
||||||
<option value="5">5 minutes (live)</option>
|
<option value="5">5 minutes (live)</option>
|
||||||
<option value="10">10 minutes (live)</option>
|
<option value="10">10 minutes (live)</option>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
|
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
|
||||||
<h3>Memory</h3>
|
<h3>Memory (MB)</h3>
|
||||||
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
|
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
|
||||||
<option value="5">5 minutes (live)</option>
|
<option value="5">5 minutes (live)</option>
|
||||||
<option value="10">10 minutes (live)</option>
|
<option value="10">10 minutes (live)</option>
|
||||||
|
@ -74,6 +74,9 @@
|
|||||||
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
||||||
href="#">Resource Operations
|
href="#">Resource Operations
|
||||||
</a>
|
</a>
|
||||||
|
<a class="menu-item" :class="activeTab === 'metrics' && 'menu-item-active'"
|
||||||
|
@click.prevent="activeTab = 'metrics'; window.location.hash = 'metrics'" href="#">Metrics
|
||||||
|
</a>
|
||||||
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
||||||
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
||||||
</a>
|
</a>
|
||||||
@ -126,6 +129,9 @@
|
|||||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||||
<livewire:project.shared.resource-operations :resource="$application" />
|
<livewire:project.shared.resource-operations :resource="$application" />
|
||||||
</div>
|
</div>
|
||||||
|
<div x-cloak x-show="activeTab === 'metrics'">
|
||||||
|
<livewire:project.shared.metrics :resource="$application" />
|
||||||
|
</div>
|
||||||
<div x-cloak x-show="activeTab === 'tags'">
|
<div x-cloak x-show="activeTab === 'tags'">
|
||||||
<livewire:project.shared.tags :resource="$application" />
|
<livewire:project.shared.tags :resource="$application" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,6 +42,9 @@
|
|||||||
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
||||||
href="#">Resource Operations
|
href="#">Resource Operations
|
||||||
</a>
|
</a>
|
||||||
|
<a class="menu-item" :class="activeTab === 'metrics' && 'menu-item-active'"
|
||||||
|
@click.prevent="activeTab = 'metrics'; window.location.hash = 'metrics'" href="#">Metrics
|
||||||
|
</a>
|
||||||
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
||||||
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
||||||
</a>
|
</a>
|
||||||
@ -92,6 +95,9 @@
|
|||||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||||
<livewire:project.shared.resource-operations :resource="$database" />
|
<livewire:project.shared.resource-operations :resource="$database" />
|
||||||
</div>
|
</div>
|
||||||
|
<div x-cloak x-show="activeTab === 'metrics'">
|
||||||
|
<livewire:project.shared.metrics :resource="$database" />
|
||||||
|
</div>
|
||||||
<div x-cloak x-show="activeTab === 'tags'">
|
<div x-cloak x-show="activeTab === 'tags'">
|
||||||
<livewire:project.shared.tags :resource="$database" />
|
<livewire:project.shared.tags :resource="$database" />
|
||||||
</div>
|
</div>
|
||||||
|
243
resources/views/livewire/project/shared/metrics.blade.php
Normal file
243
resources/views/livewire/project/shared/metrics.blade.php
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
<div>
|
||||||
|
<div class="flex items-center gap-2 ">
|
||||||
|
<h2>Metrics</h2>
|
||||||
|
</div>
|
||||||
|
<div class="pb-4">Basic metrics for your container.</div>
|
||||||
|
@if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose')
|
||||||
|
<div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div>
|
||||||
|
@else
|
||||||
|
@if (!str($resource->status)->contains('running'))
|
||||||
|
<div class="alert alert-warning">Metrics are only available when the application is running!</div>
|
||||||
|
@else
|
||||||
|
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
|
||||||
|
<option value="5">5 minutes (live)</option>
|
||||||
|
<option value="10">10 minutes (live)</option>
|
||||||
|
<option value="30">30 minutes</option>
|
||||||
|
<option value="60">1 hour</option>
|
||||||
|
<option value="720">12 hours</option>
|
||||||
|
<option value="10080">1 week</option>
|
||||||
|
<option value="43200">30 days</option>
|
||||||
|
</x-forms.select>
|
||||||
|
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()"
|
||||||
|
class="pt-5">
|
||||||
|
<h4>CPU (%)</h4>
|
||||||
|
<div wire:ignore id="{!! $chartId !!}-cpu"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
checkTheme();
|
||||||
|
const optionsServerCpu = {
|
||||||
|
stroke: {
|
||||||
|
curve: 'straight',
|
||||||
|
},
|
||||||
|
chart: {
|
||||||
|
height: '150px',
|
||||||
|
id: '{!! $chartId !!}-cpu',
|
||||||
|
type: 'area',
|
||||||
|
toolbar: {
|
||||||
|
show: false,
|
||||||
|
tools: {
|
||||||
|
download: true,
|
||||||
|
selection: false,
|
||||||
|
zoom: false,
|
||||||
|
zoomin: false,
|
||||||
|
zoomout: false,
|
||||||
|
pan: false,
|
||||||
|
reset: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animations: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: 'gradient',
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
offsetY: -10,
|
||||||
|
style: {
|
||||||
|
colors: ['#FCD452'],
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
show: true,
|
||||||
|
borderColor: '',
|
||||||
|
},
|
||||||
|
colors: [baseColor],
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: []
|
||||||
|
}],
|
||||||
|
noData: {
|
||||||
|
text: 'Loading...',
|
||||||
|
style: {
|
||||||
|
color: textColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`), optionsServerCpu);
|
||||||
|
serverCpuChart.render();
|
||||||
|
document.addEventListener('livewire:init', () => {
|
||||||
|
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
|
||||||
|
checkTheme();
|
||||||
|
serverCpuChart.updateOptions({
|
||||||
|
series: [{
|
||||||
|
data: chartData[0].seriesData,
|
||||||
|
}],
|
||||||
|
colors: [baseColor],
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
style: {
|
||||||
|
colors: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
show: true,
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
style: {
|
||||||
|
colors: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
noData: {
|
||||||
|
text: 'Loading...',
|
||||||
|
style: {
|
||||||
|
color: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h3>Memory (MB)</h3>
|
||||||
|
<div wire:ignore id="{!! $chartId !!}-memory"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
checkTheme();
|
||||||
|
const optionsServerMemory = {
|
||||||
|
stroke: {
|
||||||
|
curve: 'straight',
|
||||||
|
},
|
||||||
|
chart: {
|
||||||
|
height: '150px',
|
||||||
|
id: '{!! $chartId !!}-memory',
|
||||||
|
type: 'area',
|
||||||
|
toolbar: {
|
||||||
|
show: false,
|
||||||
|
tools: {
|
||||||
|
download: true,
|
||||||
|
selection: false,
|
||||||
|
zoom: false,
|
||||||
|
zoomin: false,
|
||||||
|
zoomout: false,
|
||||||
|
pan: false,
|
||||||
|
reset: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animations: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: 'gradient',
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
offsetY: -10,
|
||||||
|
style: {
|
||||||
|
colors: ['#FCD452'],
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
show: true,
|
||||||
|
borderColor: '',
|
||||||
|
},
|
||||||
|
colors: [baseColor],
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
style: {
|
||||||
|
colors: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: []
|
||||||
|
}],
|
||||||
|
noData: {
|
||||||
|
text: 'Loading...',
|
||||||
|
style: {
|
||||||
|
color: textColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`),
|
||||||
|
optionsServerMemory);
|
||||||
|
serverMemoryChart.render();
|
||||||
|
document.addEventListener('livewire:init', () => {
|
||||||
|
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
|
||||||
|
checkTheme();
|
||||||
|
serverMemoryChart.updateOptions({
|
||||||
|
series: [{
|
||||||
|
data: chartData[0].seriesData,
|
||||||
|
}],
|
||||||
|
colors: [baseColor],
|
||||||
|
xaxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
style: {
|
||||||
|
colors: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
min: 0,
|
||||||
|
show: true,
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
style: {
|
||||||
|
colors: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
noData: {
|
||||||
|
text: 'Loading...',
|
||||||
|
style: {
|
||||||
|
color: textColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -145,13 +145,16 @@ class="w-full mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-1
|
|||||||
helper="You can define the maximum duration for a deployment to run before timing it out." />
|
helper="You can define the maximum duration for a deployment to run before timing it out." />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h3 class="py-4">Metrics</h3>
|
<h3 class="py-4">Sentinel</h3>
|
||||||
@if ($server->isMetricsEnabled())
|
@if ($server->isSentinelEnabled())
|
||||||
<x-forms.button wire:click='restartSentinel'>Restart Collector</x-forms.button>
|
<x-forms.button wire:click='restartSentinel'>Restart</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="w-64">
|
<div class="w-64">
|
||||||
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable metrics" />
|
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable Metrics" />
|
||||||
|
{{-- <x-forms.checkbox instantSave id="server.settings.is_server_api_enabled" label="Enable Server API"
|
||||||
|
helper="You need to open port 12172 on your firewall. This API will be used to gather data from your server, which makes Coolify a lot faster than relying on SSH connections." />
|
||||||
|
<x-forms.button wire:click='checkPortForServerApi'>Check Port for Server API</x-forms.button> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
|
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
|
||||||
|
@ -7,22 +7,6 @@
|
|||||||
<livewire:server.delete :server="$server" />
|
<livewire:server.delete :server="$server" />
|
||||||
@if ($server->isFunctional() && $server->isMetricsEnabled())
|
@if ($server->isFunctional() && $server->isMetricsEnabled())
|
||||||
<div class="pt-10">
|
<div class="pt-10">
|
||||||
<script>
|
|
||||||
let theme = localStorage.theme
|
|
||||||
let baseColor = '#FCD452'
|
|
||||||
let textColor = '#ffffff'
|
|
||||||
|
|
||||||
function checkTheme() {
|
|
||||||
theme = localStorage.theme
|
|
||||||
if (theme == 'dark') {
|
|
||||||
baseColor = '#FCD452'
|
|
||||||
textColor = '#ffffff'
|
|
||||||
} else {
|
|
||||||
baseColor = 'black'
|
|
||||||
textColor = '#000000'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<livewire:charts.server-cpu :server="$server" />
|
<livewire:charts.server-cpu :server="$server" />
|
||||||
<livewire:charts.server-memory :server="$server" />
|
<livewire:charts.server-memory :server="$server" />
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user