diff --git a/README.md b/README.md
index cafff116f..8670e9c76 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,7 @@ Special thanks to our biggest sponsors!
* [Juxtdigital](https://juxtdigital.dev/?ref=coolify.io) - A digital agency offering web development, design, and digital marketing services for businesses.
* [Saasykit](https://saasykit.com/?ref=coolify.io) - A Laravel-based boilerplate providing essential components and features for building SaaS applications quickly.
* [Massivegrid](https://massivegrid.com/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes.
-* [LiquidWeb](https://liquidweb.com/?utm_source=coolify.io) - Fast web hosting provider.
+* [LiquidWeb](https://liquidweb.com/?utm_source=coolify.io) - A Fast web hosting provider.
## Github Sponsors ($40+)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 3be186b66..c28f22742 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -253,6 +253,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
return;
}
try {
+ // Make sure the private key is stored in the filesystem
+ $this->server->privateKey->storeInFileSystem();
+
// Generate custom host<->ip mapping
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
@@ -2281,7 +2284,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} 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],
+ ["{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --pull always --build -d", 'hidden' => true],
);
} else {
$this->execute_remote_command(
diff --git a/app/Jobs/SendMessageToSlackJob.php b/app/Jobs/SendMessageToSlackJob.php
index 470002d23..dd5335850 100644
--- a/app/Jobs/SendMessageToSlackJob.php
+++ b/app/Jobs/SendMessageToSlackJob.php
@@ -24,6 +24,7 @@ class SendMessageToSlackJob implements ShouldQueue
public function handle(): void
{
Http::post($this->webhookUrl, [
+ 'text' => $this->message->title,
'blocks' => [
[
'type' => 'section',
diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php
index 87b40d4dc..66f387fcf 100644
--- a/app/Livewire/Project/Application/DeploymentNavbar.php
+++ b/app/Livewire/Project/Application/DeploymentNavbar.php
@@ -53,13 +53,13 @@ class DeploymentNavbar extends Component
public function cancel()
{
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
- $build_server_id = $this->application_deployment_queue->build_server_id;
+ $build_server_id = $this->application_deployment_queue->build_server_id ?? $this->application->destination->server_id;
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
try {
if ($this->application->settings->is_build_server_enabled) {
- $server = Server::find($build_server_id);
+ $server = Server::ownedByCurrentTeam()->find($build_server_id);
} else {
- $server = Server::find($server_id);
+ $server = Server::ownedByCurrentTeam()->find($server_id);
}
if ($this->application_deployment_queue->logs) {
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
diff --git a/app/Models/GithubApp.php b/app/Models/GithubApp.php
index 0b0e93b12..5550df81f 100644
--- a/app/Models/GithubApp.php
+++ b/app/Models/GithubApp.php
@@ -33,17 +33,30 @@ class GithubApp extends BaseModel
public static function ownedByCurrentTeam()
{
- return GithubApp::whereTeamId(currentTeam()->id);
+ return GithubApp::where(function ($query) {
+ $query->where('team_id', currentTeam()->id)
+ ->orWhere('is_system_wide', true);
+ });
}
public static function public()
{
- return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get();
+ return GithubApp::where(function ($query) {
+ $query->where(function ($q) {
+ $q->where('team_id', currentTeam()->id)
+ ->orWhere('is_system_wide', true);
+ })->where('is_public', true);
+ })->whereNotNull('app_id')->get();
}
public static function private()
{
- return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(false)->whereNotNull('app_id')->get();
+ return GithubApp::where(function ($query) {
+ $query->where(function ($q) {
+ $q->where('team_id', currentTeam()->id)
+ ->orWhere('is_system_wide', true);
+ })->where('is_public', false);
+ })->whereNotNull('app_id')->get();
}
public function team()
diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php
index 33f4fa37c..e9d674650 100644
--- a/app/Models/S3Storage.php
+++ b/app/Models/S3Storage.php
@@ -43,8 +43,18 @@ class S3Storage extends BaseModel
public function testConnection(bool $shouldSave = false)
{
try {
- set_s3_target($this);
- Storage::disk('custom-s3')->files();
+ $disk = Storage::build([
+ 'driver' => 's3',
+ 'region' => $this['region'],
+ 'key' => $this['key'],
+ 'secret' => $this['secret'],
+ 'bucket' => $this['bucket'],
+ 'endpoint' => $this['endpoint'],
+ 'use_path_style_endpoint' => true,
+ ]);
+ // Test the connection by listing files with ListObjectsV2 (S3)
+ $disk->files();
+
$this->unusable_email_sent = false;
$this->is_usable = true;
} catch (\Throwable $e) {
@@ -53,13 +63,14 @@ class S3Storage extends BaseModel
$mail = new MailMessage;
$mail->subject('Coolify: S3 Storage Connection Error');
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('storage.show', ['storage_uuid' => $this->uuid])]);
- $users = collect([]);
- $members = $this->team->members()->get();
- foreach ($members as $user) {
- if ($user->isAdmin()) {
- $users->push($user);
- }
- }
+
+ // Load the team with its members and their roles explicitly
+ $team = $this->team()->with(['members' => function ($query) {
+ $query->withPivot('role');
+ }])->first();
+
+ // Get admins directly from the pivot relationship for this specific team
+ $users = $team->members()->wherePivotIn('role', ['admin', 'owner'])->get(['users.id', 'users.email']);
foreach ($users as $user) {
send_user_an_email($mail, $user->email);
}
diff --git a/app/Models/Team.php b/app/Models/Team.php
index 07959dd16..467cf45bc 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -248,15 +248,17 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
{
$sources = collect([]);
$github_apps = GithubApp::where(function ($query) {
- $query->where('team_id', $this->id)
- ->Where('is_public', false)
- ->orWhere('is_system_wide', true);
+ $query->where(function ($q) {
+ $q->where('team_id', $this->id)
+ ->orWhere('is_system_wide', true);
+ })->where('is_public', false);
})->get();
$gitlab_apps = GitlabApp::where(function ($query) {
- $query->where('team_id', $this->id)
- ->Where('is_public', false)
- ->orWhere('is_system_wide', true);
+ $query->where(function ($q) {
+ $q->where('team_id', $this->id)
+ ->orWhere('is_system_wide', true);
+ })->where('is_public', false);
})->get();
return $sources->merge($github_apps)->merge($gitlab_apps);
diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php
index 4c9da12e3..983e6d81e 100644
--- a/app/Notifications/Server/HighDiskUsage.php
+++ b/app/Notifications/Server/HighDiskUsage.php
@@ -80,7 +80,7 @@ class HighDiskUsage extends CustomEmailNotification
$description .= "Tips for cleanup: https://coolify.io/docs/knowledge-base/server/automated-cleanup\n";
$description .= "Change settings:\n";
$description .= '- Threshold: '.base_url().'/server/'.$this->server->uuid."#advanced\n";
- $description .= '- Notifications: '.base_url().'/notifications/discord';
+ $description .= '- Notifications: '.base_url().'/notifications/slack';
return new SlackMessage(
title: 'High disk usage detected',
diff --git a/bootstrap/helpers/s3.php b/bootstrap/helpers/s3.php
deleted file mode 100644
index 7029377a4..000000000
--- a/bootstrap/helpers/s3.php
+++ /dev/null
@@ -1,17 +0,0 @@
-set('filesystems.disks.custom-s3', [
- 'driver' => 's3',
- 'region' => $s3['region'],
- 'key' => $s3['key'],
- 'secret' => $s3['secret'],
- 'bucket' => $s3['bucket'],
- 'endpoint' => $s3['endpoint'],
- 'use_path_style_endpoint' => true,
- 'aws_url' => $s3->awsUrl(),
- ]);
-}
diff --git a/config/constants.php b/config/constants.php
index 981c0e331..2cf0123c0 100644
--- a/config/constants.php
+++ b/config/constants.php
@@ -2,7 +2,7 @@
return [
'coolify' => [
- 'version' => '4.0.0-beta.391',
+ 'version' => '4.0.0-beta.392',
'helper_version' => '1.0.6',
'realtime_version' => '1.0.5',
'self_hosted' => env('SELF_HOSTED', true),
diff --git a/lang/ar.json b/lang/ar.json
index b473318d3..9befd1e4a 100644
--- a/lang/ar.json
+++ b/lang/ar.json
@@ -1,5 +1,6 @@
{
"auth.login": "تسجيل الدخول",
+ "auth.login.authentik": "تسجيل الدخول باستخدام Authentik",
"auth.login.azure": "تسجيل الدخول باستخدام Microsoft",
"auth.login.bitbucket": "تسجيل الدخول باستخدام Bitbucket",
"auth.login.github": "تسجيل الدخول باستخدام GitHub",
@@ -34,5 +35,6 @@
"resource.delete_volumes": "حذف جميع المجلدات والملفات المرتبطة بهذا المورد بشكل دائم.",
"resource.delete_connected_networks": "حذف جميع الشبكات غير المحددة مسبقًا والمرتبطة بهذا المورد بشكل دائم.",
"resource.delete_configurations": "حذف جميع ملفات التعريف من الخادم بشكل دائم.",
- "database.delete_backups_locally": "حذف كافة النسخ الاحتياطية نهائيًا من التخزين المحلي."
+ "database.delete_backups_locally": "حذف كافة النسخ الاحتياطية نهائيًا من التخزين المحلي.",
+ "warning.sslipdomain": "تم حفظ ملفات التعريف الخاصة بك، ولكن استخدام نطاق sslip مع https غير مستحسن، لأن خوادم Let's Encrypt مع هذا النطاق العام محدودة المعدل (ستفشل عملية التحقق من شهادة SSL).
استخدم نطاقك الخاص بدلاً من ذلك."
}
diff --git a/lang/fr.json b/lang/fr.json
index a94d633d3..68763d5e0 100644
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -1,5 +1,6 @@
{
"auth.login": "Connexion",
+ "auth.login.authentik": "Connexion avec Authentik",
"auth.login.azure": "Connexion avec Microsoft",
"auth.login.bitbucket": "Connexion avec Bitbucket",
"auth.login.github": "Connexion avec GitHub",
@@ -34,5 +35,6 @@
"resource.delete_volumes": "Supprimer définitivement tous les volumes associés à cette ressource.",
"resource.delete_connected_networks": "Supprimer définitivement tous les réseaux non-prédéfinis associés à cette ressource.",
"resource.delete_configurations": "Supprimer définitivement tous les fichiers de configuration du serveur.",
- "database.delete_backups_locally": "Toutes les sauvegardes seront définitivement supprimées du stockage local."
+ "database.delete_backups_locally": "Toutes les sauvegardes seront définitivement supprimées du stockage local.",
+ "warning.sslipdomain": "Votre configuration est enregistrée, mais l'utilisation du domaine sslip avec https N'EST PAS recommandée, car les serveurs Let's Encrypt avec ce domaine public sont limités en taux (la validation du certificat SSL échouera).
Utilisez plutôt votre propre domaine."
}
diff --git a/lang/it.json b/lang/it.json
index 30b9f3902..1923251a5 100644
--- a/lang/it.json
+++ b/lang/it.json
@@ -1,5 +1,6 @@
{
"auth.login": "Accedi",
+ "auth.login.authentik": "Accedi con Authentik",
"auth.login.azure": "Accedi con Microsoft",
"auth.login.bitbucket": "Accedi con Bitbucket",
"auth.login.github": "Accedi con GitHub",
@@ -27,5 +28,13 @@
"input.code": "Codice monouso",
"input.recovery_code": "Codice di recupero",
"button.save": "Salva",
- "repository.url": "Esempi Per i repository pubblici, utilizza https://.... Per i repository privati, utilizza git@....
https://github.com/coollabsio/coolify-examples verrà selezionato il branch main https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify verrà selezionato il branch nodejs-fastify. https://gitea.com/sedlav/expressjs.git verrà selezionato il branch main. https://gitlab.com/andrasbacsai/nodejs-example.git verrà selezionato il branch main."
+ "repository.url": "Esempi Per i repository pubblici, utilizza https://.... Per i repository privati, utilizza git@....
https://github.com/coollabsio/coolify-examples verrà selezionato il branch main https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify verrà selezionato il branch nodejs-fastify. https://gitea.com/sedlav/expressjs.git verrà selezionato il branch main. https://gitlab.com/andrasbacsai/nodejs-example.git verrà selezionato il branch main.",
+ "service.stop": "Questo servizio verrà arrestato.",
+ "resource.docker_cleanup": "Esegui pulizia Docker (rimuove immagini non utilizzate e cache del builder).",
+ "resource.non_persistent": "Tutti i dati non persistenti verranno eliminati.",
+ "resource.delete_volumes": "Elimina definitivamente tutti i volumi associati a questa risorsa.",
+ "resource.delete_connected_networks": "Elimina definitivamente tutte le reti non predefinite associate a questa risorsa.",
+ "resource.delete_configurations": "Elimina definitivamente tutti i file di configurazione dal server.",
+ "database.delete_backups_locally": "Tutti i backup verranno eliminati definitivamente dall'archiviazione locale.",
+ "warning.sslipdomain": "La tua configurazione è stata salvata, ma il dominio sslip con https NON è raccomandato, poiché i server di Let's Encrypt con questo dominio pubblico hanno limitazioni di frequenza (la convalida del certificato SSL fallirà).
Utilizza invece il tuo dominio personale."
}
diff --git a/resources/views/components/status/running.blade.php b/resources/views/components/status/running.blade.php
index 27a6d7181..a1a93efac 100644
--- a/resources/views/components/status/running.blade.php
+++ b/resources/views/components/status/running.blade.php
@@ -26,7 +26,7 @@
@endphp
@if ($showUnhealthyHelper)
+ helper="Unhealthy state. This doesn't mean that the resource is malfunctioning.
- If the resource is accessible, it indicates that no health check is configured - it is not mandatory. - If the resource is not accessible (returning 404 or 503), it may indicate that a health check is needed and has not passed. Your action is required.