mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 12:33:17 +00:00
Compare commits
40 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7acf99cde | ||
|
|
21d52d7846 | ||
|
|
ab5929cc69 | ||
|
|
1452cdf5ad | ||
|
|
3eb1a1f48c | ||
|
|
5f7a97c31f | ||
|
|
9cba0a6df3 | ||
|
|
af57b2aa73 | ||
|
|
b9b9582601 | ||
|
|
c8ba98b93d | ||
|
|
a50e1e2f0c | ||
|
|
1093294f06 | ||
|
|
c023be2348 | ||
|
|
deece51e83 | ||
|
|
e2ab569244 | ||
|
|
93b202bde4 | ||
|
|
6bb6de188c | ||
|
|
61f58fa30f | ||
|
|
026f3fd72d | ||
|
|
9a706d55b4 | ||
|
|
9646969107 | ||
|
|
df433efe62 | ||
|
|
efa7cab3e2 | ||
|
|
a386a1bde5 | ||
|
|
cc1bf023be | ||
|
|
edb06fa233 | ||
|
|
abdb8074b1 | ||
|
|
157da798dd | ||
|
|
9c5501326e | ||
|
|
3ea462efc9 | ||
|
|
f77df5b732 | ||
|
|
b77074fe4e | ||
|
|
a7b7d3fa32 | ||
|
|
f4f3034389 | ||
|
|
4b2ffb456f | ||
|
|
e17ff99c5b | ||
|
|
1cf036bbc6 | ||
|
|
da4c2ee60f | ||
|
|
fcf7c5ddd5 | ||
|
|
27926322e1 |
@@ -5,6 +5,7 @@ namespace App\Actions\CoolifyTask;
|
||||
use App\Enums\ActivityTypes;
|
||||
use App\Enums\ProcessStatus;
|
||||
use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Process\ProcessResult;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
@@ -94,7 +95,7 @@ class RunRemoteProcess
|
||||
]);
|
||||
$this->activity->save();
|
||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||
throw new \RuntimeException($processResult->errorOutput());
|
||||
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||
}
|
||||
|
||||
return $processResult;
|
||||
@@ -102,12 +103,11 @@ class RunRemoteProcess
|
||||
|
||||
protected function getCommand(): string
|
||||
{
|
||||
$user = $this->activity->getExtraProperty('user');
|
||||
$server_ip = $this->activity->getExtraProperty('server_ip');
|
||||
$port = $this->activity->getExtraProperty('port');
|
||||
$server_uuid = $this->activity->getExtraProperty('server_uuid');
|
||||
$command = $this->activity->getExtraProperty('command');
|
||||
$server = Server::whereUuid($server_uuid)->firstOrFail();
|
||||
|
||||
return generateSshCommand($server_ip, $user, $port, $command);
|
||||
return generateSshCommand($server, $command);
|
||||
}
|
||||
|
||||
protected function handleOutput(string $type, string $output)
|
||||
|
||||
@@ -2,21 +2,26 @@
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CheckConfigurationSync
|
||||
class CheckConfiguration
|
||||
{
|
||||
public function __invoke(Server $server, bool $reset = false)
|
||||
use AsAction;
|
||||
public function handle(Server $server, bool $reset = false)
|
||||
{
|
||||
$proxy_path = get_proxy_path();
|
||||
$proxy_configuration = instant_remote_process([
|
||||
"cat $proxy_path/docker-compose.yml",
|
||||
], $server, false);
|
||||
|
||||
if ($reset || is_null($proxy_configuration)) {
|
||||
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
|
||||
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||
}
|
||||
if (!$proxy_configuration || is_null($proxy_configuration)) {
|
||||
throw new \Exception("Could not generate proxy configuration");
|
||||
}
|
||||
return $proxy_configuration;
|
||||
}
|
||||
}
|
||||
27
app/Actions/Proxy/SaveConfiguration.php
Normal file
27
app/Actions/Proxy/SaveConfiguration.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Str;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class SaveConfiguration
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
$proxy_settings = CheckConfiguration::run($server, true);
|
||||
$proxy_path = get_proxy_path();
|
||||
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||
|
||||
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->save();
|
||||
|
||||
return instant_remote_process([
|
||||
"mkdir -p $proxy_path",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||
], $server);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SaveConfigurationSync
|
||||
{
|
||||
public function __invoke(Server $server)
|
||||
{
|
||||
try {
|
||||
$proxy_settings = resolve(CheckConfigurationSync::class)($server, true);
|
||||
$proxy_path = get_proxy_path();
|
||||
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||
|
||||
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->save();
|
||||
|
||||
instant_remote_process([
|
||||
"mkdir -p $proxy_path",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||
], $server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,27 @@
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Str;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
|
||||
class StartProxy
|
||||
{
|
||||
public function __invoke(Server $server, bool $async = true): Activity|string
|
||||
use AsAction;
|
||||
public function handle(Server $server, bool $async = true): Activity|string
|
||||
{
|
||||
$proxyType = data_get($server,'proxy.type');
|
||||
if ($proxyType === 'none') {
|
||||
return 'OK';
|
||||
}
|
||||
if (is_null($proxyType)) {
|
||||
$server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||
$server->proxy->status = ProxyStatus::EXITED->value;
|
||||
$server->save();
|
||||
}
|
||||
$proxy_path = get_proxy_path();
|
||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||
return $docker['network'];
|
||||
@@ -21,27 +34,36 @@ class StartProxy
|
||||
return "docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null 2>&1 || docker network create --attachable $network > /dev/null 2>&1";
|
||||
});
|
||||
|
||||
$configuration = resolve(CheckConfigurationSync::class)($server);
|
||||
|
||||
$configuration = CheckConfiguration::run($server);
|
||||
if (!$configuration) {
|
||||
throw new \Exception("Configuration is not synced");
|
||||
}
|
||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->save();
|
||||
$commands = [
|
||||
"command -v lsof >/dev/null || echo '####### Installing lsof...'",
|
||||
"command -v lsof >/dev/null || apt-get update",
|
||||
"command -v lsof >/dev/null || apt install -y lsof",
|
||||
"command -v lsof >/dev/null || command -v fuser >/dev/null || apt install -y psmisc",
|
||||
"echo '####### Creating required Docker networks...'",
|
||||
...$create_networks_command,
|
||||
"cd $proxy_path",
|
||||
"echo '####### Creating Docker Compose file...'",
|
||||
"echo '####### Pulling docker image...'",
|
||||
'docker compose pull',
|
||||
'docker compose pull || docker-compose pull',
|
||||
"echo '####### Stopping existing coolify-proxy...'",
|
||||
'docker compose down -v --remove-orphans',
|
||||
"lsof -nt -i:80 | xargs -r kill -9",
|
||||
"lsof -nt -i:443 | xargs -r kill -9",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1 || docker-compose down -v --remove-orphans > /dev/null 2>&1 || true",
|
||||
"command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'",
|
||||
"command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true",
|
||||
"command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true",
|
||||
"command -v fuser >/dev/null && fuser -k 80/tcp || true",
|
||||
"command -v fuser >/dev/null && fuser -k 443/tcp || true",
|
||||
"systemctl disable nginx > /dev/null 2>&1 || true",
|
||||
"systemctl disable apache2 > /dev/null 2>&1 || true",
|
||||
"systemctl disable apache > /dev/null 2>&1 || true",
|
||||
"echo '####### Starting coolify-proxy...'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
'docker compose up -d --remove-orphans || docker-compose up -d --remove-orphans',
|
||||
"echo '####### Proxy installed successfully...'"
|
||||
];
|
||||
if (!$async) {
|
||||
|
||||
@@ -4,11 +4,10 @@ namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\Team;
|
||||
|
||||
class InstallDocker
|
||||
{
|
||||
public function __invoke(Server $server, Team $team)
|
||||
public function __invoke(Server $server)
|
||||
{
|
||||
$dockerVersion = '24.0';
|
||||
$config = base64_encode('{
|
||||
@@ -19,15 +18,16 @@ class InstallDocker
|
||||
}
|
||||
}');
|
||||
$found = StandaloneDocker::where('server_id', $server->id);
|
||||
if ($found->count() == 0) {
|
||||
if ($found->count() == 0 && $server->id) {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
if (isDev()) {
|
||||
return remote_process([
|
||||
|
||||
if (isDev() && $server->id === 0) {
|
||||
$command = [
|
||||
"echo '####### Installing Prerequisites...'",
|
||||
"sleep 1",
|
||||
"echo '####### Installing/updating Docker Engine...'",
|
||||
@@ -35,9 +35,9 @@ class InstallDocker
|
||||
"sleep 4",
|
||||
"echo '####### Restarting Docker Engine...'",
|
||||
"ls -l /tmp"
|
||||
], $server);
|
||||
];
|
||||
} else {
|
||||
return remote_process([
|
||||
$command = [
|
||||
"echo '####### Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || apt-get update",
|
||||
"command -v jq >/dev/null || apt install -y jq",
|
||||
@@ -53,7 +53,8 @@ class InstallDocker
|
||||
"echo '####### Creating default Docker network (coolify)...'",
|
||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||
"echo '####### Done!'"
|
||||
], $server);
|
||||
];
|
||||
}
|
||||
return remote_process($command, $server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,16 @@ use Spatie\LaravelData\Data;
|
||||
class CoolifyTaskArgs extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $server_ip,
|
||||
public string $server_uuid,
|
||||
public string $command,
|
||||
public int $port,
|
||||
public string $user,
|
||||
public string $type,
|
||||
public ?string $type_uuid = null,
|
||||
public ?Model $model = null,
|
||||
public string $status = ProcessStatus::QUEUED->value,
|
||||
public ?string $status = null ,
|
||||
public bool $ignore_errors = false,
|
||||
) {
|
||||
if(is_null($status)){
|
||||
$this->status = ProcessStatus::QUEUED->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Sentry\Laravel\Integration;
|
||||
use Sentry\State\Scope;
|
||||
@@ -45,16 +46,19 @@ class Handler extends ExceptionHandler
|
||||
public function register(): void
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
if (isDev()) {
|
||||
return;
|
||||
}
|
||||
$this->settings = InstanceSettings::get();
|
||||
if ($this->settings->do_not_track || isDev()) {
|
||||
if ($this->settings->do_not_track) {
|
||||
return;
|
||||
}
|
||||
app('sentry')->configureScope(
|
||||
function (Scope $scope) {
|
||||
$scope->setUser(
|
||||
[
|
||||
'id' => config('sentry.server_name'),
|
||||
'email' => auth()->user()->email
|
||||
'email' => auth()->user()->email,
|
||||
'instanceAdmin' => User::find(0)->email
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Throwable;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
@@ -153,7 +152,7 @@ class Controller extends BaseController
|
||||
} else {
|
||||
abort(401);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
@@ -172,7 +171,7 @@ class Controller extends BaseController
|
||||
}
|
||||
$invitation->delete();
|
||||
return redirect()->route('team.index');
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class Index extends Component
|
||||
{
|
||||
public string $currentState = 'welcome';
|
||||
|
||||
public ?string $selectedServerType = null;
|
||||
public ?Collection $privateKeys = null;
|
||||
public ?int $selectedExistingPrivateKey = null;
|
||||
public ?string $privateKeyType = null;
|
||||
@@ -36,6 +37,11 @@ class Index extends Component
|
||||
public ?int $selectedExistingProject = null;
|
||||
public ?Project $createdProject = null;
|
||||
|
||||
public bool $dockerInstallationStarted = false;
|
||||
|
||||
public string $serverPublicKey;
|
||||
public bool $serverReachable = true;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->privateKeyName = generate_random_name();
|
||||
@@ -53,7 +59,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->remoteServerHost = 'coolify-testing-host';
|
||||
}
|
||||
}
|
||||
public function explanation() {
|
||||
public function explanation()
|
||||
{
|
||||
if (isCloud()) {
|
||||
return $this->setServerType('remote');
|
||||
}
|
||||
@@ -62,12 +69,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
|
||||
public function restartBoarding()
|
||||
{
|
||||
if ($this->createdServer) {
|
||||
$this->createdServer->delete();
|
||||
}
|
||||
if ($this->createdPrivateKey) {
|
||||
$this->createdPrivateKey->delete();
|
||||
}
|
||||
return redirect()->route('boarding');
|
||||
}
|
||||
public function skipBoarding()
|
||||
@@ -82,13 +83,15 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
|
||||
public function setServerType(string $type)
|
||||
{
|
||||
if ($type === 'localhost') {
|
||||
$this->selectedServerType = $type;
|
||||
if ($this->selectedServerType === 'localhost') {
|
||||
$this->createdServer = Server::find(0);
|
||||
if (!$this->createdServer) {
|
||||
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
||||
}
|
||||
return $this->validateServer();
|
||||
} elseif ($type === 'remote') {
|
||||
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||
return $this->validateServer('localhost');
|
||||
} elseif ($this->selectedServerType === 'remote') {
|
||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
|
||||
if ($this->privateKeys->count() > 0) {
|
||||
$this->selectedExistingPrivateKey = $this->privateKeys->first()->id;
|
||||
@@ -111,11 +114,11 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
return;
|
||||
}
|
||||
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
|
||||
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||
$this->validateServer();
|
||||
$this->getProxyType();
|
||||
$this->getProjects();
|
||||
}
|
||||
public function getProxyType() {
|
||||
public function getProxyType()
|
||||
{
|
||||
$proxyTypeSet = $this->createdServer->proxy->type;
|
||||
if (!$proxyTypeSet) {
|
||||
$this->currentState = 'select-proxy';
|
||||
@@ -125,6 +128,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
}
|
||||
public function selectExistingPrivateKey()
|
||||
{
|
||||
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
||||
$this->currentState = 'create-server';
|
||||
}
|
||||
public function createNewServer()
|
||||
@@ -147,6 +151,13 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
'privateKeyName' => 'required',
|
||||
'privateKey' => 'required',
|
||||
]);
|
||||
$this->createdPrivateKey = PrivateKey::create([
|
||||
'name' => $this->privateKeyName,
|
||||
'description' => $this->privateKeyDescription,
|
||||
'private_key' => $this->privateKey,
|
||||
'team_id' => currentTeam()->id
|
||||
]);
|
||||
$this->createdPrivateKey->save();
|
||||
$this->currentState = 'create-server';
|
||||
}
|
||||
public function saveServer()
|
||||
@@ -154,16 +165,14 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->validate([
|
||||
'remoteServerName' => 'required',
|
||||
'remoteServerHost' => 'required',
|
||||
'remoteServerPort' => 'required',
|
||||
'remoteServerPort' => 'required|integer',
|
||||
'remoteServerUser' => 'required',
|
||||
]);
|
||||
$this->privateKey = formatPrivateKey($this->privateKey);
|
||||
$this->createdPrivateKey = PrivateKey::create([
|
||||
'name' => $this->privateKeyName,
|
||||
'description' => $this->privateKeyDescription,
|
||||
'private_key' => $this->privateKey,
|
||||
'team_id' => currentTeam()->id
|
||||
]);
|
||||
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
||||
if ($foundServer) {
|
||||
return $this->emit('error', 'IP address is already in use by another team.');
|
||||
}
|
||||
$this->createdServer = Server::create([
|
||||
'name' => $this->remoteServerName,
|
||||
'ip' => $this->remoteServerHost,
|
||||
@@ -171,38 +180,52 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
'user' => $this->remoteServerUser,
|
||||
'description' => $this->remoteServerDescription,
|
||||
'private_key_id' => $this->createdPrivateKey->id,
|
||||
'team_id' => currentTeam()->id
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->createdServer->save();
|
||||
$this->validateServer();
|
||||
}
|
||||
public function validateServer() {
|
||||
public function validateServer()
|
||||
{
|
||||
try {
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->createdServer);
|
||||
if (!$uptime) {
|
||||
throw new \Exception('Server is not reachable.');
|
||||
} else {
|
||||
$this->createdServer->settings->update([
|
||||
'is_reachable' => true,
|
||||
]);
|
||||
$this->emit('success', 'Server is reachable.');
|
||||
}
|
||||
ray($dockerVersion, $uptime);
|
||||
if (!$dockerVersion) {
|
||||
$this->emit('error', 'Docker is not installed on the server.');
|
||||
$this->currentState = 'install-docker';
|
||||
return;
|
||||
}
|
||||
$this->getProxyType();
|
||||
$customErrorMessage = "Server is not reachable:";
|
||||
config()->set('coolify.mux_enabled', false);
|
||||
|
||||
instant_remote_process(['uptime'], $this->createdServer, true);
|
||||
|
||||
$this->createdServer->settings()->update([
|
||||
'is_reachable' => true,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
||||
$this->serverReachable = false;
|
||||
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
||||
}
|
||||
|
||||
try {
|
||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
|
||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||
if (is_null($dockerVersion)) {
|
||||
$this->currentState = 'install-docker';
|
||||
throw new \Exception('Docker version is not supported or not installed.');
|
||||
}
|
||||
$this->createdServer->settings()->update([
|
||||
'is_usable' => true,
|
||||
]);
|
||||
$this->getProxyType();
|
||||
} catch (\Throwable $e) {
|
||||
$this->dockerInstallationStarted = false;
|
||||
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
||||
}
|
||||
}
|
||||
public function installDocker()
|
||||
{
|
||||
$activity = resolve(InstallDocker::class)($this->createdServer, currentTeam());
|
||||
$this->dockerInstallationStarted = true;
|
||||
$activity = resolve(InstallDocker::class)($this->createdServer);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
$this->currentState = 'select-proxy';
|
||||
}
|
||||
public function dockerInstalledOrSkipped()
|
||||
{
|
||||
$this->validateServer();
|
||||
}
|
||||
public function selectProxy(string|null $proxyType = null)
|
||||
{
|
||||
@@ -215,14 +238,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->getProjects();
|
||||
}
|
||||
|
||||
public function getProjects() {
|
||||
public function getProjects()
|
||||
{
|
||||
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
||||
if ($this->projects->count() > 0) {
|
||||
$this->selectedExistingProject = $this->projects->first()->id;
|
||||
}
|
||||
$this->currentState = 'create-project';
|
||||
}
|
||||
public function selectExistingProject() {
|
||||
public function selectExistingProject()
|
||||
{
|
||||
$this->createdProject = Project::find($this->selectedExistingProject);
|
||||
$this->currentState = 'create-resource';
|
||||
}
|
||||
@@ -242,7 +267,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
[
|
||||
'project_uuid' => $this->createdProject->uuid,
|
||||
'environment_name' => 'production',
|
||||
'server'=> $this->createdServer->id,
|
||||
'server' => $this->createdServer->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class Form extends Component
|
||||
$this->destination->delete();
|
||||
return redirect()->route('dashboard');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class StandaloneDocker extends Component
|
||||
$this->createNetworkAndAttachToProxy();
|
||||
return redirect()->route('destination.show', $docker->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class ForcePasswordReset extends Component
|
||||
}
|
||||
return redirect()->route('dashboard');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class Help extends Component
|
||||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||
$this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
|
||||
@@ -46,6 +46,7 @@ class DiscordSettings extends Component
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
|
||||
@@ -62,9 +62,10 @@ class EmailSettings extends Component
|
||||
'team.smtp_from_name' => 'required',
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function sendTestNotification()
|
||||
@@ -81,9 +82,10 @@ class EmailSettings extends Component
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->team->resend_enabled = false;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ class EmailSettings extends Component
|
||||
$this->submitResend();
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
@@ -104,12 +106,13 @@ class EmailSettings extends Component
|
||||
$this->submit();
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved.');
|
||||
}
|
||||
public function submit()
|
||||
@@ -127,10 +130,11 @@ class EmailSettings extends Component
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submitResend()
|
||||
@@ -143,10 +147,11 @@ class EmailSettings extends Component
|
||||
'team.resend_api_key' => 'required'
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->resend_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function copyFromInstanceSettings()
|
||||
|
||||
@@ -52,6 +52,7 @@ class TelegramSettings extends Component
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ class Change extends Component
|
||||
{
|
||||
try {
|
||||
$this->public_key = $this->private_key->publicKey();
|
||||
}catch(\Exception $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function delete()
|
||||
@@ -39,7 +39,7 @@ class Change extends Component
|
||||
}
|
||||
$this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class Change extends Component
|
||||
$this->private_key->save();
|
||||
refresh_server_connection($this->private_key);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,20 @@
|
||||
namespace App\Http\Livewire\PrivateKey;
|
||||
|
||||
use App\Models\PrivateKey;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Livewire\Component;
|
||||
use phpseclib3\Crypt\PublicKeyLoader;
|
||||
|
||||
class Create extends Component
|
||||
{
|
||||
public ?string $from = null;
|
||||
use WithRateLimiting;
|
||||
public string $name;
|
||||
public ?string $description = null;
|
||||
public string $value;
|
||||
|
||||
public ?string $from = null;
|
||||
public ?string $description = null;
|
||||
public ?string $publicKey = null;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string',
|
||||
'value' => 'required|string',
|
||||
@@ -24,9 +28,14 @@ class Create extends Component
|
||||
|
||||
public function generateNewKey()
|
||||
{
|
||||
$this->name = generate_random_name();
|
||||
$this->description = 'Created by Coolify';
|
||||
['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey();
|
||||
try {
|
||||
$this->rateLimit(10);
|
||||
$this->name = generate_random_name();
|
||||
$this->description = 'Created by Coolify';
|
||||
['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey();
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function updated($updateProperty)
|
||||
{
|
||||
@@ -34,7 +43,11 @@ class Create extends Component
|
||||
try {
|
||||
$this->publicKey = PublicKeyLoader::load($this->$updateProperty)->getPublicKey()->toString('OpenSSH',['comment' => '']);
|
||||
} catch (\Throwable $e) {
|
||||
$this->publicKey = "Invalid private key";
|
||||
if ($this->$updateProperty === "") {
|
||||
$this->publicKey = "";
|
||||
} else {
|
||||
$this->publicKey = "Invalid private key";
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->validateOnly($updateProperty);
|
||||
@@ -58,7 +71,7 @@ class Create extends Component
|
||||
}
|
||||
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class Form extends Component
|
||||
'name' => $this->name,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class AddEmpty extends Component
|
||||
]);
|
||||
return redirect()->route('project.show', $project->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->name = '';
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class AddEnvironment extends Component
|
||||
'environment_name' => $environment->name,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
general_error_handler($e, $this);
|
||||
handleError($e, $this);
|
||||
} finally {
|
||||
$this->name = '';
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class DeploymentNavbar extends Component
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ class General extends Component
|
||||
$this->application->save();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +68,11 @@ class Heading extends Component
|
||||
["docker rm -f {$containerName}"],
|
||||
$this->application->destination->server
|
||||
);
|
||||
$this->application->status = 'stopped';
|
||||
$this->application->status = 'exited';
|
||||
$this->application->save();
|
||||
// $this->application->environment->project->team->notify(new StatusChanged($this->application));
|
||||
}
|
||||
}
|
||||
$this->application->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Previews extends Component
|
||||
$this->pull_requests = $data->sortBy('number')->values();
|
||||
} catch (\Throwable $e) {
|
||||
$this->rate_limit_remaining = 0;
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class Previews extends Component
|
||||
'environment_name' => $this->parameters['environment_name'],
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class Previews extends Component
|
||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
||||
$this->application->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class Rollback extends Component
|
||||
];
|
||||
})->toArray();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class CreateScheduledBackup extends Component
|
||||
]);
|
||||
$this->emit('refreshScheduledBackups');
|
||||
} catch (\Throwable $e) {
|
||||
general_error_handler($e, $this);
|
||||
handleError($e, $this);
|
||||
} finally {
|
||||
$this->frequency = '';
|
||||
$this->save_s3 = true;
|
||||
|
||||
@@ -35,7 +35,7 @@ class Heading extends Component
|
||||
|
||||
public function stop()
|
||||
{
|
||||
remote_process(
|
||||
instant_remote_process(
|
||||
["docker rm -f {$this->database->uuid}"],
|
||||
$this->database->destination->server
|
||||
);
|
||||
@@ -43,9 +43,9 @@ class Heading extends Component
|
||||
stopPostgresProxy($this->database);
|
||||
$this->database->is_public = false;
|
||||
}
|
||||
$this->database->status = 'stopped';
|
||||
$this->database->status = 'exited';
|
||||
$this->database->save();
|
||||
$this->emit('refresh');
|
||||
$this->check_status();
|
||||
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class InitScript extends Component
|
||||
$this->script['filename'] = $this->filename;
|
||||
$this->emitUp('save_init_script', $this->script);
|
||||
} catch (Exception $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Livewire\Project\Database\Postgresql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
use function Aws\filter;
|
||||
|
||||
class General extends Component
|
||||
@@ -73,9 +74,9 @@ class General extends Component
|
||||
}
|
||||
$this->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch(Exception $e) {
|
||||
} catch(\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -140,7 +141,7 @@ class General extends Component
|
||||
$this->database->save();
|
||||
$this->emit('success', 'Database updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class Edit extends Component
|
||||
$this->project->save();
|
||||
$this->emit('saved');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ class GithubPrivateRepository extends Component
|
||||
'project_uuid' => $project->uuid,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,14 +76,14 @@ class PublicGitRepository extends Component
|
||||
$this->get_branch();
|
||||
$this->selected_branch = $this->git_branch;
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||
try {
|
||||
$this->git_branch = 'master';
|
||||
$this->get_branch();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ class PublicGitRepository extends Component
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class Select extends Component
|
||||
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
|
||||
// $this->emit('success', 'Successfully connected to the database.');
|
||||
// } catch (\Throwable $e) {
|
||||
// return general_error_handler($e, $this);
|
||||
// return handleError($e, $this);
|
||||
// }
|
||||
// }
|
||||
public function setType(string $type)
|
||||
|
||||
@@ -114,7 +114,7 @@ class All extends Component
|
||||
$this->refreshEnvs();
|
||||
$this->emit('success', 'Environment variable added successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ResourceLimits extends Component
|
||||
$this->resource->save();
|
||||
$this->emit('success', 'Resource limits updated successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class All extends Component
|
||||
$this->emit('success', 'Storage added successfully');
|
||||
$this->emit('clearAddStorage');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class RunCommand extends Component
|
||||
$activity = remote_process([$this->command], Server::where('uuid', $this->server)->first(), ignore_errors: true);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class Form extends Component
|
||||
public $dockerVersion;
|
||||
public string|null $wildcard_domain = null;
|
||||
public int $cleanup_after_percentage;
|
||||
public bool $dockerInstallationStarted = false;
|
||||
|
||||
protected $rules = [
|
||||
'server.name' => 'required|min:6',
|
||||
@@ -44,19 +45,23 @@ class Form extends Component
|
||||
|
||||
public function installDocker()
|
||||
{
|
||||
$activity = resolve(InstallDocker::class)($this->server, currentTeam());
|
||||
$this->dockerInstallationStarted = true;
|
||||
$activity = resolve(InstallDocker::class)($this->server);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
|
||||
public function validateServer()
|
||||
{
|
||||
try {
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server);
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
||||
if ($uptime) {
|
||||
$this->uptime = $uptime;
|
||||
$this->emit('success', 'Server is reachable!');
|
||||
$this->emit('success', 'Server is reachable.');
|
||||
} else {
|
||||
$this->emit('error', 'Server is not reachable');
|
||||
ray($this->uptime);
|
||||
|
||||
$this->emit('error', 'Server is not reachable.');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($dockerVersion) {
|
||||
@@ -64,10 +69,10 @@ class Form extends Component
|
||||
$this->emit('proxyStatusUpdated');
|
||||
$this->emit('success', 'Docker Engine 23+ is installed!');
|
||||
} else {
|
||||
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.');
|
||||
$this->emit('error', 'No Docker Engine or older than 23 version installed.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, that: $this);
|
||||
return handleError($e, $this, customErrorMessage: "Server is not reachable: ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +87,7 @@ class Form extends Component
|
||||
$this->server->delete();
|
||||
return redirect()->route('server.all');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
|
||||
@@ -26,7 +26,7 @@ class ByIp extends Component
|
||||
protected $rules = [
|
||||
'name' => 'required|string',
|
||||
'description' => 'nullable|string',
|
||||
'ip' => 'required|ip',
|
||||
'ip' => 'required',
|
||||
'user' => 'required|string',
|
||||
'port' => 'required|integer',
|
||||
];
|
||||
@@ -79,7 +79,7 @@ class ByIp extends Component
|
||||
$server->settings->save();
|
||||
return redirect()->route('server.show', $server->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
namespace App\Http\Livewire\Server;
|
||||
|
||||
use App\Actions\Proxy\CheckConfigurationSync;
|
||||
use App\Actions\Proxy\SaveConfigurationSync;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Actions\Proxy\CheckConfiguration;
|
||||
use App\Actions\Proxy\SaveConfiguration;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -48,34 +47,32 @@ class Proxy extends Component
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
resolve(SaveConfigurationSync::class)($this->server);
|
||||
|
||||
SaveConfiguration::run($this->server);
|
||||
$this->server->proxy->redirect_url = $this->redirect_url;
|
||||
$this->server->save();
|
||||
|
||||
setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
|
||||
$this->emit('success', 'Proxy configuration saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function reset_proxy_configuration()
|
||||
{
|
||||
try {
|
||||
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server, true);
|
||||
$this->proxy_settings = CheckConfiguration::run($this->server, true);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function loadProxyConfiguration()
|
||||
{
|
||||
try {
|
||||
ray('loadProxyConfiguration');
|
||||
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server);
|
||||
$this->proxy_settings = CheckConfiguration::run($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Actions\Proxy\SaveConfigurationSync;
|
||||
use App\Actions\Proxy\SaveConfiguration;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
@@ -13,20 +13,25 @@ class Deploy extends Component
|
||||
public $proxy_settings = null;
|
||||
protected $listeners = ['proxyStatusUpdated'];
|
||||
|
||||
public function proxyStatusUpdated() {
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function startProxy()
|
||||
{
|
||||
if (
|
||||
$this->server->proxy->last_applied_settings &&
|
||||
$this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings
|
||||
) {
|
||||
resolve(SaveConfigurationSync::class)($this->server);
|
||||
}
|
||||
try {
|
||||
if (
|
||||
$this->server->proxy->last_applied_settings &&
|
||||
$this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings
|
||||
) {
|
||||
SaveConfiguration::run($this->server);
|
||||
}
|
||||
|
||||
$activity = resolve(StartProxy::class)($this->server);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
$activity = StartProxy::run($this->server);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function stop()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -18,13 +19,11 @@ class Status extends Component
|
||||
{
|
||||
try {
|
||||
if ($this->server->isFunctional()) {
|
||||
$container = getContainerStatus(server: $this->server, container_id: 'coolify-proxy');
|
||||
$this->server->proxy->status = $container;
|
||||
$this->server->save();
|
||||
dispatch_sync(new ContainerStatusJob($this->server));
|
||||
$this->emit('proxyStatusUpdated');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
public function getProxyStatusWithNoti()
|
||||
|
||||
@@ -18,7 +18,7 @@ class Show extends Component
|
||||
return redirect()->route('server.all');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
|
||||
@@ -28,27 +28,40 @@ class ShowPrivateKey extends Component
|
||||
]);
|
||||
$this->server->refresh();
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
return general_error_handler($e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkConnection()
|
||||
{
|
||||
try {
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server);
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
||||
if ($uptime) {
|
||||
$this->server->settings->update([
|
||||
'is_reachable' => true
|
||||
]);
|
||||
$this->emit('success', 'Server is reachable with this private key.');
|
||||
} else {
|
||||
$this->server->settings->update([
|
||||
'is_reachable' => false,
|
||||
'is_usable' => false
|
||||
]);
|
||||
$this->emit('error', 'Server is not reachable with this private key.');
|
||||
return;
|
||||
}
|
||||
if ($dockerVersion) {
|
||||
$this->server->settings->update([
|
||||
'is_usable' => true
|
||||
]);
|
||||
$this->emit('success', 'Server is usable for Coolify.');
|
||||
} else {
|
||||
$this->server->settings->update([
|
||||
'is_usable' => false
|
||||
]);
|
||||
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class Email extends Component
|
||||
$this->settings->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submitResend() {
|
||||
@@ -64,7 +64,7 @@ class Email extends Component
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->settings->resend_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSaveResend() {
|
||||
@@ -72,7 +72,7 @@ class Email extends Component
|
||||
$this->settings->smtp_enabled = false;
|
||||
$this->submitResend();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
@@ -81,7 +81,7 @@ class Email extends Component
|
||||
$this->settings->resend_enabled = false;
|
||||
$this->submit();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class Email extends Component
|
||||
$this->settings->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class Change extends Component
|
||||
$this->validate();
|
||||
$this->github_app->save();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class Change extends Component
|
||||
$this->github_app->delete();
|
||||
redirect()->route('source.all');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class Create extends Component
|
||||
}
|
||||
redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class Actions extends Component
|
||||
$this->emit('reloadWindow', 5000);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function resume()
|
||||
@@ -66,7 +66,7 @@ class Actions extends Component
|
||||
$this->emit('reloadWindow', 5000);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function stripeCustomerPortal() {
|
||||
|
||||
@@ -32,7 +32,7 @@ class Create extends Component
|
||||
refreshSession();
|
||||
return redirect()->route('team.index');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ class Form extends Component
|
||||
$this->validate();
|
||||
try {
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class InviteLink extends Component
|
||||
try {
|
||||
$member_emails = currentTeam()->members()->get()->pluck('email');
|
||||
if ($member_emails->contains($this->email)) {
|
||||
return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
|
||||
return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
|
||||
}
|
||||
$uuid = new Cuid2(32);
|
||||
$link = url('/') . config('constants.invitation.link.base_url') . $uuid;
|
||||
@@ -57,7 +57,7 @@ class InviteLink extends Component
|
||||
if (!is_null($invitation)) {
|
||||
$invitationValid = $invitation->isValid();
|
||||
if ($invitationValid) {
|
||||
return general_error_handler(that: $this, customErrorMessage: "Pending invitation already exists for $this->email.");
|
||||
return handleError(livewire: $this, customErrorMessage: "Pending invitation already exists for $this->email.");
|
||||
} else {
|
||||
$invitation->delete();
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class InviteLink extends Component
|
||||
if ($e->getCode() === '23505') {
|
||||
$error_message = 'Invitation already sent.';
|
||||
}
|
||||
return general_error_handler(err: $e, that: $this, customErrorMessage: $error_message);
|
||||
return handleError(error: $e, livewire: $this, customErrorMessage: $error_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class Create extends Component
|
||||
$this->storage->save();
|
||||
return redirect()->route('team.storages.show', $this->storage->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class Create extends Component
|
||||
$this->storage->testConnection();
|
||||
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class Form extends Component
|
||||
$this->storage->testConnection();
|
||||
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class Form extends Component
|
||||
$this->storage->delete();
|
||||
return redirect()->route('team.storages.all');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class Form extends Component
|
||||
$this->storage->save();
|
||||
$this->emit('success', 'Storage settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ class Upgrade extends Component
|
||||
}
|
||||
$this->showProgress = true;
|
||||
resolve(UpdateCoolify::class)(true);
|
||||
Toaster::success("Upgrading to {$this->latestVersion} version...");
|
||||
$this->emit('success', "Upgrading to {$this->latestVersion} version...");
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class Index extends Component
|
||||
$this->emit('success', 'Check your email to verify your email address.');
|
||||
dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid));
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||
|
||||
$this->container_name = generateApplicationContainerName($this->application->uuid, $this->pull_request_id);
|
||||
addPrivateKeyToSshAgent($this->server);
|
||||
savePrivateKeyToFs($this->server);
|
||||
$this->saved_outputs = collect();
|
||||
|
||||
// Set preview fqdn
|
||||
@@ -177,7 +177,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->prepare_builder_image();
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
$this->execute_in_builder("echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile")
|
||||
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile")
|
||||
],
|
||||
);
|
||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
||||
@@ -302,7 +302,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->stop_running_container();
|
||||
$this->execute_remote_command(
|
||||
["echo -n 'Starting preview deployment.'"],
|
||||
[$this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -324,16 +324,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"hidden" => true,
|
||||
],
|
||||
[
|
||||
"command" => $this->execute_in_builder("mkdir -p {$this->workdir}")
|
||||
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private function execute_in_builder(string $command)
|
||||
{
|
||||
return "docker exec {$this->deployment_uuid} bash -c '{$command}'";
|
||||
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
||||
}
|
||||
|
||||
|
||||
private function clone_repository()
|
||||
{
|
||||
@@ -345,7 +341,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->importing_git_repository()
|
||||
],
|
||||
[
|
||||
$this->execute_in_builder("cd {$this->workdir} && git rev-parse HEAD"),
|
||||
executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git rev-parse HEAD"),
|
||||
"hidden" => true,
|
||||
"save" => "git_commit_sha"
|
||||
],
|
||||
@@ -372,13 +368,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
|
||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||
|
||||
$commands->push($this->execute_in_builder($git_clone_command));
|
||||
$commands->push(executeInDocker($this->deployment_uuid, $git_clone_command));
|
||||
} else {
|
||||
$github_access_token = generate_github_installation_token($this->source);
|
||||
$commands->push($this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"));
|
||||
$commands->push(executeInDocker($this->deployment_uuid, "git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"));
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$commands->push($this->execute_in_builder("cd {$this->workdir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name"));
|
||||
$commands->push(executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name"));
|
||||
}
|
||||
return $commands->implode(' && ');
|
||||
}
|
||||
@@ -388,10 +384,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->workdir}";
|
||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||
$commands = collect([
|
||||
$this->execute_in_builder("mkdir -p /root/.ssh"),
|
||||
$this->execute_in_builder("echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
|
||||
$this->execute_in_builder("chmod 600 /root/.ssh/id_rsa"),
|
||||
$this->execute_in_builder($git_clone_command)
|
||||
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh"),
|
||||
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
|
||||
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa"),
|
||||
executeInDocker($this->deployment_uuid, $git_clone_command)
|
||||
]);
|
||||
return $commands->implode(' && ');
|
||||
}
|
||||
@@ -414,7 +410,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function cleanup_git()
|
||||
{
|
||||
$this->execute_remote_command(
|
||||
[$this->execute_in_builder("rm -fr {$this->workdir}/.git")],
|
||||
[executeInDocker($this->deployment_uuid, "rm -fr {$this->workdir}/.git")],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -425,8 +421,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"echo -n 'Generating nixpacks configuration.'",
|
||||
],
|
||||
[$this->nixpacks_build_cmd()],
|
||||
[$this->execute_in_builder("cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
|
||||
[$this->execute_in_builder("rm -f {$this->workdir}/.nixpacks/Dockerfile")]
|
||||
[executeInDocker($this->deployment_uuid, "cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
|
||||
[executeInDocker($this->deployment_uuid, "rm -f {$this->workdir}/.nixpacks/Dockerfile")]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -444,7 +440,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
||||
}
|
||||
$nixpacks_command .= " {$this->workdir}";
|
||||
return $this->execute_in_builder($nixpacks_command);
|
||||
return executeInDocker($this->deployment_uuid, $nixpacks_command);
|
||||
}
|
||||
|
||||
private function generate_env_variables()
|
||||
@@ -522,7 +518,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||
$this->execute_remote_command([$this->execute_in_builder("echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
@@ -679,7 +675,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
if ($this->application->settings->is_static) {
|
||||
$this->execute_remote_command([
|
||||
$this->execute_in_builder("docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||
]);
|
||||
|
||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||
@@ -706,18 +702,18 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
$this->execute_in_builder("echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile-prod")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile-prod")
|
||||
],
|
||||
[
|
||||
$this->execute_in_builder("echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||
],
|
||||
[
|
||||
$this->execute_in_builder("docker build --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
$this->execute_in_builder("docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -727,7 +723,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
if ($this->currently_running_container_name) {
|
||||
$this->execute_remote_command(
|
||||
["echo -n 'Removing old version of your application.'"],
|
||||
[$this->execute_in_builder("docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -736,7 +732,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
{
|
||||
$this->execute_remote_command(
|
||||
["echo -n 'Rolling update started.'"],
|
||||
[$this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -759,7 +755,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
private function add_build_env_variables_to_dockerfile()
|
||||
{
|
||||
$this->execute_remote_command([
|
||||
$this->execute_in_builder("cat {$this->workdir}/Dockerfile"), "hidden" => true, "save" => 'dockerfile'
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/Dockerfile"), "hidden" => true, "save" => 'dockerfile'
|
||||
]);
|
||||
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||
|
||||
@@ -768,7 +764,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
$this->execute_remote_command([
|
||||
$this->execute_in_builder("echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}/Dockerfile"),
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}/Dockerfile"),
|
||||
"hidden" => true
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -40,20 +40,19 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
return $this->server->uuid;
|
||||
}
|
||||
|
||||
private function checkServerConnection() {
|
||||
ray("Checking server connection to {$this->server->ip}");
|
||||
private function checkServerConnection()
|
||||
{
|
||||
$uptime = instant_remote_process(['uptime'], $this->server, false);
|
||||
if (!is_null($uptime)) {
|
||||
ray('Server is up');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
ray()->clearAll();
|
||||
// ray()->clearAll();
|
||||
$serverUptimeCheckNumber = 0;
|
||||
$serverUptimeCheckNumberMax = 5;
|
||||
$serverUptimeCheckNumberMax = 3;
|
||||
while (true) {
|
||||
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
||||
$this->server->settings()->update(['is_reachable' => false]);
|
||||
@@ -67,19 +66,28 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$serverUptimeCheckNumber++;
|
||||
sleep(5);
|
||||
}
|
||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
||||
if (!$containers) {
|
||||
return;
|
||||
}
|
||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
$applications = $this->server->applications();
|
||||
$databases = $this->server->databases();
|
||||
$previews = $this->server->previews();
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
resolve(StartProxy::class)($this->server, false);
|
||||
|
||||
/// Check if proxy is running
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||
$this->server->save();
|
||||
}
|
||||
$foundApplications = [];
|
||||
$foundApplicationPreviews = [];
|
||||
@@ -90,10 +98,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||
$labelId = data_get($labels, 'coolify.applicationId');
|
||||
if ($labelId) {
|
||||
if (str_contains($labelId,'-pr-')) {
|
||||
if (str_contains($labelId, '-pr-')) {
|
||||
$previewId = (int) Str::after($labelId, '-pr-');
|
||||
$applicationId = (int) Str::before($labelId, '-pr-');
|
||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id',$previewId)->first();
|
||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $previewId)->first();
|
||||
if ($preview) {
|
||||
$foundApplicationPreviews[] = $preview->id;
|
||||
$statusFromDb = $preview->status;
|
||||
@@ -130,10 +138,9 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$notRunningApplications = $applications->pluck('id')->diff($foundApplications);
|
||||
foreach($notRunningApplications as $applicationId) {
|
||||
foreach ($notRunningApplications as $applicationId) {
|
||||
$application = $applications->where('id', $applicationId)->first();
|
||||
if ($application->status === 'exited') {
|
||||
continue;
|
||||
@@ -172,14 +179,14 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||
foreach($notRunningDatabases as $database) {
|
||||
foreach ($notRunningDatabases as $database) {
|
||||
$database = $databases->where('id', $database)->first();
|
||||
if ($database->status === 'exited') {
|
||||
continue;
|
||||
}
|
||||
$database->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($database, 'name');
|
||||
$name = data_get($database, 'name');
|
||||
$fqdn = data_get($database, 'fqdn');
|
||||
|
||||
$containerName = $name;
|
||||
@@ -190,97 +197,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
|
||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return;
|
||||
foreach ($applications as $application) {
|
||||
$uuid = data_get($application, 'uuid');
|
||||
$id = data_get($application, 'id');
|
||||
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid) {
|
||||
$labels = data_get($value, 'Config.Labels');
|
||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||
$labelId = data_get($labels, 'coolify.applicationId');
|
||||
if ($labelId == $id) {
|
||||
return $value;
|
||||
}
|
||||
$isPR = Str::startsWith(data_get($value, 'Name'), "/$uuid");
|
||||
$isPR = Str::contains(data_get($value, 'Name'), "-pr-");
|
||||
if ($isPR) {
|
||||
ray('is pr');
|
||||
return false;
|
||||
}
|
||||
return $value;
|
||||
})->first();
|
||||
ray($foundContainer);
|
||||
if ($foundContainer) {
|
||||
$containerStatus = data_get($foundContainer, 'State.Status');
|
||||
$databaseStatus = data_get($application, 'status');
|
||||
if ($containerStatus !== $databaseStatus) {
|
||||
$application->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
$databaseStatus = data_get($application, 'status');
|
||||
if ($databaseStatus !== 'exited') {
|
||||
$application->update(['status' => 'exited']);
|
||||
$name = data_get($application, 'name');
|
||||
$fqdn = data_get($application, 'fqdn');
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
$project = data_get($application, 'environment.project');
|
||||
$environment = data_get($application, 'environment');
|
||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $application->uuid;
|
||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
}
|
||||
$previews = $application->previews;
|
||||
foreach ($previews as $preview) {
|
||||
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid, $preview) {
|
||||
$labels = data_get($value, 'Config.Labels');
|
||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||
$labelId = data_get($labels, 'coolify.applicationId');
|
||||
if ($labelId == "$id-pr-{$preview->id}") {
|
||||
return $value;
|
||||
}
|
||||
return Str::startsWith(data_get($value, 'Name'), "/$uuid-pr-{$preview->id}");
|
||||
})->first();
|
||||
}
|
||||
|
||||
}
|
||||
foreach ($databases as $database) {
|
||||
$uuid = data_get($database, 'uuid');
|
||||
$foundContainer = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
return Str::startsWith(data_get($value, 'Name'), "/$uuid");
|
||||
})->first();
|
||||
|
||||
if ($foundContainer) {
|
||||
$containerStatus = data_get($foundContainer, 'State.Status');
|
||||
$databaseStatus = data_get($database, 'status');
|
||||
if ($containerStatus !== $databaseStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
$databaseStatus = data_get($database, 'status');
|
||||
if ($databaseStatus !== 'exited') {
|
||||
$database->update(['status' => 'exited']);
|
||||
$name = data_get($database, 'name');
|
||||
$containerName = $name;
|
||||
$project = data_get($database, 'environment.project');
|
||||
$environment = data_get($database, 'environment');
|
||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
|
||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO Monitor other containers not managed by Coolify
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
|
||||
@@ -18,7 +18,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
@@ -117,7 +116,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
$this->backup_status = 'success';
|
||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->backup_status = 'failed';
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
|
||||
|
||||
@@ -21,9 +21,9 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::saved(function () {
|
||||
refreshSession();
|
||||
});
|
||||
// static::saved(function () {
|
||||
// refreshSession();
|
||||
// });
|
||||
}
|
||||
|
||||
public function routeNotificationForDiscord()
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace App\Notifications\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -16,15 +14,16 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $tries = 1;
|
||||
public Application $application;
|
||||
public string $deployment_uuid;
|
||||
public ?ApplicationPreview $preview = null;
|
||||
|
||||
public string $deployment_uuid;
|
||||
public string $application_name;
|
||||
public ?string $deployment_url = null;
|
||||
public string $project_uuid;
|
||||
public string $environment_name;
|
||||
|
||||
public ?string $deployment_url = null;
|
||||
public ?string $fqdn = null;
|
||||
|
||||
public function __construct(Application $application, string $deployment_uuid, ?ApplicationPreview $preview = null)
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace App\Notifications\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -16,16 +14,17 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $tries = 1;
|
||||
public Application $application;
|
||||
public string $deployment_uuid;
|
||||
public ApplicationPreview|null $preview = null;
|
||||
|
||||
public string $deployment_uuid;
|
||||
public string $application_name;
|
||||
public string|null $deployment_url = null;
|
||||
public string $project_uuid;
|
||||
public string $environment_name;
|
||||
public string|null $fqdn;
|
||||
|
||||
public ?string $deployment_url = null;
|
||||
public ?string $fqdn;
|
||||
|
||||
public function __construct(Application $application, string $deployment_uuid, ApplicationPreview|null $preview = null)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
namespace App\Notifications\Application;
|
||||
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Models\Application;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -14,14 +13,15 @@ class StatusChanged extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $application;
|
||||
public $tries = 1;
|
||||
|
||||
public Application $application;
|
||||
public string $application_name;
|
||||
public string|null $application_url = null;
|
||||
public string $project_uuid;
|
||||
public string $environment_name;
|
||||
public string|null $fqdn;
|
||||
|
||||
public ?string $application_url = null;
|
||||
public ?string $fqdn;
|
||||
|
||||
public function __construct($application)
|
||||
{
|
||||
|
||||
@@ -6,27 +6,34 @@ use Exception;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Log;
|
||||
|
||||
class EmailChannel
|
||||
{
|
||||
public function send(SendsEmail $notifiable, Notification $notification): void
|
||||
{
|
||||
$this->bootConfigs($notifiable);
|
||||
$recepients = $notifiable->getRecepients($notification);
|
||||
try {
|
||||
$this->bootConfigs($notifiable);
|
||||
$recepients = $notifiable->getRecepients($notification);
|
||||
ray($recepients);
|
||||
if (count($recepients) === 0) {
|
||||
throw new Exception('No email recipients found');
|
||||
}
|
||||
|
||||
if (count($recepients) === 0) {
|
||||
throw new Exception('No email recipients found');
|
||||
$mailMessage = $notification->toMail($notifiable);
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
->to($recepients)
|
||||
->subject($mailMessage->subject)
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
ray($e->getMessage());
|
||||
send_internal_notification("EmailChannel error: {$e->getMessage()}. Failed to send email to: " . implode(', ', $recepients) . " with subject: {$mailMessage->subject}");
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$mailMessage = $notification->toMail($notifiable);
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
->to($recepients)
|
||||
->subject($mailMessage->subject)
|
||||
->html((string)$mailMessage->render())
|
||||
);
|
||||
}
|
||||
|
||||
private function bootConfigs($notifiable): void
|
||||
|
||||
@@ -12,7 +12,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $tries = 1;
|
||||
|
||||
|
||||
public function __construct(public string $name, public Server $server, public ?string $url = null)
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
namespace App\Notifications\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -14,7 +12,7 @@ class BackupFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $tries = 1;
|
||||
public string $name;
|
||||
public string $frequency;
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
namespace App\Notifications\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -14,7 +12,7 @@ class BackupSuccess extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $tries = 1;
|
||||
public string $name;
|
||||
public string $frequency;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use Illuminate\Support\Str;
|
||||
|
||||
trait ExecuteRemoteCommand
|
||||
{
|
||||
public string|null $save = null;
|
||||
public ?string $save = null;
|
||||
|
||||
public function execute_remote_command(...$commands)
|
||||
{
|
||||
@@ -25,11 +25,8 @@ trait ExecuteRemoteCommand
|
||||
throw new \RuntimeException('Server is not set or is not an instance of Server model');
|
||||
}
|
||||
|
||||
$ip = data_get($this->server, 'ip');
|
||||
$user = data_get($this->server, 'user');
|
||||
$port = data_get($this->server, 'port');
|
||||
|
||||
$commandsText->each(function ($single_command) use ($ip, $user, $port) {
|
||||
$commandsText->each(function ($single_command) {
|
||||
$command = data_get($single_command, 'command') ?? $single_command[0] ?? null;
|
||||
if ($command === null) {
|
||||
throw new \RuntimeException('Command is not set');
|
||||
@@ -38,7 +35,7 @@ trait ExecuteRemoteCommand
|
||||
$ignore_errors = data_get($single_command, 'ignore_errors', false);
|
||||
$this->save = data_get($single_command, 'save');
|
||||
|
||||
$remote_command = generateSshCommand( $ip, $user, $port, $command);
|
||||
$remote_command = generateSshCommand($this->server, $command);
|
||||
$process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden) {
|
||||
$output = Str::of($output)->trim();
|
||||
$new_log_entry = [
|
||||
|
||||
@@ -59,6 +59,18 @@ function format_docker_envs_to_json($rawOutput)
|
||||
return collect([]);
|
||||
}
|
||||
}
|
||||
function checkMinimumDockerEngineVersion($dockerVersion) {
|
||||
$majorDockerVersion = Str::of($dockerVersion)->before('.')->value();
|
||||
if ($majorDockerVersion <= 22) {
|
||||
$dockerVersion = null;
|
||||
}
|
||||
return $dockerVersion;
|
||||
}
|
||||
function executeInDocker(string $containerId, string $command)
|
||||
{
|
||||
return "docker exec {$containerId} bash -c '{$command}'";
|
||||
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
||||
}
|
||||
|
||||
function getApplicationContainerStatus(Application $application) {
|
||||
$server = data_get($application,'destination.server');
|
||||
|
||||
@@ -96,7 +96,7 @@ function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
||||
$traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml";
|
||||
ray($redirect_url);
|
||||
if (empty($redirect_url)) {
|
||||
remote_process([
|
||||
instant_remote_process([
|
||||
"rm -f $traefik_default_redirect_file",
|
||||
], $server);
|
||||
} else {
|
||||
@@ -157,7 +157,7 @@ function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
||||
|
||||
$base64 = base64_encode($yaml);
|
||||
ray("mkdir -p $traefik_dynamic_conf_path");
|
||||
remote_process([
|
||||
instant_remote_process([
|
||||
"mkdir -p $traefik_dynamic_conf_path",
|
||||
"echo '$base64' | base64 -d > $traefik_default_redirect_file",
|
||||
], $server);
|
||||
|
||||
@@ -7,25 +7,25 @@ use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use App\Notifications\Server\NotReachable;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Sleep;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Activitylog\Contracts\Activity;
|
||||
|
||||
function remote_process(
|
||||
array $command,
|
||||
Server $server,
|
||||
string $type = ActivityTypes::INLINE->value,
|
||||
?string $type = null,
|
||||
?string $type_uuid = null,
|
||||
?Model $model = null,
|
||||
bool $ignore_errors = false,
|
||||
): Activity {
|
||||
|
||||
if (is_null($type)) {
|
||||
$type = ActivityTypes::INLINE->value;
|
||||
}
|
||||
$command_string = implode("\n", $command);
|
||||
if (auth()->user()) {
|
||||
$teams = auth()->user()->teams->pluck('id');
|
||||
@@ -36,12 +36,10 @@ function remote_process(
|
||||
|
||||
return resolve(PrepareCoolifyTask::class, [
|
||||
'remoteProcessArgs' => new CoolifyTaskArgs(
|
||||
server_ip: $server->ip,
|
||||
server_uuid: $server->uuid,
|
||||
command: <<<EOT
|
||||
{$command_string}
|
||||
EOT,
|
||||
port: $server->port,
|
||||
user: $server->user,
|
||||
type: $type,
|
||||
type_uuid: $type_uuid,
|
||||
model: $model,
|
||||
@@ -57,7 +55,7 @@ function remote_process(
|
||||
// }
|
||||
// // processWithEnv()->run("echo '{$server->privateKey->private_key}' | ssh-add -d -");
|
||||
// }
|
||||
function addPrivateKeyToSshAgent(Server $server)
|
||||
function savePrivateKeyToFs(Server $server)
|
||||
{
|
||||
if (data_get($server, 'privateKey.private_key') === null) {
|
||||
throw new \Exception("Server {$server->name} does not have a private key");
|
||||
@@ -66,16 +64,15 @@ function addPrivateKeyToSshAgent(Server $server)
|
||||
Storage::disk('ssh-keys')->makeDirectory('.');
|
||||
Storage::disk('ssh-mux')->makeDirectory('.');
|
||||
Storage::disk('ssh-keys')->put($sshKeyFileLocation, $server->privateKey->private_key);
|
||||
return '/var/www/html/storage/app/ssh/keys/' . $sshKeyFileLocation;
|
||||
$location = '/var/www/html/storage/app/ssh/keys/' . $sshKeyFileLocation;
|
||||
return $location;
|
||||
}
|
||||
|
||||
function generateSshCommand(string $server_ip, string $user, string $port, string $command, bool $isMux = true)
|
||||
function generateSshCommand(Server $server, string $command, bool $isMux = true)
|
||||
{
|
||||
$server = Server::where('ip', $server_ip)->first();
|
||||
if (!$server) {
|
||||
throw new \Exception("Server with ip {$server_ip} not found");
|
||||
}
|
||||
$privateKeyLocation = addPrivateKeyToSshAgent($server);
|
||||
$user = $server->user;
|
||||
$port = $server->port;
|
||||
$privateKeyLocation = savePrivateKeyToFs($server);
|
||||
$timeout = config('constants.ssh.command_timeout');
|
||||
$connectionTimeout = config('constants.ssh.connection_timeout');
|
||||
$serverInterval = config('constants.ssh.server_interval');
|
||||
@@ -95,35 +92,46 @@ function generateSshCommand(string $server_ip, string $user, string $port, strin
|
||||
. '-o RequestTTY=no '
|
||||
. '-o LogLevel=ERROR '
|
||||
. "-p {$port} "
|
||||
. "{$user}@{$server_ip} "
|
||||
. "{$user}@{$server->ip} "
|
||||
. " 'bash -se' << \\$delimiter" . PHP_EOL
|
||||
. $command . PHP_EOL
|
||||
. $delimiter;
|
||||
// ray($ssh_command);
|
||||
return $ssh_command;
|
||||
}
|
||||
function instant_remote_process(array $command, Server $server, $throwError = true, $repeat = 1)
|
||||
function instant_remote_process(array $command, Server $server, $throwError = true)
|
||||
{
|
||||
$command_string = implode("\n", $command);
|
||||
$ssh_command = generateSshCommand($server->ip, $server->user, $server->port, $command_string);
|
||||
$ssh_command = generateSshCommand($server, $command_string);
|
||||
$process = Process::run($ssh_command);
|
||||
$output = trim($process->output());
|
||||
$exitCode = $process->exitCode();
|
||||
if ($exitCode !== 0) {
|
||||
if ($repeat > 1) {
|
||||
ray("repeat: ", $repeat);
|
||||
Sleep::for(200)->milliseconds();
|
||||
return instant_remote_process($command, $server, $throwError, $repeat - 1);
|
||||
}
|
||||
// ray('ERROR OCCURED: ' . $process->errorOutput());
|
||||
if (!$throwError) {
|
||||
return null;
|
||||
}
|
||||
throw new \RuntimeException($process->errorOutput(), $exitCode);
|
||||
return excludeCertainErrors($process->errorOutput(), $exitCode);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
function excludeCertainErrors(string $errorOutput, ?int $exitCode = null) {
|
||||
$ignoredErrors = collect([
|
||||
'Permission denied (publickey',
|
||||
'Could not resolve hostname',
|
||||
]);
|
||||
$ignored = false;
|
||||
foreach ($ignoredErrors as $ignoredError) {
|
||||
if (Str::contains($errorOutput, $ignoredError)) {
|
||||
$ignored = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ignored) {
|
||||
// TODO: Create new exception and disable in sentry
|
||||
throw new \RuntimeException($errorOutput, $exitCode);
|
||||
}
|
||||
throw new \RuntimeException($errorOutput, $exitCode);
|
||||
}
|
||||
function decode_remote_command_output(?ApplicationDeploymentQueue $application_deployment_queue = null): Collection
|
||||
{
|
||||
$application = Application::find(data_get($application_deployment_queue, 'application_id'));
|
||||
@@ -161,11 +169,10 @@ function refresh_server_connection(PrivateKey $private_key)
|
||||
}
|
||||
}
|
||||
|
||||
function validateServer(Server $server)
|
||||
function validateServer(Server $server, bool $throwError = false)
|
||||
{
|
||||
try {
|
||||
refresh_server_connection($server->privateKey);
|
||||
$uptime = instant_remote_process(['uptime'], $server, false);
|
||||
$uptime = instant_remote_process(['uptime'], $server, $throwError);
|
||||
if (!$uptime) {
|
||||
$server->settings->is_reachable = false;
|
||||
return [
|
||||
@@ -175,7 +182,7 @@ function validateServer(Server $server)
|
||||
}
|
||||
$server->settings->is_reachable = true;
|
||||
|
||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, false);
|
||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
||||
if (!$dockerVersion) {
|
||||
$dockerVersion = null;
|
||||
return [
|
||||
@@ -183,9 +190,8 @@ function validateServer(Server $server)
|
||||
"dockerVersion" => null,
|
||||
];
|
||||
}
|
||||
$majorDockerVersion = Str::of($dockerVersion)->before('.')->value();
|
||||
if ($majorDockerVersion <= 22) {
|
||||
$dockerVersion = null;
|
||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||
if (is_null($dockerVersion)) {
|
||||
$server->settings->is_usable = false;
|
||||
} else {
|
||||
$server->settings->is_usable = true;
|
||||
|
||||
@@ -62,19 +62,41 @@ function showBoarding(): bool
|
||||
function refreshSession(?Team $team = null): void
|
||||
{
|
||||
if (!$team) {
|
||||
if (auth()->user()->currentTeam()) {
|
||||
if (auth()->user()?->currentTeam()) {
|
||||
$team = Team::find(auth()->user()->currentTeam()->id);
|
||||
} else {
|
||||
$team = User::find(auth()->user()->id)->teams->first();
|
||||
}
|
||||
}
|
||||
Cache::forget('team:' . auth()->user()->id);
|
||||
Cache::remember('team:' . auth()->user()->id, 3600, function() use ($team) {
|
||||
Cache::remember('team:' . auth()->user()->id, 3600, function () use ($team) {
|
||||
return $team;
|
||||
});
|
||||
session(['currentTeam' => $team]);
|
||||
}
|
||||
function general_error_handler(Throwable | null $err = null, $that = null, $isJson = false, $customErrorMessage = null): mixed
|
||||
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
|
||||
{
|
||||
ray('handleError');
|
||||
if ($error instanceof Throwable) {
|
||||
$message = $error->getMessage();
|
||||
} else {
|
||||
$message = null;
|
||||
}
|
||||
if ($customErrorMessage) {
|
||||
$message = $customErrorMessage . ' ' . $message;
|
||||
}
|
||||
if ($error instanceof TooManyRequestsException) {
|
||||
if (isset($livewire)) {
|
||||
return $livewire->emit('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.");
|
||||
}
|
||||
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
|
||||
}
|
||||
if (isset($livewire)) {
|
||||
return $livewire->emit('error', $message);
|
||||
}
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
function general_error_handler(Throwable $err, Livewire\Component $that = null, $isJson = false, $customErrorMessage = null): mixed
|
||||
{
|
||||
try {
|
||||
ray($err);
|
||||
@@ -95,7 +117,7 @@ function general_error_handler(Throwable | null $err = null, $that = null, $isJs
|
||||
}
|
||||
throw new Exception($customErrorMessage ?? $err->getMessage());
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
if ($that) {
|
||||
return $that->emit('error', $customErrorMessage ?? $e->getMessage());
|
||||
} elseif ($isJson) {
|
||||
@@ -122,7 +144,7 @@ function get_latest_version_of_coolify(): string
|
||||
$response = Http::get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
$versions = $response->json();
|
||||
return data_get($versions, 'coolify.v4.version');
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
//throw $e;
|
||||
ray($e->getMessage());
|
||||
return '0.0.0';
|
||||
@@ -321,7 +343,8 @@ function setNotificationChannels($notifiable, $event)
|
||||
}
|
||||
return $channels;
|
||||
}
|
||||
function parseEnvFormatToArray($env_file_contents) {
|
||||
function parseEnvFormatToArray($env_file_contents)
|
||||
{
|
||||
$env_array = array();
|
||||
$lines = explode("\n", $env_file_contents);
|
||||
foreach ($lines as $line) {
|
||||
@@ -334,8 +357,7 @@ function parseEnvFormatToArray($env_file_contents) {
|
||||
$value = substr($line, $equals_pos + 1);
|
||||
if (substr($value, 0, 1) === '"' && substr($value, -1) === '"') {
|
||||
$value = substr($value, 1, -1);
|
||||
}
|
||||
elseif (substr($value, 0, 1) === "'" && substr($value, -1) === "'") {
|
||||
} elseif (substr($value, 0, 1) === "'" && substr($value, -1) === "'") {
|
||||
$value = substr($value, 1, -1);
|
||||
}
|
||||
$env_array[$key] = $value;
|
||||
|
||||
@@ -138,5 +138,6 @@ function allowedPathsForBoardingAccounts()
|
||||
...allowedPathsForUnsubscribedAccounts(),
|
||||
'boarding',
|
||||
'livewire/message/boarding.index',
|
||||
'livewire/message/activity-monitor'
|
||||
];
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"lcobucci/jwt": "^5.0.0",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"livewire/livewire": "^v2.12.3",
|
||||
"lorisleiva/laravel-actions": "^2.7",
|
||||
"masmerise/livewire-toaster": "^1.2",
|
||||
"nubs/random-name-generator": "^2.2",
|
||||
"phpseclib/phpseclib": "~3.0",
|
||||
|
||||
151
composer.lock
generated
151
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "cf138424c896f30b035bc8cdff63e8d1",
|
||||
"content-hash": "de2c45be3f03d43430549d963778dc4a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@@ -3059,6 +3059,153 @@
|
||||
],
|
||||
"time": "2023-08-11T04:02:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lorisleiva/laravel-actions",
|
||||
"version": "v2.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lorisleiva/laravel-actions.git",
|
||||
"reference": "5250614fd6b77e8e2780be0206174e069e94661d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lorisleiva/laravel-actions/zipball/5250614fd6b77e8e2780be0206174e069e94661d",
|
||||
"reference": "5250614fd6b77e8e2780be0206174e069e94661d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/contracts": "9.0 - 9.34 || ^9.36 || ^10.0",
|
||||
"lorisleiva/lody": "^0.4",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^8.5",
|
||||
"pestphp/pest": "^1.23",
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Lorisleiva\\Actions\\ActionServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Action": "Lorisleiva\\Actions\\Facades\\Actions"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lorisleiva\\Actions\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Loris Leiva",
|
||||
"email": "loris.leiva@gmail.com",
|
||||
"homepage": "https://lorisleiva.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Laravel components that take care of one specific task",
|
||||
"homepage": "https://github.com/lorisleiva/laravel-actions",
|
||||
"keywords": [
|
||||
"action",
|
||||
"command",
|
||||
"component",
|
||||
"controller",
|
||||
"job",
|
||||
"laravel",
|
||||
"object"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/lorisleiva/laravel-actions/issues",
|
||||
"source": "https://github.com/lorisleiva/laravel-actions/tree/v2.7.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/lorisleiva",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-24T10:20:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lorisleiva/lody",
|
||||
"version": "v0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lorisleiva/lody.git",
|
||||
"reference": "1a43e8e423f3b2b64119542bc44a2071208fae16"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lorisleiva/lody/zipball/1a43e8e423f3b2b64119542bc44a2071208fae16",
|
||||
"reference": "1a43e8e423f3b2b64119542bc44a2071208fae16",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/contracts": "^8.0|^9.0|^10.0",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^8.0",
|
||||
"pestphp/pest": "^1.20.0",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Lorisleiva\\Lody\\LodyServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Lody": "Lorisleiva\\Lody\\Lody"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lorisleiva\\Lody\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Loris Leiva",
|
||||
"email": "loris.leiva@gmail.com",
|
||||
"homepage": "https://lorisleiva.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Load files and classes as lazy collections in Laravel.",
|
||||
"homepage": "https://github.com/lorisleiva/lody",
|
||||
"keywords": [
|
||||
"classes",
|
||||
"collection",
|
||||
"files",
|
||||
"laravel",
|
||||
"load"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/lorisleiva/lody/issues",
|
||||
"source": "https://github.com/lorisleiva/lody/tree/v0.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/lorisleiva",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-05T15:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masmerise/livewire-toaster",
|
||||
"version": "1.3.0",
|
||||
@@ -13089,5 +13236,5 @@
|
||||
"php": "^8.2"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ return [
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.37',
|
||||
'server_name' => env('APP_ID', 'coolify'),
|
||||
'release' => '4.0.0-beta.43',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
||||
@@ -30,14 +30,14 @@ return [
|
||||
*
|
||||
* Minimum: 3000 (in milliseconds)
|
||||
*/
|
||||
'duration' => 5000,
|
||||
'duration' => 3000,
|
||||
|
||||
/**
|
||||
* The horizontal position of each toast.
|
||||
*
|
||||
* Supported: "center", "left" or "right"
|
||||
*/
|
||||
'position' => 'right',
|
||||
'position' => 'center',
|
||||
|
||||
/**
|
||||
* Whether messages passed as translation keys should be translated automatically.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.37';
|
||||
return '4.0.0-beta.43';
|
||||
|
||||
@@ -11,6 +11,9 @@ use App\Models\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use DB;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -19,6 +22,18 @@ class ProductionSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// Fix for 4.0.0-beta.37
|
||||
if (User::find(0) !== null && Team::find(0) !== null) {
|
||||
if (DB::table('team_user')->where('user_id', 0)->first() === null) {
|
||||
DB::table('team_user')->insert([
|
||||
'user_id' => 0,
|
||||
'team_id' => 0,
|
||||
'role' => 'owner',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (InstanceSettings::find(0) == null) {
|
||||
InstanceSettings::create([
|
||||
'id' => 0
|
||||
|
||||
@@ -24,17 +24,17 @@
|
||||
<form action="/login" method="POST" class="flex flex-col gap-2">
|
||||
@csrf
|
||||
@env('local')
|
||||
<x-forms.input value="test@example.com" type="email" name="email"
|
||||
<x-forms.input value="test@example.com" type="email" name="email" required
|
||||
label="{{ __('input.email') }}" autofocus />
|
||||
|
||||
<x-forms.input value="password" type="password" name="password"
|
||||
<x-forms.input value="password" type="password" name="password" required
|
||||
label="{{ __('input.password') }}" />
|
||||
<a href="/forgot-password" class="text-xs">
|
||||
{{ __('auth.forgot_password') }}?
|
||||
</a>
|
||||
@else
|
||||
<x-forms.input type="email" name="email" label="{{ __('input.email') }}" autofocus />
|
||||
<x-forms.input type="password" name="password" label="{{ __('input.password') }}" />
|
||||
<x-forms.input type="email" name="email" required label="{{ __('input.email') }}" autofocus />
|
||||
<x-forms.input type="password" name="password" required label="{{ __('input.password') }}" />
|
||||
<a href="/forgot-password" class="text-xs">
|
||||
{{ __('auth.forgot_password') }}?
|
||||
</a>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="flex-1"></div>
|
||||
<x-applications.advanced :application="$application" />
|
||||
|
||||
@if ($application->status === 'running')
|
||||
@if ($application->status !== 'exited')
|
||||
<button wire:click='deploy' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="flex-1"></div>
|
||||
{{-- <x-applications.advanced :application="$application" /> --}}
|
||||
|
||||
@if ($database->status === 'running')
|
||||
@if ($database->status !== 'exited')
|
||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</label>
|
||||
@endif
|
||||
<textarea placeholder="{{ $placeholder }}" {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
@if ($realtimeValidation) wire:model.debounce.500ms="{{ $id }}"
|
||||
@if ($realtimeValidation) wire:model.debounce.200ms="{{ $id }}"
|
||||
@else
|
||||
wire:model.defer={{ $value ?? $id }}
|
||||
wire:dirty.class="input-warning"@endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@props([
|
||||
'showSubscribeButtons' => true,
|
||||
])
|
||||
<div x-data="{ selected: 'yearly' }" class="w-full pb-20">
|
||||
<div x-data="{ selected: 'monthly' }" class="w-full pb-20">
|
||||
<div class="px-6 mx-auto lg:px-8">
|
||||
<div class="flex justify-center">
|
||||
<fieldset
|
||||
@@ -24,80 +24,36 @@
|
||||
<div class="py-2 text-center"><span class="font-bold text-warning">{{ config('constants.limits.trial_period') }}
|
||||
days trial</span> included on all plans, without credit card details.</div>
|
||||
<div x-show="selected === 'monthly'" class="flex justify-center h-10 mt-3 text-sm leading-6 ">
|
||||
<div>Save <span class="font-bold text-warning">1 month</span> annually with the yearly plans.
|
||||
<div>Save <span class="font-bold text-warning">10%</span> annually with the yearly plans.
|
||||
</div>
|
||||
</div>
|
||||
<div x-show="selected === 'yearly'" class="flex justify-center h-10 mt-3 text-sm leading-6 ">
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 rounded bg-coolgray-400">
|
||||
<h2 id="tier-hobby" class="flex items-start gap-4 text-4xl font-bold tracking-tight">Unlimited Trial
|
||||
<x-forms.button><a class="font-bold text-white hover:no-underline"
|
||||
href="https://github.com/coollabsio/coolify">Get Started</a></x-forms.button>
|
||||
</h2>
|
||||
<p class="mt-4 text-sm leading-6">Start self-hosting <span class="text-warning">without limits</span> with
|
||||
our
|
||||
OSS version. Same features as the paid version, but you have to manage by yourself.</p>
|
||||
</div>
|
||||
<div class="flow-root mt-12">
|
||||
<div
|
||||
class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-4 lg:divide-x lg:divide-y-0 xl:-mx-4">
|
||||
<div class="px-8 pt-16 lg:pt-0">
|
||||
<h3 id="tier-trial" class="text-base font-semibold leading-7 text-white">Unlimited Trial</h3>
|
||||
<p class="flex items-baseline mt-6 gap-x-1">
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">Free</span>
|
||||
</span>
|
||||
<span x-show="selected === 'yearly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">Still Free </span>
|
||||
</span>
|
||||
</p>
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
<span>billed monthly</span>
|
||||
</span>
|
||||
<span x-show="selected === 'yearly'" x-cloak>
|
||||
<span>billed annually</span>
|
||||
</span>
|
||||
<a href="https://github.com/coollabsio/coolify" aria-describedby="tier-trial" class="buyme">Get
|
||||
Started</a>
|
||||
<p class="mt-10 text-sm leading-6 text-white h-[6.5rem]">Start self-hosting without limits with our
|
||||
OSS
|
||||
version.</p>
|
||||
<ul role="list" class="space-y-3 text-sm leading-6 ">
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
You manage everything
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Community Support
|
||||
</li>
|
||||
<li class="flex font-bold text-white gap-x-3">
|
||||
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3-5a9 9 0 0 0 6-8a3 3 0 0 0-3-3a9 9 0 0 0-8 6a6 6 0 0 0-5 3" />
|
||||
<path d="M7 14a6 6 0 0 0-3 6a6 6 0 0 0 6-3m4-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0" />
|
||||
</g>
|
||||
</svg>
|
||||
+ All upcoming features
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-3 lg:divide-x lg:divide-y-0 xl:-mx-4">
|
||||
|
||||
<div class="pt-16 lg:px-8 lg:pt-0 xl:px-14">
|
||||
<h3 id="tier-basic" class="text-base font-semibold leading-7 text-white">Basic</h3>
|
||||
<p class="flex items-baseline mt-6 gap-x-1">
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$5</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month + VAT</span>
|
||||
</span>
|
||||
<span x-show="selected === 'yearly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$4</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month + VAT</span>
|
||||
</span>
|
||||
</p>
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
@@ -139,8 +95,8 @@
|
||||
<li class="flex font-bold text-white gap-x-3">
|
||||
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3-5a9 9 0 0 0 6-8a3 3 0 0 0-3-3a9 9 0 0 0-8 6a6 6 0 0 0-5 3" />
|
||||
<path d="M7 14a6 6 0 0 0-3 6a6 6 0 0 0 6-3m4-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0" />
|
||||
@@ -154,12 +110,12 @@
|
||||
<h3 id="tier-pro" class="text-base font-semibold leading-7 text-white">Pro</h3>
|
||||
<p class="flex items-baseline mt-6 gap-x-1">
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$29</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month</span>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$30</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month + VAT</span>
|
||||
</span>
|
||||
<span x-show="selected === 'yearly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$26</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month</span>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$27</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month + VAT</span>
|
||||
</span>
|
||||
</p>
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
@@ -192,7 +148,7 @@
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Basic Support
|
||||
Included Email System
|
||||
</li>
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||
@@ -201,7 +157,7 @@
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Included Email System
|
||||
Email Support
|
||||
</li>
|
||||
<li class="flex font-bold text-white gap-x-3">
|
||||
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
|
||||
@@ -221,12 +177,12 @@
|
||||
<h3 id="tier-ultimate" class="text-base font-semibold leading-7 text-white">Ultimate</h3>
|
||||
<p class="flex items-baseline mt-6 gap-x-1">
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$69</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month</span>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$?</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month + VAT</span>
|
||||
</span>
|
||||
<span x-show="selected === 'yearly'" x-cloak>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$63</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month</span>
|
||||
<span class="text-4xl font-bold tracking-tight text-white">$?</span>
|
||||
<span class="text-sm font-semibold leading-6 ">/month + VAT</span>
|
||||
</span>
|
||||
</p>
|
||||
<span x-show="selected === 'monthly'" x-cloak>
|
||||
@@ -250,17 +206,9 @@
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
25 servers <x-helper helper="Bring Your Own Server. All you need is n SSH connection." />
|
||||
</li>
|
||||
<li class="flex font-bold text-white gap-x-3">
|
||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Priority Support
|
||||
? servers <x-helper helper="Bring Your Own Server. All you need is n SSH connection." />
|
||||
</li>
|
||||
|
||||
<li class="flex gap-x-3">
|
||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
@@ -270,6 +218,15 @@
|
||||
</svg>
|
||||
Included Email System
|
||||
</li>
|
||||
<li class="flex font-bold text-white gap-x-3">
|
||||
<svg class="flex-none w-5 h-6 text-warning" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
Priority (Email/Chat) Support
|
||||
</li>
|
||||
<li class="flex font-bold text-white gap-x-3">
|
||||
<svg width="512" height="512" class="flex-none w-5 h-6 text-green-600"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -285,176 +242,186 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-10">Need unlimited servers or official support for your Coolify instance? <a
|
||||
href="https://docs.coollabs.io/contact" class='text-warning'>Contact us.</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-8 pb-12 text-4xl font-bold text-center text-white">Included in all plans</div>
|
||||
<div class="grid grid-cols-1 gap-10 md:grid-cols-2 gap-y-28">
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 7a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3zm12 13H6a3 3 0 0 1-3-3v-2a3 3 0 0 1 3-3h12M7 8v.01M7 16v.01M20 15l-2 3h3l-2 3" />
|
||||
</svg>
|
||||
<div class="p-4 mt-10 rounded">
|
||||
<div class="flex items-start gap-4 text-xl tracking-tight">Need official support for
|
||||
your self-hosted instance?
|
||||
<x-forms.button>
|
||||
<a class="font-bold text-white hover:no-underline"
|
||||
href="https://docs.coollabs.io/contact">Contact Us</a>
|
||||
</x-forms.button>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Bring Your Own Servers</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Bring your own server from any cloud providers, or even your own server at home! All you need is SSH
|
||||
access. You will have full control over your server, and you can even use it for other purposes.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M7 7h10a2 2 0 0 1 2 2v1l1 1v3l-1 1v3a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-3l-1-1v-3l1-1V9a2 2 0 0 1 2-2zm3 9h4" />
|
||||
<circle cx="8.5" cy="11.5" r=".5" fill="#000000" />
|
||||
<circle cx="15.5" cy="11.5" r=".5" fill="#000000" />
|
||||
<path d="M9 7L8 3m7 4l1-4" />
|
||||
</g>
|
||||
</svg>
|
||||
<div class="pt-8 pb-12 text-4xl font-bold text-center text-white">Included in all plans</div>
|
||||
<div class="grid grid-cols-1 gap-10 md:grid-cols-2 gap-y-28">
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 7a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3zm12 13H6a3 3 0 0 1-3-3v-2a3 3 0 0 1 3-3h12M7 8v.01M7 16v.01M20 15l-2 3h3l-2 3" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Bring Your Own Servers</div>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Server Automations</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Once you connected your server, Coolify will start managing it and do a
|
||||
lot of adminstrative tasks for you. You can also write your own scripts to
|
||||
automate your server<span class="text-warning">*</span>.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" viewBox="0 0 24 24" class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path d="M15 11h2a2 2 0 0 1 2 2v2m0 4a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2h4" />
|
||||
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 1 0-2 0m-3-5V8m.347-3.631A4 4 0 0 1 16 6M3 3l18 18" />
|
||||
</g>
|
||||
</svg>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Bring your own server from any cloud providers, or even your own server at home! All you need is SSH
|
||||
access. You will have full control over your server, and you can even use it for other purposes.
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">No Vendor Lock-in</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
You own your own data. All configurations saved on your own servers, so if
|
||||
you decide to stop using Coolify, you can still continue to manage your
|
||||
deployed resources.
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M7 7h10a2 2 0 0 1 2 2v1l1 1v3l-1 1v3a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-3l-1-1v-3l1-1V9a2 2 0 0 1 2-2zm3 9h4" />
|
||||
<circle cx="8.5" cy="11.5" r=".5" fill="#000000" />
|
||||
<circle cx="15.5" cy="11.5" r=".5" fill="#000000" />
|
||||
<path d="M9 7L8 3m7 4l1-4" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Server Automations</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Once you connected your server, Coolify will start managing it and do a
|
||||
lot of adminstrative tasks for you. You can also write your own scripts to
|
||||
automate your server<span class="text-warning">*</span>.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" viewBox="0 0 24 24" class="icon"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M15 11h2a2 2 0 0 1 2 2v2m0 4a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2h4" />
|
||||
<path
|
||||
d="M11 16a1 1 0 1 0 2 0a1 1 0 1 0-2 0m-3-5V8m.347-3.631A4 4 0 0 1 16 6M3 3l18 18" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">No Vendor Lock-in</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
You own your own data. All configurations saved on your own servers, so if
|
||||
you decide to stop using Coolify, you can still continue to manage your
|
||||
deployed resources.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="3" y="4" width="18" height="12" rx="1" />
|
||||
<path d="M7 20h10" />
|
||||
<path d="M9 16v4" />
|
||||
<path d="M15 16v4" />
|
||||
<path d="M7 10h2l2 3l2 -6l1 3h3" />
|
||||
</svg>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<rect x="3" y="4" width="18" height="12" rx="1" />
|
||||
<path d="M7 20h10" />
|
||||
<path d="M9 16v4" />
|
||||
<path d="M15 16v4" />
|
||||
<path d="M7 10h2l2 3l2 -6l1 3h3" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Monitoring</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Coolify will automatically monitor your configured servers and deployed
|
||||
resources. Notifies you if something goes wrong on your favourite
|
||||
channels, like Discord, Telegram, via Email and more...
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Monitoring</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Coolify will automatically monitor your configured servers and deployed
|
||||
resources. Notifies you if something goes wrong on your favourite
|
||||
channels, like Discord, Telegram, via Email and more...
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path d="M6 4h10l4 4v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2" />
|
||||
<path d="M10 14a2 2 0 1 0 4 0a2 2 0 1 0-4 0m4-10v4H8V4" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Automatic Backups</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
We automatically backup your databases to any S3 compatible solution. If
|
||||
something goes wrong, you can easily restore your data with a few clicks.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<polyline points="5 7 10 12 5 17" />
|
||||
<line x1="13" y1="17" x2="19" y2="17" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Powerful API</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Programatically deploy, query, and manage your servers & resources.
|
||||
Integrate to your CI/CD pipelines, or build your own custom integrations. <span
|
||||
class="text-warning">*</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M4 18a2 2 0 1 0 4 0a2 2 0 1 0-4 0M4 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0m12 12a2 2 0 1 0 4 0a2 2 0 1 0-4 0M6 8v8" />
|
||||
<path d="M11 6h5a2 2 0 0 1 2 2v8" />
|
||||
<path d="m14 9l-3-3l3-3" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Push to Deploy</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Git integration is default today. We support hosted (github.com,
|
||||
gitlab.com<span class="inline-block text-warning">*</span>) or self-hosted<span
|
||||
class="text-warning">*</span>
|
||||
(Github Enterprise, Gitlab) Git repositories.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0-4 0m-2 8v-1a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1M15 5a2 2 0 1 0 4 0a2 2 0 0 0-4 0m2 5h2a2 2 0 0 1 2 2v1M5 5a2 2 0 1 0 4 0a2 2 0 0 0-4 0m-2 8v-1a2 2 0 0 1 2-2h2" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Pull Request Deployments</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Automagically deploy new commits and pull requests separately to quickly
|
||||
review contributions and speed up your teamwork!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path d="M6 4h10l4 4v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2" />
|
||||
<path d="M10 14a2 2 0 1 0 4 0a2 2 0 1 0-4 0m4-10v4H8V4" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Automatic Backups</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
We automatically backup your databases to any S3 compatible solution. If
|
||||
something goes wrong, you can easily restore your data with a few clicks.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<polyline points="5 7 10 12 5 17" />
|
||||
<line x1="13" y1="17" x2="19" y2="17" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Powerful API</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Programatically deploy, query, and manage your servers & resources.
|
||||
Integrate to your CI/CD pipelines, or build your own custom integrations. <span
|
||||
class="text-warning">*</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<path
|
||||
d="M4 18a2 2 0 1 0 4 0a2 2 0 1 0-4 0M4 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0m12 12a2 2 0 1 0 4 0a2 2 0 1 0-4 0M6 8v8" />
|
||||
<path d="M11 6h5a2 2 0 0 1 2 2v8" />
|
||||
<path d="m14 9l-3-3l3-3" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Push to Deploy</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Git integration is default today. We support hosted (github.com,
|
||||
gitlab.com<span class="inline-block text-warning">*</span>) or self-hosted<span class="text-warning">*</span>
|
||||
(Github Enterprise, Gitlab) Git repositories.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center justify-center w-10 h-10 text-white rounded-lg bg-coolgray-500">
|
||||
<svg width="512" height="512" class="icon" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0-4 0m-2 8v-1a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1M15 5a2 2 0 1 0 4 0a2 2 0 0 0-4 0m2 5h2a2 2 0 0 1 2 2v1M5 5a2 2 0 1 0 4 0a2 2 0 0 0-4 0m-2 8v-1a2 2 0 0 1 2-2h2" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-2xl font-semibold text-white">Pull Request Deployments</div>
|
||||
</div>
|
||||
<div class="mt-1 text-base leading-7 text-gray-300">
|
||||
Automagically deploy new commits and pull requests separately to quickly
|
||||
review contributions and speed up your teamwork!
|
||||
</div>
|
||||
<div class="pt-20 text-xs">
|
||||
<span class="text-warning">*</span> Some features are work in progress and will be available soon.
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-20 text-xs">
|
||||
<span class="text-warning">*</span> Some features are work in progress and will be available soon.
|
||||
</div>
|
||||
</div>
|
||||
@isset($other)
|
||||
{{ $other }}
|
||||
@endisset
|
||||
@isset($other)
|
||||
{{ $other }}
|
||||
@endisset
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="pb-6">
|
||||
<livewire:server.proxy.modal :server="$server" />
|
||||
<livewire:server.proxy.modal :server="$server" />
|
||||
<div class="flex items-center gap-2">
|
||||
<h1>Server</h1>
|
||||
@if ($server->settings->is_reachable)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@props([
|
||||
'text' => 'Stopped',
|
||||
'text' => 'Restarting',
|
||||
])
|
||||
<x-loading wire:loading.delay />
|
||||
<div class="flex items-center gap-2" wire:loading.remove.delay.longer>
|
||||
<div class="badge badge-error badge-xs"></div>
|
||||
<div class="text-xs font-medium tracking-wide text-error">{{ $text }}</div>
|
||||
<div class="badge badge-warning badge-xs"></div>
|
||||
<div class="text-xs font-medium tracking-wide text-warning">{{ $text }}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<x-emails.layout>
|
||||
|
||||
Container ({{ $containerName }}) has been restarted automatically on {{$serverName}}, because it was stopped unexpected.
|
||||
Container ({{ $containerName }}) has been restarted automatically on {{$serverName}}, because it was stopped unexpectedly.
|
||||
|
||||
@if ($containerName === 'coolify-proxy')
|
||||
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<x-emails.layout>
|
||||
|
||||
Container {{ $containerName }} has been stopped unexpected on {{$serverName}}.
|
||||
Container {{ $containerName }} has been stopped unexpectedly on {{$serverName}}.
|
||||
|
||||
@if ($url)
|
||||
Please check what is going on [here]({{ $url }}).
|
||||
|
||||
@@ -58,6 +58,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
function revive() {
|
||||
if (checkHealthInterval) return true;
|
||||
console.log('Checking server\'s health...')
|
||||
checkHealthInterval = setInterval(() => {
|
||||
fetch('/api/health')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
Toaster.success('Coolify is back online. Reloading...')
|
||||
if (checkHealthInterval) clearInterval(checkHealthInterval);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000)
|
||||
} else {
|
||||
console.log('Waiting for server to come back from dead...');
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function upgrade() {
|
||||
if (checkIfIamDeadInterval) return true;
|
||||
console.log('Update initiated.')
|
||||
checkIfIamDeadInterval = setInterval(() => {
|
||||
fetch('/api/health')
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
console.log('It\'s alive. Waiting for server to be dead...');
|
||||
} else {
|
||||
Toaster.success('Update done, restarting Coolify!')
|
||||
console.log('It\'s dead. Reviving... Standby... Bzz... Bzz...')
|
||||
if (checkIfIamDeadInterval) clearInterval(checkIfIamDeadInterval);
|
||||
revive();
|
||||
}
|
||||
})
|
||||
}, 2000);
|
||||
}
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
Livewire.emit('success', 'Copied to clipboard.');
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
@extends('layouts.base')
|
||||
@section('body')
|
||||
<main class="min-h-screen hero">
|
||||
<div class="hero-content">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</main>
|
||||
@parent
|
||||
<x-modal noSubmit modalId="installDocker">
|
||||
<x-slot:modalBody>
|
||||
<livewire:activity-monitor header="Docker Installation Logs" />
|
||||
</x-slot:modalBody>
|
||||
<x-slot:modalSubmit>
|
||||
<x-forms.button onclick="installDocker.close()" type="submit">
|
||||
Close
|
||||
</x-forms.button>
|
||||
</x-slot:modalSubmit>
|
||||
</x-modal>
|
||||
<main class="min-h-screen hero">
|
||||
<div class="hero-content">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</main>
|
||||
@parent
|
||||
@endsection
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
<x-highlighted text="Self-hosting with superpowers!" /></span>
|
||||
</x-slot:question>
|
||||
<x-slot:explanation>
|
||||
<p><x-highlighted text="Task automation:" /> You do not to manage your servers too much. Coolify do it for you.</p>
|
||||
<p><x-highlighted text="No vendor lock-in:" /> All configurations are stored on your server, so everything works without Coolify (except integrations and automations).</p>
|
||||
<p><x-highlighted text="Monitoring:" />You will get notified on your favourite platform (Discord, Telegram, Email, etc.) when something goes wrong, or an action needed from your side.</p>
|
||||
<p><x-highlighted text="Task automation:" /> You do not to manage your servers too much. Coolify do
|
||||
it for you.</p>
|
||||
<p><x-highlighted text="No vendor lock-in:" /> All configurations are stored on your server, so
|
||||
everything works without Coolify (except integrations and automations).</p>
|
||||
<p><x-highlighted text="Monitoring:" />You will get notified on your favourite platform (Discord,
|
||||
Telegram, Email, etc.) when something goes wrong, or an action needed from your side.</p>
|
||||
</x-slot:explanation>
|
||||
<x-slot:actions>
|
||||
<x-forms.button class="justify-center box" wire:click="explanation">Next
|
||||
@@ -43,9 +46,21 @@
|
||||
<x-forms.button class="justify-center box" wire:target="setServerType('localhost')"
|
||||
wire:click="setServerType('localhost')">Localhost
|
||||
</x-forms.button>
|
||||
|
||||
<x-forms.button class="justify-center box" wire:target="setServerType('remote')"
|
||||
wire:click="setServerType('remote')">Remote Server
|
||||
</x-forms.button>
|
||||
@if (!$serverReachable)
|
||||
Localhost is not reachable with the following public key.
|
||||
<br /> <br />
|
||||
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
|
||||
'root' or skip the boarding process and add a new private key manually to Coolify and to the
|
||||
server.
|
||||
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
|
||||
<x-forms.button class="box" wire:target="setServerType('localhost')"
|
||||
wire:click="setServerType('localhost')">Check again
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>Servers are the main building blocks, as they will host your applications, databases,
|
||||
@@ -115,6 +130,17 @@
|
||||
<x-forms.button type="submit">Use this Server</x-forms.button>
|
||||
</form>
|
||||
</div>
|
||||
@if (!$serverReachable)
|
||||
This server is not reachable with the following public key.
|
||||
<br /> <br />
|
||||
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
|
||||
'root' or skip the boarding process and add a new private key manually to Coolify and to the
|
||||
server.
|
||||
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
|
||||
<x-forms.button class="box" wire:target="validateServer"
|
||||
wire:click="validateServer">Check again
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
|
||||
@@ -182,7 +208,7 @@
|
||||
placeholder="Username to connect to your server. Default is root." label="Username"
|
||||
id="remoteServerUser" />
|
||||
</div>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
<x-forms.button type="submit">Check Connection</x-forms.button>
|
||||
</form>
|
||||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
@@ -194,25 +220,18 @@
|
||||
</div>
|
||||
<div>
|
||||
@if ($currentState === 'install-docker')
|
||||
<x-modal modalId="installDocker">
|
||||
<x-slot:modalBody>
|
||||
<livewire:activity-monitor header="Docker Installation Logs" />
|
||||
</x-slot:modalBody>
|
||||
<x-slot:modalSubmit>
|
||||
<x-forms.button onclick="installDocker.close()" type="submit">
|
||||
Close
|
||||
</x-forms.button>
|
||||
</x-slot:modalSubmit>
|
||||
</x-modal>
|
||||
<x-boarding-step title="Install Docker">
|
||||
<x-slot:question>
|
||||
Could not find Docker Engine on your server. Do you want me to install it for you?
|
||||
</x-slot:question>
|
||||
<x-slot:actions>
|
||||
@if ($dockerInstallationStarted)
|
||||
<x-forms.button class="justify-center box" wire:click="installDocker"
|
||||
onclick="installDocker.showModal()">
|
||||
Let's do
|
||||
it!</x-forms.button>
|
||||
Let's do it!</x-forms.button>
|
||||
<x-forms.button class="justify-center box" wire:click="dockerInstalledOrSkipped">
|
||||
Next</x-forms.button>
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>This will install the latest Docker Engine on your server, configure a few things to be able
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<div>
|
||||
<x-forms.button class="mb-4" wire:click="generateNewKey">Generate new SSH key for me</x-forms.button>
|
||||
<form class="flex flex-col gap-2" wire:submit.prevent='createPrivateKey'>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
@@ -6,11 +7,10 @@
|
||||
</div>
|
||||
<x-forms.textarea realtimeValidation id="value" rows="10"
|
||||
placeholder="-----BEGIN OPENSSH PRIVATE KEY-----" label="Private Key" required />
|
||||
<x-forms.button wire:click="generateNewKey">Generate new SSH key for me</x-forms.button>
|
||||
<x-forms.textarea id="publicKey" rows="6" readonly label="Public Key" />
|
||||
<span class="font-bold text-warning">ACTION REQUIRED: Copy the 'Public Key' to your server's
|
||||
<x-forms.input id="publicKey" readonly label="Public Key" />
|
||||
<span class="pt-2 pb-4 font-bold text-warning">ACTION REQUIRED: Copy the 'Public Key' to your server's
|
||||
~/.ssh/authorized_keys
|
||||
file.</span>
|
||||
file</span>
|
||||
<x-forms.button type="submit">
|
||||
Save Private Key
|
||||
</x-forms.button>
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<div class="">The destination server / network where your application will be deployed to.</div>
|
||||
<div class="py-4 ">
|
||||
<p>Server: {{ data_get($destination, 'server.name') }}</p>
|
||||
<p>Destination Network: {{ $destination->network }}</p>
|
||||
<p>Destination Network: {{ data_get($destination, 'server.network') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
</x-forms.button>
|
||||
</form>
|
||||
<div class="container w-full pt-10 mx-auto">
|
||||
<livewire:activity-monitor header="Logs" />
|
||||
<livewire:activity-monitor header="Command output" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,10 +48,16 @@
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($server->settings->is_reachable && !$server->settings->is_usable && $server->id !== 0)
|
||||
<x-forms.button class="mt-8 mb-4 box" onclick="installDocker.showModal()" wire:click.prevent='installDocker'
|
||||
isHighlighted>
|
||||
Install Docker Engine 24.0
|
||||
</x-forms.button>
|
||||
@if ($dockerInstallationStarted)
|
||||
<x-forms.button class="mt-8 mb-4 box" wire:click.prevent='validateServer'>
|
||||
Validate Server
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button class="mt-8 mb-4 box" onclick="installDocker.showModal()"
|
||||
wire:click.prevent='installDocker' isHighlighted>
|
||||
Install Docker Engine 24.0
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
@if ($server->isFunctional())
|
||||
<h3 class="py-4">Settings</h3>
|
||||
|
||||
@@ -55,19 +55,19 @@
|
||||
@else
|
||||
<div>
|
||||
<h2>Proxy</h2>
|
||||
<div class="subtitle ">Select a proxy you would like to use on this server.</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.button class="w-32 box" wire:click="select_proxy('NONE')">
|
||||
<div class="subtitle">Select a proxy you would like to use on this server.</div>
|
||||
<div class="grid gap-4">
|
||||
<x-forms.button class="box" wire:click="select_proxy('NONE')">
|
||||
Custom (None)
|
||||
</x-forms.button>
|
||||
<x-forms.button class="w-32 box" wire:click="select_proxy('TRAEFIK_V2')">
|
||||
<x-forms.button class="box" wire:click="select_proxy('TRAEFIK_V2')">
|
||||
Traefik
|
||||
v2
|
||||
</x-forms.button>
|
||||
<x-forms.button disabled class="w-32 box">
|
||||
<x-forms.button disabled class="box">
|
||||
Nginx
|
||||
</x-forms.button>
|
||||
<x-forms.button disabled class="w-32 box">
|
||||
<x-forms.button disabled class="box">
|
||||
Caddy
|
||||
</x-forms.button>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</x-slot:modalBody>
|
||||
</x-modal>
|
||||
@if (is_null(data_get($server, 'proxy.type')) || data_get($server, 'proxy.type') !== 'NONE')
|
||||
@if (data_get($server, 'proxy.status') === 'running')
|
||||
@if (data_get($server, 'proxy.status') !== 'exited')
|
||||
<div class="flex gap-4">
|
||||
<button>
|
||||
<a target="_blank" href="http://{{$server->ip}}:8080">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
<div class="flex gap-2" x-init="$wire.getProxyStatus">
|
||||
@if ($server->proxy->status === 'running')
|
||||
<x-status.running text="Proxy Running" />
|
||||
@@ -7,7 +6,8 @@
|
||||
@else
|
||||
<x-status.stopped text="Proxy Stopped" />
|
||||
@endif
|
||||
<button wire:click.prevent='getProxyStatusWithNoti'><svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<button wire:loading.remove.delay.longer wire:click.prevent='getProxyStatusWithNoti'>
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#FCD44F">
|
||||
<path
|
||||
d="M12.079 3v-.75V3Zm-8.4 8.333h-.75h.75Zm0 1.667l-.527.532a.75.75 0 0 0 1.056 0L3.68 13Zm2.209-1.134A.75.75 0 1 0 4.83 10.8l1.057 1.065ZM2.528 10.8a.75.75 0 0 0-1.056 1.065L2.528 10.8Zm16.088-3.408a.75.75 0 1 0 1.277-.786l-1.277.786ZM12.079 2.25c-5.047 0-9.15 4.061-9.15 9.083h1.5c0-4.182 3.42-7.583 7.65-7.583v-1.5Zm-9.15 9.083V13h1.5v-1.667h-1.5Zm1.28 2.2l1.679-1.667L4.83 10.8l-1.68 1.667l1.057 1.064Zm0-1.065L2.528 10.8l-1.057 1.065l1.68 1.666l1.056-1.064Zm15.684-5.86A9.158 9.158 0 0 0 12.08 2.25v1.5a7.658 7.658 0 0 1 6.537 3.643l1.277-.786Z" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user