refactor server view (phuuu)
This commit is contained in:
@@ -5,7 +5,7 @@ namespace App\Actions\Server;
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class InstallLogDrain
|
class StartLogDrain
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
@@ -13,12 +13,16 @@ class InstallLogDrain
|
|||||||
{
|
{
|
||||||
if ($server->settings->is_logdrain_newrelic_enabled) {
|
if ($server->settings->is_logdrain_newrelic_enabled) {
|
||||||
$type = 'newrelic';
|
$type = 'newrelic';
|
||||||
|
StopLogDrain::run($server);
|
||||||
} elseif ($server->settings->is_logdrain_highlight_enabled) {
|
} elseif ($server->settings->is_logdrain_highlight_enabled) {
|
||||||
$type = 'highlight';
|
$type = 'highlight';
|
||||||
|
StopLogDrain::run($server);
|
||||||
} elseif ($server->settings->is_logdrain_axiom_enabled) {
|
} elseif ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
$type = 'axiom';
|
$type = 'axiom';
|
||||||
|
StopLogDrain::run($server);
|
||||||
} elseif ($server->settings->is_logdrain_custom_enabled) {
|
} elseif ($server->settings->is_logdrain_custom_enabled) {
|
||||||
$type = 'custom';
|
$type = 'custom';
|
||||||
|
StopLogDrain::run($server);
|
||||||
} else {
|
} else {
|
||||||
$type = 'none';
|
$type = 'none';
|
||||||
}
|
}
|
||||||
@@ -151,6 +155,8 @@ services:
|
|||||||
- ./parsers.conf:/parsers.conf
|
- ./parsers.conf:/parsers.conf
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:24224:24224
|
- 127.0.0.1:24224:24224
|
||||||
|
labels:
|
||||||
|
- coolify.managed=true
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
');
|
');
|
||||||
$readme = base64_encode('# New Relic Log Drain
|
$readme = base64_encode('# New Relic Log Drain
|
||||||
@@ -202,15 +208,11 @@ Files:
|
|||||||
throw new \Exception('Unknown log drain type.');
|
throw new \Exception('Unknown log drain type.');
|
||||||
}
|
}
|
||||||
$restart_command = [
|
$restart_command = [
|
||||||
"echo 'Stopping old Fluent Bit'",
|
|
||||||
"cd $config_path && docker compose down --remove-orphans || true",
|
|
||||||
"echo 'Starting Fluent Bit'",
|
"echo 'Starting Fluent Bit'",
|
||||||
"cd $config_path && docker compose up -d --remove-orphans",
|
"cd $config_path && docker compose up -d",
|
||||||
];
|
];
|
||||||
$command = array_merge($command, $add_envs_command, $restart_command);
|
$command = array_merge($command, $add_envs_command, $restart_command);
|
||||||
|
|
||||||
StopLogDrain::run($server);
|
|
||||||
|
|
||||||
return instant_remote_process($command, $server);
|
return instant_remote_process($command, $server);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
@@ -12,7 +12,7 @@ class StopLogDrain
|
|||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return instant_remote_process(['docker rm -f coolify-log-drain || true'], $server);
|
return instant_remote_process(['docker rm -f coolify-log-drain'], $server, false);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ use App\Actions\Database\StartDatabaseProxy;
|
|||||||
use App\Actions\Database\StopDatabaseProxy;
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
use App\Actions\Proxy\CheckProxy;
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Actions\Server\InstallLogDrain;
|
use App\Actions\Server\StartLogDrain;
|
||||||
use App\Actions\Shared\ComplexStatusCheck;
|
use App\Actions\Shared\ComplexStatusCheck;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
@@ -362,7 +362,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
private function checkLogDrainContainer()
|
private function checkLogDrainContainer()
|
||||||
{
|
{
|
||||||
if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) {
|
if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) {
|
||||||
InstallLogDrain::dispatch($this->server);
|
StartLogDrain::dispatch($this->server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ namespace App\Jobs;
|
|||||||
use App\Actions\Docker\GetContainersStatus;
|
use App\Actions\Docker\GetContainersStatus;
|
||||||
use App\Actions\Proxy\CheckProxy;
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Actions\Server\InstallLogDrain;
|
use App\Actions\Server\StartLogDrain;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Notifications\Container\ContainerRestarted;
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@@ -109,10 +109,10 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($foundLogDrainContainer) {
|
if ($foundLogDrainContainer) {
|
||||||
$status = data_get($foundLogDrainContainer, 'State.Status');
|
$status = data_get($foundLogDrainContainer, 'State.Status');
|
||||||
if ($status !== 'running') {
|
if ($status !== 'running') {
|
||||||
InstallLogDrain::dispatch($this->server);
|
StartLogDrain::dispatch($this->server);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
InstallLogDrain::dispatch($this->server);
|
StartLogDrain::dispatch($this->server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,6 @@ class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
if (is_null($this->percentage)) {
|
if (is_null($this->percentage)) {
|
||||||
$this->percentage = $this->server->storageCheck();
|
$this->percentage = $this->server->storageCheck();
|
||||||
loggy('Server storage check percentage: '.$this->percentage);
|
|
||||||
}
|
}
|
||||||
if (! $this->percentage) {
|
if (! $this->percentage) {
|
||||||
return 'No percentage could be retrieved.';
|
return 'No percentage could be retrieved.';
|
||||||
|
@@ -4,45 +4,82 @@ namespace App\Livewire\Server;
|
|||||||
|
|
||||||
use App\Jobs\DockerCleanupJob;
|
use App\Jobs\DockerCleanupJob;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Livewire\Attributes\Rule;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Advanced extends Component
|
class Advanced extends Component
|
||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
protected $rules = [
|
public array $parameters = [];
|
||||||
'server.settings.concurrent_builds' => 'required|integer|min:1',
|
|
||||||
'server.settings.dynamic_timeout' => 'required|integer|min:1',
|
|
||||||
'server.settings.force_docker_cleanup' => 'required|boolean',
|
|
||||||
'server.settings.docker_cleanup_frequency' => 'required_if:server.settings.force_docker_cleanup,true|string',
|
|
||||||
'server.settings.docker_cleanup_threshold' => 'required_if:server.settings.force_docker_cleanup,false|integer|min:1|max:100',
|
|
||||||
'server.settings.server_disk_usage_notification_threshold' => 'required|integer|min:50|max:100',
|
|
||||||
'server.settings.delete_unused_volumes' => 'boolean',
|
|
||||||
'server.settings.delete_unused_networks' => 'boolean',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $validationAttributes = [
|
#[Rule(['integer', 'min:1'])]
|
||||||
|
public int $concurrentBuilds = 1;
|
||||||
|
|
||||||
'server.settings.concurrent_builds' => 'Concurrent Builds',
|
#[Rule(['integer', 'min:1'])]
|
||||||
'server.settings.dynamic_timeout' => 'Dynamic Timeout',
|
public int $dynamicTimeout = 1;
|
||||||
'server.settings.force_docker_cleanup' => 'Force Docker Cleanup',
|
|
||||||
'server.settings.docker_cleanup_frequency' => 'Docker Cleanup Frequency',
|
#[Rule('boolean')]
|
||||||
'server.settings.docker_cleanup_threshold' => 'Docker Cleanup Threshold',
|
public bool $forceDockerCleanup = false;
|
||||||
'server.settings.server_disk_usage_notification_threshold' => 'Server Disk Usage Notification Threshold',
|
|
||||||
'server.settings.delete_unused_volumes' => 'Delete Unused Volumes',
|
#[Rule('string')]
|
||||||
'server.settings.delete_unused_networks' => 'Delete Unused Networks',
|
public string $dockerCleanupFrequency = '*/10 * * * *';
|
||||||
];
|
|
||||||
|
#[Rule(['integer', 'min:1', 'max:99'])]
|
||||||
|
public int $dockerCleanupThreshold = 10;
|
||||||
|
|
||||||
|
#[Rule(['integer', 'min:1', 'max:99'])]
|
||||||
|
public int $serverDiskUsageNotificationThreshold = 50;
|
||||||
|
|
||||||
|
#[Rule('boolean')]
|
||||||
|
public bool $deleteUnusedVolumes = false;
|
||||||
|
|
||||||
|
#[Rule('boolean')]
|
||||||
|
public bool $deleteUnusedNetworks = false;
|
||||||
|
|
||||||
|
public function mount(string $server_uuid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->syncData();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return redirect()->route('server.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncData(bool $toModel = false)
|
||||||
|
{
|
||||||
|
if ($toModel) {
|
||||||
|
$this->validate();
|
||||||
|
$this->server->settings->concurrent_builds = $this->concurrentBuilds;
|
||||||
|
$this->server->settings->dynamic_timeout = $this->dynamicTimeout;
|
||||||
|
$this->server->settings->force_docker_cleanup = $this->forceDockerCleanup;
|
||||||
|
$this->server->settings->docker_cleanup_frequency = $this->dockerCleanupFrequency;
|
||||||
|
$this->server->settings->docker_cleanup_threshold = $this->dockerCleanupThreshold;
|
||||||
|
$this->server->settings->server_disk_usage_notification_threshold = $this->serverDiskUsageNotificationThreshold;
|
||||||
|
$this->server->settings->delete_unused_volumes = $this->deleteUnusedVolumes;
|
||||||
|
$this->server->settings->delete_unused_networks = $this->deleteUnusedNetworks;
|
||||||
|
$this->server->settings->save();
|
||||||
|
} else {
|
||||||
|
$this->concurrentBuilds = $this->server->settings->concurrent_builds;
|
||||||
|
$this->dynamicTimeout = $this->server->settings->dynamic_timeout;
|
||||||
|
$this->forceDockerCleanup = $this->server->settings->force_docker_cleanup;
|
||||||
|
$this->dockerCleanupFrequency = $this->server->settings->docker_cleanup_frequency;
|
||||||
|
$this->dockerCleanupThreshold = $this->server->settings->docker_cleanup_threshold;
|
||||||
|
$this->serverDiskUsageNotificationThreshold = $this->server->settings->server_disk_usage_notification_threshold;
|
||||||
|
$this->deleteUnusedVolumes = $this->server->settings->delete_unused_volumes;
|
||||||
|
$this->deleteUnusedNetworks = $this->server->settings->delete_unused_networks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->syncData(true);
|
||||||
$this->server->settings->save();
|
|
||||||
$this->dispatch('success', 'Server updated.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
$this->dispatch('refreshServerShow');
|
// $this->dispatch('refreshServerShow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->server->settings->refresh();
|
|
||||||
|
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,12 +97,11 @@ class Advanced extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$frequency = $this->server->settings->docker_cleanup_frequency;
|
if (! validate_cron_expression($this->dockerCleanupFrequency)) {
|
||||||
if (empty($frequency) || ! validate_cron_expression($frequency)) {
|
$this->dockerCleanupFrequency = $this->server->settings->getOriginal('docker_cleanup_frequency');
|
||||||
$this->server->settings->docker_cleanup_frequency = '*/10 * * * *';
|
throw new \Exception('Invalid Cron / Human expression for Docker Cleanup Frequency.');
|
||||||
throw new \Exception('Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.');
|
|
||||||
}
|
}
|
||||||
$this->server->settings->save();
|
$this->syncData(true);
|
||||||
$this->dispatch('success', 'Server updated.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
@@ -19,6 +19,15 @@ class Charts extends Component
|
|||||||
|
|
||||||
public bool $poll = true;
|
public bool $poll = true;
|
||||||
|
|
||||||
|
public function mount(string $server_uuid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function pollData()
|
public function pollData()
|
||||||
{
|
{
|
||||||
if ($this->poll || $this->interval <= 10) {
|
if ($this->poll || $this->interval <= 10) {
|
||||||
|
@@ -3,27 +3,33 @@
|
|||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Livewire\Attributes\Rule;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class CloudflareTunnels extends Component
|
class CloudflareTunnels extends Component
|
||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
protected $rules = [
|
#[Rule(['required', 'boolean'])]
|
||||||
'server.settings.is_cloudflare_tunnel' => 'required|boolean',
|
public bool $isCloudflareTunnelsEnabled;
|
||||||
];
|
|
||||||
|
|
||||||
protected $validationAttributes = [
|
public function mount(string $server_uuid)
|
||||||
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
{
|
||||||
];
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
|
$this->isCloudflareTunnelsEnabled = $this->server->settings->is_cloudflare_tunnel;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
$this->server->settings->is_cloudflare_tunnel = $this->isCloudflareTunnelsEnabled;
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
$this->dispatch('success', 'Server updated.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
$this->dispatch('refreshServerShow');
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -31,6 +37,7 @@ class CloudflareTunnels extends Component
|
|||||||
|
|
||||||
public function manualCloudflareConfig()
|
public function manualCloudflareConfig()
|
||||||
{
|
{
|
||||||
|
$this->isCloudflareTunnelsEnabled = true;
|
||||||
$this->server->settings->is_cloudflare_tunnel = true;
|
$this->server->settings->is_cloudflare_tunnel = true;
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
$this->server->refresh();
|
$this->server->refresh();
|
||||||
|
@@ -4,6 +4,7 @@ namespace App\Livewire\Server;
|
|||||||
|
|
||||||
use App\Actions\Server\DeleteServer;
|
use App\Actions\Server\DeleteServer;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Server;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
@@ -13,7 +14,16 @@ class Delete extends Component
|
|||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
use AuthorizesRequests;
|
||||||
|
|
||||||
public $server;
|
public Server $server;
|
||||||
|
|
||||||
|
public function mount(string $server_uuid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function delete($password)
|
public function delete($password)
|
||||||
{
|
{
|
||||||
|
@@ -3,27 +3,87 @@
|
|||||||
namespace App\Livewire\Server\Destination;
|
namespace App\Livewire\Server\Destination;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public ?Server $server = null;
|
public Server $server;
|
||||||
|
|
||||||
public $parameters = [];
|
public Collection $networks;
|
||||||
|
|
||||||
public function mount()
|
public function mount(string $server_uuid)
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->networks = collect();
|
||||||
if (is_null($this->server)) {
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
return redirect()->route('server.index');
|
loggy($this->server);
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createNetworkAndAttachToProxy()
|
||||||
|
{
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($name)
|
||||||
|
{
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
$found = $this->server->swarmDockers()->where('network', $name)->first();
|
||||||
|
if ($found) {
|
||||||
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
SwarmDocker::create([
|
||||||
|
'name' => $this->server->name.'-'.$name,
|
||||||
|
'network' => $this->name,
|
||||||
|
'server_id' => $this->server->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$found = $this->server->standaloneDockers()->where('network', $name)->first();
|
||||||
|
if ($found) {
|
||||||
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
StandaloneDocker::create([
|
||||||
|
'name' => $this->server->name.'-'.$name,
|
||||||
|
'network' => $name,
|
||||||
|
'server_id' => $this->server->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->createNetworkAndAttachToProxy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scan()
|
||||||
|
{
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
$alreadyAddedNetworks = $this->server->swarmDockers;
|
||||||
|
} else {
|
||||||
|
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
||||||
|
}
|
||||||
|
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
||||||
|
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
||||||
|
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
||||||
|
})->filter(function ($network) use ($alreadyAddedNetworks) {
|
||||||
|
return ! $alreadyAddedNetworks->contains('network', $network['Name']);
|
||||||
|
});
|
||||||
|
if ($this->networks->count() === 0) {
|
||||||
|
$this->dispatch('success', 'No new destinations found on this server.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->dispatch('success', 'Scan done.');
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.server.destination.show');
|
return view('livewire.server.destination.show');
|
||||||
|
@@ -111,7 +111,7 @@ class Form extends Component
|
|||||||
{
|
{
|
||||||
if ($field === 'server.settings.docker_cleanup_frequency') {
|
if ($field === 'server.settings.docker_cleanup_frequency') {
|
||||||
$frequency = $this->server->settings->docker_cleanup_frequency;
|
$frequency = $this->server->settings->docker_cleanup_frequency;
|
||||||
if (empty($frequency) || ! validate_cron_expression($frequency)) {
|
if (! validate_cron_expression($frequency)) {
|
||||||
$this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.');
|
$this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.');
|
||||||
$this->server->settings->docker_cleanup_frequency = '*/10 * * * *';
|
$this->server->settings->docker_cleanup_frequency = '*/10 * * * *';
|
||||||
}
|
}
|
||||||
|
@@ -2,84 +2,96 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
use App\Actions\Server\InstallLogDrain;
|
use App\Actions\Server\StartLogDrain;
|
||||||
use App\Actions\Server\StopLogDrain;
|
use App\Actions\Server\StopLogDrain;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Livewire\Attributes\Rule;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class LogDrains extends Component
|
class LogDrains extends Component
|
||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
public $parameters = [];
|
#[Rule(['boolean'])]
|
||||||
|
public bool $isLogDrainNewRelicEnabled = false;
|
||||||
|
|
||||||
protected $rules = [
|
#[Rule(['boolean'])]
|
||||||
'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean',
|
public bool $isLogDrainCustomEnabled = false;
|
||||||
'server.settings.logdrain_newrelic_license_key' => 'required|string',
|
|
||||||
'server.settings.logdrain_newrelic_base_uri' => 'required|string',
|
|
||||||
'server.settings.is_logdrain_highlight_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_highlight_project_id' => 'required|string',
|
|
||||||
'server.settings.is_logdrain_axiom_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_axiom_dataset_name' => 'required|string',
|
|
||||||
'server.settings.logdrain_axiom_api_key' => 'required|string',
|
|
||||||
'server.settings.is_logdrain_custom_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_custom_config' => 'required|string',
|
|
||||||
'server.settings.logdrain_custom_config_parser' => 'nullable',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $validationAttributes = [
|
#[Rule(['boolean'])]
|
||||||
'server.settings.is_logdrain_newrelic_enabled' => 'New Relic log drain',
|
public bool $isLogDrainAxiomEnabled = false;
|
||||||
'server.settings.logdrain_newrelic_license_key' => 'New Relic license key',
|
|
||||||
'server.settings.logdrain_newrelic_base_uri' => 'New Relic base URI',
|
|
||||||
'server.settings.is_logdrain_highlight_enabled' => 'Highlight log drain',
|
|
||||||
'server.settings.logdrain_highlight_project_id' => 'Highlight project ID',
|
|
||||||
'server.settings.is_logdrain_axiom_enabled' => 'Axiom log drain',
|
|
||||||
'server.settings.logdrain_axiom_dataset_name' => 'Axiom dataset name',
|
|
||||||
'server.settings.logdrain_axiom_api_key' => 'Axiom API key',
|
|
||||||
'server.settings.is_logdrain_custom_enabled' => 'Custom log drain',
|
|
||||||
'server.settings.logdrain_custom_config' => 'Custom log drain configuration',
|
|
||||||
'server.settings.logdrain_custom_config_parser' => 'Custom log drain configuration parser',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
#[Rule(['string', 'nullable'])]
|
||||||
|
public ?string $logDrainNewRelicLicenseKey = null;
|
||||||
|
|
||||||
|
#[Rule(['url', 'nullable'])]
|
||||||
|
public ?string $logDrainNewRelicBaseUri = null;
|
||||||
|
|
||||||
|
#[Rule(['string', 'nullable'])]
|
||||||
|
public ?string $logDrainAxiomDatasetName = null;
|
||||||
|
|
||||||
|
#[Rule(['string', 'nullable'])]
|
||||||
|
public ?string $logDrainAxiomApiKey = null;
|
||||||
|
|
||||||
|
#[Rule(['string', 'nullable'])]
|
||||||
|
public ?string $logDrainCustomConfig = null;
|
||||||
|
|
||||||
|
#[Rule(['string', 'nullable'])]
|
||||||
|
public ?string $logDrainCustomConfigParser = null;
|
||||||
|
|
||||||
|
public function mount(string $server_uuid)
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
try {
|
try {
|
||||||
$server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
if (is_null($server)) {
|
$this->syncData();
|
||||||
return redirect()->route('server.index');
|
|
||||||
}
|
|
||||||
$this->server = $server;
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureLogDrain()
|
public function syncData(bool $toModel = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ($toModel) {
|
||||||
|
$this->validate();
|
||||||
|
$this->server->settings->is_logdrain_newrelic_enabled = $this->isLogDrainNewRelicEnabled;
|
||||||
|
$this->server->settings->is_logdrain_axiom_enabled = $this->isLogDrainAxiomEnabled;
|
||||||
|
$this->server->settings->is_logdrain_custom_enabled = $this->isLogDrainCustomEnabled;
|
||||||
|
|
||||||
|
$this->server->settings->logdrain_newrelic_license_key = $this->logDrainNewRelicLicenseKey;
|
||||||
|
$this->server->settings->logdrain_newrelic_base_uri = $this->logDrainNewRelicBaseUri;
|
||||||
|
$this->server->settings->logdrain_axiom_dataset_name = $this->logDrainAxiomDatasetName;
|
||||||
|
$this->server->settings->logdrain_axiom_api_key = $this->logDrainAxiomApiKey;
|
||||||
|
$this->server->settings->logdrain_custom_config = $this->logDrainCustomConfig;
|
||||||
|
$this->server->settings->logdrain_custom_config_parser = $this->logDrainCustomConfigParser;
|
||||||
|
|
||||||
|
$this->server->settings->save();
|
||||||
|
} else {
|
||||||
|
$this->isLogDrainNewRelicEnabled = $this->server->settings->is_logdrain_newrelic_enabled;
|
||||||
|
$this->isLogDrainAxiomEnabled = $this->server->settings->is_logdrain_axiom_enabled;
|
||||||
|
$this->isLogDrainCustomEnabled = $this->server->settings->is_logdrain_custom_enabled;
|
||||||
|
|
||||||
|
$this->logDrainNewRelicLicenseKey = $this->server->settings->logdrain_newrelic_license_key;
|
||||||
|
$this->logDrainNewRelicBaseUri = $this->server->settings->logdrain_newrelic_base_uri;
|
||||||
|
$this->logDrainAxiomDatasetName = $this->server->settings->logdrain_axiom_dataset_name;
|
||||||
|
$this->logDrainAxiomApiKey = $this->server->settings->logdrain_axiom_api_key;
|
||||||
|
$this->logDrainCustomConfig = $this->server->settings->logdrain_custom_config;
|
||||||
|
$this->logDrainCustomConfigParser = $this->server->settings->logdrain_custom_config_parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
InstallLogDrain::run($this->server);
|
$this->syncData(true);
|
||||||
if (! $this->server->isLogDrainEnabled()) {
|
if ($this->server->isLogDrainEnabled()) {
|
||||||
$this->dispatch('serverRefresh');
|
StartLogDrain::run($this->server);
|
||||||
$this->dispatch('success', 'Log drain service stopped.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->dispatch('serverRefresh');
|
|
||||||
$this->dispatch('success', 'Log drain service started.');
|
$this->dispatch('success', 'Log drain service started.');
|
||||||
} catch (\Throwable $e) {
|
} else {
|
||||||
return handleError($e, $this);
|
StopLogDrain::run($this->server);
|
||||||
|
$this->dispatch('success', 'Log drain service stopped.');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function instantSave(string $type)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$ok = $this->submit($type);
|
|
||||||
if (! $ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->configureLogDrain();
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -88,79 +100,10 @@ class LogDrains extends Component
|
|||||||
public function submit(string $type)
|
public function submit(string $type)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->resetErrorBag();
|
$this->syncData(true);
|
||||||
if ($type === 'newrelic') {
|
|
||||||
$this->validate([
|
|
||||||
'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_newrelic_license_key' => 'required|string',
|
|
||||||
'server.settings.logdrain_newrelic_base_uri' => 'required|string',
|
|
||||||
]);
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_highlight_enabled' => false,
|
|
||||||
'is_logdrain_axiom_enabled' => false,
|
|
||||||
'is_logdrain_custom_enabled' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($type === 'highlight') {
|
|
||||||
$this->validate([
|
|
||||||
'server.settings.is_logdrain_highlight_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_highlight_project_id' => 'required|string',
|
|
||||||
]);
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_newrelic_enabled' => false,
|
|
||||||
'is_logdrain_axiom_enabled' => false,
|
|
||||||
'is_logdrain_custom_enabled' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($type === 'axiom') {
|
|
||||||
$this->validate([
|
|
||||||
'server.settings.is_logdrain_axiom_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_axiom_dataset_name' => 'required|string',
|
|
||||||
'server.settings.logdrain_axiom_api_key' => 'required|string',
|
|
||||||
]);
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_newrelic_enabled' => false,
|
|
||||||
'is_logdrain_highlight_enabled' => false,
|
|
||||||
'is_logdrain_custom_enabled' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($type === 'custom') {
|
|
||||||
$this->validate([
|
|
||||||
'server.settings.is_logdrain_custom_enabled' => 'required|boolean',
|
|
||||||
'server.settings.logdrain_custom_config' => 'required|string',
|
|
||||||
'server.settings.logdrain_custom_config_parser' => 'nullable',
|
|
||||||
]);
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_newrelic_enabled' => false,
|
|
||||||
'is_logdrain_highlight_enabled' => false,
|
|
||||||
'is_logdrain_axiom_enabled' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (! $this->server->isLogDrainEnabled()) {
|
|
||||||
StopLogDrain::dispatch($this->server);
|
|
||||||
}
|
|
||||||
$this->server->settings->save();
|
|
||||||
$this->dispatch('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
if ($type === 'newrelic') {
|
return handleError($e, $this);
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_newrelic_enabled' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($type === 'highlight') {
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_highlight_enabled' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($type === 'axiom') {
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_axiom_enabled' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($type === 'custom') {
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_logdrain_custom_enabled' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
handleError($e, $this);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,26 +8,63 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public ?Server $server = null;
|
public Server $server;
|
||||||
|
|
||||||
public $privateKeys = [];
|
public $privateKeys = [];
|
||||||
|
|
||||||
public $parameters = [];
|
public $parameters = [];
|
||||||
|
|
||||||
public function mount()
|
public function mount(string $server_uuid)
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
if (is_null($this->server)) {
|
|
||||||
return redirect()->route('server.index');
|
|
||||||
}
|
|
||||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
|
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setPrivateKey($privateKeyId)
|
||||||
|
{
|
||||||
|
$ownedPrivateKey = PrivateKey::ownedByCurrentTeam()->find($privateKeyId);
|
||||||
|
if (is_null($ownedPrivateKey)) {
|
||||||
|
$this->dispatch('error', 'You are not allowed to use this private key.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$originalPrivateKeyId = $this->server->getOriginal('private_key_id');
|
||||||
|
try {
|
||||||
|
$this->server->update(['private_key_id' => $privateKeyId]);
|
||||||
|
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(justCheckingNewKey: true);
|
||||||
|
if ($uptime) {
|
||||||
|
$this->dispatch('success', 'Private key updated successfully.');
|
||||||
|
} else {
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->server->update(['private_key_id' => $originalPrivateKeyId]);
|
||||||
|
$this->server->validateConnection();
|
||||||
|
$this->dispatch('error', $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkConnection()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||||
|
if ($uptime) {
|
||||||
|
$this->dispatch('success', 'Server is reachable.');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('error', 'Server is not reachable.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.<br><br>Error: '.$error);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.server.private-key.show');
|
return view('livewire.server.private-key.show');
|
||||||
|
@@ -3,38 +3,203 @@
|
|||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Livewire\Attributes\Rule;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
|
||||||
|
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
public array $parameters;
|
#[Rule(['required'])]
|
||||||
|
public string $name;
|
||||||
|
|
||||||
protected $listeners = ['refreshServerShow'];
|
#[Rule(['nullable'])]
|
||||||
|
public ?string $description;
|
||||||
|
|
||||||
public function mount()
|
#[Rule(['required'])]
|
||||||
|
public string $ip;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public string $user;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public string $port;
|
||||||
|
|
||||||
|
#[Rule(['nullable'])]
|
||||||
|
public ?string $validationLogs = null;
|
||||||
|
|
||||||
|
#[Rule(['nullable', 'url'])]
|
||||||
|
public ?string $wildcardDomain;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isReachable;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isUsable;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isSwarmManager;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isSwarmWorker;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isBuildServer;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isMetricsEnabled;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public string $sentinelToken;
|
||||||
|
|
||||||
|
#[Rule(['nullable'])]
|
||||||
|
public ?string $sentinelUpdatedAt;
|
||||||
|
|
||||||
|
#[Rule(['required', 'integer', 'min:1'])]
|
||||||
|
public int $sentinelMetricsRefreshRateSeconds;
|
||||||
|
|
||||||
|
#[Rule(['required', 'integer', 'min:1'])]
|
||||||
|
public int $sentinelMetricsHistoryDays;
|
||||||
|
|
||||||
|
#[Rule(['required', 'integer', 'min:10'])]
|
||||||
|
public int $sentinelPushIntervalSeconds;
|
||||||
|
|
||||||
|
#[Rule(['nullable', 'url'])]
|
||||||
|
public ?string $sentinelCustomUrl;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isSentinelEnabled;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public bool $isSentinelDebugEnabled;
|
||||||
|
|
||||||
|
#[Rule(['required'])]
|
||||||
|
public string $serverTimezone;
|
||||||
|
|
||||||
|
public array $timezones;
|
||||||
|
|
||||||
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
|
|
||||||
|
return [
|
||||||
|
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => '$refresh',
|
||||||
|
'refreshServerShow' => '$refresh',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount(string $server_uuid)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->firstOrFail();
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||||
$this->parameters = get_route_parameters();
|
$this->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray();
|
||||||
|
$this->syncData();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refreshServerShow()
|
public function syncData(bool $toModel = false)
|
||||||
{
|
{
|
||||||
$this->server->refresh();
|
if ($toModel) {
|
||||||
$this->dispatch('$refresh');
|
$this->validate();
|
||||||
|
$this->server->name = $this->name;
|
||||||
|
$this->server->description = $this->description;
|
||||||
|
$this->server->ip = $this->ip;
|
||||||
|
$this->server->user = $this->user;
|
||||||
|
$this->server->port = $this->port;
|
||||||
|
$this->server->validation_logs = $this->validationLogs;
|
||||||
|
$this->server->save();
|
||||||
|
|
||||||
|
$this->server->settings->is_swarm_manager = $this->isSwarmManager;
|
||||||
|
$this->server->settings->is_swarm_worker = $this->isSwarmWorker;
|
||||||
|
$this->server->settings->is_build_server = $this->isBuildServer;
|
||||||
|
$this->server->settings->is_metrics_enabled = $this->isMetricsEnabled;
|
||||||
|
$this->server->settings->sentinel_token = $this->sentinelToken;
|
||||||
|
$this->server->settings->sentinel_metrics_refresh_rate_seconds = $this->sentinelMetricsRefreshRateSeconds;
|
||||||
|
$this->server->settings->sentinel_metrics_history_days = $this->sentinelMetricsHistoryDays;
|
||||||
|
$this->server->settings->sentinel_push_interval_seconds = $this->sentinelPushIntervalSeconds;
|
||||||
|
$this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl;
|
||||||
|
$this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled;
|
||||||
|
$this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled;
|
||||||
|
$this->server->settings->server_timezone = $this->serverTimezone;
|
||||||
|
$this->server->settings->save();
|
||||||
|
} else {
|
||||||
|
$this->name = $this->server->name;
|
||||||
|
$this->description = $this->server->description;
|
||||||
|
$this->ip = $this->server->ip;
|
||||||
|
$this->user = $this->server->user;
|
||||||
|
$this->port = $this->server->port;
|
||||||
|
$this->wildcardDomain = $this->server->settings->wildcard_domain;
|
||||||
|
$this->isReachable = $this->server->settings->is_reachable;
|
||||||
|
$this->isUsable = $this->server->settings->is_usable;
|
||||||
|
$this->isSwarmManager = $this->server->settings->is_swarm_manager;
|
||||||
|
$this->isSwarmWorker = $this->server->settings->is_swarm_worker;
|
||||||
|
$this->isBuildServer = $this->server->settings->is_build_server;
|
||||||
|
$this->isMetricsEnabled = $this->server->settings->is_metrics_enabled;
|
||||||
|
$this->sentinelToken = $this->server->settings->sentinel_token;
|
||||||
|
$this->sentinelMetricsRefreshRateSeconds = $this->server->settings->sentinel_metrics_refresh_rate_seconds;
|
||||||
|
$this->sentinelMetricsHistoryDays = $this->server->settings->sentinel_metrics_history_days;
|
||||||
|
$this->sentinelPushIntervalSeconds = $this->server->settings->sentinel_push_interval_seconds;
|
||||||
|
$this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url;
|
||||||
|
$this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled;
|
||||||
|
$this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled;
|
||||||
|
$this->sentinelUpdatedAt = $this->server->settings->updated_at;
|
||||||
|
$this->serverTimezone = $this->server->settings->server_timezone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateServer($install = true)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validationLogs = $this->server->validation_logs = null;
|
||||||
|
$this->server->save();
|
||||||
|
$this->dispatch('init', $install);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkLocalhostConnection()
|
||||||
|
{
|
||||||
|
$this->syncData(true);
|
||||||
|
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||||
|
if ($uptime) {
|
||||||
|
$this->dispatch('success', 'Server is reachable.');
|
||||||
|
$this->server->settings->is_reachable = $this->isReachable = true;
|
||||||
|
$this->server->settings->is_usable = $this->isUsable = true;
|
||||||
|
$this->server->settings->save();
|
||||||
|
$this->dispatch('proxyStatusUpdated');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function regenerateSentinelToken()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->server->settings->generateSentinelToken();
|
||||||
|
$this->dispatch('success', 'Token regenerated & Sentinel restarted.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->dispatch('serverRefresh', false);
|
try {
|
||||||
|
$this->syncData(true);
|
||||||
|
$this->dispatch('success', 'Server updated');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@@ -1,68 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire\Server;
|
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
|
||||||
use App\Models\Server;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class ShowPrivateKey extends Component
|
|
||||||
{
|
|
||||||
public Server $server;
|
|
||||||
|
|
||||||
public $privateKeys;
|
|
||||||
|
|
||||||
public $parameters;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setPrivateKey($privateKeyId)
|
|
||||||
{
|
|
||||||
$ownedPrivateKey = PrivateKey::ownedByCurrentTeam()->find($privateKeyId);
|
|
||||||
if (is_null($ownedPrivateKey)) {
|
|
||||||
$this->dispatch('error', 'You are not allowed to use this private key.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$originalPrivateKeyId = $this->server->getOriginal('private_key_id');
|
|
||||||
try {
|
|
||||||
$this->server->update(['private_key_id' => $privateKeyId]);
|
|
||||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(justCheckingNewKey: true);
|
|
||||||
if ($uptime) {
|
|
||||||
$this->dispatch('success', 'Private key updated successfully.');
|
|
||||||
} else {
|
|
||||||
throw new \Exception($error);
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->server->update(['private_key_id' => $originalPrivateKeyId]);
|
|
||||||
$this->server->validateConnection();
|
|
||||||
$this->dispatch('error', $e->getMessage());
|
|
||||||
} finally {
|
|
||||||
$this->dispatch('refreshServerShow');
|
|
||||||
$this->server->refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkConnection()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
|
||||||
if ($uptime) {
|
|
||||||
$this->dispatch('success', 'Server is reachable.');
|
|
||||||
} else {
|
|
||||||
$this->dispatch('error', 'Server is not reachable.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.<br><br>Error: '.$error);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
} finally {
|
|
||||||
$this->dispatch('refreshServerShow');
|
|
||||||
$this->server->refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -63,7 +63,11 @@ class Server extends BaseModel
|
|||||||
$payload['ip'] = str($server->ip)->trim();
|
$payload['ip'] = str($server->ip)->trim();
|
||||||
}
|
}
|
||||||
$server->forceFill($payload);
|
$server->forceFill($payload);
|
||||||
|
});
|
||||||
|
static::saved(function ($server) {
|
||||||
|
if ($server->privateKey->isDirty()) {
|
||||||
|
refresh_server_connection($server->privateKey);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
static::created(function ($server) {
|
static::created(function ($server) {
|
||||||
ServerSetting::create([
|
ServerSetting::create([
|
||||||
@@ -1027,7 +1031,6 @@ $schema://$host {
|
|||||||
$this->refresh();
|
$this->refresh();
|
||||||
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
||||||
$isReachable = (bool) $this->settings->is_reachable;
|
$isReachable = (bool) $this->settings->is_reachable;
|
||||||
loggy('Server setting is_reachable changed to '.$isReachable.' for server '.$this->id.'. Unreachable notification sent: '.$unreachableNotificationSent);
|
|
||||||
// If the server is reachable, send the reachable notification if it was sent before
|
// If the server is reachable, send the reachable notification if it was sent before
|
||||||
if ($isReachable === true) {
|
if ($isReachable === true) {
|
||||||
if ($unreachableNotificationSent === true) {
|
if ($unreachableNotificationSent === true) {
|
||||||
|
@@ -117,7 +117,6 @@ class ServerSetting extends Model
|
|||||||
$domain = 'http://'.$settings->public_ipv6.':8000';
|
$domain = 'http://'.$settings->public_ipv6.':8000';
|
||||||
}
|
}
|
||||||
$this->sentinel_custom_url = $domain;
|
$this->sentinel_custom_url = $domain;
|
||||||
loggy('Sentinel URL: '.$domain);
|
|
||||||
if ($save) {
|
if ($save) {
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\View\Components\Server;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\View\Component;
|
|
||||||
|
|
||||||
class Sidebar extends Component
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a new component instance.
|
|
||||||
*/
|
|
||||||
public function __construct(public Server $server, public $parameters)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the view / contents that represent the component.
|
|
||||||
*/
|
|
||||||
public function render(): View|Closure|string
|
|
||||||
{
|
|
||||||
return view('components.server.sidebar');
|
|
||||||
}
|
|
||||||
}
|
|
@@ -127,7 +127,6 @@ function refreshSession(?Team $team = null): void
|
|||||||
}
|
}
|
||||||
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
|
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
|
||||||
{
|
{
|
||||||
loggy($error);
|
|
||||||
if ($error instanceof TooManyRequestsException) {
|
if ($error instanceof TooManyRequestsException) {
|
||||||
if (isset($livewire)) {
|
if (isset($livewire)) {
|
||||||
return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.");
|
return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.");
|
||||||
@@ -370,6 +369,9 @@ function translate_cron_expression($expression_to_validate): string
|
|||||||
}
|
}
|
||||||
function validate_cron_expression($expression_to_validate): bool
|
function validate_cron_expression($expression_to_validate): bool
|
||||||
{
|
{
|
||||||
|
if (empty($expression_to_validate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$isValid = false;
|
$isValid = false;
|
||||||
$expression = new CronExpression($expression_to_validate);
|
$expression = new CronExpression($expression_to_validate);
|
||||||
$isValid = $expression->isValid();
|
$isValid = $expression->isValid();
|
||||||
|
@@ -14,10 +14,7 @@
|
|||||||
'w-full' => $fullWidth,
|
'w-full' => $fullWidth,
|
||||||
])>
|
])>
|
||||||
@if (!$hideLabel)
|
@if (!$hideLabel)
|
||||||
<label @class([
|
<label @class(['flex gap-4 px-0 min-w-fit label', 'opacity-40' => $disabled])>
|
||||||
"flex gap-4 px-0 min-w-fit label",
|
|
||||||
'opacity-40' => $disabled,
|
|
||||||
])>
|
|
||||||
<span class="flex gap-2">
|
<span class="flex gap-2">
|
||||||
@if ($label)
|
@if ($label)
|
||||||
{!! $label !!}
|
{!! $label !!}
|
||||||
@@ -34,4 +31,5 @@
|
|||||||
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
|
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||||
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
|
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
|
||||||
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
|
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
@props([
|
@props([
|
||||||
'title' => 'Are you sure?',
|
'title' => 'Are you sure?',
|
||||||
'isErrorButton' => false,
|
'isErrorButton' => false,
|
||||||
|
'isHighlightedButton' => false,
|
||||||
'buttonTitle' => 'Confirm Action',
|
'buttonTitle' => 'Confirm Action',
|
||||||
'buttonFullWidth' => false,
|
'buttonFullWidth' => false,
|
||||||
'customButton' => null,
|
'customButton' => null,
|
||||||
@@ -143,6 +144,16 @@
|
|||||||
{{ $buttonTitle }}
|
{{ $buttonTitle }}
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
|
@elseif($isHighlightedButton)
|
||||||
|
@if ($buttonFullWidth)
|
||||||
|
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" isHighlighted wire:target>
|
||||||
|
{{ $buttonTitle }}
|
||||||
|
</x-forms.button>
|
||||||
|
@else
|
||||||
|
<x-forms.button @click="modalOpen=true" class="flex gap-2" isHighlighted wire:target>
|
||||||
|
{{ $buttonTitle }}
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
@else
|
@else
|
||||||
@if ($buttonFullWidth)
|
@if ($buttonFullWidth)
|
||||||
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" wire:target>
|
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" wire:target>
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
'title' => 'Are you sure?',
|
'title' => 'Are you sure?',
|
||||||
'buttonTitle' => 'Open Modal',
|
'buttonTitle' => 'Open Modal',
|
||||||
'isErrorButton' => false,
|
'isErrorButton' => false,
|
||||||
|
'isHighlightedButton' => false,
|
||||||
'disabled' => false,
|
'disabled' => false,
|
||||||
'action' => 'delete',
|
'action' => 'delete',
|
||||||
'content' => null,
|
'content' => null,
|
||||||
@@ -18,6 +19,8 @@
|
|||||||
<x-forms.button isError disabled>{{ $buttonTitle }}</x-forms.button>
|
<x-forms.button isError disabled>{{ $buttonTitle }}</x-forms.button>
|
||||||
@elseif ($isErrorButton)
|
@elseif ($isErrorButton)
|
||||||
<x-forms.button isError @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
|
<x-forms.button isError @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
|
||||||
|
@elseif ($isHighlightedButton)
|
||||||
|
<x-forms.button isHighlighted @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
|
||||||
@else
|
@else
|
||||||
<x-forms.button @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
|
<x-forms.button @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
<nav class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap">
|
<nav class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap">
|
||||||
<a class="{{ request()->routeIs('server.show') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.show') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.show', [
|
href="{{ route('server.show', [
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -28,20 +28,20 @@
|
|||||||
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
|
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
|
||||||
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.proxy', [
|
href="{{ route('server.proxy', [
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Proxy</button>
|
<button>Proxy</button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.resources', [
|
href="{{ route('server.resources', [
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Resources</button>
|
<button>Resources</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.command', [
|
href="{{ route('server.command', [
|
||||||
'server_uuid' => data_get($parameters, 'server_uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Terminal</button>
|
<button>Terminal</button>
|
||||||
</a>
|
</a>
|
||||||
|
16
resources/views/components/server/sidebar-proxy.blade.php
Normal file
16
resources/views/components/server/sidebar-proxy.blade.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@if ($server->proxySet())
|
||||||
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
|
<a class="{{ request()->routeIs('server.proxy') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
|
href="{{ route('server.proxy', $parameters) }}">
|
||||||
|
<button>Configuration</button>
|
||||||
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
|
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
|
||||||
|
<button>Dynamic Configurations</button>
|
||||||
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('server.proxy.logs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
|
href="{{ route('server.proxy.logs', $parameters) }}">
|
||||||
|
<button>Logs</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
@@ -1,16 +1,29 @@
|
|||||||
@if ($server->proxySet())
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<a class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
|
||||||
<a class="{{ request()->routeIs('server.proxy') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
href="{{ route('server.show', ['server_uuid' => $server->uuid]) }}" wire:navigate>General</a>
|
||||||
href="{{ route('server.proxy', $parameters) }}">
|
@if ($server->isFunctional())
|
||||||
<button>Configuration</button>
|
<a class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
|
||||||
|
href="{{ route('server.advanced', ['server_uuid' => $server->uuid]) }}" wire:navigate>Advanced
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
@endif
|
||||||
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
|
<a class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
|
||||||
<button>Dynamic Configurations</button>
|
href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}" wire:navigate>Private Key
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.proxy.logs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
@if ($server->isFunctional())
|
||||||
href="{{ route('server.proxy.logs', $parameters) }}">
|
<a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
|
||||||
<button>Logs</button>
|
href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}" wire:navigate>Cloudflare
|
||||||
|
Tunnels</a>
|
||||||
|
<a class="menu-item {{ $activeMenu === 'destinations' ? 'menu-item-active' : '' }}"
|
||||||
|
href="{{ route('server.destinations', ['server_uuid' => $server->uuid]) }}" wire:navigate>Destinations
|
||||||
</a>
|
</a>
|
||||||
</div>
|
<a class="menu-item {{ $activeMenu === 'log-drains' ? 'menu-item-active' : '' }}"
|
||||||
@endif
|
href="{{ route('server.log-drains', ['server_uuid' => $server->uuid]) }}" wire:navigate>Log
|
||||||
|
Drains</a>
|
||||||
|
<a class="menu-item {{ $activeMenu === 'metrics' ? 'menu-item-active' : '' }}"
|
||||||
|
href="{{ route('server.charts', ['server_uuid' => $server->uuid]) }}">Metrics</a>
|
||||||
|
@endif
|
||||||
|
@if (!$server->isLocalhost())
|
||||||
|
<a class="menu-item {{ $activeMenu === 'danger' ? 'menu-item-active' : '' }}"
|
||||||
|
href="{{ route('server.delete', ['server_uuid' => $server->uuid]) }}" wire:navigate>Danger</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@@ -1,18 +1,22 @@
|
|||||||
<form wire:submit='submit'>
|
<div>
|
||||||
|
<x-server.navbar :server="$server" />
|
||||||
|
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="advanced" />
|
||||||
|
<form wire:submit='submit' class="w-full">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h2>Advanced</h2>
|
<h2>Advanced</h2>
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
|
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
|
||||||
submitAction="manualCleanup" :actions="[
|
isHighlightedButton submitAction="manualCleanup" :actions="[
|
||||||
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
|
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
|
||||||
'Permanently deletes all unused images',
|
'Permanently deletes all unused images',
|
||||||
'Clears build cache',
|
'Clears build cache',
|
||||||
'Removes old versions of the Coolify helper image',
|
'Removes old versions of the Coolify helper image',
|
||||||
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
|
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
|
||||||
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
|
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
|
||||||
]" :confirmWithText="false" :confirmWithPassword="false"
|
]" :confirmWithText="false"
|
||||||
step2ButtonText="Trigger Docker Cleanup" />
|
:confirmWithPassword="false" step2ButtonText="Trigger Docker Cleanup" />
|
||||||
</div>
|
</div>
|
||||||
<div>Advanced configuration for your server.</div>
|
<div>Advanced configuration for your server.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -20,7 +24,7 @@
|
|||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
|
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
|
||||||
<x-forms.input id="server.settings.server_disk_usage_notification_threshold"
|
<x-forms.input id="serverDiskUsageNotificationThreshold"
|
||||||
label="Server disk usage notification threshold (%)" required
|
label="Server disk usage notification threshold (%)" required
|
||||||
helper="If the server disk usage exceeds this threshold, Coolify will send a notification to the team members." />
|
helper="If the server disk usage exceeds this threshold, Coolify will send a notification to the team members." />
|
||||||
</div>
|
</div>
|
||||||
@@ -30,13 +34,12 @@
|
|||||||
<h3>Docker Cleanup</h3>
|
<h3>Docker Cleanup</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-4">
|
<div class="flex flex-wrap items-center gap-4">
|
||||||
@if ($server->settings->force_docker_cleanup)
|
@if ($forceDockerCleanup)
|
||||||
<x-forms.input placeholder="*/10 * * * *" id="server.settings.docker_cleanup_frequency"
|
<x-forms.input placeholder="*/10 * * * *" id="dockerCleanupFrequency"
|
||||||
label="Docker cleanup frequency" required
|
label="Docker cleanup frequency" required
|
||||||
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
|
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
|
||||||
@else
|
@else
|
||||||
<x-forms.input id="server.settings.docker_cleanup_threshold" label="Docker cleanup threshold (%)"
|
<x-forms.input id="dockerCleanupThreshold" label="Docker cleanup threshold (%)" required
|
||||||
required
|
|
||||||
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
|
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
|
||||||
@endif
|
@endif
|
||||||
<div class="w-96">
|
<div class="w-96">
|
||||||
@@ -50,7 +53,7 @@
|
|||||||
<li>Optionally delete unused volumes (if enabled in advanced options).</li>
|
<li>Optionally delete unused volumes (if enabled in advanced options).</li>
|
||||||
<li>Optionally remove unused networks (if enabled in advanced options).</li>
|
<li>Optionally remove unused networks (if enabled in advanced options).</li>
|
||||||
</ul>"
|
</ul>"
|
||||||
instantSave id="server.settings.force_docker_cleanup" label="Force Docker Cleanup" />
|
instantSave id="forceDockerCleanup" label="Force Docker Cleanup" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -61,14 +64,14 @@
|
|||||||
functional issues.
|
functional issues.
|
||||||
</p>
|
</p>
|
||||||
<div class="w-96">
|
<div class="w-96">
|
||||||
<x-forms.checkbox instantSave id="server.settings.delete_unused_volumes" label="Delete Unused Volumes"
|
<x-forms.checkbox instantSave id="deleteUnusedVolumes" label="Delete Unused Volumes"
|
||||||
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
|
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
|
||||||
<ul class='list-disc pl-4 mt-2'>
|
<ul class='list-disc pl-4 mt-2'>
|
||||||
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
|
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
|
||||||
<li>Data from stopped containers volumes will be permanently lost.</li>
|
<li>Data from stopped containers volumes will be permanently lost.</li>
|
||||||
<li>No way to recover deleted volume data.</li>
|
<li>No way to recover deleted volume data.</li>
|
||||||
</ul>" />
|
</ul>" />
|
||||||
<x-forms.checkbox instantSave id="server.settings.delete_unused_networks" label="Delete Unused Networks"
|
<x-forms.checkbox instantSave id="deleteUnusedNetworks" label="Delete Unused Networks"
|
||||||
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
|
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
|
||||||
<ul class='list-disc pl-4 mt-2'>
|
<ul class='list-disc pl-4 mt-2'>
|
||||||
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>
|
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>
|
||||||
@@ -82,11 +85,13 @@
|
|||||||
<h3>Builds</h3>
|
<h3>Builds</h3>
|
||||||
<div>Customize the build process.</div>
|
<div>Customize the build process.</div>
|
||||||
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
|
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
|
||||||
<x-forms.input id="server.settings.concurrent_builds" label="Number of concurrent builds" required
|
<x-forms.input id="concurrentBuilds" label="Number of concurrent builds" required
|
||||||
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
|
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
|
||||||
<x-forms.input id="server.settings.dynamic_timeout" label="Deployment timeout (seconds)" required
|
<x-forms.input id="dynamicTimeout" label="Deployment timeout (seconds)" required
|
||||||
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@@ -1,4 +1,14 @@
|
|||||||
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
|
<div>
|
||||||
|
<x-slot:title>
|
||||||
|
{{ data_get_str($server, 'name')->limit(10) }} > Server Destinations | Coolify
|
||||||
|
</x-slot>
|
||||||
|
<x-server.navbar :server="$server" />
|
||||||
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="metrics" />
|
||||||
|
<div class="w-full">
|
||||||
|
<h2>Metrics</h2>
|
||||||
|
<div class="pb-4">Basic metrics for your container.</div>
|
||||||
|
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
|
||||||
<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>
|
||||||
@@ -237,4 +247,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,14 +1,24 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<x-slot:title>
|
||||||
|
{{ data_get_str($server, 'name')->limit(10) }} > Cloudflare Tunnels | Coolify
|
||||||
|
</x-slot>
|
||||||
|
<x-server.navbar :server="$server" />
|
||||||
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="cloudflare-tunnels" />
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex flex-col">
|
||||||
<div class="flex gap-1 items-center">
|
<div class="flex gap-1 items-center">
|
||||||
<h2>Cloudflare Tunnels</h2>
|
<h2>Cloudflare Tunnels</h2>
|
||||||
<x-helper class="inline-flex"
|
<x-helper class="inline-flex"
|
||||||
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br> You then can close your server's SSH port in the firewall of your hosting provider.<br><span class='dark:text-warning'>If you choose manual configuration, Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
|
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br> You then can close your server's SSH port in the firewall of your hosting provider.<br><span class='dark:text-warning'>If you choose manual configuration, Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
|
||||||
</div>
|
</div>
|
||||||
|
<div>Secure your servers with Cloudflare Tunnels</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<div class="flex flex-col gap-2 pt-6">
|
<div class="flex flex-col gap-2 pt-6">
|
||||||
@if ($server->settings->is_cloudflare_tunnel)
|
@if ($isCloudflareTunnelsEnabled)
|
||||||
<div class="w-64">
|
<div class="w-64">
|
||||||
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel"
|
<x-forms.checkbox instantSave id="isCloudflareTunnelsEnabled" label="Enabled" />
|
||||||
label="Enabled" />
|
|
||||||
</div>
|
</div>
|
||||||
@elseif (!$server->isFunctional())
|
@elseif (!$server->isFunctional())
|
||||||
<div
|
<div
|
||||||
@@ -18,25 +28,27 @@
|
|||||||
domain configured.
|
domain configured.
|
||||||
<br />
|
<br />
|
||||||
To <span class="font-semibold">manually</span> configure Cloudflare Tunnels, please
|
To <span class="font-semibold">manually</span> configure Cloudflare Tunnels, please
|
||||||
click <span wire:click="manualCloudflareConfig"
|
click <span wire:click="manualCloudflareConfig" class="underline cursor-pointer">here</span>,
|
||||||
class="underline cursor-pointer">here</span>, then you should validate the server.
|
then you should validate the server.
|
||||||
<br /><br />
|
<br /><br />
|
||||||
For more information, please read our <a
|
For more information, please read our <a
|
||||||
href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/" target="_blank"
|
href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/" target="_blank"
|
||||||
class="font-medium underline hover:text-yellow-600 dark:hover:text-yellow-200">documentation</a>.
|
class="font-medium underline hover:text-yellow-600 dark:hover:text-yellow-200">documentation</a>.
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if (!$server->settings->is_cloudflare_tunnel && $server->isFunctional())
|
@if (!$isCloudflareTunnelsEnabled && $server->isFunctional())
|
||||||
<x-modal-input buttonTitle="Automated Configuration" title="Cloudflare Tunnels"
|
<h4>Configuration</h4>
|
||||||
class="w-full" :closeOutside="false">
|
<div class="flex gap-2">
|
||||||
|
<x-modal-input buttonTitle="Automated" title="Cloudflare Tunnels" :closeOutside="false"
|
||||||
|
isHighlightedButton>
|
||||||
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
|
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
|
||||||
</x-modal-input>
|
</x-modal-input>
|
||||||
@endif
|
<x-forms.button wire:click="manualCloudflareConfig" class="w-20">
|
||||||
@if ($server->isFunctional() && !$server->settings->is_cloudflare_tunnel)
|
Manual
|
||||||
<div wire:click="manualCloudflareConfig" class="w-full underline cursor-pointer">
|
</x-forms.button>
|
||||||
I have configured Cloudflare Tunnels manually
|
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,11 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<x-slot:title>
|
||||||
|
{{ data_get_str($server, 'name')->limit(10) }} > Delete Server | Coolify
|
||||||
|
</x-slot>
|
||||||
|
<x-server.navbar :server="$server" />
|
||||||
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="danger" />
|
||||||
|
<div class="w-full">
|
||||||
@if ($server->id !== 0)
|
@if ($server->id !== 0)
|
||||||
<h2>Danger Zone</h2>
|
<h2>Danger Zone</h2>
|
||||||
<div class="">Woah. I hope you know what are you doing.</div>
|
<div class="">Woah. I hope you know what are you doing.</div>
|
||||||
@@ -8,8 +15,8 @@
|
|||||||
</div>
|
</div>
|
||||||
@if ($server->definedResources()->count() > 0)
|
@if ($server->definedResources()->count() > 0)
|
||||||
<div class="pb-2 text-red-500">You need to delete all resources before deleting this server.</div>
|
<div class="pb-2 text-red-500">You need to delete all resources before deleting this server.</div>
|
||||||
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete" submitAction="delete"
|
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
|
||||||
:actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
|
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
|
||||||
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
|
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
|
||||||
@else
|
@else
|
||||||
@@ -19,4 +26,6 @@
|
|||||||
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
|
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -2,6 +2,48 @@
|
|||||||
<x-slot:title>
|
<x-slot:title>
|
||||||
{{ data_get_str($server, 'name')->limit(10) }} > Server Destinations | Coolify
|
{{ data_get_str($server, 'name')->limit(10) }} > Server Destinations | Coolify
|
||||||
</x-slot>
|
</x-slot>
|
||||||
{{-- <x-server.navbar :server="$server" :parameters="$parameters" /> --}}
|
<x-server.navbar :server="$server" />
|
||||||
<livewire:destination.show :server="$server" />
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="destinations" />
|
||||||
|
<div class="w-full">
|
||||||
|
@if ($server->isFunctional())
|
||||||
|
<div class="flex items-end gap-2">
|
||||||
|
<h2>Destinations</h2>
|
||||||
|
<x-modal-input buttonTitle="+ Add" title="New Destination">
|
||||||
|
<livewire:destination.new.docker :server_id="$server->id" />
|
||||||
|
</x-modal-input>
|
||||||
|
<x-forms.button isHighlighted wire:click='scan'>Scan for Destinations</x-forms.button>
|
||||||
|
</div>
|
||||||
|
<div>Destinations are used to segregate resources by network.</div>
|
||||||
|
<h4 class="pt-4 pb-2">Available Destinations</h4>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
@foreach ($server->standaloneDockers as $docker)
|
||||||
|
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
|
||||||
|
<x-forms.button>{{ data_get($docker, 'network') }} </x-forms.button>
|
||||||
|
</a>
|
||||||
|
@endforeach
|
||||||
|
@foreach ($server->swarmDockers as $docker)
|
||||||
|
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
|
||||||
|
<x-forms.button>{{ data_get($docker, 'network') }} </x-forms.button>
|
||||||
|
</a>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@if ($networks->count() > 0)
|
||||||
|
<div class="pt-2">
|
||||||
|
<h3 class="pb-4">Found Destinations</h3>
|
||||||
|
<div class="flex flex-wrap gap-2 ">
|
||||||
|
@foreach ($networks as $network)
|
||||||
|
<div class="min-w-fit">
|
||||||
|
<x-forms.button wire:click="add('{{ data_get($network, 'Name') }}')">Add
|
||||||
|
{{ data_get($network, 'Name') }}</x-forms.button>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<div>Server is not validated. Validate first.</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,32 +1,41 @@
|
|||||||
<div>
|
<div>
|
||||||
<x-slot:title>
|
<x-slot:title>
|
||||||
{{ data_get_str($server, 'name')->limit(10) }} > Server LogDrains | Coolify
|
{{ data_get_str($server, 'name')->limit(10) }} > Server Log Drains | Coolify
|
||||||
</x-slot>
|
</x-slot>
|
||||||
{{-- <x-server.navbar :server="$server" :parameters="$parameters" /> --}}
|
<x-server.navbar :server="$server" />
|
||||||
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="log-drains" />
|
||||||
|
<div class="w-full">
|
||||||
@if ($server->isFunctional())
|
@if ($server->isFunctional())
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
<h2>Log Drains</h2>
|
<h2>Log Drains</h2>
|
||||||
<div class="pb-4">Sends service logs to 3rd party tools.</div>
|
<x-loading wire:target="instantSave" wire:loading.delay />
|
||||||
|
</div>
|
||||||
|
<div class="">Sends service logs to 3rd party tools.</div>
|
||||||
<div class="flex flex-col gap-4 pt-4">
|
<div class="flex flex-col gap-4 pt-4">
|
||||||
<div class="p-4 border dark:border-coolgray-300">
|
<div class="p-4 border dark:border-coolgray-300">
|
||||||
<form wire:submit='submit("newrelic")' class="flex flex-col">
|
<form wire:submit='submit("newrelic")' class="flex flex-col">
|
||||||
<h3>New Relic</h3>
|
<h3>New Relic</h3>
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
<x-forms.checkbox instantSave='instantSave("newrelic")'
|
@if ($isLogDrainAxiomEnabled || $isLogDrainCustomEnabled)
|
||||||
id="server.settings.is_logdrain_newrelic_enabled" label="Enabled" />
|
<x-forms.checkbox disabled id="isLogDrainNewRelicEnabled" label="Enabled" />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox instantSave id="isLogDrainNewRelicEnabled" label="Enabled" />
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||||
@if ($server->isLogDrainEnabled())
|
@if ($server->isLogDrainEnabled())
|
||||||
<x-forms.input disabled type="password" required
|
<x-forms.input disabled type="password" required id="logDrainNewRelicLicenseKey"
|
||||||
id="server.settings.logdrain_newrelic_license_key" label="License Key" />
|
label="License Key" />
|
||||||
<x-forms.input disabled required id="server.settings.logdrain_newrelic_base_uri"
|
<x-forms.input disabled required id="logDrainNewRelicBaseUri"
|
||||||
placeholder="https://log-api.eu.newrelic.com/log/v1"
|
placeholder="https://log-api.eu.newrelic.com/log/v1"
|
||||||
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
|
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
|
||||||
label="Endpoint" />
|
label="Endpoint" />
|
||||||
@else
|
@else
|
||||||
<x-forms.input type="password" required
|
<x-forms.input type="password" required id="logDrainNewRelicLicenseKey"
|
||||||
id="server.settings.logdrain_newrelic_license_key" label="License Key" />
|
label="License Key" />
|
||||||
<x-forms.input required id="server.settings.logdrain_newrelic_base_uri"
|
<x-forms.input required id="logDrainNewRelicBaseUri"
|
||||||
placeholder="https://log-api.eu.newrelic.com/log/v1"
|
placeholder="https://log-api.eu.newrelic.com/log/v1"
|
||||||
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
|
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
|
||||||
label="Endpoint" />
|
label="Endpoint" />
|
||||||
@@ -42,22 +51,24 @@
|
|||||||
|
|
||||||
<h3>Axiom</h3>
|
<h3>Axiom</h3>
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
<x-forms.checkbox instantSave='instantSave("axiom")' id="server.settings.is_logdrain_axiom_enabled"
|
@if ($isLogDrainNewRelicEnabled || $isLogDrainCustomEnabled)
|
||||||
label="Enabled" />
|
<x-forms.checkbox disabled id="isLogDrainAxiomEnabled" label="Enabled" />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox instantSave id="isLogDrainAxiomEnabled" label="Enabled" />
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<form wire:submit='submit("axiom")' class="flex flex-col">
|
<form wire:submit='submit("axiom")' class="flex flex-col">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||||
@if ($server->isLogDrainEnabled())
|
@if ($server->isLogDrainEnabled())
|
||||||
<x-forms.input disabled type="password" required
|
<x-forms.input disabled type="password" required id="logDrainAxiomApiKey"
|
||||||
id="server.settings.logdrain_axiom_api_key" label="API Key" />
|
label="API Key" />
|
||||||
<x-forms.input disabled required id="server.settings.logdrain_axiom_dataset_name"
|
<x-forms.input disabled required id="logDrainAxiomDatasetName"
|
||||||
label="Dataset Name" />
|
label="Dataset Name" />
|
||||||
@else
|
@else
|
||||||
<x-forms.input type="password" required id="server.settings.logdrain_axiom_api_key"
|
<x-forms.input type="password" required id="logDrainAxiomApiKey"
|
||||||
label="API Key" />
|
label="API Key" />
|
||||||
<x-forms.input required id="server.settings.logdrain_axiom_dataset_name"
|
<x-forms.input required id="logDrainAxiomDatasetName" label="Dataset Name" />
|
||||||
label="Dataset Name" />
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,40 +78,25 @@
|
|||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{-- <h3>Highlight.io</h3>
|
<h3>Custom FluentBit</h3>
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
<x-forms.checkbox instantSave='instantSave("highlight")'
|
@if ($isLogDrainNewRelicEnabled || $isLogDrainAxiomEnabled)
|
||||||
id="server.settings.is_logdrain_highlight_enabled" label="Enabled" />
|
<x-forms.checkbox disabled id="isLogDrainCustomEnabled" label="Enabled" />
|
||||||
</div>
|
@else
|
||||||
<form wire:submit='submit("highlight")' class="flex flex-col">
|
<x-forms.checkbox instantSave id="isLogDrainCustomEnabled" label="Enabled" />
|
||||||
<div class="flex flex-col gap-4">
|
@endif
|
||||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
|
||||||
<x-forms.input type="password" required id="server.settings.logdrain_highlight_project_id"
|
|
||||||
label="Project Id" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end gap-4 pt-6">
|
|
||||||
<x-forms.button type="submit">
|
|
||||||
Save
|
|
||||||
</x-forms.button>
|
|
||||||
</div>
|
|
||||||
</form> --}}
|
|
||||||
<h3>Custom FluentBit configuration</h3>
|
|
||||||
<div class="w-32">
|
|
||||||
<x-forms.checkbox instantSave='instantSave("custom")'
|
|
||||||
id="server.settings.is_logdrain_custom_enabled" label="Enabled" />
|
|
||||||
</div>
|
</div>
|
||||||
<form wire:submit='submit("custom")' class="flex flex-col">
|
<form wire:submit='submit("custom")' class="flex flex-col">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
@if ($server->isLogDrainEnabled())
|
@if ($server->isLogDrainEnabled())
|
||||||
<x-forms.textarea disabled rows="6" required
|
<x-forms.textarea disabled rows="6" required id="logDrainCustomConfig"
|
||||||
id="server.settings.logdrain_custom_config" label="Custom FluentBit Configuration" />
|
label="Custom FluentBit Configuration" />
|
||||||
<x-forms.textarea disabled id="server.settings.logdrain_custom_config_parser"
|
<x-forms.textarea disabled id="logDrainCustomConfigParser"
|
||||||
label="Custom Parser Configuration" />
|
label="Custom Parser Configuration" />
|
||||||
@else
|
@else
|
||||||
<x-forms.textarea rows="6" required id="server.settings.logdrain_custom_config"
|
<x-forms.textarea rows="6" required id="logDrainCustomConfig"
|
||||||
label="Custom FluentBit Configuration" />
|
label="Custom FluentBit Configuration" />
|
||||||
<x-forms.textarea id="server.settings.logdrain_custom_config_parser"
|
<x-forms.textarea id="logDrainCustomConfigParser"
|
||||||
label="Custom Parser Configuration" />
|
label="Custom Parser Configuration" />
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@@ -117,4 +113,6 @@
|
|||||||
@else
|
@else
|
||||||
<div>Server is not validated. Validate first.</div>
|
<div>Server is not validated. Validate first.</div>
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,43 @@
|
|||||||
<div>
|
<div>
|
||||||
<x-slot:title>
|
<x-slot:title>
|
||||||
Server Connection | Coolify
|
{{ data_get_str($server, 'name')->limit(10) }} > Server Connection | Coolify
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<livewire:server.show-private-key :server="$server" :privateKeys="$privateKeys" />
|
<x-server.navbar :server="$server" />
|
||||||
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
|
<x-server.sidebar :server="$server" activeMenu="private-key" />
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex items-end gap-2">
|
||||||
|
<h2>Private Key</h2>
|
||||||
|
<x-modal-input buttonTitle="+ Add" title="New Private Key">
|
||||||
|
<livewire:security.private-key.create />
|
||||||
|
</x-modal-input>
|
||||||
|
<x-forms.button isHighlighted wire:click.prevent='checkConnection'>
|
||||||
|
Check connection
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
<div class="pb-4">Change your server's private key.</div>
|
||||||
|
<div class="grid xl:grid-cols-2 grid-cols-1 gap-2">
|
||||||
|
@forelse ($privateKeys as $private_key)
|
||||||
|
<div
|
||||||
|
class="box-without-bg justify-between dark:bg-coolgray-100 bg-white items-center flex flex-col gap-2">
|
||||||
|
<div class="flex flex-col w-full">
|
||||||
|
<div class="box-title">{{ $private_key->name }}</div>
|
||||||
|
<div class="box-description">{{ $private_key->description }}</div>
|
||||||
|
</div>
|
||||||
|
@if (data_get($server, 'privateKey.uuid') !== $private_key->uuid)
|
||||||
|
<x-forms.button class="w-full" wire:click='setPrivateKey({{ $private_key->id }})'>
|
||||||
|
Use this key
|
||||||
|
</x-forms.button>
|
||||||
|
@else
|
||||||
|
<x-forms.button class="w-full" disabled>
|
||||||
|
Currently used
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div>No private keys found. </div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
<x-server.navbar :server="$server" :parameters="$parameters" />
|
<x-server.navbar :server="$server" :parameters="$parameters" />
|
||||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
<x-server.sidebar :server="$server" :parameters="$parameters" />
|
<x-server.sidebar-proxy :server="$server" :parameters="$parameters" />
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@if ($server->isFunctional())
|
@if ($server->isFunctional())
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
<x-server.navbar :server="$server" :parameters="$parameters" />
|
<x-server.navbar :server="$server" :parameters="$parameters" />
|
||||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
<x-server.sidebar :server="$server" :parameters="$parameters" />
|
<x-server.sidebar-proxy :server="$server" :parameters="$parameters" />
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<h2 class="pb-4">Logs</h2>
|
<h2 class="pb-4">Logs</h2>
|
||||||
<livewire:project.shared.get-logs :server="$server" container="coolify-proxy" />
|
<livewire:project.shared.get-logs :server="$server" container="coolify-proxy" />
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<x-server.navbar :server="$server" :parameters="$parameters" />
|
<x-server.navbar :server="$server" :parameters="$parameters" />
|
||||||
@if ($server->isFunctional())
|
@if ($server->isFunctional())
|
||||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
<x-server.sidebar :server="$server" :parameters="$parameters" />
|
<x-server.sidebar-proxy :server="$server" :parameters="$parameters" />
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<livewire:server.proxy :server="$server" />
|
<livewire:server.proxy :server="$server" />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
<div>
|
|
||||||
<div class="flex items-end gap-2">
|
|
||||||
<h2>Private Key</h2>
|
|
||||||
<x-modal-input buttonTitle="+ Add" title="New Private Key">
|
|
||||||
<livewire:security.private-key.create />
|
|
||||||
</x-modal-input>
|
|
||||||
<x-forms.button wire:click.prevent='checkConnection'>
|
|
||||||
Check connection
|
|
||||||
</x-forms.button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<div class="pb-4">Change your server's private key.</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid xl:grid-cols-2 grid-cols-1 gap-2">
|
|
||||||
@forelse ($privateKeys as $private_key)
|
|
||||||
<div class="box-without-bg justify-between dark:bg-coolgray-100 bg-white items-center flex flex-col gap-2">
|
|
||||||
<div class="flex flex-col w-full">
|
|
||||||
<div class="box-title">{{ $private_key->name }}</div>
|
|
||||||
<div class="box-description">{{ $private_key->description }}</div>
|
|
||||||
</div>
|
|
||||||
@if (data_get($server, 'privateKey.uuid') !== $private_key->uuid)
|
|
||||||
<x-forms.button class="w-full" wire:click='setPrivateKey({{ $private_key->id }})'>
|
|
||||||
Use this key
|
|
||||||
</x-forms.button>
|
|
||||||
@else
|
|
||||||
<x-forms.button class="w-full" disabled>
|
|
||||||
Currently used
|
|
||||||
</x-forms.button>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
@empty
|
|
||||||
<div>No private keys found. </div>
|
|
||||||
@endforelse
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@@ -2,72 +2,238 @@
|
|||||||
<x-slot:title>
|
<x-slot:title>
|
||||||
{{ data_get_str($server, 'name')->limit(10) }} > Server Configurations | Coolify
|
{{ data_get_str($server, 'name')->limit(10) }} > Server Configurations | Coolify
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<x-server.navbar :server="$server" :parameters="$parameters" />
|
<x-server.navbar :server="$server" />
|
||||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<x-server.sidebar :server="$server" activeMenu="general" />
|
||||||
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
|
||||||
@if ($server->isFunctional())
|
|
||||||
<a class="menu-item" :class="activeTab === 'advanced' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'advanced'; window.location.hash = 'advanced'" href="#">Advanced
|
|
||||||
</a>
|
|
||||||
@endif
|
|
||||||
<a class="menu-item" :class="activeTab === 'private-key' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'private-key'; window.location.hash = 'private-key'" href="#">Private
|
|
||||||
Key</a>
|
|
||||||
@if ($server->isFunctional())
|
|
||||||
<a class="menu-item" :class="activeTab === 'cloudflare-tunnels' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'cloudflare-tunnels'; window.location.hash = 'cloudflare-tunnels'"
|
|
||||||
href="#">Cloudflare Tunnels</a>
|
|
||||||
<a class="menu-item" :class="activeTab === 'destinations' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'destinations'; window.location.hash = 'destinations'"
|
|
||||||
href="#">Destinations</a>
|
|
||||||
<a class="menu-item" :class="activeTab === 'log-drains' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'log-drains'; window.location.hash = 'log-drains'" href="#">Log
|
|
||||||
Drains</a>
|
|
||||||
<a class="menu-item" :class="activeTab === 'metrics' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'metrics'; window.location.hash = 'metrics'" href="#">Metrics</a>
|
|
||||||
@endif
|
|
||||||
@if (!$server->isLocalhost())
|
|
||||||
<a class="menu-item" :class="activeTab === 'danger' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'danger'; window.location.hash = 'danger'" href="#">Danger</a>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
<form wire:submit.prevent='submit' class="flex flex-col">
|
||||||
<livewire:server.form :server="$server" />
|
<div class="flex gap-2">
|
||||||
</div>
|
<h2>General</h2>
|
||||||
<div x-cloak x-show="activeTab === 'advanced'" class="h-full">
|
@if ($server->id === 0)
|
||||||
<livewire:server.advanced :server="$server" />
|
<x-modal-confirmation title="Confirm Server Settings Change?" buttonTitle="Save"
|
||||||
</div>
|
submitAction="submit" :actions="[
|
||||||
<div x-cloak x-show="activeTab === 'private-key'" class="h-full">
|
'If you missconfigure the server, you could lose a lot of functionalities of Coolify.',
|
||||||
<livewire:server.private-key.show :server="$server" />
|
]" :confirmWithText="false" :confirmWithPassword="false"
|
||||||
</div>
|
step2ButtonText="Save" />
|
||||||
<div x-cloak x-show="activeTab === 'cloudflare-tunnels'" class="h-full">
|
|
||||||
<livewire:server.cloudflare-tunnels :server="$server" />
|
|
||||||
</div>
|
|
||||||
<div x-cloak x-show="activeTab === 'destinations'" class="h-full">
|
|
||||||
<livewire:server.destination.show :server="$server" />
|
|
||||||
</div>
|
|
||||||
<div x-cloak x-show="activeTab === 'log-drains'" class="h-full">
|
|
||||||
<livewire:server.log-drains :server="$server" />
|
|
||||||
</div>
|
|
||||||
<div x-cloak x-show="activeTab === 'metrics'" class="h-full">
|
|
||||||
@if ($server->isFunctional() && $server->isMetricsEnabled())
|
|
||||||
<h2>Metrics</h2>
|
|
||||||
<div class="pb-4">Basic metrics for your container.</div>
|
|
||||||
<div>
|
|
||||||
<livewire:server.charts :server="$server" />
|
|
||||||
</div>
|
|
||||||
@else
|
@else
|
||||||
No metrics available.
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
@if ($server->isFunctional())
|
||||||
|
<x-slide-over closeWithX fullScreen>
|
||||||
|
<x-slot:title>Validate & configure</x-slot:title>
|
||||||
|
<x-slot:content>
|
||||||
|
<livewire:server.validate-and-install :server="$server" ask />
|
||||||
|
</x-slot:content>
|
||||||
|
<x-forms.button @click="slideOverOpen=true" wire:click.prevent='validateServer'
|
||||||
|
isHighlighted>
|
||||||
|
Revalidate server
|
||||||
|
</x-forms.button>
|
||||||
|
</x-slide-over>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if (!$server->isLocalhost())
|
@if ($server->isFunctional())
|
||||||
<div x-cloak x-show="activeTab === 'danger'" class="h-full">
|
Server is reachable and validated.
|
||||||
<livewire:server.delete :server="$server" />
|
@else
|
||||||
|
You can't use this server until it is validated.
|
||||||
|
@endif
|
||||||
|
@if ((!$isReachable || !$isUsable) && $server->id !== 0)
|
||||||
|
<x-slide-over closeWithX fullScreen>
|
||||||
|
<x-slot:title>Validate & configure</x-slot:title>
|
||||||
|
<x-slot:content>
|
||||||
|
<livewire:server.validate-and-install :server="$server" />
|
||||||
|
</x-slot:content>
|
||||||
|
<x-forms.button @click="slideOverOpen=true"
|
||||||
|
class="mt-8 mb-4 w-full font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
|
||||||
|
wire:click.prevent='validateServer' isHighlighted>
|
||||||
|
Validate Server & Install Docker Engine
|
||||||
|
</x-forms.button>
|
||||||
|
</x-slide-over>
|
||||||
|
@if ($server->validation_logs)
|
||||||
|
<h4>Previous Validation Logs</h4>
|
||||||
|
<div class="pb-8">
|
||||||
|
{!! $server->validation_logs !!}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@endif
|
||||||
|
@if ((!$isReachable || !$isUsable) && $server->id === 0)
|
||||||
|
<x-forms.button class="mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
|
||||||
|
wire:click.prevent='checkLocalhostConnection' isHighlighted>
|
||||||
|
Validate Server
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
|
@if ($server->isForceDisabled() && isCloud())
|
||||||
|
<div class="pt-4 font-bold text-red-500">The system has disabled the server because you have
|
||||||
|
exceeded the
|
||||||
|
number of servers for which you have paid.</div>
|
||||||
|
@endif
|
||||||
|
<div class="flex flex-col gap-2 pt-4">
|
||||||
|
<div class="flex flex-col gap-2 w-full lg:flex-row">
|
||||||
|
<x-forms.input id="name" label="Name" required />
|
||||||
|
<x-forms.input id="description" label="Description" />
|
||||||
|
@if (!$isSwarmWorker && !$isBuildServer)
|
||||||
|
<x-forms.input placeholder="https://example.com" id="wildcard_domain"
|
||||||
|
label="Wildcard Domain"
|
||||||
|
helper='A wildcard domain allows you to receive a randomly generated domain for your new applications. <br><br>For instance, if you set "https://example.com" as your wildcard domain, your applications will receive domains like "https://randomId.example.com".' />
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 w-full lg:flex-row">
|
||||||
|
<x-forms.input type="password" id="ip" label="IP Address/Domain"
|
||||||
|
helper="An IP Address (127.0.0.1) or domain (example.com). Make sure there is no protocol like http(s):// so you provide a FQDN not a URL."
|
||||||
|
required />
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<x-forms.input id="user" label="User" required />
|
||||||
|
<x-forms.input type="number" id="port" label="Port" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full" x-data="{
|
||||||
|
open: false,
|
||||||
|
search: '{{ $serverTimezone ?: '' }}',
|
||||||
|
timezones: @js($timezones),
|
||||||
|
placeholder: '{{ $serverTimezone ? 'Search timezone...' : 'Select Server Timezone' }}',
|
||||||
|
init() {
|
||||||
|
this.$watch('search', value => {
|
||||||
|
if (value === '') {
|
||||||
|
this.open = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}">
|
||||||
|
<div class="flex items-center mb-1">
|
||||||
|
<label for="serverTimezone">Server
|
||||||
|
Timezone</label>
|
||||||
|
<x-helper class="ml-2"
|
||||||
|
helper="Server's timezone. This is used for backups, cron jobs, etc." />
|
||||||
|
</div>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="inline-flex relative items-center w-64">
|
||||||
|
<input autocomplete="off"
|
||||||
|
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
|
||||||
|
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
|
||||||
|
@focus="open = true" @click.away="open = false" @input="open = true"
|
||||||
|
class="w-full input" :placeholder="placeholder"
|
||||||
|
wire:model.debounce.300ms="serverTimezone">
|
||||||
|
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||||
|
@click="open = true">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div x-show="open"
|
||||||
|
class="overflow-auto overflow-x-hidden absolute z-50 mt-1 w-64 max-h-60 bg-white rounded-md border shadow-lg dark:bg-coolgray-100 dark:border-coolgray-200 scrollbar">
|
||||||
|
<template
|
||||||
|
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
|
||||||
|
:key="timezone">
|
||||||
|
<div @click="search = timezone; open = false; $wire.set('server.settings.server_timezone', timezone)"
|
||||||
|
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
|
||||||
|
x-text="timezone"></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full">
|
||||||
|
@if (!$server->isLocalhost())
|
||||||
|
<div class="w-96">
|
||||||
|
<x-forms.checkbox instantSave id="isBuildServer" label="Use it as a build server?" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel)
|
||||||
|
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span>
|
||||||
|
</h3>
|
||||||
|
<div class="pb-4">Read the docs <a class='underline dark:text-white'
|
||||||
|
href='https://coolify.io/docs/knowledge-base/docker/swarm'
|
||||||
|
target='_blank'>here</a>.
|
||||||
|
</div>
|
||||||
|
<div class="w-96">
|
||||||
|
@if ($server->settings->is_swarm_worker)
|
||||||
|
<x-forms.checkbox disabled instantSave type="checkbox" id="isSwarmManager"
|
||||||
|
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
|
||||||
|
label="Is it a Swarm Manager?" />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox instantSave type="checkbox" id="isSwarmManager"
|
||||||
|
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
|
||||||
|
label="Is it a Swarm Manager?" />
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($server->settings->is_swarm_manager)
|
||||||
|
<x-forms.checkbox disabled instantSave type="checkbox" id="isSwarmWorker"
|
||||||
|
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
|
||||||
|
label="Is it a Swarm Worker?" />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox instantSave type="checkbox" id="isSwarmWorker"
|
||||||
|
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
|
||||||
|
label="Is it a Swarm Worker?" />
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer())
|
||||||
|
<form wire:submit.prevent='submit'>
|
||||||
|
<div class="flex gap-2 items-center pt-4 pb-2">
|
||||||
|
<h3>Sentinel</h3>
|
||||||
|
@if ($server->isSentinelEnabled())
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
|
@if ($server->isSentinelLive())
|
||||||
|
<x-status.running status="In sync" noLoading title="{{ $sentinelUpdatedAt }}" />
|
||||||
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
<x-forms.button wire:click='restartSentinel'>Restart</x-forms.button>
|
||||||
|
@else
|
||||||
|
<x-status.stopped status="Out of sync" noLoading
|
||||||
|
title="{{ $sentinelUpdatedAt }}" />
|
||||||
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
<x-forms.button wire:click='restartSentinel'>Sync</x-forms.button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="flex gap-2">Experimental feature <x-helper
|
||||||
|
helper="Sentinel reports your server's & container's health and collects metrics." />
|
||||||
|
</div>
|
||||||
|
<div class="w-64">
|
||||||
|
<x-forms.checkbox wire:model.live="isSentinelEnabled" label="Enable Sentinel" />
|
||||||
|
@if ($server->isSentinelEnabled())
|
||||||
|
<x-forms.checkbox id="isSentinelDebugEnabled" label="Enable Sentinel Debug"
|
||||||
|
instantSave />
|
||||||
|
<x-forms.checkbox instantSave id="isMetricsEnabled" label="Enable Metrics" />
|
||||||
|
@else
|
||||||
|
<x-forms.checkbox id="isSentinelDebugEnabled" label="Enable Sentinel Debug" disabled
|
||||||
|
instantSave />
|
||||||
|
<x-forms.checkbox instantSave disabled id="isMetricsEnabled" label="Enable Metrics" />
|
||||||
|
label="Enable Metrics" />
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@if ($server->isSentinelEnabled())
|
||||||
|
<div class="flex flex-wrap gap-2 sm:flex-nowrap items-end">
|
||||||
|
<x-forms.input type="password" id="sentinelToken" label="Sentinel token" required
|
||||||
|
helper="Token for Sentinel." />
|
||||||
|
<x-forms.button wire:click="regenerateSentinelToken">Regenerate</x-forms.button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<x-forms.input id="sentinelCustomUrl" required label="Coolify URL"
|
||||||
|
helper="URL to your Coolify instance. If it is empty that means you do not have a FQDN set for your Coolify instance." />
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
|
||||||
|
<x-forms.input id="sentinelMetricsRefreshRateSeconds"
|
||||||
|
label="Metrics rate (seconds)" required
|
||||||
|
helper="The interval for gathering metrics. Lower means more disk space will be used." />
|
||||||
|
<x-forms.input id="sentinelMetricsHistoryDays" label="Metrics history (days)"
|
||||||
|
required helper="How many days should the metrics data should be reserved." />
|
||||||
|
<x-forms.input id="sentinelPushIntervalSeconds" label="Push interval (seconds)"
|
||||||
|
required
|
||||||
|
helper="How many seconds should the metrics data should be pushed to the collector." />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -34,6 +34,10 @@ use App\Livewire\Project\Show as ProjectShow;
|
|||||||
use App\Livewire\Security\ApiTokens;
|
use App\Livewire\Security\ApiTokens;
|
||||||
use App\Livewire\Security\PrivateKey\Index as SecurityPrivateKeyIndex;
|
use App\Livewire\Security\PrivateKey\Index as SecurityPrivateKeyIndex;
|
||||||
use App\Livewire\Security\PrivateKey\Show as SecurityPrivateKeyShow;
|
use App\Livewire\Security\PrivateKey\Show as SecurityPrivateKeyShow;
|
||||||
|
use App\Livewire\Server\Advanced as ServerAdvanced;
|
||||||
|
use App\Livewire\Server\Charts as ServerCharts;
|
||||||
|
use App\Livewire\Server\CloudflareTunnels;
|
||||||
|
use App\Livewire\Server\Delete as DeleteServer;
|
||||||
use App\Livewire\Server\Destination\Show as DestinationShow;
|
use App\Livewire\Server\Destination\Show as DestinationShow;
|
||||||
use App\Livewire\Server\Index as ServerIndex;
|
use App\Livewire\Server\Index as ServerIndex;
|
||||||
use App\Livewire\Server\LogDrains;
|
use App\Livewire\Server\LogDrains;
|
||||||
@@ -205,13 +209,17 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
|
|
||||||
Route::prefix('server/{server_uuid}')->group(function () {
|
Route::prefix('server/{server_uuid}')->group(function () {
|
||||||
Route::get('/', ServerShow::class)->name('server.show');
|
Route::get('/', ServerShow::class)->name('server.show');
|
||||||
|
Route::get('/advanced', ServerAdvanced::class)->name('server.advanced');
|
||||||
|
Route::get('/private-key', PrivateKeyShow::class)->name('server.private-key');
|
||||||
Route::get('/resources', ResourcesShow::class)->name('server.resources');
|
Route::get('/resources', ResourcesShow::class)->name('server.resources');
|
||||||
|
Route::get('/cloudflare-tunnels', CloudflareTunnels::class)->name('server.cloudflare-tunnels');
|
||||||
|
Route::get('/destinations', DestinationShow::class)->name('server.destinations');
|
||||||
|
Route::get('/log-drains', LogDrains::class)->name('server.log-drains');
|
||||||
|
Route::get('/metrics', ServerCharts::class)->name('server.charts');
|
||||||
|
Route::get('/danger', DeleteServer::class)->name('server.delete');
|
||||||
Route::get('/proxy', ProxyShow::class)->name('server.proxy');
|
Route::get('/proxy', ProxyShow::class)->name('server.proxy');
|
||||||
Route::get('/proxy/dynamic', ProxyDynamicConfigurations::class)->name('server.proxy.dynamic-confs');
|
Route::get('/proxy/dynamic', ProxyDynamicConfigurations::class)->name('server.proxy.dynamic-confs');
|
||||||
Route::get('/proxy/logs', ProxyLogs::class)->name('server.proxy.logs');
|
Route::get('/proxy/logs', ProxyLogs::class)->name('server.proxy.logs');
|
||||||
Route::get('/private-key', PrivateKeyShow::class)->name('server.private-key');
|
|
||||||
Route::get('/destinations', DestinationShow::class)->name('server.destinations');
|
|
||||||
Route::get('/log-drains', LogDrains::class)->name('server.log-drains');
|
|
||||||
Route::get('/terminal', ExecuteContainerCommand::class)->name('server.command');
|
Route::get('/terminal', ExecuteContainerCommand::class)->name('server.command');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user