mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
chore: merge main into release/v4.5 (#12206)
Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Andras Bacsai <andrasbacsai@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Andras Bacsai
Cursor Agent
Andras Bacsai
Claude Opus 5.5
parent
cf71fda5c1
commit
e0f6b41dac
@@ -26,7 +26,7 @@ Your configurations stay on your servers. If you stop using Coolify, your runnin
|
||||
## What can Coolify do?
|
||||
|
||||
- **Deploy any application:** Build from GitHub, GitLab, Bitbucket, or Gitea with Nixpacks, Railpack, Dockerfile, Docker Compose, or a prebuilt Docker image.
|
||||
- **Run databases and services:** Deploy managed databases and more than 300 one-click services with persistent storage and generated credentials.
|
||||
- **Run databases and services:** Deploy managed databases and more than 360 one-click services with persistent storage and generated credentials.
|
||||
- **Automate deployments:** Deploy on every Git push, create pull-request previews, call deployment webhooks, and roll back to retained application images.
|
||||
- **Manage networking:** Configure custom domains, automatic HTTPS certificates, reverse proxies, health checks, and container networks.
|
||||
- **Operate your infrastructure:** Manage multiple servers, inspect deployment and runtime logs, open container terminals, and monitor resource status.
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Session\TokenMismatchException;
|
||||
use Psr\Log\LogLevel;
|
||||
use RuntimeException;
|
||||
use Sentry\Laravel\Integration;
|
||||
use Sentry\State\Scope;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
@@ -49,8 +44,6 @@ class Handler extends ExceptionHandler
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
private InstanceSettings $settings;
|
||||
|
||||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
||||
@@ -108,44 +101,4 @@ class Handler extends ExceptionHandler
|
||||
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
if (isDev()) {
|
||||
return;
|
||||
}
|
||||
if ($e instanceof RuntimeException) {
|
||||
return;
|
||||
}
|
||||
$this->settings = instanceSettings();
|
||||
if ($this->settings->do_not_track) {
|
||||
return;
|
||||
}
|
||||
app('sentry')->configureScope(
|
||||
function (Scope $scope) {
|
||||
$email = auth()?->user() ? auth()->user()->email : 'guest';
|
||||
$instanceAdmin = User::find(0)->email ?? 'admin@localhost';
|
||||
$scope->setUser(
|
||||
[
|
||||
'email' => $email,
|
||||
'instanceAdmin' => $instanceAdmin,
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
// Check for errors that should not be reported to Sentry
|
||||
if (str($e->getMessage())->contains('No space left on device')) {
|
||||
// Log locally but don't send to Sentry
|
||||
logger()->warning('Disk space error: '.$e->getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Integration::captureUnhandledException($e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Exceptions;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Exception that should not be reported to Sentry or other error tracking services.
|
||||
* Exception that should not be reported to error tracking services.
|
||||
* Use this for known, expected errors that don't require external tracking.
|
||||
*/
|
||||
class NonReportableException extends Exception
|
||||
|
||||
@@ -50,6 +50,8 @@ class GithubApp extends BaseModel
|
||||
'type' => 'string',
|
||||
'webhook_events' => 'array',
|
||||
'runner_group_id' => 'integer',
|
||||
'client_secret' => 'encrypted',
|
||||
'webhook_secret' => 'encrypted',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
||||
@@ -51,6 +51,7 @@ class GitlabApp extends BaseModel
|
||||
'access_token' => 'encrypted',
|
||||
'refresh_token' => 'encrypted',
|
||||
'client_secret' => 'encrypted',
|
||||
'app_secret' => 'encrypted',
|
||||
'is_system_wide' => 'boolean',
|
||||
'is_public' => 'boolean',
|
||||
];
|
||||
|
||||
@@ -200,6 +200,7 @@ class ServerSetting extends Model
|
||||
'is_geoip_enabled' => 'boolean',
|
||||
'geoip_refresh_days' => 'integer',
|
||||
'geoip_maxmind_license_key' => 'encrypted',
|
||||
'logdrain_custom_config' => 'encrypted',
|
||||
'traffic_ip_mode' => TrafficIpMode::class,
|
||||
'docker_version_checked_at' => 'datetime',
|
||||
'compose_version_checked_at' => 'datetime',
|
||||
|
||||
@@ -80,6 +80,7 @@ class StandaloneMariadb extends BaseModel
|
||||
'health_check_retries' => 'integer',
|
||||
'health_check_start_period' => 'integer',
|
||||
'mariadb_password' => 'encrypted',
|
||||
'mariadb_root_password' => 'encrypted',
|
||||
'public_port_timeout' => 'integer',
|
||||
'restart_count' => 'integer',
|
||||
'last_restart_at' => 'datetime',
|
||||
|
||||
@@ -74,6 +74,7 @@ class StandaloneMongodb extends BaseModel
|
||||
protected $casts = [
|
||||
'include_coolify_default_variables' => 'boolean',
|
||||
'health_check_enabled' => 'boolean',
|
||||
'mongo_initdb_root_password' => 'encrypted',
|
||||
'health_check_interval' => 'integer',
|
||||
'health_check_timeout' => 'integer',
|
||||
'health_check_retries' => 'integer',
|
||||
@@ -283,22 +284,6 @@ class StandaloneMongodb extends BaseModel
|
||||
return null;
|
||||
}
|
||||
|
||||
public function mongoInitdbRootPassword(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function ($value) {
|
||||
try {
|
||||
return decrypt($value);
|
||||
} catch (\Throwable $th) {
|
||||
$this->mongo_initdb_root_password = encrypt($value);
|
||||
$this->save();
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function portsMappings(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
|
||||
@@ -15,6 +15,10 @@ class TeamInvitation extends Model
|
||||
'via',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'link' => 'encrypted',
|
||||
];
|
||||
|
||||
/**
|
||||
* Set the email attribute to lowercase.
|
||||
*/
|
||||
|
||||
@@ -157,7 +157,7 @@ class EmailChannel
|
||||
json_encode(app(SensitiveDataRedactor::class)->redactValue($emailSettings), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
||||
));
|
||||
|
||||
// Don't report expected errors (invalid keys, validation) to Sentry
|
||||
// Don't report expected errors (invalid keys, validation) to error tracking
|
||||
if (in_array($statusCode, [403, 401, 400, 422], true)) {
|
||||
throw NonReportableException::fromException(new Exception($userMessage, $e->getCode(), $e));
|
||||
}
|
||||
@@ -178,7 +178,7 @@ class EmailChannel
|
||||
} catch (\Throwable $e) {
|
||||
// Check if this is a Resend domain verification error on cloud instances
|
||||
if (isCloud() && str_contains($e->getMessage(), 'domain is not verified')) {
|
||||
// Throw as NonReportableException so it won't go to Sentry
|
||||
// Throw as NonReportableException so it won't go to error tracking
|
||||
throw NonReportableException::fromException($e);
|
||||
}
|
||||
throw $e;
|
||||
|
||||
@@ -253,8 +253,7 @@ function instant_remote_process(Collection|array $command, Server $server, bool
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws the remote command error. Handler::register() never sends RuntimeException to Sentry,
|
||||
* so SSH key, DNS, and timeout failures on user servers are not reported.
|
||||
* Throws the remote command error.
|
||||
*/
|
||||
function excludeCertainErrors(string $errorOutput, ?int $exitCode = null): never
|
||||
{
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
"purplepixie/phpdns": "^2.4.0",
|
||||
"pusher/pusher-php-server": "^7.3.0",
|
||||
"resend/resend-laravel": "^1.6.0",
|
||||
"sentry/sentry-laravel": "^4.28.0",
|
||||
"socialiteproviders/authentik": "^5.3.0",
|
||||
"socialiteproviders/clerk": "^5.1.0",
|
||||
"socialiteproviders/discord": "^4.2.0",
|
||||
|
||||
Generated
+61
-485
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "664589c32e86e8e6fc2a0fb17feab410",
|
||||
"content-hash": "f97af22f1628cec49170f1d40c582a9d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@@ -1837,66 +1837,6 @@
|
||||
],
|
||||
"time": "2026-08-24T09:15:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": "^2.1.0",
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.6",
|
||||
"rector/rector": "^2.0",
|
||||
"vimeo/psalm": "^4.3 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jean85\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alessandro Lai",
|
||||
"email": "alessandro.lai85@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to get pretty versions strings of installed dependencies",
|
||||
"keywords": [
|
||||
"composer",
|
||||
"package",
|
||||
"release",
|
||||
"versions"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
|
||||
},
|
||||
"time": "2025-03-19T14:43:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/fortify",
|
||||
"version": "v1.40.0",
|
||||
@@ -4746,84 +4686,6 @@
|
||||
],
|
||||
"time": "2026-02-16T23:10:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nyholm/psr7",
|
||||
"version": "1.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Nyholm/psr7.git",
|
||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0"
|
||||
},
|
||||
"provide": {
|
||||
"php-http/message-factory-implementation": "1.0",
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"http-interop/http-factory-tests": "^0.9",
|
||||
"php-http/message-factory": "^1.0",
|
||||
"php-http/psr7-integration-tests": "^1.0",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
||||
"symfony/error-handler": "^4.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.8-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nyholm\\Psr7\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Martijn van der Ven",
|
||||
"email": "martijn@vanderven.se"
|
||||
}
|
||||
],
|
||||
"description": "A fast PHP7 implementation of PSR-7",
|
||||
"homepage": "https://tnyholm.se",
|
||||
"keywords": [
|
||||
"psr-17",
|
||||
"psr-7"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Nyholm/psr7/issues",
|
||||
"source": "https://github.com/Nyholm/psr7/tree/1.8.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Zegnat",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nyholm",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T07:06:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v3.1.3",
|
||||
@@ -7161,194 +7023,6 @@
|
||||
},
|
||||
"time": "2026-09-25T18:18:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sentry/sentry",
|
||||
"version": "4.32.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-php.git",
|
||||
"reference": "c345642bdc0b678965a35ab61fc6323808eb3d2c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/c345642bdc0b678965a35ab61fc6323808eb3d2c",
|
||||
"reference": "c345642bdc0b678965a35ab61fc6323808eb3d2c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"guzzlehttp/psr7": "^1.8.4|^2.1.1|^3.0.0",
|
||||
"jean85/pretty-package-versions": "^1.5|^2.0.4",
|
||||
"php": "^7.2|^8.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0",
|
||||
"symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"raven/raven": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"carthage-software/mago": "1.30.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.4",
|
||||
"guzzlehttp/guzzle": "^7.0|^8.0",
|
||||
"guzzlehttp/promises": "^2.0.3|^3.0.0",
|
||||
"monolog/monolog": "^1.6|^2.0|^3.0",
|
||||
"nyholm/psr7": "^1.8",
|
||||
"open-telemetry/api": "^1.1",
|
||||
"open-telemetry/context": "^1.1",
|
||||
"open-telemetry/exporter-otlp": "^1.1",
|
||||
"open-telemetry/sdk": "^1.1",
|
||||
"open-telemetry/sem-conv": "^1.27",
|
||||
"phpstan/phpstan": "^1.3",
|
||||
"phpunit/phpunit": "^8.5.52|^9.6.34",
|
||||
"spiral/roadrunner-http": "^3.6",
|
||||
"spiral/roadrunner-worker": "^3.6"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-excimer": "Enable Sentry profiling with the Excimer PHP extension.",
|
||||
"monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Sentry\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sentry",
|
||||
"email": "accounts@sentry.io"
|
||||
}
|
||||
],
|
||||
"description": "PHP SDK for Sentry (http://sentry.io)",
|
||||
"homepage": "http://sentry.io",
|
||||
"keywords": [
|
||||
"crash-reporting",
|
||||
"crash-reports",
|
||||
"error-handler",
|
||||
"error-monitoring",
|
||||
"log",
|
||||
"logging",
|
||||
"profiling",
|
||||
"sentry",
|
||||
"tracing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-php/issues",
|
||||
"source": "https://github.com/getsentry/sentry-php/tree/4.32.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://sentry.io/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://sentry.io/pricing/",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-24T13:32:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sentry/sentry-laravel",
|
||||
"version": "4.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/getsentry/sentry-laravel.git",
|
||||
"reference": "34bf18fbf99b884a9bfbb46957d0178d2d0b988e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/34bf18fbf99b884a9bfbb46957d0178d2d0b988e",
|
||||
"reference": "34bf18fbf99b884a9bfbb46957d0178d2d0b988e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0 | ^13.0",
|
||||
"nyholm/psr7": "^1.0",
|
||||
"php": "^7.2 | ^8.0",
|
||||
"sentry/sentry": "^4.31.0",
|
||||
"symfony/psr-http-message-bridge": "^1.0 | ^2.0 | ^6.0 | ^7.0 | ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.11",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/ai": "^0.1.5",
|
||||
"laravel/folio": "^1.1",
|
||||
"laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0 | ^13.0",
|
||||
"laravel/octane": "^2.15",
|
||||
"laravel/pennant": "^1.0",
|
||||
"livewire/livewire": "^2.0 | ^3.0 | ^4.0",
|
||||
"mockery/mockery": "^1.3",
|
||||
"orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^8.5 | ^9.6 | ^10.4 | ^11.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Sentry": "Sentry\\Laravel\\Facade"
|
||||
},
|
||||
"providers": [
|
||||
"Sentry\\Laravel\\ServiceProvider",
|
||||
"Sentry\\Laravel\\Tracing\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Sentry\\Laravel\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sentry",
|
||||
"email": "accounts@sentry.io"
|
||||
}
|
||||
],
|
||||
"description": "Laravel SDK for Sentry (https://sentry.io)",
|
||||
"homepage": "https://sentry.io",
|
||||
"keywords": [
|
||||
"crash-reporting",
|
||||
"crash-reports",
|
||||
"error-handler",
|
||||
"error-monitoring",
|
||||
"laravel",
|
||||
"log",
|
||||
"logging",
|
||||
"profiling",
|
||||
"sentry",
|
||||
"tracing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/getsentry/sentry-laravel/issues",
|
||||
"source": "https://github.com/getsentry/sentry-laravel/tree/4.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://sentry.io/",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://sentry.io/pricing/",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-23T14:29:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "socialiteproviders/authentik",
|
||||
"version": "5.3.0",
|
||||
@@ -9853,77 +9527,6 @@
|
||||
],
|
||||
"time": "2026-09-04T10:45:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v8.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "88f9c561f678a02d54b897014049fa839e33ff82"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/88f9c561f678a02d54b897014049fa839e33ff82",
|
||||
"reference": "88f9c561f678a02d54b897014049fa839e33ff82",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.4.1",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\OptionsResolver\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides an improved replacement for the array_replace PHP function",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"config",
|
||||
"configuration",
|
||||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v8.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-05-29T05:06:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.37.0",
|
||||
@@ -11065,93 +10668,6 @@
|
||||
],
|
||||
"time": "2026-09-28T19:13:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/psr-http-message-bridge",
|
||||
"version": "v8.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/psr-http-message-bridge.git",
|
||||
"reference": "67fd34de15ded1763aa1e330fe345f080a94022c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/67fd34de15ded1763aa1e330fe345f080a94022c",
|
||||
"reference": "67fd34de15ded1763aa1e330fe345f080a94022c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.4.1",
|
||||
"psr/http-message": "^1.0|^2.0",
|
||||
"symfony/http-foundation": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"php-http/discovery": "<1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
"nyholm/psr7": "^1.1",
|
||||
"php-http/discovery": "^1.15",
|
||||
"psr/log": "^1.1.4|^2|^3",
|
||||
"symfony/browser-kit": "^7.4|^8.0",
|
||||
"symfony/config": "^7.4|^8.0",
|
||||
"symfony/event-dispatcher": "^7.4|^8.0",
|
||||
"symfony/framework-bundle": "^7.4|^8.0",
|
||||
"symfony/http-kernel": "^7.4|^8.0",
|
||||
"symfony/runtime": "^7.4|^8.0"
|
||||
},
|
||||
"type": "symfony-bridge",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Bridge\\PsrHttpMessage\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "PSR HTTP message bridge",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr-17",
|
||||
"psr-7"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/psr-http-message-bridge/tree/v8.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-05-29T05:06:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v7.4.20",
|
||||
@@ -14616,6 +14132,66 @@
|
||||
},
|
||||
"time": "2026-03-17T11:56:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-runtime-api": "^2.1.0",
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.6",
|
||||
"rector/rector": "^2.0",
|
||||
"vimeo/psalm": "^4.3 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jean85\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alessandro Lai",
|
||||
"email": "alessandro.lai85@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to get pretty versions strings of installed dependencies",
|
||||
"keywords": [
|
||||
"composer",
|
||||
"package",
|
||||
"release",
|
||||
"versions"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
|
||||
},
|
||||
"time": "2025-03-19T14:43:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kelunik/certificate",
|
||||
"version": "v1.1.3",
|
||||
|
||||
@@ -105,10 +105,6 @@ return [
|
||||
'verification_code_expiry_minutes' => 10,
|
||||
],
|
||||
|
||||
'sentry' => [
|
||||
'sentry_dsn' => env('SENTRY_DSN'),
|
||||
],
|
||||
|
||||
'sentinel' => [
|
||||
'dev_url' => env('DEV_SENTINEL_URL'),
|
||||
// How often (seconds) PushServerUpdateJob is force-dispatched even when
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
|
||||
'dsn' => config('constants.sentry.sentry_dsn'),
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => config('constants.coolify.version'),
|
||||
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
'breadcrumbs' => [
|
||||
// Capture Laravel logs in breadcrumbs
|
||||
'logs' => true,
|
||||
|
||||
// Capture Laravel cache events in breadcrumbs
|
||||
'cache' => true,
|
||||
|
||||
// Capture Livewire components in breadcrumbs
|
||||
'livewire' => true,
|
||||
|
||||
// Capture SQL queries in breadcrumbs
|
||||
'sql_queries' => true,
|
||||
|
||||
// Capture bindings on SQL queries logged in breadcrumbs
|
||||
'sql_bindings' => true,
|
||||
|
||||
// Capture queue job information in breadcrumbs
|
||||
'queue_info' => true,
|
||||
|
||||
// Capture command information in breadcrumbs
|
||||
'command_info' => true,
|
||||
|
||||
// Capture HTTP client requests information in breadcrumbs
|
||||
'http_client_requests' => true,
|
||||
],
|
||||
|
||||
'tracing' => [
|
||||
// Trace queue jobs as their own transactions
|
||||
'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', false),
|
||||
|
||||
// Capture queue jobs as spans when executed on the sync driver
|
||||
'queue_jobs' => true,
|
||||
|
||||
// Capture SQL queries as spans
|
||||
'sql_queries' => true,
|
||||
|
||||
// Try to find out where the SQL query originated from and add it to the query spans
|
||||
'sql_origin' => true,
|
||||
|
||||
// Capture views as spans
|
||||
'views' => true,
|
||||
|
||||
// Capture Livewire components as spans
|
||||
'livewire' => true,
|
||||
|
||||
// Capture HTTP client requests as spans
|
||||
'http_client_requests' => true,
|
||||
|
||||
// Capture Redis operations as spans (this enables Redis events in Laravel)
|
||||
'redis_commands' => env('SENTRY_TRACE_REDIS_COMMANDS', false),
|
||||
|
||||
// Try to find out where the Redis command originated from and add it to the command spans
|
||||
'redis_origin' => true,
|
||||
|
||||
// Indicates if the tracing integrations supplied by Sentry should be loaded
|
||||
'default_integrations' => true,
|
||||
|
||||
// Indicates that requests without a matching route should be traced
|
||||
'missing_routes' => false,
|
||||
],
|
||||
|
||||
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send-default-pii
|
||||
'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false),
|
||||
|
||||
// @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate
|
||||
'enable_tracing' => env('SENTRY_ENABLE_TRACING', false),
|
||||
'traces_sample_rate' => 0.2,
|
||||
|
||||
'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_PROFILES_SAMPLE_RATE'),
|
||||
|
||||
];
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Encrypt secret columns that were stored as plaintext, to match their new `encrypted` casts.
|
||||
* Values that are already encrypted are skipped, so the migration is safe to run again.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// Ciphertext does not fit in the original varchar(255) column.
|
||||
Schema::table('gitlab_apps', function (Blueprint $table) {
|
||||
$table->text('app_secret')->nullable()->change();
|
||||
});
|
||||
|
||||
$this->encryptColumns('github_apps', ['client_secret', 'webhook_secret']);
|
||||
$this->encryptColumns('gitlab_apps', ['app_secret']);
|
||||
$this->encryptColumns('standalone_mariadbs', ['mariadb_root_password']);
|
||||
// The old accessor stored encrypt($value), which serializes the value before encryption.
|
||||
$this->encryptColumns('standalone_mongodbs', ['mongo_initdb_root_password'], legacySerialized: true);
|
||||
$this->encryptColumns('server_settings', ['logdrain_custom_config']);
|
||||
$this->encryptColumns('team_invitations', ['link']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $columns
|
||||
*/
|
||||
private function encryptColumns(string $table, array $columns, bool $legacySerialized = false): void
|
||||
{
|
||||
try {
|
||||
DB::table($table)->select(['id', ...$columns])->chunkById(100, function ($rows) use ($table, $columns, $legacySerialized) {
|
||||
foreach ($rows as $row) {
|
||||
$updates = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$value = $row->{$column};
|
||||
|
||||
if (blank($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$plaintext = $this->plaintextToEncrypt($value, $legacySerialized);
|
||||
|
||||
if ($plaintext !== null) {
|
||||
$updates[$column] = Crypt::encryptString($plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
if ($updates !== []) {
|
||||
DB::table($table)->where('id', $row->id)->update($updates);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception $e) {
|
||||
echo "Encrypting secrets in {$table} failed.";
|
||||
echo $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plaintext that still needs encryption, or null when the value is already stored as encryptString() output.
|
||||
*/
|
||||
private function plaintextToEncrypt(string $value, bool $legacySerialized): ?string
|
||||
{
|
||||
try {
|
||||
$decrypted = Crypt::decryptString($value);
|
||||
} catch (DecryptException) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($legacySerialized) {
|
||||
$unserialized = @unserialize($decrypted, ['allowed_classes' => false]);
|
||||
|
||||
if (is_string($unserialized) && serialize($unserialized) === $decrypted) {
|
||||
return $unserialized;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -191,7 +191,6 @@
|
||||
</style>
|
||||
@if (config('app.name') == 'Coolify Cloud')
|
||||
<script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
|
||||
<script src="https://js.sentry-cdn.com/0f8593910512b5cdd48c6da78d4093be.min.js" crossorigin="anonymous"></script>
|
||||
@endif
|
||||
@auth
|
||||
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Environment;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\Team;
|
||||
use App\Models\TeamInvitation;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$team = Team::factory()->create();
|
||||
$server = Server::factory()->create(['team_id' => $team->id]);
|
||||
$destination = $server->standaloneDockers()->firstOrFail();
|
||||
$environment = Environment::factory()->create([
|
||||
'project_id' => Project::factory()->create(['team_id' => $team->id])->id,
|
||||
]);
|
||||
$databaseAttributes = [
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
'destination_id' => $destination->id,
|
||||
'environment_id' => $environment->id,
|
||||
];
|
||||
$gitAppAttributes = [
|
||||
'html_url' => 'https://example.com',
|
||||
'custom_user' => 'git',
|
||||
'custom_port' => 22,
|
||||
'team_id' => $team->id,
|
||||
'is_system_wide' => false,
|
||||
];
|
||||
|
||||
$githubApp = GithubApp::create([
|
||||
...$gitAppAttributes,
|
||||
'name' => 'github',
|
||||
'api_url' => 'https://api.github.com',
|
||||
'client_secret' => 'github-client-secret',
|
||||
'webhook_secret' => 'github-webhook-secret',
|
||||
]);
|
||||
$gitlabApp = GitlabApp::create([
|
||||
...$gitAppAttributes,
|
||||
'name' => 'gitlab',
|
||||
'api_url' => 'https://gitlab.com/api/v4',
|
||||
'app_secret' => str_repeat('g', 64),
|
||||
]);
|
||||
$mariadb = StandaloneMariadb::create([
|
||||
...$databaseAttributes,
|
||||
'name' => 'mariadb',
|
||||
'mariadb_root_password' => 'mariadb-root-password',
|
||||
'mariadb_password' => 'mariadb-password',
|
||||
]);
|
||||
$mongodb = StandaloneMongodb::create([
|
||||
...$databaseAttributes,
|
||||
'name' => 'mongodb',
|
||||
'mongo_initdb_root_password' => 'mongo-root-password',
|
||||
]);
|
||||
$server->settings->update(['logdrain_custom_config' => "[OUTPUT]\n Name http\n http_Passwd secret"]);
|
||||
$invitation = TeamInvitation::create([
|
||||
'team_id' => $team->id,
|
||||
'uuid' => 'invite-uuid',
|
||||
'email' => 'new@example.com',
|
||||
'role' => 'member',
|
||||
'link' => 'https://coolify.test/auth/link?token=secret-token',
|
||||
'via' => 'link',
|
||||
]);
|
||||
|
||||
$this->secrets = [
|
||||
['github_apps', $githubApp, 'client_secret', 'github-client-secret'],
|
||||
['github_apps', $githubApp, 'webhook_secret', 'github-webhook-secret'],
|
||||
['gitlab_apps', $gitlabApp, 'app_secret', str_repeat('g', 64)],
|
||||
['standalone_mariadbs', $mariadb, 'mariadb_root_password', 'mariadb-root-password'],
|
||||
['standalone_mongodbs', $mongodb, 'mongo_initdb_root_password', 'mongo-root-password'],
|
||||
['server_settings', $server->settings, 'logdrain_custom_config', "[OUTPUT]\n Name http\n http_Passwd secret"],
|
||||
['team_invitations', $invitation, 'link', 'https://coolify.test/auth/link?token=secret-token'],
|
||||
];
|
||||
});
|
||||
|
||||
function rawSecret(string $table, $model, string $column): ?string
|
||||
{
|
||||
return DB::table($table)->where('id', $model->id)->value($column);
|
||||
}
|
||||
|
||||
it('encrypts secrets at rest when they are saved through the model', function () {
|
||||
foreach ($this->secrets as [$table, $model, $column, $plaintext]) {
|
||||
expect(Crypt::decryptString(rawSecret($table, $model, $column)))->toBe($plaintext, "{$table}.{$column}")
|
||||
->and($model->fresh()->{$column})->toBe($plaintext, "{$table}.{$column}");
|
||||
}
|
||||
});
|
||||
|
||||
it('stores the GitLab app secret in a text column so the ciphertext fits', function () {
|
||||
expect(Schema::getColumnType('gitlab_apps', 'app_secret'))->toBe('text');
|
||||
});
|
||||
|
||||
it('migrates plaintext and legacy serialized secrets without encrypting them twice', function () {
|
||||
foreach ($this->secrets as [$table, $model, $column, $plaintext]) {
|
||||
$legacyValue = $column === 'mongo_initdb_root_password' ? encrypt($plaintext) : $plaintext;
|
||||
DB::table($table)->where('id', $model->id)->update([$column => $legacyValue]);
|
||||
}
|
||||
$emptyGithubApp = GithubApp::create([
|
||||
'name' => 'empty',
|
||||
'api_url' => 'https://api.github.com',
|
||||
'html_url' => 'https://github.com',
|
||||
'team_id' => $this->secrets[0][1]->team_id,
|
||||
'is_system_wide' => false,
|
||||
]);
|
||||
|
||||
$migration = require database_path('migrations/2026_10_19_083521_encrypt_plaintext_secrets.php');
|
||||
$migration->up();
|
||||
$migration->up();
|
||||
|
||||
foreach ($this->secrets as [$table, $model, $column, $plaintext]) {
|
||||
expect(Crypt::decryptString(rawSecret($table, $model, $column)))->toBe($plaintext, "{$table}.{$column}")
|
||||
->and($model->fresh()->{$column})->toBe($plaintext, "{$table}.{$column}");
|
||||
}
|
||||
expect(rawSecret('github_apps', $emptyGithubApp, 'client_secret'))->toBeNull()
|
||||
->and(rawSecret('github_apps', $emptyGithubApp, 'webhook_secret'))->toBeNull();
|
||||
});
|
||||
Reference in New Issue
Block a user