mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 20:59:24 +00:00
Compare commits
34 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e2c932f07 | ||
|
|
e4aed185a2 | ||
|
|
dddbe40bbe | ||
|
|
59d6818f70 | ||
|
|
7678cd47df | ||
|
|
b101fbacd4 | ||
|
|
a61a86dc3b | ||
|
|
0b3cde44c3 | ||
|
|
6b302ab786 | ||
|
|
3c623f13e2 | ||
|
|
da54c24e8d | ||
|
|
1e39c3d5ab | ||
|
|
6071412986 | ||
|
|
ba7148206a | ||
|
|
59c5b22e6c | ||
|
|
be7f2ad9c4 | ||
|
|
62295ef573 | ||
|
|
ceb9fcf3b6 | ||
|
|
60282f7b6c | ||
|
|
f14b0a3411 | ||
|
|
30af317bd9 | ||
|
|
95faa1c3ad | ||
|
|
fb280afe41 | ||
|
|
fd488a561a | ||
|
|
ab57a5d8ef | ||
|
|
f5ae222a6e | ||
|
|
5d95d8b79a | ||
|
|
fbb5f2ca2e | ||
|
|
16cbca36c1 | ||
|
|
24a578bedb | ||
|
|
36dc479772 | ||
|
|
83d6e488e4 | ||
|
|
a4d358d512 | ||
|
|
c0c197101d |
@@ -27,3 +27,5 @@ You can ask for guidance anytime on our
|
|||||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
||||||
|
|
||||||
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class StartDatabaseProxy
|
|||||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||||
"docker compose --project-directory {$configuration_dir} up --build -d >/dev/null",
|
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||||
], $database->destination->server);
|
], $database->destination->server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,41 +2,50 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
|
||||||
|
|
||||||
class CheckProxy
|
class CheckProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Server $server)
|
public function handle(Server $server, $fromUI = false)
|
||||||
{
|
{
|
||||||
if (!$server->isProxyShouldRun()) {
|
if (!$server->isProxyShouldRun()) {
|
||||||
throw new \Exception("Proxy should not run");
|
if ($fromUI) {
|
||||||
|
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$status = getContainerStatus($server, 'coolify-proxy');
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
$server->proxy->set('status', 'running');
|
$server->proxy->set('status', 'running');
|
||||||
$server->save();
|
$server->save();
|
||||||
return 'OK';
|
return false;
|
||||||
}
|
}
|
||||||
$ip = $server->ip;
|
$ip = $server->ip;
|
||||||
if ($server->id === 0) {
|
if ($server->id === 0) {
|
||||||
$ip = 'host.docker.internal';
|
$ip = 'host.docker.internal';
|
||||||
}
|
}
|
||||||
|
|
||||||
$connection = @fsockopen($ip, '80');
|
$connection80 = @fsockopen($ip, '80');
|
||||||
$connection = @fsockopen($ip, '443');
|
$connection443 = @fsockopen($ip, '443');
|
||||||
$port80 = is_resource($connection) && fclose($connection);
|
$port80 = is_resource($connection80) && fclose($connection80);
|
||||||
$port443 = is_resource($connection) && fclose($connection);
|
$port443 = is_resource($connection443) && fclose($connection443);
|
||||||
ray($ip);
|
|
||||||
if ($port80) {
|
if ($port80) {
|
||||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
if ($fromUI) {
|
||||||
|
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($port443) {
|
if ($port443) {
|
||||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>>");
|
if ($fromUI) {
|
||||||
|
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ class StartProxy
|
|||||||
public function handle(Server $server, bool $async = true): string|Activity
|
public function handle(Server $server, bool $async = true): string|Activity
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
CheckProxy::run($server);
|
|
||||||
|
|
||||||
$proxyType = $server->proxyType();
|
$proxyType = $server->proxyType();
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class Kernel extends ConsoleKernel
|
|||||||
// $this->check_scheduled_backups($schedule);
|
// $this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->cleanup_servers($schedule);
|
$this->cleanup_servers($schedule);
|
||||||
|
$this->check_scheduled_backups($schedule);
|
||||||
} else {
|
} else {
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
|
|||||||
@@ -60,12 +60,16 @@ class DeploymentNavbar extends Component
|
|||||||
$previous_logs[] = $new_log_entry;
|
$previous_logs[] = $new_log_entry;
|
||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
||||||
'current_process_id' => null,
|
|
||||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->application_deployment_queue->update([
|
||||||
|
'current_process_id' => null,
|
||||||
|
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||||
|
]);
|
||||||
|
queue_next_deployment($this->application);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,9 @@
|
|||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
{
|
{
|
||||||
@@ -23,6 +20,10 @@ class General extends Component
|
|||||||
public ?string $git_commit_sha = null;
|
public ?string $git_commit_sha = null;
|
||||||
public string $build_pack;
|
public string $build_pack;
|
||||||
|
|
||||||
|
public $customLabels;
|
||||||
|
public bool $labelsChanged = false;
|
||||||
|
public bool $isConfigurationChanged = false;
|
||||||
|
|
||||||
public bool $is_static;
|
public bool $is_static;
|
||||||
public bool $is_git_submodules_enabled;
|
public bool $is_git_submodules_enabled;
|
||||||
public bool $is_git_lfs_enabled;
|
public bool $is_git_lfs_enabled;
|
||||||
@@ -52,6 +53,7 @@ class General extends Component
|
|||||||
'application.docker_registry_image_name' => 'nullable',
|
'application.docker_registry_image_name' => 'nullable',
|
||||||
'application.docker_registry_image_tag' => 'nullable',
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
'application.dockerfile_location' => 'nullable',
|
'application.dockerfile_location' => 'nullable',
|
||||||
|
'application.custom_labels' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -73,16 +75,47 @@ class General extends Component
|
|||||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||||
'application.dockerfile_location' => 'Dockerfile location',
|
'application.dockerfile_location' => 'Dockerfile location',
|
||||||
|
'application.custom_labels' => 'Custom labels',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function updatedApplicationBuildPack(){
|
public function mount()
|
||||||
|
{
|
||||||
|
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||||
|
$this->application->isConfigurationChanged(true);
|
||||||
|
}
|
||||||
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
|
if (is_null(data_get($this->application, 'custom_labels'))) {
|
||||||
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
|
} else {
|
||||||
|
$this->customLabels = str($this->application->custom_labels)->replace(',', "\n");
|
||||||
|
}
|
||||||
|
if (data_get($this->application, 'settings')) {
|
||||||
|
$this->is_static = $this->application->settings->is_static;
|
||||||
|
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||||
|
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||||
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
|
}
|
||||||
|
$this->checkLabelUpdates();
|
||||||
|
}
|
||||||
|
public function updatedApplicationBuildPack()
|
||||||
|
{
|
||||||
if ($this->application->build_pack !== 'nixpacks') {
|
if ($this->application->build_pack !== 'nixpacks') {
|
||||||
$this->application->settings->is_static = $this->is_static = false;
|
$this->application->settings->is_static = $this->is_static = false;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
}
|
}
|
||||||
$this->submit();
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
public function checkLabelUpdates()
|
||||||
|
{
|
||||||
|
if (md5($this->application->custom_labels) !== md5(implode(",", generateLabelsApplication($this->application)))) {
|
||||||
|
$this->labelsChanged = true;
|
||||||
|
} else {
|
||||||
|
$this->labelsChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
// @TODO: find another way - if possible
|
// @TODO: find another way - if possible
|
||||||
@@ -102,37 +135,36 @@ class General extends Component
|
|||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
|
$this->checkLabelUpdates();
|
||||||
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWildcardDomain() {
|
public function getWildcardDomain()
|
||||||
|
{
|
||||||
$server = data_get($this->application, 'destination.server');
|
$server = data_get($this->application, 'destination.server');
|
||||||
if ($server) {
|
if ($server) {
|
||||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||||
ray($fqdn);
|
|
||||||
$this->application->fqdn = $fqdn;
|
$this->application->fqdn = $fqdn;
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public function mount()
|
public function resetDefaultLabels($showToaster = true)
|
||||||
{
|
{
|
||||||
if (data_get($this->application,'settings')) {
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
$this->is_static = $this->application->settings->is_static;
|
$this->submit($showToaster);
|
||||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
|
||||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
|
||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function updatedApplicationFqdn()
|
||||||
|
{
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
|
$this->emit('success', 'Labels reseted to default!');
|
||||||
|
}
|
||||||
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
if (data_get($this->application,'build_pack') === 'dockerimage') {
|
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'application.docker_registry_image_name' => 'required',
|
'application.docker_registry_image_name' => 'required',
|
||||||
'application.docker_registry_image_tag' => 'required',
|
'application.docker_registry_image_tag' => 'required',
|
||||||
@@ -156,10 +188,17 @@ class General extends Component
|
|||||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||||
}
|
}
|
||||||
|
if (gettype($this->customLabels) === 'string') {
|
||||||
|
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
||||||
|
}
|
||||||
|
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$showToaster && $this->emit('success', 'Application settings updated!');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->checkLabelUpdates();
|
||||||
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,5 +34,6 @@ class Navbar extends Component
|
|||||||
StopService::run($this->service);
|
StopService::run($this->service);
|
||||||
$this->service->refresh();
|
$this->service->refresh();
|
||||||
$this->emit('success', 'Service stopped successfully.');
|
$this->emit('success', 'Service stopped successfully.');
|
||||||
|
$this->checkStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class StackForm extends Component
|
class StackForm extends Component
|
||||||
{
|
{
|
||||||
|
public $service;
|
||||||
protected $listeners = ["saveCompose"];
|
protected $listeners = ["saveCompose"];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'service.docker_compose_raw' => 'required',
|
'service.docker_compose_raw' => 'required',
|
||||||
@@ -13,7 +14,6 @@ class StackForm extends Component
|
|||||||
'service.name' => 'required',
|
'service.name' => 'required',
|
||||||
'service.description' => 'nullable',
|
'service.description' => 'nullable',
|
||||||
];
|
];
|
||||||
public $service;
|
|
||||||
public function saveCompose($raw)
|
public function saveCompose($raw)
|
||||||
{
|
{
|
||||||
$this->service->docker_compose_raw = $raw;
|
$this->service->docker_compose_raw = $raw;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Deploy extends Component
|
|||||||
public function checkProxy()
|
public function checkProxy()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
CheckProxy::run($this->server);
|
CheckProxy::run($this->server, true);
|
||||||
$this->emit('startProxyPolling');
|
$this->emit('startProxyPolling');
|
||||||
$this->emit('proxyChecked');
|
$this->emit('proxyChecked');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class Status extends Component
|
|||||||
}
|
}
|
||||||
$this->numberOfPolls++;
|
$this->numberOfPolls++;
|
||||||
}
|
}
|
||||||
CheckProxy::run($this->server);
|
CheckProxy::run($this->server, true);
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
if ($this->server->proxy->status === 'running') {
|
if ($this->server->proxy->status === 'running') {
|
||||||
$this->polling = false;
|
$this->polling = false;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private ApplicationPreview|null $preview = null;
|
private ApplicationPreview|null $preview = null;
|
||||||
|
|
||||||
private string $container_name;
|
private string $container_name;
|
||||||
private string|null $currently_running_container_name = null;
|
private ?string $currently_running_container_name = null;
|
||||||
private string $basedir;
|
private string $basedir;
|
||||||
private string $workdir;
|
private string $workdir;
|
||||||
private ?string $build_pack = null;
|
private ?string $build_pack = null;
|
||||||
@@ -67,14 +67,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private $docker_compose;
|
private $docker_compose;
|
||||||
private $docker_compose_base64;
|
private $docker_compose_base64;
|
||||||
private string $dockerfile_location = '/Dockerfile';
|
private string $dockerfile_location = '/Dockerfile';
|
||||||
|
private ?string $addHosts = null;
|
||||||
private $log_model;
|
private $log_model;
|
||||||
private Collection $saved_outputs;
|
private Collection $saved_outputs;
|
||||||
|
|
||||||
|
private string $serverUser = 'root';
|
||||||
|
private string $serverUserHomeDir = '/root';
|
||||||
|
private string $dockerConfigFileExists = 'NOK';
|
||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
public function __construct(int $application_deployment_queue_id)
|
public function __construct(int $application_deployment_queue_id)
|
||||||
{
|
{
|
||||||
ray()->clearScreen();
|
// ray()->clearScreen();
|
||||||
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
|
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
|
||||||
$this->log_model = $this->application_deployment_queue;
|
$this->log_model = $this->application_deployment_queue;
|
||||||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
$this->application = Application::find($this->application_deployment_queue->application_id);
|
||||||
@@ -92,13 +96,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
|
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
|
||||||
$this->server = $this->destination->server;
|
$this->server = $this->destination->server;
|
||||||
|
$this->serverUser = $this->server->user;
|
||||||
$this->basedir = "/artifacts/{$this->deployment_uuid}";
|
$this->basedir = "/artifacts/{$this->deployment_uuid}";
|
||||||
$this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/');
|
$this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/');
|
||||||
$this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
|
$this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
|
||||||
ray($this->basedir, $this->workdir);
|
|
||||||
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||||
savePrivateKeyToFs($this->server);
|
savePrivateKeyToFs($this->server);
|
||||||
$this->saved_outputs = collect();
|
$this->saved_outputs = collect();
|
||||||
@@ -138,6 +141,32 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Generate custom host<->ip mapping
|
||||||
|
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||||
|
$allContainers = format_docker_command_output_to_json($allContainers);
|
||||||
|
$ips = collect([]);
|
||||||
|
if (count($allContainers) > 0) {
|
||||||
|
$allContainers = $allContainers[0];
|
||||||
|
foreach ($allContainers as $container) {
|
||||||
|
$containerName = data_get($container, 'Name');
|
||||||
|
if ($containerName === 'coolify-proxy') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$containerIp = data_get($container, 'IPv4Address');
|
||||||
|
if ($containerName && $containerIp) {
|
||||||
|
$containerIp = str($containerIp)->before('/');
|
||||||
|
$ips->put($containerName, $containerIp->value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->addHosts = $ips->map(function ($ip, $name) {
|
||||||
|
return "--add-host $name:$ip";
|
||||||
|
})->implode(' ');
|
||||||
|
|
||||||
|
// Get user home directory
|
||||||
|
$this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
|
||||||
|
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
||||||
try {
|
try {
|
||||||
if ($this->application->dockerfile) {
|
if ($this->application->dockerfile) {
|
||||||
$this->deploy_simple_dockerfile();
|
$this->deploy_simple_dockerfile();
|
||||||
@@ -156,6 +185,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
dispatch(new ContainerStatusJob($this->server));
|
dispatch(new ContainerStatusJob($this->server));
|
||||||
}
|
}
|
||||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||||
|
$this->application->isConfigurationChanged(true);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
$this->fail($e);
|
$this->fail($e);
|
||||||
@@ -324,14 +354,19 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
|
"docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
|
||||||
]);
|
]);
|
||||||
if (Str::of($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
if (Str::of($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo 'Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped...'"
|
"echo 'No configuration changed & Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped.'",
|
||||||
]);
|
]);
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ($this->application->isConfigurationChanged()) {
|
||||||
|
$this->execute_remote_command([
|
||||||
|
"echo 'Configuration changed. Rebuilding image.'",
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->cleanup_git();
|
$this->cleanup_git();
|
||||||
$this->generate_nixpacks_confs();
|
$this->generate_nixpacks_confs();
|
||||||
@@ -428,7 +463,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->stop_running_container();
|
$this->stop_running_container();
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting preview deployment.'"],
|
["echo -n 'Starting preview deployment.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +471,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
$pull = "--pull=always";
|
$pull = "--pull=always";
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
if ($this->dockerConfigFileExists === 'OK') {
|
||||||
|
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||||
|
} else {
|
||||||
|
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||||
|
}
|
||||||
|
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
@@ -616,6 +655,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables($ports);
|
$environment_variables = $this->generate_environment_variables($ports);
|
||||||
|
|
||||||
|
$labels = generateLabelsApplication($this->application, $this->preview);
|
||||||
|
if (data_get($this->application, 'custom_labels')) {
|
||||||
|
$labels = str($this->application->custom_labels)->explode(',')->toArray();
|
||||||
|
}
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
@@ -624,7 +667,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'container_name' => $this->container_name,
|
'container_name' => $this->container_name,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'environment' => $environment_variables,
|
'environment' => $environment_variables,
|
||||||
'labels' => generateLabelsApplication($this->application, $this->preview, $ports),
|
'labels' => $labels,
|
||||||
'expose' => $ports,
|
'expose' => $ports,
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->destination->network,
|
$this->destination->network,
|
||||||
@@ -766,7 +809,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($this->application->settings->is_static) {
|
if ($this->application->settings->is_static) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
@@ -799,12 +842,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
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
|
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -830,7 +873,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting application (could take a while).'"],
|
["echo -n 'Starting application (could take a while).'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d >/dev/null"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -47,7 +48,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ray("checking server status for {$this->server->id}");
|
// ray("checking server status for {$this->server->id}");
|
||||||
// ray()->clearAll();
|
// ray()->clearAll();
|
||||||
$serverUptimeCheckNumber = $this->server->unreachable_count;
|
$serverUptimeCheckNumber = $this->server->unreachable_count;
|
||||||
$serverUptimeCheckNumberMax = 3;
|
$serverUptimeCheckNumberMax = 3;
|
||||||
@@ -117,10 +118,18 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return data_get($value, 'Name') === '/coolify-proxy';
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
})->first();
|
})->first();
|
||||||
if (!$foundProxyContainer) {
|
if (!$foundProxyContainer) {
|
||||||
if ($this->server->isProxyShouldRun()) {
|
try {
|
||||||
StartProxy::run($this->server, false);
|
$shouldStart = CheckProxy::run($this->server);
|
||||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
if ($shouldStart) {
|
||||||
|
StartProxy::run($this->server, false);
|
||||||
|
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
|
} else {
|
||||||
|
ray('Proxy could not be started.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
|
|||||||
@@ -277,4 +277,31 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public function isConfigurationChanged($save = false)
|
||||||
|
{
|
||||||
|
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||||
|
if ($this->pull_request_id === 0) {
|
||||||
|
$newConfigHash .= json_encode($this->environment_variables->all());
|
||||||
|
} else {
|
||||||
|
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
||||||
|
}
|
||||||
|
$newConfigHash = md5($newConfigHash);
|
||||||
|
$oldConfigHash = data_get($this, 'config_hash');
|
||||||
|
if ($oldConfigHash === null) {
|
||||||
|
if ($save) {
|
||||||
|
$this->config_hash = $newConfigHash;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($oldConfigHash === $newConfigHash) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if ($save) {
|
||||||
|
$this->config_hash = $newConfigHash;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,23 +193,24 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
public function isProxyShouldRun()
|
public function isProxyShouldRun()
|
||||||
{
|
{
|
||||||
$shouldRun = false;
|
|
||||||
if ($this->proxyType() === ProxyTypes::NONE->value) {
|
if ($this->proxyType() === ProxyTypes::NONE->value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($this->applications() as $application) {
|
// foreach ($this->applications() as $application) {
|
||||||
if (data_get($application, 'fqdn')) {
|
// if (data_get($application, 'fqdn')) {
|
||||||
$shouldRun = true;
|
// $shouldRun = true;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if ($this->id === 0) {
|
// ray($this->services()->get());
|
||||||
$settings = InstanceSettings::get();
|
|
||||||
if (data_get($settings, 'fqdn')) {
|
// if ($this->id === 0) {
|
||||||
$shouldRun = true;
|
// $settings = InstanceSettings::get();
|
||||||
}
|
// if (data_get($settings, 'fqdn')) {
|
||||||
}
|
// $shouldRun = true;
|
||||||
return $shouldRun;
|
// }
|
||||||
|
// }
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
public function isFunctional()
|
public function isFunctional()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ class Service extends BaseModel
|
|||||||
$value = Str::of($variable);
|
$value = Str::of($variable);
|
||||||
}
|
}
|
||||||
if ($key->startsWith('SERVICE_FQDN')) {
|
if ($key->startsWith('SERVICE_FQDN')) {
|
||||||
if ($isNew) {
|
if ($isNew || $savedService->fqdn === null) {
|
||||||
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
||||||
$fqdn = generateFqdn($this->server, "{$name->value()}-{$this->uuid}");
|
$fqdn = generateFqdn($this->server, "{$name->value()}-{$this->uuid}");
|
||||||
if (substr_count($key->value(), '_') === 3) {
|
if (substr_count($key->value(), '_') === 3) {
|
||||||
@@ -568,7 +568,7 @@ class Service extends BaseModel
|
|||||||
'networks' => $topLevelNetworks->toArray(),
|
'networks' => $topLevelNetworks->toArray(),
|
||||||
];
|
];
|
||||||
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||||
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||||
$this->save();
|
$this->save();
|
||||||
$this->saveComposeConfigs();
|
$this->saveComposeConfigs();
|
||||||
return collect([]);
|
return collect([]);
|
||||||
|
|||||||
@@ -205,8 +205,9 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled,
|
|||||||
|
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null, $ports): array
|
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
||||||
{
|
{
|
||||||
|
$ports = $application->settings->is_static ? [80] : $application->ports_exposes_array;
|
||||||
$onlyPort = null;
|
$onlyPort = null;
|
||||||
if (count($ports) === 1) {
|
if (count($ports) === 1) {
|
||||||
$onlyPort = $ports[0];
|
$onlyPort = $ports[0];
|
||||||
@@ -226,7 +227,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
}
|
}
|
||||||
// Add Traefik labels no matter which proxy is selected
|
// Add Traefik labels no matter which proxy is selected
|
||||||
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled,$onlyPort));
|
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled, $onlyPort));
|
||||||
}
|
}
|
||||||
|
ray($labels);
|
||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.86',
|
'release' => '4.0.0-beta.94',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.86';
|
return '4.0.0-beta.94';
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->text('custom_labels')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('custom_labels');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="preconnect" href="https://api.fonts.coollabs.io" crossorigin>
|
<link rel="preconnect" href="https://api.fonts.coollabs.io" crossorigin>
|
||||||
<link href="https://api.fonts.coollabs.io/css2?family=Inter&display=swap" rel="stylesheet">
|
<link href="https://api.fonts.coollabs.io/css2?family=Inter&display=swap" rel="stylesheet">
|
||||||
|
<meta name="robots" content="noindex">
|
||||||
<title>Coolify</title>
|
<title>Coolify</title>
|
||||||
@env('local')
|
@env('local')
|
||||||
<link rel="icon" href="{{ asset('favicon-dev.png') }}" type="image/x-icon" />
|
<link rel="icon" href="{{ asset('favicon-dev.png') }}" type="image/x-icon" />
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
@if ($projects->count() === 1)
|
@if ($projects->count() === 1)
|
||||||
<div class="grid grid-cols-1 gap-2">
|
<div class="grid grid-cols-1 gap-2">
|
||||||
@else
|
@else
|
||||||
<div class="grid grid-cols-3 gap-2">
|
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||||
@endif
|
@endif
|
||||||
@foreach ($projects as $project)
|
@foreach ($projects as $project)
|
||||||
<div class="gap-2 border border-transparent cursor-pointer box group" x-data
|
<div class="gap-2 border border-transparent cursor-pointer box group" x-data
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
{{ $project->description }}</div>
|
{{ $project->description }}</div>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a class="mx-4 rounded group-hover:text-white hover:no-underline "
|
<a class="mx-4 rounded group-hover:text-white hover:no-underline"
|
||||||
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
||||||
<span class="font-bold hover:text-warning">+ New Resource</span>
|
<span class="font-bold hover:text-warning">+ New Resource</span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -7,6 +7,6 @@
|
|||||||
@endif
|
@endif
|
||||||
@if (data_get($application_deployment_queue, 'status') === 'in_progress' ||
|
@if (data_get($application_deployment_queue, 'status') === 'in_progress' ||
|
||||||
data_get($application_deployment_queue, 'status') === 'queued')
|
data_get($application_deployment_queue, 'status') === 'queued')
|
||||||
<x-forms.button wire:click.prevent="cancel">Cancel deployment</x-forms.button>
|
<x-forms.button wire:click.prevent="cancel">Cancel Deployment</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,8 +5,12 @@
|
|||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
|
@if ($isConfigurationChanged && !is_null($application->config_hash))
|
||||||
|
<div class="font-bold text-warning">Configuration not applied to the running application. You need to
|
||||||
|
redeploy.</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="">General configuration for your application.</div>
|
<div>General configuration for your application.</div>
|
||||||
<div class="flex flex-col gap-2 py-4">
|
<div class="flex flex-col gap-2 py-4">
|
||||||
<div class="flex flex-col items-end gap-2 xl:flex-row">
|
<div class="flex flex-col items-end gap-2 xl:flex-row">
|
||||||
<x-forms.input id="application.name" label="Name" required />
|
<x-forms.input id="application.name" label="Name" required />
|
||||||
@@ -81,7 +85,6 @@
|
|||||||
@if ($application->dockerfile)
|
@if ($application->dockerfile)
|
||||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<h3>Network</h3>
|
<h3>Network</h3>
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
@if ($application->settings->is_static)
|
@if ($application->settings->is_static)
|
||||||
@@ -93,6 +96,12 @@
|
|||||||
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
||||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||||
</div>
|
</div>
|
||||||
|
@if ($labelsChanged)
|
||||||
|
<x-forms.textarea label="Custom Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||||
|
@else
|
||||||
|
<x-forms.textarea label="Coolify Generated Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||||
|
@endif
|
||||||
|
<x-forms.button wire:click="resetDefaultLabels">Reset to Coolify Generated Labels</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<h3>Advanced</h3>
|
<h3>Advanced</h3>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
|||||||
@@ -152,6 +152,9 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
<div class="py-4 pb-10">Trademarks Policy: The respective trademarks mentioned here are owned by the
|
||||||
|
respective
|
||||||
|
companies, and use of them does not imply any affiliation or endorsement.</div>
|
||||||
@endif
|
@endif
|
||||||
@if ($current_step === 'servers')
|
@if ($current_step === 'servers')
|
||||||
<ul class="pb-10 steps">
|
<ul class="pb-10 steps">
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@if ($application->required_fqdn)
|
@if ($application->required_fqdn)
|
||||||
<x-forms.input required placeholder="https://app.coolify.io" label="Domains"
|
<x-forms.input required placeholder="https://app.coolify.io" label="Domains"
|
||||||
id="application.fqdn"></x-forms.input>
|
id="application.fqdn" helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||||
@else
|
@else
|
||||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
||||||
id="application.fqdn"></x-forms.input>
|
id="application.fqdn" helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||||
@endif
|
@endif
|
||||||
<x-forms.input required
|
<x-forms.input required
|
||||||
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div x-init="$wire.checkStatus">
|
<div x-init="$wire.checkStatus" wire:poll.2500ms='checkStatus'>
|
||||||
<livewire:project.service.modal />
|
<livewire:project.service.modal />
|
||||||
<h1>Configuration</h1>
|
<h1>Configuration</h1>
|
||||||
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
||||||
|
|||||||
@@ -46,9 +46,11 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
@if (Str::of(data_get($application, 'status'))->startsWith('running'))
|
@if (Str::of(data_get($application, 'status'))->startsWith('running'))
|
||||||
<div class="absolute bg-green-400 -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
|
||||||
@elseif (Str::of(data_get($application, 'status'))->startsWith('exited'))
|
@elseif (Str::of(data_get($application, 'status'))->startsWith('exited'))
|
||||||
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
||||||
|
@elseif (Str::of(data_get($application, 'status'))->startsWith('restarting'))
|
||||||
|
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
|
||||||
@endif
|
@endif
|
||||||
</a>
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -60,9 +62,11 @@
|
|||||||
<div class="text-xs text-gray-400 group-hover:text-white">{{ $database->description }}</div>
|
<div class="text-xs text-gray-400 group-hover:text-white">{{ $database->description }}</div>
|
||||||
</div>
|
</div>
|
||||||
@if (Str::of(data_get($database, 'status'))->startsWith('running'))
|
@if (Str::of(data_get($database, 'status'))->startsWith('running'))
|
||||||
<div class="absolute bg-green-400 -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
|
||||||
@elseif (Str::of(data_get($database, 'status'))->startsWith('exited'))
|
@elseif (Str::of(data_get($database, 'status'))->startsWith('exited'))
|
||||||
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
||||||
|
@elseif (Str::of(data_get($database, 'status'))->startsWith('restaring'))
|
||||||
|
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
|
||||||
@endif
|
@endif
|
||||||
</a>
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -74,9 +78,9 @@
|
|||||||
<div class="text-xs text-gray-400 group-hover:text-white">{{ $service->description }}</div>
|
<div class="text-xs text-gray-400 group-hover:text-white">{{ $service->description }}</div>
|
||||||
</div>
|
</div>
|
||||||
@if (Str::of(serviceStatus($service))->startsWith('running'))
|
@if (Str::of(serviceStatus($service))->startsWith('running'))
|
||||||
<div class="absolute bg-green-400 -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
|
||||||
@elseif (Str::of(serviceStatus($service))->startsWith('degraded'))
|
@elseif (Str::of(serviceStatus($service))->startsWith('degraded'))
|
||||||
<div class="absolute bg-yellow-400 -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
|
||||||
@elseif (Str::of(serviceStatus($service))->startsWith('exited'))
|
@elseif (Str::of(serviceStatus($service))->startsWith('exited'))
|
||||||
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ if [ $EUID != 0 ]; then
|
|||||||
echo "Please run as root"
|
echo "Please run as root"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if [ $OS_TYPE != "ubuntu" ] && [ $OS_TYPE != "debian" ]; then
|
if [ $OS_TYPE != "ubuntu" ] && [ $OS_TYPE != "debian" ] && [ $OS_TYPE != "raspbian" ]; then
|
||||||
echo "This script only supports Ubuntu and Debian for now."
|
echo "This script only supports Ubuntu and Debian for now."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ function help {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setup:dev {
|
function setup:dev {
|
||||||
docker exec coolify bash -c "php artisan key:generate"
|
|
||||||
docker exec coolify bash -c "composer install"
|
docker exec coolify bash -c "composer install"
|
||||||
|
docker exec coolify bash -c "php artisan key:generate"
|
||||||
docker exec coolify bash -c "php artisan migrate:fresh --seed"
|
docker exec coolify bash -c "php artisan migrate:fresh --seed"
|
||||||
|
sudo chmod -R o+rwx .
|
||||||
}
|
}
|
||||||
function sync:v3 {
|
function sync:v3 {
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
|||||||
"version": "3.12.36"
|
"version": "3.12.36"
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.86"
|
"version": "4.0.0-beta.94"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user