rector: arrrrr
This commit is contained in:
@@ -88,7 +88,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private bool $is_this_additional_server = false;
|
||||
|
||||
private ?ApplicationPreview $preview = null;
|
||||
private ?ApplicationPreview $applicationPreview = null;
|
||||
|
||||
private ?string $git_type = null;
|
||||
|
||||
@@ -174,8 +174,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
$this->nixpacks_plan_json = collect([]);
|
||||
|
||||
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
|
||||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
||||
$this->application_deployment_queue = ApplicationDeploymentQueue::query()->find($application_deployment_queue_id);
|
||||
$this->application = Application::query()->find($this->application_deployment_queue->application_id);
|
||||
$this->build_pack = data_get($this->application, 'build_pack');
|
||||
$this->build_args = collect([]);
|
||||
|
||||
@@ -199,7 +199,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($source) {
|
||||
$this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first();
|
||||
}
|
||||
$this->server = Server::find($this->application_deployment_queue->server_id);
|
||||
$this->server = Server::query()->find($this->application_deployment_queue->server_id);
|
||||
$this->timeout = $this->server->settings->dynamic_timeout;
|
||||
$this->destination = $this->server->destinations()->where('id', $this->application_deployment_queue->destination_id)->first();
|
||||
$this->server = $this->mainServer = $this->destination->server;
|
||||
@@ -225,14 +225,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
// Set preview fqdn
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$this->preview = $this->application->generate_preview_fqdn($this->pull_request_id);
|
||||
$this->applicationPreview = $this->application->generate_preview_fqdn($this->pull_request_id);
|
||||
if ($this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->applicationPreview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
|
||||
}
|
||||
if ($this->application->build_pack === 'dockerfile') {
|
||||
if (data_get($this->application, 'dockerfile_location')) {
|
||||
$this->dockerfile_location = $this->application->dockerfile_location;
|
||||
}
|
||||
if ($this->application->build_pack === 'dockerfile' && data_get($this->application, 'dockerfile_location')) {
|
||||
$this->dockerfile_location = $this->application->dockerfile_location;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,15 +261,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if (count($allContainers) > 0) {
|
||||
$allContainers = $allContainers[0];
|
||||
$allContainers = collect($allContainers)->sort()->values();
|
||||
foreach ($allContainers as $container) {
|
||||
$containerName = data_get($container, 'Name');
|
||||
foreach ($allContainers as $allContainer) {
|
||||
$containerName = data_get($allContainer, 'Name');
|
||||
if ($containerName === 'coolify-proxy') {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/-(\d{12})/', $containerName)) {
|
||||
continue;
|
||||
}
|
||||
$containerIp = data_get($container, 'IPv4Address');
|
||||
$containerIp = data_get($allContainer, 'IPv4Address');
|
||||
if ($containerName && $containerIp) {
|
||||
$containerIp = str($containerIp)->before('/');
|
||||
$ips->put($containerName, $containerIp->value());
|
||||
@@ -312,7 +310,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->decide_what_to_do();
|
||||
} catch (Exception $e) {
|
||||
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->applicationPreview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
|
||||
}
|
||||
$this->fail($e);
|
||||
throw $e;
|
||||
@@ -340,7 +338,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->just_restart();
|
||||
|
||||
return;
|
||||
} elseif ($this->pull_request_id !== 0) {
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$this->deploy_pull_request();
|
||||
} elseif ($this->application->dockerfile) {
|
||||
$this->deploy_simple_dockerfile();
|
||||
@@ -364,10 +363,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
GetContainersStatus::dispatch($this->server);
|
||||
}
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
if ($this->pull_request_id !== 0) {
|
||||
if ($this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
|
||||
}
|
||||
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
|
||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->applicationPreview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
|
||||
}
|
||||
$this->run_post_deployment_command();
|
||||
$this->application->isConfigurationChanged(true);
|
||||
@@ -470,7 +467,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||
$this->save_environment_variables();
|
||||
} else {
|
||||
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
||||
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->applicationPreview, 'id'));
|
||||
$this->save_environment_variables();
|
||||
if (! is_null($this->env_filename)) {
|
||||
$services = collect(data_get($composeFile, 'services', []));
|
||||
@@ -553,34 +550,32 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
['command' => $command, 'hidden' => true],
|
||||
);
|
||||
}
|
||||
} elseif ($this->docker_compose_custom_start_command) {
|
||||
$this->write_deployment_configurations();
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
if ($this->docker_compose_custom_start_command) {
|
||||
$command = "{$this->coolify_variables} docker compose";
|
||||
if ($this->preserveRepository) {
|
||||
if ($this->env_filename) {
|
||||
$command .= " --env-file {$server_workdir}/{$this->env_filename}";
|
||||
}
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
|
||||
$this->write_deployment_configurations();
|
||||
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true],
|
||||
['command' => $command, 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
$command = "{$this->coolify_variables} docker compose";
|
||||
if ($this->preserveRepository) {
|
||||
if ($this->env_filename) {
|
||||
$command .= " --env-file {$server_workdir}/{$this->env_filename}";
|
||||
}
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
|
||||
$this->write_deployment_configurations();
|
||||
|
||||
$this->execute_remote_command(
|
||||
['command' => $command, 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
if ($this->env_filename) {
|
||||
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
|
||||
}
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
if ($this->env_filename) {
|
||||
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
|
||||
}
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,7 +683,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
}
|
||||
if (isset($this->docker_compose_base64)) {
|
||||
if ($this->docker_compose_base64 !== null) {
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
@@ -773,9 +768,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->application_deployment_queue->addLogEntry('Failed to push image to docker registry. Please check debug logs for more information.');
|
||||
if ($forceFail) {
|
||||
throw new RuntimeException($e->getMessage(), 69420);
|
||||
}
|
||||
throw new RuntimeException($e->getMessage(), 69420, $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,9 +839,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->rolling_update();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
$this->application_deployment_queue->addLogEntry('Configuration changed. Rebuilding image.');
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry('Configuration changed. Rebuilding image.');
|
||||
} else {
|
||||
$this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Building new image.");
|
||||
}
|
||||
@@ -908,11 +900,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
||||
$envs->push("COOLIFY_DOMAIN_URL={$this->preview->fqdn}");
|
||||
$envs->push("COOLIFY_FQDN={$this->applicationPreview->fqdn}");
|
||||
$envs->push("COOLIFY_DOMAIN_URL={$this->applicationPreview->fqdn}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$url = str($this->applicationPreview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
$envs->push("COOLIFY_DOMAIN_FQDN={$url}");
|
||||
}
|
||||
@@ -927,24 +919,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables_preview);
|
||||
|
||||
foreach ($sorted_environment_variables_preview as $env) {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
foreach ($sorted_environment_variables_preview as $sorted_environment_variable_preview) {
|
||||
$real_value = $sorted_environment_variable_preview->real_value;
|
||||
if ($sorted_environment_variable_preview->version === '4.0.0-beta.239') {
|
||||
$real_value = $sorted_environment_variable_preview->real_value;
|
||||
} elseif ($sorted_environment_variable_preview->is_literal || $sorted_environment_variable_preview->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
if ($env->is_literal || $env->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$real_value = escapeEnvVariables($sorted_environment_variable_preview->real_value);
|
||||
}
|
||||
$envs->push($env->key.'='.$real_value);
|
||||
$envs->push($sorted_environment_variable_preview->key.'='.$real_value);
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->build_pack !== 'dockercompose') {
|
||||
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
if ($this->build_pack !== 'dockercompose' && $this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
|
||||
@@ -986,24 +974,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables);
|
||||
|
||||
foreach ($sorted_environment_variables as $env) {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
foreach ($sorted_environment_variables as $sorted_environment_variable) {
|
||||
$real_value = $sorted_environment_variable->real_value;
|
||||
if ($sorted_environment_variable->version === '4.0.0-beta.239') {
|
||||
$real_value = $sorted_environment_variable->real_value;
|
||||
} elseif ($sorted_environment_variable->is_literal || $sorted_environment_variable->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
if ($env->is_literal || $env->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$real_value = escapeEnvVariables($sorted_environment_variable->real_value);
|
||||
}
|
||||
$envs->push($env->key.'='.$real_value);
|
||||
$envs->push($sorted_environment_variable->key.'='.$real_value);
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->build_pack !== 'dockercompose') {
|
||||
if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
if ($this->build_pack !== 'dockercompose' && $this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
|
||||
@@ -1067,11 +1051,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function elixir_finetunes()
|
||||
{
|
||||
if ($this->pull_request_id === 0) {
|
||||
$envType = 'environment_variables';
|
||||
} else {
|
||||
$envType = 'environment_variables_preview';
|
||||
}
|
||||
$envType = $this->pull_request_id === 0 ? 'environment_variables' : 'environment_variables_preview';
|
||||
$mix_env = $this->application->{$envType}->where('key', 'MIX_ENV')->first();
|
||||
if ($mix_env) {
|
||||
if ($mix_env->is_build_time === false) {
|
||||
@@ -1106,11 +1086,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function laravel_finetunes()
|
||||
{
|
||||
if ($this->pull_request_id === 0) {
|
||||
$envType = 'environment_variables';
|
||||
} else {
|
||||
$envType = 'environment_variables_preview';
|
||||
}
|
||||
$envType = $this->pull_request_id === 0 ? 'environment_variables' : 'environment_variables_preview';
|
||||
$nixpacks_php_fallback_path = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
|
||||
$nixpacks_php_root_dir = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
||||
|
||||
@@ -1194,7 +1170,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.');
|
||||
}
|
||||
// ray('New container name: ', $this->container_name);
|
||||
if ($this->container_name) {
|
||||
if ($this->container_name !== '' && $this->container_name !== '0') {
|
||||
$counter = 1;
|
||||
$this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.');
|
||||
if ($this->full_healthcheck_url) {
|
||||
@@ -1344,12 +1320,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.');
|
||||
}
|
||||
$runCommand = "docker run -d --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}";
|
||||
} elseif ($this->dockerConfigFileExists === 'OK') {
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --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 {
|
||||
if ($this->dockerConfigFileExists === 'OK') {
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --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 -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
}
|
||||
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
|
||||
$this->execute_remote_command(
|
||||
@@ -1389,7 +1363,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
return;
|
||||
}
|
||||
foreach ($destination_ids as $destination_id) {
|
||||
$destination = StandaloneDocker::find($destination_id);
|
||||
$destination = StandaloneDocker::query()->find($destination_id);
|
||||
$server = $destination->server;
|
||||
if ($server->team_id !== $this->mainServer->team_id) {
|
||||
$this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!");
|
||||
@@ -1417,17 +1391,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
private function set_coolify_variables()
|
||||
{
|
||||
$this->coolify_variables = "SOURCE_COMMIT={$this->commit} ";
|
||||
if ($this->pull_request_id === 0) {
|
||||
$fqdn = $this->application->fqdn;
|
||||
} else {
|
||||
$fqdn = $this->preview->fqdn;
|
||||
}
|
||||
$fqdn = $this->pull_request_id === 0 ? $this->application->fqdn : $this->applicationPreview->fqdn;
|
||||
if (isset($fqdn)) {
|
||||
$this->coolify_variables .= "COOLIFY_FQDN={$fqdn} ";
|
||||
$url = str($fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$this->coolify_variables .= "COOLIFY_URL={$url} ";
|
||||
}
|
||||
if (isset($this->application->git_branch)) {
|
||||
if (property_exists($this->application, 'git_branch') && $this->application->git_branch !== null) {
|
||||
$this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} ";
|
||||
}
|
||||
}
|
||||
@@ -1598,9 +1568,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->application->install_command) {
|
||||
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
||||
}
|
||||
$nixpacks_command .= " {$this->workdir}";
|
||||
|
||||
return $nixpacks_command;
|
||||
return $nixpacks_command." {$this->workdir}";
|
||||
}
|
||||
|
||||
private function generate_nixpacks_env_variables()
|
||||
@@ -1666,7 +1635,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
});
|
||||
if ($found_caddy_labels->count() === 0) {
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$domains = str(data_get($this->preview, 'fqdn'))->explode(',');
|
||||
$domains = str(data_get($this->applicationPreview, 'fqdn'))->explode(',');
|
||||
} else {
|
||||
$domains = str(data_get($this->application, 'fqdn'))->explode(',');
|
||||
}
|
||||
@@ -1682,13 +1651,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
$this->application->custom_labels = base64_encode($labels->implode("\n"));
|
||||
$this->application->save();
|
||||
} else {
|
||||
if (! $this->application->settings->is_container_label_readonly_enabled) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
}
|
||||
} elseif (! $this->application->settings->is_container_label_readonly_enabled) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->applicationPreview));
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->applicationPreview));
|
||||
}
|
||||
if ($this->application->settings->is_container_label_escape_enabled) {
|
||||
$labels = $labels->map(function ($value, $key) {
|
||||
@@ -1874,23 +1841,21 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (count($custom_compose) > 0) {
|
||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||
data_forget($custom_compose, 'ip');
|
||||
data_forget($custom_compose, 'ip6');
|
||||
if ($ipv4 || $ipv6) {
|
||||
data_forget($docker_compose['services'][$this->container_name], 'networks');
|
||||
}
|
||||
if ($ipv4) {
|
||||
$docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
|
||||
}
|
||||
if ($ipv6) {
|
||||
$docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
|
||||
}
|
||||
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
|
||||
} elseif (count($custom_compose) > 0) {
|
||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||
data_forget($custom_compose, 'ip');
|
||||
data_forget($custom_compose, 'ip6');
|
||||
if ($ipv4 || $ipv6) {
|
||||
data_forget($docker_compose['services'][$this->container_name], 'networks');
|
||||
}
|
||||
if ($ipv4) {
|
||||
$docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
|
||||
}
|
||||
if ($ipv6) {
|
||||
$docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
|
||||
}
|
||||
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2105,86 +2070,82 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
} elseif ($this->application->dockerfile) {
|
||||
// Pure Dockerfile based deployment
|
||||
if ($this->application->dockerfile) {
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
} else {
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
} else {
|
||||
$build_command = "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
} elseif ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
} else {
|
||||
if ($this->force_rebuild) {
|
||||
$build_command = "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
} else {
|
||||
$build_command = "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'cat /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
|
||||
}
|
||||
@@ -2214,7 +2175,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||
);
|
||||
}
|
||||
} catch (\Exception $error) {
|
||||
} catch (Exception $error) {
|
||||
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr');
|
||||
}
|
||||
|
||||
@@ -2267,16 +2228,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} pull"), 'hidden' => true],
|
||||
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} up --build -d"), 'hidden' => true],
|
||||
);
|
||||
} elseif ($this->use_build_server) {
|
||||
$this->execute_remote_command(
|
||||
["{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
if ($this->use_build_server) {
|
||||
$this->execute_remote_command(
|
||||
["{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true],
|
||||
);
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true],
|
||||
);
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry('New container started.');
|
||||
}
|
||||
@@ -2398,7 +2357,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
]);
|
||||
}
|
||||
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) {
|
||||
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
||||
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->applicationPreview));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -2406,20 +2365,20 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
if (! $this->only_this_server) {
|
||||
$this->deploy_to_additional_destinations();
|
||||
}
|
||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->applicationPreview));
|
||||
}
|
||||
}
|
||||
|
||||
public function failed(Throwable $exception): void
|
||||
public function failed(Throwable $throwable): void
|
||||
{
|
||||
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
||||
$this->application_deployment_queue->addLogEntry('Oops something is not okay, are you okay? 😢', 'stderr');
|
||||
if (str($exception->getMessage())->isNotEmpty()) {
|
||||
$this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr');
|
||||
if (str($throwable->getMessage())->isNotEmpty()) {
|
||||
$this->application_deployment_queue->addLogEntry($throwable->getMessage(), 'stderr');
|
||||
}
|
||||
|
||||
if ($this->application->build_pack !== 'dockercompose') {
|
||||
$code = $exception->getCode();
|
||||
$code = $throwable->getCode();
|
||||
if ($code !== 69420) {
|
||||
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
|
||||
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -22,8 +23,8 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public function __construct(
|
||||
public Application $application,
|
||||
public ApplicationPreview $preview,
|
||||
public ProcessStatus $status,
|
||||
public ApplicationPreview $applicationPreview,
|
||||
public ProcessStatus $processStatus,
|
||||
public ?string $deployment_uuid = null
|
||||
) {
|
||||
$this->onQueue('high');
|
||||
@@ -33,39 +34,42 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
if ($this->application->is_public_repository()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if ($this->status === ProcessStatus::CLOSED) {
|
||||
if ($this->processStatus === ProcessStatus::CLOSED) {
|
||||
$this->delete_comment();
|
||||
|
||||
return;
|
||||
} elseif ($this->status === ProcessStatus::IN_PROGRESS) {
|
||||
return null;
|
||||
}
|
||||
if ($this->processStatus === ProcessStatus::IN_PROGRESS) {
|
||||
$this->body = "The preview deployment is in progress. 🟡\n\n";
|
||||
} elseif ($this->status === ProcessStatus::FINISHED) {
|
||||
} elseif ($this->processStatus === ProcessStatus::FINISHED) {
|
||||
$this->body = "The preview deployment is ready. 🟢\n\n";
|
||||
if ($this->preview->fqdn) {
|
||||
$this->body .= "[Open Preview]({$this->preview->fqdn}) | ";
|
||||
if ($this->applicationPreview->fqdn) {
|
||||
$this->body .= "[Open Preview]({$this->applicationPreview->fqdn}) | ";
|
||||
}
|
||||
} elseif ($this->status === ProcessStatus::ERROR) {
|
||||
} elseif ($this->processStatus === ProcessStatus::ERROR) {
|
||||
$this->body = "The preview deployment failed. 🔴\n\n";
|
||||
}
|
||||
$this->build_logs_url = base_url()."/project/{$this->application->environment->project->uuid}/{$this->application->environment->name}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
|
||||
$this->body .= '[Open Build Logs]('.$this->build_logs_url.")\n\n\n";
|
||||
$this->body .= 'Last updated at: '.now()->toDateTimeString().' CET';
|
||||
if ($this->preview->pull_request_issue_comment_id) {
|
||||
if ($this->applicationPreview->pull_request_issue_comment_id) {
|
||||
$this->update_comment();
|
||||
} else {
|
||||
$this->create_comment();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
return $e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function update_comment()
|
||||
{
|
||||
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'patch', data: [
|
||||
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->applicationPreview->pull_request_issue_comment_id}", method: 'patch', data: [
|
||||
'body' => $this->body,
|
||||
], throwError: false);
|
||||
if (data_get($data, 'message') === 'Not Found') {
|
||||
@@ -75,15 +79,15 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function create_comment()
|
||||
{
|
||||
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->preview->pull_request_id}/comments", method: 'post', data: [
|
||||
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->applicationPreview->pull_request_id}/comments", method: 'post', data: [
|
||||
'body' => $this->body,
|
||||
]);
|
||||
$this->preview->pull_request_issue_comment_id = $data['id'];
|
||||
$this->preview->save();
|
||||
$this->applicationPreview->pull_request_issue_comment_id = $data['id'];
|
||||
$this->applicationPreview->save();
|
||||
}
|
||||
|
||||
private function delete_comment()
|
||||
{
|
||||
githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'delete');
|
||||
githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->applicationPreview->pull_request_issue_comment_id}", method: 'delete');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,19 +34,18 @@ class CheckAndStartSentinelJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
// If sentinel is running, check if it needs an update
|
||||
$runningVersion = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false);
|
||||
if (empty($runningVersion)) {
|
||||
if ($runningVersion === null || $runningVersion === '' || $runningVersion === '0') {
|
||||
$runningVersion = '0.0.0';
|
||||
}
|
||||
if ($latestVersion === '0.0.0' && $runningVersion === '0.0.0') {
|
||||
StartSentinel::run(server: $this->server, restart: true, latestVersion: 'latest');
|
||||
|
||||
return;
|
||||
} else {
|
||||
if (version_compare($runningVersion, $latestVersion, '<')) {
|
||||
StartSentinel::run(server: $this->server, restart: true, latestVersion: $latestVersion);
|
||||
}
|
||||
if (version_compare($runningVersion, $latestVersion, '<')) {
|
||||
StartSentinel::run(server: $this->server, restart: true, latestVersion: $latestVersion);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Throwable;
|
||||
|
||||
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -37,7 +38,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$settings->update(['new_version_available' => false]);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
// Consider implementing a notification to administrators
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Throwable;
|
||||
|
||||
class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -16,8 +17,6 @@ class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public $timeout = 1000;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
@@ -31,7 +30,7 @@ class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$settings->update(['helper_version' => $latest_version]);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
send_internal_notification('CheckHelperImageJob failed with: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
|
||||
{
|
||||
@@ -27,7 +28,7 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
|
||||
instant_remote_process(['docker container rm -f '.$containerId], $this->server, false);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
send_internal_notification('CleanupHelperContainersJob failed with error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,12 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
|
||||
class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()];
|
||||
@@ -28,7 +27,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
|
||||
{
|
||||
try {
|
||||
$this->cleanupInvitationLink();
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
Log::error('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class CleanupStaleMultiplexedConnections implements ShouldQueue
|
||||
|
||||
foreach ($muxFiles as $muxFile) {
|
||||
$serverUuid = $this->extractServerUuidFromMuxFile($muxFile);
|
||||
$server = Server::where('uuid', $serverUuid)->first();
|
||||
$server = Server::query()->where('uuid', $serverUuid)->first();
|
||||
|
||||
if (! $server) {
|
||||
$this->removeMultiplexFile($muxFile);
|
||||
@@ -57,7 +57,7 @@ class CleanupStaleMultiplexedConnections implements ShouldQueue
|
||||
private function cleanupNonExistentServerConnections()
|
||||
{
|
||||
$muxFiles = Storage::disk('ssh-mux')->files();
|
||||
$existingServerUuids = Server::pluck('uuid')->toArray();
|
||||
$existingServerUuids = Server::query()->pluck('uuid')->toArray();
|
||||
|
||||
foreach ($muxFiles as $muxFile) {
|
||||
$serverUuid = $this->extractServerUuidFromMuxFile($muxFile);
|
||||
|
||||
@@ -33,13 +33,13 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$remote_process = resolve(RunRemoteProcess::class, [
|
||||
$runRemoteProcess = resolve(RunRemoteProcess::class, [
|
||||
'activity' => $this->activity,
|
||||
'ignore_errors' => $this->ignore_errors,
|
||||
'call_event_on_finish' => $this->call_event_on_finish,
|
||||
'call_event_data' => $this->call_event_data,
|
||||
]);
|
||||
|
||||
$remote_process();
|
||||
$runRemoteProcess();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Models\Team;
|
||||
use App\Notifications\Database\BackupFailed;
|
||||
use App\Notifications\Database\BackupSuccess;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -23,6 +24,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Str;
|
||||
use Throwable;
|
||||
|
||||
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -56,7 +58,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public ?S3Storage $s3 = null;
|
||||
|
||||
public function __construct(public ScheduledDatabaseBackup $backup)
|
||||
public function __construct(public ScheduledDatabaseBackup $scheduledDatabaseBackup)
|
||||
{
|
||||
$this->onQueue('high');
|
||||
}
|
||||
@@ -66,26 +68,26 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
try {
|
||||
$databasesToBackup = null;
|
||||
|
||||
$this->team = Team::find($this->backup->team_id);
|
||||
$this->team = Team::query()->find($this->scheduledDatabaseBackup->team_id);
|
||||
if (! $this->team) {
|
||||
$this->backup->delete();
|
||||
$this->scheduledDatabaseBackup->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
if (data_get($this->backup, 'database_type') === \App\Models\ServiceDatabase::class) {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
if (data_get($this->scheduledDatabaseBackup, 'database_type') === ServiceDatabase::class) {
|
||||
$this->database = data_get($this->scheduledDatabaseBackup, 'database');
|
||||
$this->server = $this->database->service->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
$this->s3 = $this->scheduledDatabaseBackup->s3;
|
||||
} else {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->database = data_get($this->scheduledDatabaseBackup, 'database');
|
||||
$this->server = $this->database->destination->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
$this->s3 = $this->scheduledDatabaseBackup->s3;
|
||||
}
|
||||
if (is_null($this->server)) {
|
||||
throw new \Exception('Server not found?!');
|
||||
throw new Exception('Server not found?!');
|
||||
}
|
||||
if (is_null($this->database)) {
|
||||
throw new \Exception('Database not found?!');
|
||||
throw new Exception('Database not found?!');
|
||||
}
|
||||
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
@@ -94,7 +96,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if (! $status->startsWith('running') && $this->database->id !== 0) {
|
||||
return;
|
||||
}
|
||||
if (data_get($this->backup, 'database_type') === \App\Models\ServiceDatabase::class) {
|
||||
if (data_get($this->scheduledDatabaseBackup, 'database_type') === ServiceDatabase::class) {
|
||||
$databaseType = $this->database->databaseType();
|
||||
$serviceUuid = $this->database->service->uuid;
|
||||
$serviceName = str($this->database->service->name)->slug();
|
||||
@@ -108,21 +110,13 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$user = $envs->filter(function ($env) {
|
||||
return str($env)->startsWith('POSTGRES_USER=');
|
||||
})->first();
|
||||
if ($user) {
|
||||
$this->database->postgres_user = str($user)->after('POSTGRES_USER=')->value();
|
||||
} else {
|
||||
$this->database->postgres_user = 'postgres';
|
||||
}
|
||||
$this->database->postgres_user = $user ? str($user)->after('POSTGRES_USER=')->value() : 'postgres';
|
||||
|
||||
$db = $envs->filter(function ($env) {
|
||||
return str($env)->startsWith('POSTGRES_DB=');
|
||||
})->first();
|
||||
|
||||
if ($db) {
|
||||
$databasesToBackup = str($db)->after('POSTGRES_DB=')->value();
|
||||
} else {
|
||||
$databasesToBackup = $this->database->postgres_user;
|
||||
}
|
||||
$databasesToBackup = $db ? str($db)->after('POSTGRES_DB=')->value() : $this->database->postgres_user;
|
||||
$this->postgres_password = $envs->filter(function ($env) {
|
||||
return str($env)->startsWith('POSTGRES_PASSWORD=');
|
||||
})->first();
|
||||
@@ -150,7 +144,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($db) {
|
||||
$databasesToBackup = str($db)->after('MYSQL_DATABASE=')->value();
|
||||
} else {
|
||||
throw new \Exception('MYSQL_DATABASE not found');
|
||||
throw new Exception('MYSQL_DATABASE not found');
|
||||
}
|
||||
} elseif (str($databaseType)->contains('mariadb')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
@@ -186,7 +180,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($db) {
|
||||
$databasesToBackup = str($db)->after('MYSQL_DATABASE=')->value();
|
||||
} else {
|
||||
throw new \Exception('MARIADB_DATABASE or MYSQL_DATABASE not found');
|
||||
throw new Exception('MARIADB_DATABASE or MYSQL_DATABASE not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +189,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->container_name = $this->database->uuid;
|
||||
$this->directory_name = $databaseName.'-'.$this->container_name;
|
||||
$databaseType = $this->database->type();
|
||||
$databasesToBackup = data_get($this->backup, 'databases_to_backup');
|
||||
$databasesToBackup = data_get($this->scheduledDatabaseBackup, 'databases_to_backup');
|
||||
}
|
||||
if (blank($databasesToBackup)) {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
@@ -209,26 +203,24 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} elseif (str($databaseType)->contains('postgres')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} elseif (str($databaseType)->contains('mongodb')) {
|
||||
// Format: db1:collection1,collection2|db2:collection3,collection4
|
||||
$databasesToBackup = explode('|', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} elseif (str($databaseType)->contains('mysql')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} elseif (str($databaseType)->contains('mariadb')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} elseif (str($databaseType)->contains('mongodb')) {
|
||||
// Format: db1:collection1,collection2|db2:collection3,collection4
|
||||
$databasesToBackup = explode('|', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} elseif (str($databaseType)->contains('mysql')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} elseif (str($databaseType)->contains('mariadb')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
|
||||
if ($this->database->name === 'coolify-db') {
|
||||
@@ -237,82 +229,80 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$ip = Str::slug($this->server->ip);
|
||||
$this->backup_dir = backup_dir().'/coolify'."/coolify-db-$ip";
|
||||
}
|
||||
foreach ($databasesToBackup as $database) {
|
||||
foreach ($databasesToBackup as $databaseToBackup) {
|
||||
$size = 0;
|
||||
try {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->backup->dump_all) {
|
||||
$this->backup_file = "/pg-dump-{$databaseToBackup}-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->scheduledDatabaseBackup->dump_all) {
|
||||
$this->backup_file = '/pg-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||
}
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::query()->create([
|
||||
'database_name' => $databaseToBackup,
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
'scheduled_database_backup_id' => $this->scheduledDatabaseBackup->id,
|
||||
]);
|
||||
$this->backup_standalone_postgresql($database);
|
||||
$this->backup_standalone_postgresql($databaseToBackup);
|
||||
} elseif (str($databaseType)->contains('mongodb')) {
|
||||
if ($database === '*') {
|
||||
$database = 'all';
|
||||
if ($databaseToBackup === '*') {
|
||||
$databaseToBackup = 'all';
|
||||
$databaseName = 'all';
|
||||
} elseif (str($databaseToBackup)->contains(':')) {
|
||||
$databaseName = str($databaseToBackup)->before(':');
|
||||
} else {
|
||||
if (str($database)->contains(':')) {
|
||||
$databaseName = str($database)->before(':');
|
||||
} else {
|
||||
$databaseName = $database;
|
||||
}
|
||||
$databaseName = $databaseToBackup;
|
||||
}
|
||||
$this->backup_file = "/mongo-dump-$databaseName-".Carbon::now()->timestamp.'.tar.gz';
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::query()->create([
|
||||
'database_name' => $databaseName,
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
'scheduled_database_backup_id' => $this->scheduledDatabaseBackup->id,
|
||||
]);
|
||||
$this->backup_standalone_mongodb($database);
|
||||
$this->backup_standalone_mongodb($databaseToBackup);
|
||||
} elseif (str($databaseType)->contains('mysql')) {
|
||||
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->backup->dump_all) {
|
||||
$this->backup_file = "/mysql-dump-{$databaseToBackup}-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->scheduledDatabaseBackup->dump_all) {
|
||||
$this->backup_file = '/mysql-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||
}
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::query()->create([
|
||||
'database_name' => $databaseToBackup,
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
'scheduled_database_backup_id' => $this->scheduledDatabaseBackup->id,
|
||||
]);
|
||||
$this->backup_standalone_mysql($database);
|
||||
$this->backup_standalone_mysql($databaseToBackup);
|
||||
} elseif (str($databaseType)->contains('mariadb')) {
|
||||
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->backup->dump_all) {
|
||||
$this->backup_file = "/mariadb-dump-{$databaseToBackup}-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->scheduledDatabaseBackup->dump_all) {
|
||||
$this->backup_file = '/mariadb-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||
}
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::query()->create([
|
||||
'database_name' => $databaseToBackup,
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
'scheduled_database_backup_id' => $this->scheduledDatabaseBackup->id,
|
||||
]);
|
||||
$this->backup_standalone_mariadb($database);
|
||||
$this->backup_standalone_mariadb($databaseToBackup);
|
||||
} else {
|
||||
throw new \Exception('Unsupported database type');
|
||||
throw new Exception('Unsupported database type');
|
||||
}
|
||||
$size = $this->calculate_size();
|
||||
$this->remove_old_backups();
|
||||
if ($this->backup->save_s3) {
|
||||
if ($this->scheduledDatabaseBackup->save_s3) {
|
||||
$this->upload_to_s3();
|
||||
}
|
||||
|
||||
$this->team->notify(new BackupSuccess($this->backup, $this->database, $database));
|
||||
$this->team->notify(new BackupSuccess($this->scheduledDatabaseBackup, $this->database, $databaseToBackup));
|
||||
|
||||
$this->backup_log->update([
|
||||
'status' => 'success',
|
||||
'message' => $this->backup_output,
|
||||
'size' => $size,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->backup_log) {
|
||||
} catch (Throwable $e) {
|
||||
if ($this->backup_log instanceof ScheduledDatabaseBackupExecution) {
|
||||
$this->backup_log->update([
|
||||
'status' => 'failed',
|
||||
'message' => $this->backup_output,
|
||||
@@ -320,13 +310,13 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
'filename' => null,
|
||||
]);
|
||||
}
|
||||
$this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output, $database));
|
||||
$this->team?->notify(new BackupFailed($this->scheduledDatabaseBackup, $this->database, $this->backup_output, $databaseToBackup));
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
} finally {
|
||||
if ($this->team) {
|
||||
if ($this->team instanceof Team) {
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
}
|
||||
}
|
||||
@@ -358,12 +348,10 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
|
||||
}
|
||||
} elseif (str($this->database->image)->startsWith('mongo:4')) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location";
|
||||
} else {
|
||||
if (str($this->database->image)->startsWith('mongo:4')) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location";
|
||||
}
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location";
|
||||
}
|
||||
}
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
@@ -371,7 +359,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->backup_output === '') {
|
||||
$this->backup_output = null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
@@ -385,7 +373,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->postgres_password) {
|
||||
$backupCommand .= " -e PGPASSWORD=$this->postgres_password";
|
||||
}
|
||||
if ($this->backup->dump_all) {
|
||||
if ($this->scheduledDatabaseBackup->dump_all) {
|
||||
$backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
|
||||
} else {
|
||||
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
|
||||
@@ -397,7 +385,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->backup_output === '') {
|
||||
$this->backup_output = null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
@@ -407,7 +395,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||
if ($this->backup->dump_all) {
|
||||
if ($this->scheduledDatabaseBackup->dump_all) {
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress | gzip > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
|
||||
@@ -417,7 +405,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->backup_output === '') {
|
||||
$this->backup_output = null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
@@ -427,7 +415,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||
if ($this->backup->dump_all) {
|
||||
if ($this->scheduledDatabaseBackup->dump_all) {
|
||||
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
|
||||
@@ -437,7 +425,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->backup_output === '') {
|
||||
$this->backup_output = null;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
@@ -445,11 +433,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function add_to_backup_output($output): void
|
||||
{
|
||||
if ($this->backup_output) {
|
||||
$this->backup_output = $this->backup_output."\n".$output;
|
||||
} else {
|
||||
$this->backup_output = $output;
|
||||
}
|
||||
$this->backup_output = $this->backup_output ? $this->backup_output."\n".$output : $output;
|
||||
}
|
||||
|
||||
private function calculate_size()
|
||||
@@ -459,10 +443,10 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function remove_old_backups(): void
|
||||
{
|
||||
if ($this->backup->number_of_backups_locally === 0) {
|
||||
$deletable = $this->backup->executions()->where('status', 'success');
|
||||
if ($this->scheduledDatabaseBackup->number_of_backups_locally === 0) {
|
||||
$deletable = $this->scheduledDatabaseBackup->executions()->where('status', 'success');
|
||||
} else {
|
||||
$deletable = $this->backup->executions()->where('status', 'success')->skip($this->backup->number_of_backups_locally - 1);
|
||||
$deletable = $this->scheduledDatabaseBackup->executions()->where('status', 'success')->skip($this->scheduledDatabaseBackup->number_of_backups_locally - 1);
|
||||
}
|
||||
foreach ($deletable->get() as $execution) {
|
||||
delete_backup_locally($execution->filename, $this->server);
|
||||
@@ -482,7 +466,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$bucket = $this->s3->bucket;
|
||||
$endpoint = $this->s3->endpoint;
|
||||
$this->s3->testConnection(shouldSave: true);
|
||||
if (data_get($this->backup, 'database_type') === \App\Models\ServiceDatabase::class) {
|
||||
if (data_get($this->scheduledDatabaseBackup, 'database_type') === ServiceDatabase::class) {
|
||||
$network = $this->database->service->destination->network;
|
||||
} else {
|
||||
$network = $this->database->destination->network;
|
||||
@@ -493,29 +477,29 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if (isDev()) {
|
||||
if ($this->database->name === 'coolify-db') {
|
||||
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/coolify/coolify-db-'.$this->server->ip.$this->backup_file;
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->scheduledDatabaseBackup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
} else {
|
||||
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name.$this->backup_file;
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->scheduledDatabaseBackup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
}
|
||||
} else {
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->scheduledDatabaseBackup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
|
||||
}
|
||||
if ($this->s3->isHetzner()) {
|
||||
$endpointWithoutBucket = 'https://'.str($endpoint)->after('https://')->after('.')->value();
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
|
||||
$commands[] = "docker exec backup-of-{$this->scheduledDatabaseBackup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
|
||||
} else {
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||
$commands[] = "docker exec backup-of-{$this->scheduledDatabaseBackup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||
}
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||
$commands[] = "docker exec backup-of-{$this->scheduledDatabaseBackup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||
instant_remote_process($commands, $this->server);
|
||||
|
||||
$this->add_to_backup_output('Uploaded to S3.');
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
throw $e;
|
||||
} finally {
|
||||
$command = "docker rm -f backup-of-{$this->backup->uuid}";
|
||||
$command = "docker rm -f backup-of-{$this->scheduledDatabaseBackup->uuid}";
|
||||
instant_remote_process([$command], $this->server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Throwable;
|
||||
|
||||
class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -80,7 +81,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|| $this->resource instanceof StandaloneKeydb
|
||||
|| $this->resource instanceof StandaloneDragonfly
|
||||
|| $this->resource instanceof StandaloneClickhouse;
|
||||
$server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server');
|
||||
$server = data_get($this->resource, 'server', data_get($this->resource, 'destination.server'));
|
||||
if (($this->dockerCleanup || $isDatabase) && $server) {
|
||||
CleanupDocker::dispatch($server, true);
|
||||
}
|
||||
@@ -88,7 +89,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->deleteConnectedNetworks && ! $isDatabase) {
|
||||
$this->resource?->delete_connected_networks($this->resource->uuid);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
} finally {
|
||||
$this->resource->forceDelete();
|
||||
|
||||
@@ -13,6 +13,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -66,7 +67,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
} else {
|
||||
$this->server->team?->notify(new DockerCleanupSuccess($this->server, 'No cleanup needed for '.$this->server->name));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
$this->server->team?->notify(new DockerCleanupFailed($this->server, 'Docker cleanup job failed with the following error: '.$e->getMessage()));
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Throwable;
|
||||
|
||||
class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -22,25 +23,25 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
|
||||
return isDev() ? 1 : 3;
|
||||
}
|
||||
|
||||
public function __construct(public GithubApp $github_app) {}
|
||||
public function __construct(public GithubApp $githubApp) {}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$github_access_token = generate_github_jwt_token($this->github_app);
|
||||
$github_access_token = generate_github_jwt_token($this->githubApp);
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => "Bearer $github_access_token",
|
||||
'Accept' => 'application/vnd.github+json',
|
||||
])->get("{$this->github_app->api_url}/app");
|
||||
])->get("{$this->githubApp->api_url}/app");
|
||||
$response = $response->json();
|
||||
$permissions = data_get($response, 'permissions');
|
||||
$this->github_app->contents = data_get($permissions, 'contents');
|
||||
$this->github_app->metadata = data_get($permissions, 'metadata');
|
||||
$this->github_app->pull_requests = data_get($permissions, 'pull_requests');
|
||||
$this->github_app->administration = data_get($permissions, 'administration');
|
||||
$this->github_app->save();
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
} catch (\Throwable $e) {
|
||||
$this->githubApp->contents = data_get($permissions, 'contents');
|
||||
$this->githubApp->metadata = data_get($permissions, 'metadata');
|
||||
$this->githubApp->pull_requests = data_get($permissions, 'pull_requests');
|
||||
$this->githubApp->administration = data_get($permissions, 'administration');
|
||||
$this->githubApp->save();
|
||||
$this->githubApp->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
} catch (Throwable $e) {
|
||||
send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Throwable;
|
||||
|
||||
class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -35,7 +36,7 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
|
||||
} else {
|
||||
send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body());
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
send_internal_notification('PullTemplatesAndVersions failed with: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Models\Server;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Notifications\Container\ContainerRestarted;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -21,6 +22,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
use Throwable;
|
||||
|
||||
class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -92,7 +94,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
// TODO: Swarm is not supported yet
|
||||
if (! $this->data) {
|
||||
throw new \Exception('No data provided');
|
||||
throw new Exception('No data provided');
|
||||
}
|
||||
$data = collect($this->data);
|
||||
|
||||
@@ -154,7 +156,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
$this->updateApplicationPreviewStatus($applicationId, $containerStatus);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
} elseif ($labels->has('coolify.serviceId')) {
|
||||
$serviceId = $labels->get('coolify.serviceId');
|
||||
@@ -173,14 +175,12 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($name === 'coolify-proxy' && $this->isRunning($containerStatus)) {
|
||||
$this->foundProxy = true;
|
||||
} elseif ($type === 'service' && $this->isRunning($containerStatus)) {
|
||||
} else {
|
||||
if ($this->allDatabaseUuids->contains($uuid) && $this->isRunning($containerStatus)) {
|
||||
$this->foundDatabaseUuids->push($uuid);
|
||||
if ($this->allTcpProxyUuids->contains($uuid) && $this->isRunning($containerStatus)) {
|
||||
$this->updateDatabaseStatus($uuid, $containerStatus, tcpProxy: true);
|
||||
} else {
|
||||
$this->updateDatabaseStatus($uuid, $containerStatus, tcpProxy: false);
|
||||
}
|
||||
} elseif ($this->allDatabaseUuids->contains($uuid) && $this->isRunning($containerStatus)) {
|
||||
$this->foundDatabaseUuids->push($uuid);
|
||||
if ($this->allTcpProxyUuids->contains($uuid) && $this->isRunning($containerStatus)) {
|
||||
$this->updateDatabaseStatus($uuid, $containerStatus, tcpProxy: true);
|
||||
} else {
|
||||
$this->updateDatabaseStatus($uuid, $containerStatus, tcpProxy: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +224,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$notFoundApplicationIds = $this->allApplicationIds->diff($this->foundApplicationIds);
|
||||
if ($notFoundApplicationIds->isNotEmpty()) {
|
||||
$notFoundApplicationIds->each(function ($applicationId) {
|
||||
$application = Application::find($applicationId);
|
||||
$application = Application::query()->find($applicationId);
|
||||
if ($application) {
|
||||
$application->status = 'exited';
|
||||
$application->save();
|
||||
@@ -238,7 +238,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$notFoundApplicationPreviewsIds = $this->allApplicationPreviewsIds->diff($this->foundApplicationPreviewsIds);
|
||||
if ($notFoundApplicationPreviewsIds->isNotEmpty()) {
|
||||
$notFoundApplicationPreviewsIds->each(function ($applicationPreviewId) {
|
||||
$applicationPreview = ApplicationPreview::find($applicationPreviewId);
|
||||
$applicationPreview = ApplicationPreview::query()->find($applicationPreviewId);
|
||||
if ($applicationPreview) {
|
||||
$applicationPreview->status = 'exited';
|
||||
$applicationPreview->save();
|
||||
@@ -257,7 +257,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
} else {
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
@@ -327,7 +327,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$notFoundServiceDatabaseIds = $this->allServiceDatabaseIds->diff($this->foundServiceDatabaseIds);
|
||||
if ($notFoundServiceApplicationIds->isNotEmpty()) {
|
||||
$notFoundServiceApplicationIds->each(function ($serviceApplicationId) {
|
||||
$application = ServiceApplication::find($serviceApplicationId);
|
||||
$application = ServiceApplication::query()->find($serviceApplicationId);
|
||||
if ($application) {
|
||||
$application->status = 'exited';
|
||||
$application->save();
|
||||
@@ -336,7 +336,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
if ($notFoundServiceDatabaseIds->isNotEmpty()) {
|
||||
$notFoundServiceDatabaseIds->each(function ($serviceDatabaseId) {
|
||||
$database = ServiceDatabase::find($serviceDatabaseId);
|
||||
$database = ServiceDatabase::query()->find($serviceDatabaseId);
|
||||
if ($database) {
|
||||
$database->status = 'exited';
|
||||
$database->save();
|
||||
|
||||
@@ -11,11 +11,14 @@ use App\Models\Service;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\ScheduledTask\TaskFailed;
|
||||
use App\Notifications\ScheduledTask\TaskSuccess;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
class ScheduledTaskJob implements ShouldQueue
|
||||
{
|
||||
@@ -49,9 +52,9 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
} elseif ($application = $task->application()->first()) {
|
||||
$this->resource = $application;
|
||||
} else {
|
||||
throw new \RuntimeException('ScheduledTaskJob failed: No resource found.');
|
||||
throw new RuntimeException('ScheduledTaskJob failed: No resource found.');
|
||||
}
|
||||
$this->team = Team::findOrFail($task->team_id);
|
||||
$this->team = Team::query()->findOrFail($task->team_id);
|
||||
$this->server_timezone = $this->getServerTimezone();
|
||||
}
|
||||
|
||||
@@ -59,17 +62,15 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
{
|
||||
if ($this->resource instanceof Application) {
|
||||
return $this->resource->destination->server->settings->server_timezone;
|
||||
} elseif ($this->resource instanceof Service) {
|
||||
return $this->resource->server->settings->server_timezone;
|
||||
}
|
||||
|
||||
return 'UTC';
|
||||
return $this->resource->server->settings->server_timezone;
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$this->task_log = ScheduledTaskExecution::create([
|
||||
$this->task_log = ScheduledTaskExecution::query()->create([
|
||||
'scheduled_task_id' => $this->task->id,
|
||||
]);
|
||||
|
||||
@@ -95,17 +96,17 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
});
|
||||
}
|
||||
if (count($this->containers) == 0) {
|
||||
throw new \Exception('ScheduledTaskJob failed: No containers running.');
|
||||
throw new Exception('ScheduledTaskJob failed: No containers running.');
|
||||
}
|
||||
|
||||
if (count($this->containers) > 1 && empty($this->task->container)) {
|
||||
throw new \Exception('ScheduledTaskJob failed: More than one container exists but no container name was provided.');
|
||||
throw new Exception('ScheduledTaskJob failed: More than one container exists but no container name was provided.');
|
||||
}
|
||||
|
||||
foreach ($this->containers as $containerName) {
|
||||
if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container.'-'.$this->resource->uuid)) {
|
||||
foreach ($this->containers as $container) {
|
||||
if (count($this->containers) == 1 || str_starts_with($container, $this->task->container.'-'.$this->resource->uuid)) {
|
||||
$cmd = "sh -c '".str_replace("'", "'\''", $this->task->command)."'";
|
||||
$exec = "docker exec {$containerName} {$cmd}";
|
||||
$exec = "docker exec {$container} {$cmd}";
|
||||
$this->task_output = instant_remote_process([$exec], $this->server, true);
|
||||
$this->task_log->update([
|
||||
'status' => 'success',
|
||||
@@ -119,9 +120,9 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
}
|
||||
|
||||
// No valid container was found.
|
||||
throw new \Exception('ScheduledTaskJob failed: No valid container was found. Is the container name correct?');
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->task_log) {
|
||||
throw new Exception('ScheduledTaskJob failed: No valid container was found. Is the container name correct?');
|
||||
} catch (Throwable $e) {
|
||||
if ($this->task_log instanceof ScheduledTaskExecution) {
|
||||
$this->task_log->update([
|
||||
'status' => 'failed',
|
||||
'message' => $this->task_output ?? $e->getMessage(),
|
||||
|
||||
@@ -30,7 +30,7 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
|
||||
public int $maxExceptions = 5;
|
||||
|
||||
public function __construct(
|
||||
public DiscordMessage $message,
|
||||
public DiscordMessage $discordMessage,
|
||||
public string $webhookUrl
|
||||
) {
|
||||
$this->onQueue('high');
|
||||
@@ -41,6 +41,6 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Http::post($this->webhookUrl, $this->message->toPayload());
|
||||
Http::post($this->webhookUrl, $this->discordMessage->toPayload());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
class SendMessageToPushoverJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -30,7 +31,7 @@ class SendMessageToPushoverJob implements ShouldBeEncrypted, ShouldQueue
|
||||
public int $maxExceptions = 5;
|
||||
|
||||
public function __construct(
|
||||
public PushoverMessage $message,
|
||||
public PushoverMessage $pushoverMessage,
|
||||
public string $token,
|
||||
public string $user,
|
||||
) {
|
||||
@@ -42,9 +43,9 @@ class SendMessageToPushoverJob implements ShouldBeEncrypted, ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$response = Http::post('https://api.pushover.net/1/messages.json', $this->message->toPayload($this->token, $this->user));
|
||||
$response = Http::post('https://api.pushover.net/1/messages.json', $this->pushoverMessage->toPayload($this->token, $this->user));
|
||||
if ($response->failed()) {
|
||||
throw new \RuntimeException('Pushover notification failed with ' . $response->status() . ' status code.' . $response->body());
|
||||
throw new RuntimeException('Pushover notification failed with '.$response->status().' status code.'.$response->body());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class SendMessageToSlackJob implements ShouldQueue
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
private SlackMessage $message,
|
||||
private SlackMessage $slackMessage,
|
||||
private string $webhookUrl
|
||||
) {
|
||||
$this->onQueue('high');
|
||||
@@ -35,20 +35,20 @@ class SendMessageToSlackJob implements ShouldQueue
|
||||
],
|
||||
'attachments' => [
|
||||
[
|
||||
'color' => $this->message->color,
|
||||
'color' => $this->slackMessage->color,
|
||||
'blocks' => [
|
||||
[
|
||||
'type' => 'header',
|
||||
'text' => [
|
||||
'type' => 'plain_text',
|
||||
'text' => $this->message->title,
|
||||
'text' => $this->slackMessage->title,
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => 'section',
|
||||
'text' => [
|
||||
'type' => 'mrkdwn',
|
||||
'text' => $this->message->description,
|
||||
'text' => $this->slackMessage->description,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
|
||||
class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -44,18 +45,16 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
$url = 'https://api.telegram.org/bot'.$this->token.'/sendMessage';
|
||||
$inlineButtons = [];
|
||||
if (! empty($this->buttons)) {
|
||||
foreach ($this->buttons as $button) {
|
||||
$buttonUrl = data_get($button, 'url');
|
||||
$text = data_get($button, 'text', 'Click here');
|
||||
if ($buttonUrl && Str::contains($buttonUrl, 'http://localhost')) {
|
||||
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
||||
}
|
||||
$inlineButtons[] = [
|
||||
'text' => $text,
|
||||
'url' => $buttonUrl,
|
||||
];
|
||||
foreach ($this->buttons as $button) {
|
||||
$buttonUrl = data_get($button, 'url');
|
||||
$text = data_get($button, 'text', 'Click here');
|
||||
if ($buttonUrl && Str::contains($buttonUrl, 'http://localhost')) {
|
||||
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
||||
}
|
||||
$inlineButtons[] = [
|
||||
'text' => $text,
|
||||
'url' => $buttonUrl,
|
||||
];
|
||||
}
|
||||
$payload = [
|
||||
// 'parse_mode' => 'markdown',
|
||||
@@ -72,7 +71,7 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
$response = Http::post($url, $payload);
|
||||
if ($response->failed()) {
|
||||
throw new \RuntimeException('Telegram notification failed with '.$response->status().' status code.'.$response->body());
|
||||
throw new RuntimeException('Telegram notification failed with '.$response->status().' status code.'.$response->body());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -60,9 +61,9 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$foundProxyContainer = $this->containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
}
|
||||
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
})->first();
|
||||
if (! $foundProxyContainer) {
|
||||
try {
|
||||
@@ -71,7 +72,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||
@@ -81,9 +82,11 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function checkLogDrainContainer()
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class ServerCheckNewJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -27,8 +28,10 @@ class ServerCheckNewJob implements ShouldBeEncrypted, ShouldQueue
|
||||
try {
|
||||
ServerCheck::run($this->server);
|
||||
ResourcesCheck::dispatch($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class ServerCleanupMux implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -33,8 +34,10 @@ class ServerCleanupMux implements ShouldBeEncrypted, ShouldQueue
|
||||
return 'Server is not reachable or not ready.';
|
||||
}
|
||||
SshMultiplexingHelper::removeMuxFile($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -46,10 +47,12 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
send_internal_notification('ServerLimitCheckJob failed with: '.$e->getMessage());
|
||||
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Throwable;
|
||||
|
||||
class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -58,8 +59,10 @@ class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
} else {
|
||||
RateLimiter::hit('high-disk-usage:'.$this->server->id, 600);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ namespace App\Jobs;
|
||||
|
||||
use App\Models\Subscription;
|
||||
use App\Models\Team;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
use Stripe\StripeClient;
|
||||
|
||||
class StripeProcessJob implements ShouldQueue
|
||||
{
|
||||
@@ -33,26 +36,26 @@ class StripeProcessJob implements ShouldQueue
|
||||
$data = data_get($this->event, 'data.object');
|
||||
switch ($type) {
|
||||
case 'radar.early_fraud_warning.created':
|
||||
$stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
|
||||
$stripeClient = new StripeClient(config('subscription.stripe_api_key'));
|
||||
$id = data_get($data, 'id');
|
||||
$charge = data_get($data, 'charge');
|
||||
if ($charge) {
|
||||
$stripe->refunds->create(['charge' => $charge]);
|
||||
$stripeClient->refunds->create(['charge' => $charge]);
|
||||
}
|
||||
$pi = data_get($data, 'payment_intent');
|
||||
$piData = $stripe->paymentIntents->retrieve($pi, []);
|
||||
$piData = $stripeClient->paymentIntents->retrieve($pi, []);
|
||||
$customerId = data_get($piData, 'customer');
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->first();
|
||||
if ($subscription) {
|
||||
$subscriptionId = data_get($subscription, 'stripe_subscription_id');
|
||||
$stripe->subscriptions->cancel($subscriptionId, []);
|
||||
$stripeClient->subscriptions->cancel($subscriptionId, []);
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => false,
|
||||
]);
|
||||
send_internal_notification("Early fraud warning created Refunded, subscription canceled. Charge: {$charge}, id: {$id}, pi: {$pi}");
|
||||
} else {
|
||||
send_internal_notification("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}");
|
||||
throw new \RuntimeException("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}");
|
||||
throw new RuntimeException("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}");
|
||||
}
|
||||
break;
|
||||
case 'checkout.session.completed':
|
||||
@@ -65,13 +68,13 @@ class StripeProcessJob implements ShouldQueue
|
||||
$teamId = Str::after($clientReferenceId, ':');
|
||||
$subscriptionId = data_get($data, 'subscription');
|
||||
$customerId = data_get($data, 'customer');
|
||||
$team = Team::find($teamId);
|
||||
$team = Team::query()->find($teamId);
|
||||
$found = $team->members->where('id', $userId)->first();
|
||||
if (! $found->isAdmin()) {
|
||||
send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||
throw new \RuntimeException("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||
throw new RuntimeException("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||
}
|
||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||
$subscription = Subscription::query()->where('team_id', $teamId)->first();
|
||||
if ($subscription) {
|
||||
send_internal_notification('Old subscription activated for team: '.$teamId);
|
||||
$subscription->update([
|
||||
@@ -81,7 +84,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
]);
|
||||
} else {
|
||||
send_internal_notification('New subscription for team: '.$teamId);
|
||||
Subscription::create([
|
||||
Subscription::query()->create([
|
||||
'team_id' => $teamId,
|
||||
'stripe_subscription_id' => $subscriptionId,
|
||||
'stripe_customer_id' => $customerId,
|
||||
@@ -96,26 +99,26 @@ class StripeProcessJob implements ShouldQueue
|
||||
send_internal_notification('Subscription excluded.');
|
||||
break;
|
||||
}
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->first();
|
||||
if ($subscription) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => true,
|
||||
]);
|
||||
} else {
|
||||
throw new \RuntimeException("No subscription found for customer: {$customerId}");
|
||||
throw new RuntimeException("No subscription found for customer: {$customerId}");
|
||||
}
|
||||
break;
|
||||
case 'invoice.payment_failed':
|
||||
$customerId = data_get($data, 'customer');
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->first();
|
||||
if (! $subscription) {
|
||||
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId);
|
||||
throw new \RuntimeException("No subscription found for customer: {$customerId}");
|
||||
throw new RuntimeException("No subscription found for customer: {$customerId}");
|
||||
}
|
||||
$team = data_get($subscription, 'team');
|
||||
if (! $team) {
|
||||
send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId);
|
||||
throw new \RuntimeException("No team found in Coolify for customer: {$customerId}");
|
||||
throw new RuntimeException("No team found in Coolify for customer: {$customerId}");
|
||||
}
|
||||
if (! $subscription->stripe_invoice_paid) {
|
||||
SubscriptionInvoiceFailedJob::dispatch($team);
|
||||
@@ -126,10 +129,10 @@ class StripeProcessJob implements ShouldQueue
|
||||
break;
|
||||
case 'payment_intent.payment_failed':
|
||||
$customerId = data_get($data, 'customer');
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->first();
|
||||
if (! $subscription) {
|
||||
send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId);
|
||||
throw new \RuntimeException("No subscription found in Coolify for customer: {$customerId}");
|
||||
throw new RuntimeException("No subscription found in Coolify for customer: {$customerId}");
|
||||
}
|
||||
if ($subscription->stripe_invoice_paid) {
|
||||
send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId);
|
||||
@@ -144,49 +147,48 @@ class StripeProcessJob implements ShouldQueue
|
||||
$teamId = data_get($data, 'metadata.team_id');
|
||||
$userId = data_get($data, 'metadata.user_id');
|
||||
if (! $teamId || ! $userId) {
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->first();
|
||||
if ($subscription) {
|
||||
throw new \RuntimeException("Subscription already exists for customer: {$customerId}");
|
||||
throw new RuntimeException("Subscription already exists for customer: {$customerId}");
|
||||
}
|
||||
throw new \RuntimeException('No team id or user id found');
|
||||
throw new RuntimeException('No team id or user id found');
|
||||
}
|
||||
$team = Team::find($teamId);
|
||||
$team = Team::query()->find($teamId);
|
||||
$found = $team->members->where('id', $userId)->first();
|
||||
if (! $found->isAdmin()) {
|
||||
send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}.");
|
||||
throw new \RuntimeException("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}.");
|
||||
throw new RuntimeException("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}.");
|
||||
}
|
||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||
$subscription = Subscription::query()->where('team_id', $teamId)->first();
|
||||
if ($subscription) {
|
||||
send_internal_notification("Subscription already exists for team: {$teamId}");
|
||||
throw new \RuntimeException("Subscription already exists for team: {$teamId}");
|
||||
} else {
|
||||
Subscription::create([
|
||||
'team_id' => $teamId,
|
||||
'stripe_subscription_id' => $subscriptionId,
|
||||
'stripe_customer_id' => $customerId,
|
||||
'stripe_invoice_paid' => false,
|
||||
]);
|
||||
throw new RuntimeException("Subscription already exists for team: {$teamId}");
|
||||
}
|
||||
Subscription::query()->create([
|
||||
'team_id' => $teamId,
|
||||
'stripe_subscription_id' => $subscriptionId,
|
||||
'stripe_customer_id' => $customerId,
|
||||
'stripe_invoice_paid' => false,
|
||||
]);
|
||||
case 'customer.subscription.updated':
|
||||
$teamId = data_get($data, 'metadata.team_id');
|
||||
$userId = data_get($data, 'metadata.user_id');
|
||||
$customerId = data_get($data, 'customer');
|
||||
$status = data_get($data, 'status');
|
||||
$subscriptionId = data_get($data, 'items.data.0.subscription') ?? data_get($data, 'id');
|
||||
$planId = data_get($data, 'items.data.0.plan.id') ?? data_get($data, 'plan.id');
|
||||
$subscriptionId = data_get($data, 'items.data.0.subscription', data_get($data, 'id'));
|
||||
$planId = data_get($data, 'items.data.0.plan.id', data_get($data, 'plan.id'));
|
||||
if (Str::contains($excludedPlans, $planId)) {
|
||||
send_internal_notification('Subscription excluded.');
|
||||
break;
|
||||
}
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->first();
|
||||
if (! $subscription) {
|
||||
if ($status === 'incomplete_expired') {
|
||||
send_internal_notification('Subscription incomplete expired');
|
||||
throw new \RuntimeException('Subscription incomplete expired');
|
||||
throw new RuntimeException('Subscription incomplete expired');
|
||||
}
|
||||
if ($teamId) {
|
||||
$subscription = Subscription::create([
|
||||
$subscription = Subscription::query()->create([
|
||||
'team_id' => $teamId,
|
||||
'stripe_subscription_id' => $subscriptionId,
|
||||
'stripe_customer_id' => $customerId,
|
||||
@@ -194,7 +196,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
]);
|
||||
} else {
|
||||
send_internal_notification('No subscription and team id found');
|
||||
throw new \RuntimeException('No subscription and team id found');
|
||||
throw new RuntimeException('No subscription and team id found');
|
||||
}
|
||||
}
|
||||
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
||||
@@ -217,19 +219,15 @@ class StripeProcessJob implements ShouldQueue
|
||||
'stripe_plan_id' => $planId,
|
||||
'stripe_cancel_at_period_end' => $cancelAtPeriodEnd,
|
||||
]);
|
||||
if ($status === 'paused' || $status === 'incomplete_expired') {
|
||||
if ($subscription->stripe_subscription_id === $subscriptionId) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => false,
|
||||
]);
|
||||
}
|
||||
if (($status === 'paused' || $status === 'incomplete_expired') && $subscription->stripe_subscription_id === $subscriptionId) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => false,
|
||||
]);
|
||||
}
|
||||
if ($status === 'active') {
|
||||
if ($subscription->stripe_subscription_id === $subscriptionId) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => true,
|
||||
]);
|
||||
}
|
||||
if ($status === 'active' && $subscription->stripe_subscription_id === $subscriptionId) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => true,
|
||||
]);
|
||||
}
|
||||
if ($feedback) {
|
||||
$reason = "Cancellation feedback for {$customerId}: '".$feedback."'";
|
||||
@@ -242,24 +240,24 @@ class StripeProcessJob implements ShouldQueue
|
||||
case 'customer.subscription.deleted':
|
||||
$customerId = data_get($data, 'customer');
|
||||
$subscriptionId = data_get($data, 'id');
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->where('stripe_subscription_id', $subscriptionId)->first();
|
||||
$subscription = Subscription::query()->where('stripe_customer_id', $customerId)->where('stripe_subscription_id', $subscriptionId)->first();
|
||||
if ($subscription) {
|
||||
$team = data_get($subscription, 'team');
|
||||
if ($team) {
|
||||
$team->subscriptionEnded();
|
||||
} else {
|
||||
send_internal_notification('Subscription deleted but no team found in Coolify for customer: '.$customerId);
|
||||
throw new \RuntimeException("No team found in Coolify for customer: {$customerId}");
|
||||
throw new RuntimeException("No team found in Coolify for customer: {$customerId}");
|
||||
}
|
||||
} else {
|
||||
send_internal_notification('Subscription deleted but no subscription found in Coolify for customer: '.$customerId);
|
||||
throw new \RuntimeException("No subscription found in Coolify for customer: {$customerId}");
|
||||
throw new RuntimeException("No subscription found in Coolify for customer: {$customerId}");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException("Unhandled event type: {$type}");
|
||||
throw new RuntimeException("Unhandled event type: {$type}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
send_internal_notification('StripeProcessJob error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Throwable;
|
||||
|
||||
class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -24,17 +25,17 @@ class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$session = getStripeCustomerPortalSession($this->team);
|
||||
$mail = new MailMessage;
|
||||
$mail->view('emails.subscription-invoice-failed', [
|
||||
$mailMessage = new MailMessage;
|
||||
$mailMessage->view('emails.subscription-invoice-failed', [
|
||||
'stripeCustomerPortal' => $session->url,
|
||||
]);
|
||||
$mail->subject('Your last payment was failed for Coolify Cloud.');
|
||||
$this->team->members()->each(function ($member) use ($mail) {
|
||||
$mailMessage->subject('Your last payment was failed for Coolify Cloud.');
|
||||
$this->team->members()->each(function ($member) use ($mailMessage) {
|
||||
if ($member->isAdmin()) {
|
||||
send_user_an_email($mail, $member->email);
|
||||
send_user_an_email($mailMessage, $member->email);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
send_internal_notification('SubscriptionInvoiceFailedJob failed with: '.$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Throwable;
|
||||
|
||||
class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -34,7 +35,7 @@ class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
$server = Server::findOrFail(0);
|
||||
$server = Server::query()->findOrFail(0);
|
||||
if (! $server) {
|
||||
Log::error('Server not found. Cannot proceed with update.');
|
||||
|
||||
@@ -46,7 +47,7 @@ class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
$settings->update(['new_version_available' => false]);
|
||||
Log::info('Coolify update completed successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
} catch (Throwable $e) {
|
||||
Log::error('UpdateCoolifyJob failed: '.$e->getMessage());
|
||||
// Consider implementing a notification to administrators
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user