rector: arrrrr

This commit is contained in:
Andras Bacsai
2025-01-07 14:52:08 +01:00
parent c702ebff6d
commit 16c0cd10d8
349 changed files with 4204 additions and 3712 deletions

View File

@@ -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()) {

View File

@@ -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');
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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();

View File

@@ -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(),

View File

@@ -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());
}
}

View File

@@ -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());
}
}
}

View File

@@ -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,
],
],
],

View File

@@ -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());
}
}
}

View File

@@ -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()

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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
}