2023-05-24 12:26:50 +00:00
< ? php
2024-06-11 10:37:39 +00:00
use App\Enums\ApplicationDeploymentStatus ;
2024-08-23 18:54:38 +00:00
use App\Enums\ProxyTypes ;
2024-03-13 17:26:30 +00:00
use App\Jobs\ServerFilesFromServerJob ;
2023-10-20 12:51:01 +00:00
use App\Models\Application ;
2024-06-12 09:31:14 +00:00
use App\Models\ApplicationDeploymentQueue ;
2024-08-23 18:54:38 +00:00
use App\Models\ApplicationPreview ;
use App\Models\EnvironmentVariable ;
2024-11-12 10:32:18 +00:00
use App\Models\GithubApp ;
2023-06-12 10:00:01 +00:00
use App\Models\InstanceSettings ;
2023-11-24 14:48:23 +00:00
use App\Models\LocalFileVolume ;
use App\Models\LocalPersistentVolume ;
2023-09-28 08:53:00 +00:00
use App\Models\Server ;
2023-10-20 12:51:01 +00:00
use App\Models\Service ;
2023-11-24 14:48:23 +00:00
use App\Models\ServiceApplication ;
2024-08-23 18:54:38 +00:00
use App\Models\ServiceDatabase ;
2024-04-10 13:00:46 +00:00
use App\Models\StandaloneClickhouse ;
use App\Models\StandaloneDragonfly ;
use App\Models\StandaloneKeydb ;
2023-10-25 08:43:07 +00:00
use App\Models\StandaloneMariadb ;
2023-10-20 12:51:01 +00:00
use App\Models\StandaloneMongodb ;
2023-10-25 08:43:07 +00:00
use App\Models\StandaloneMysql ;
2023-10-20 12:51:01 +00:00
use App\Models\StandalonePostgresql ;
use App\Models\StandaloneRedis ;
2023-08-16 14:03:30 +00:00
use App\Models\Team ;
2023-09-14 16:22:08 +00:00
use App\Models\User ;
2024-10-31 15:47:08 +00:00
use Carbon\CarbonImmutable ;
2023-08-16 14:03:30 +00:00
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException ;
2024-01-30 08:48:51 +00:00
use Illuminate\Database\UniqueConstraintViolationException ;
2024-04-25 11:52:52 +00:00
use Illuminate\Process\Pool ;
2024-04-16 18:57:54 +00:00
use Illuminate\Support\Collection ;
2024-11-04 13:18:16 +00:00
use Illuminate\Support\Facades\Auth ;
2023-09-15 09:19:36 +00:00
use Illuminate\Support\Facades\Cache ;
2023-10-04 12:40:26 +00:00
use Illuminate\Support\Facades\File ;
2023-05-24 12:26:50 +00:00
use Illuminate\Support\Facades\Http ;
2024-04-25 11:52:52 +00:00
use Illuminate\Support\Facades\Process ;
2024-10-25 13:13:23 +00:00
use Illuminate\Support\Facades\RateLimiter ;
2023-12-11 19:01:54 +00:00
use Illuminate\Support\Facades\Request ;
2023-05-24 12:26:50 +00:00
use Illuminate\Support\Facades\Route ;
2024-06-26 11:32:36 +00:00
use Illuminate\Support\Facades\Validator ;
2023-05-24 12:26:50 +00:00
use Illuminate\Support\Str ;
2023-09-27 10:45:53 +00:00
use Illuminate\Support\Stringable ;
2025-01-10 17:27:48 +00:00
use Laravel\Horizon\Contracts\JobRepository ;
2024-02-28 12:48:39 +00:00
use Lcobucci\JWT\Encoding\ChainedFormatter ;
use Lcobucci\JWT\Encoding\JoseEncoder ;
use Lcobucci\JWT\Signer\Hmac\Sha256 ;
2024-06-10 20:43:34 +00:00
use Lcobucci\JWT\Signer\Key\InMemory ;
2024-02-28 12:48:39 +00:00
use Lcobucci\JWT\Token\Builder ;
2024-04-03 11:45:49 +00:00
use phpseclib3\Crypt\EC ;
2023-08-22 15:44:49 +00:00
use phpseclib3\Crypt\RSA ;
2024-06-10 20:43:34 +00:00
use Poliander\Cron\CronExpression ;
use PurplePixie\PhpDns\DNSQuery ;
2023-09-22 12:47:25 +00:00
use Spatie\Url\Url ;
2023-11-24 14:48:23 +00:00
use Symfony\Component\Yaml\Yaml ;
2024-06-10 20:43:34 +00:00
use Visus\Cuid2\Cuid2 ;
2023-05-24 12:26:50 +00:00
2023-09-21 15:48:31 +00:00
function base_configuration_dir () : string
{
return '/data/coolify' ;
}
2023-08-09 13:57:53 +00:00
function application_configuration_dir () : string
{
2024-12-09 14:38:21 +00:00
return base_configuration_dir () . '/applications' ;
2023-09-21 15:48:31 +00:00
}
function service_configuration_dir () : string
{
2024-12-09 14:38:21 +00:00
return base_configuration_dir () . '/services' ;
2023-08-09 12:44:36 +00:00
}
2023-08-09 13:57:53 +00:00
function database_configuration_dir () : string
{
2024-12-09 14:38:21 +00:00
return base_configuration_dir () . '/databases' ;
2023-08-09 12:44:36 +00:00
}
2023-09-07 11:23:34 +00:00
function database_proxy_dir ( $uuid ) : string
{
2024-12-09 14:38:21 +00:00
return base_configuration_dir () . " /databases/ $uuid /proxy " ;
2023-09-07 11:23:34 +00:00
}
2023-08-09 13:57:53 +00:00
function backup_dir () : string
{
2024-12-09 14:38:21 +00:00
return base_configuration_dir () . '/backups' ;
2023-08-09 12:44:36 +00:00
}
2024-06-20 08:44:31 +00:00
function metrics_dir () : string
{
2024-12-09 14:38:21 +00:00
return base_configuration_dir () . '/metrics' ;
2024-06-20 08:44:31 +00:00
}
2023-08-09 13:57:53 +00:00
2024-12-03 14:39:24 +00:00
function sanitize_string ( ? string $input = null ) : ? string
2024-12-02 21:49:41 +00:00
{
2024-12-03 14:39:24 +00:00
if ( is_null ( $input )) {
return null ;
}
2024-12-02 21:49:41 +00:00
// Remove any HTML/PHP tags
$sanitized = strip_tags ( $input );
// Convert special characters to HTML entities
$sanitized = htmlspecialchars ( $sanitized , ENT_QUOTES | ENT_HTML5 , 'UTF-8' );
// Remove any control characters
$sanitized = preg_replace ( '/[\x00-\x1F\x7F]/u' , '' , $sanitized );
// Trim whitespace
$sanitized = trim ( $sanitized );
return $sanitized ;
2024-06-20 08:44:31 +00:00
}
2023-08-09 13:57:53 +00:00
function generate_readme_file ( string $name , string $updated_at ) : string
{
2024-12-02 21:49:41 +00:00
$name = sanitize_string ( $name );
$updated_at = sanitize_string ( $updated_at );
2023-08-09 13:57:53 +00:00
return " Resource name: $name\nLatest Deployment Date: $updated_at " ;
2023-08-09 12:44:36 +00:00
}
2023-08-22 15:44:49 +00:00
function isInstanceAdmin ()
2023-08-14 14:56:13 +00:00
{
2023-08-22 15:44:49 +00:00
return auth () ? -> user () ? -> isInstanceAdmin () ? ? false ;
2023-08-14 14:56:13 +00:00
}
2023-08-22 15:44:49 +00:00
function currentTeam ()
{
2024-11-04 13:18:16 +00:00
return Auth :: user () ? -> currentTeam () ? ? null ;
2023-08-22 15:44:49 +00:00
}
function showBoarding () : bool
{
2024-11-05 10:53:11 +00:00
if ( Auth :: user () ? -> isMember ()) {
2024-05-21 12:29:06 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2023-08-27 13:44:36 +00:00
return currentTeam () -> show_boarding ? ? false ;
2023-08-22 15:44:49 +00:00
}
2023-09-08 16:33:26 +00:00
function refreshSession ( ? Team $team = null ) : void
2023-08-22 15:44:49 +00:00
{
2024-12-09 14:38:21 +00:00
if ( ! $team ) {
2024-11-04 13:18:16 +00:00
if ( Auth :: user () -> currentTeam ()) {
$team = Team :: find ( Auth :: user () -> currentTeam () -> id );
2023-09-15 09:28:44 +00:00
} else {
2024-11-04 13:18:16 +00:00
$team = User :: find ( Auth :: id ()) -> teams -> first ();
2023-09-15 09:28:44 +00:00
}
2023-09-08 16:33:26 +00:00
}
2024-12-09 14:38:21 +00:00
Cache :: forget ( 'team:' . Auth :: id ());
Cache :: remember ( 'team:' . Auth :: id (), 3600 , function () use ( $team ) {
2023-09-08 16:33:26 +00:00
return $team ;
});
2023-08-30 14:01:38 +00:00
session ([ 'currentTeam' => $team ]);
2023-08-22 15:44:49 +00:00
}
2023-09-15 13:34:25 +00:00
function handleError ( ? Throwable $error = null , ? Livewire\Component $livewire = null , ? string $customErrorMessage = null )
{
2023-11-20 09:32:06 +00:00
if ( $error instanceof TooManyRequestsException ) {
if ( isset ( $livewire )) {
2024-03-27 10:35:57 +00:00
return $livewire -> dispatch ( 'error' , " Too many requests. Please try again in { $error -> secondsUntilAvailable } seconds. " );
2023-11-20 09:32:06 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-20 09:32:06 +00:00
return " Too many requests. Please try again in { $error -> secondsUntilAvailable } seconds. " ;
}
2024-01-30 08:48:51 +00:00
if ( $error instanceof UniqueConstraintViolationException ) {
if ( isset ( $livewire )) {
2024-02-22 13:56:41 +00:00
return $livewire -> dispatch ( 'error' , 'Duplicate entry found. Please use a different name.' );
2024-01-30 08:48:51 +00:00
}
2024-06-10 20:43:34 +00:00
return 'Duplicate entry found. Please use a different name.' ;
2024-01-30 08:48:51 +00:00
}
2023-11-20 09:32:06 +00:00
2024-10-17 19:48:47 +00:00
if ( $error instanceof \Illuminate\Database\Eloquent\ModelNotFoundException ) {
abort ( 404 );
}
2023-09-15 13:34:25 +00:00
if ( $error instanceof Throwable ) {
$message = $error -> getMessage ();
} else {
$message = null ;
}
if ( $customErrorMessage ) {
2024-12-09 14:38:21 +00:00
$message = $customErrorMessage . ' ' . $message ;
2023-09-15 13:34:25 +00:00
}
2023-11-20 09:32:06 +00:00
2023-09-15 13:34:25 +00:00
if ( isset ( $livewire )) {
2023-12-07 18:06:32 +00:00
return $livewire -> dispatch ( 'error' , $message );
2023-09-15 13:34:25 +00:00
}
2023-11-21 11:07:06 +00:00
throw new Exception ( $message );
2023-05-24 12:26:50 +00:00
}
2023-08-09 13:57:53 +00:00
function get_route_parameters () : array
2023-05-24 12:26:50 +00:00
{
return Route :: current () -> parameters ();
}
2024-05-08 17:19:32 +00:00
function get_latest_sentinel_version () : string
{
try {
2024-10-15 11:39:19 +00:00
$response = Http :: get ( 'https://cdn.coollabs.io/coolify/versions.json' );
2024-05-08 17:19:32 +00:00
$versions = $response -> json ();
2024-06-18 14:43:18 +00:00
2024-10-15 12:03:10 +00:00
return data_get ( $versions , 'coolify.sentinel.version' );
2024-10-31 14:19:37 +00:00
} catch ( \Throwable ) {
2024-05-08 17:19:32 +00:00
return '0.0.0' ;
}
}
2023-08-09 13:57:53 +00:00
function get_latest_version_of_coolify () : string
2023-05-24 12:26:50 +00:00
{
2023-06-19 13:43:53 +00:00
try {
2024-05-22 07:23:17 +00:00
$versions = File :: get ( base_path ( 'versions.json' ));
$versions = json_decode ( $versions , true );
2024-06-10 20:43:34 +00:00
2023-06-19 13:43:53 +00:00
return data_get ( $versions , 'coolify.v4.version' );
2023-09-15 13:34:25 +00:00
} catch ( \Throwable $e ) {
2023-09-11 15:36:30 +00:00
ray ( $e -> getMessage ());
2024-06-10 20:43:34 +00:00
2023-06-19 13:43:53 +00:00
return '0.0.0' ;
}
2023-05-24 12:26:50 +00:00
}
2023-09-19 13:51:13 +00:00
function generate_random_name ( ? string $cuid = null ) : string
2023-05-24 12:26:50 +00:00
{
2023-11-13 10:44:13 +00:00
$generator = new \Nubs\RandomNameGenerator\All (
[
2024-07-24 19:11:12 +00:00
new \Nubs\RandomNameGenerator\Alliteration ,
2023-11-13 10:44:13 +00:00
]
);
2023-09-19 13:51:13 +00:00
if ( is_null ( $cuid )) {
2024-07-25 11:31:59 +00:00
$cuid = new Cuid2 ;
2023-09-19 13:51:13 +00:00
}
2024-06-10 20:43:34 +00:00
2023-08-09 13:57:53 +00:00
return Str :: kebab ( " { $generator -> getName () } - $cuid " );
2023-05-24 12:26:50 +00:00
}
2024-04-03 11:45:49 +00:00
function generateSSHKey ( string $type = 'rsa' )
{
if ( $type === 'rsa' ) {
$key = RSA :: createKey ();
2024-06-10 20:43:34 +00:00
2024-04-03 11:45:49 +00:00
return [
'private' => $key -> toString ( 'PKCS1' ),
2024-06-10 20:43:34 +00:00
'public' => $key -> getPublicKey () -> toString ( 'OpenSSH' , [ 'comment' => 'coolify-generated-ssh-key' ]),
2024-04-03 11:45:49 +00:00
];
2024-06-10 20:43:34 +00:00
} elseif ( $type === 'ed25519' ) {
2024-04-03 11:45:49 +00:00
$key = EC :: createKey ( 'Ed25519' );
2024-06-10 20:43:34 +00:00
2024-04-03 11:45:49 +00:00
return [
'private' => $key -> toString ( 'OpenSSH' ),
2024-06-10 20:43:34 +00:00
'public' => $key -> getPublicKey () -> toString ( 'OpenSSH' , [ 'comment' => 'coolify-generated-ssh-key' ]),
2024-04-03 11:45:49 +00:00
];
}
throw new Exception ( 'Invalid key type' );
2023-08-22 15:44:49 +00:00
}
2023-08-31 13:00:59 +00:00
function formatPrivateKey ( string $privateKey )
{
2023-08-22 15:44:49 +00:00
$privateKey = trim ( $privateKey );
2024-12-09 14:38:21 +00:00
if ( ! str_ends_with ( $privateKey , " \n " )) {
2023-08-22 15:44:49 +00:00
$privateKey .= " \n " ;
}
2024-06-10 20:43:34 +00:00
2023-08-22 15:44:49 +00:00
return $privateKey ;
}
2023-09-19 13:51:13 +00:00
function generate_application_name ( string $git_repository , string $git_branch , ? string $cuid = null ) : string
2023-05-24 13:47:04 +00:00
{
2023-09-19 13:51:13 +00:00
if ( is_null ( $cuid )) {
2024-07-25 11:31:59 +00:00
$cuid = new Cuid2 ;
2023-09-19 13:51:13 +00:00
}
2024-06-10 20:43:34 +00:00
2023-08-09 13:57:53 +00:00
return Str :: kebab ( " $git_repository : $git_branch - $cuid " );
2023-05-24 13:47:04 +00:00
}
2023-06-12 10:00:01 +00:00
2023-08-09 13:57:53 +00:00
function base_ip () : string
2023-06-16 11:37:02 +00:00
{
2023-08-27 13:23:47 +00:00
if ( isDev ()) {
2024-06-10 20:43:34 +00:00
return 'localhost' ;
2023-07-28 11:40:47 +00:00
}
2024-10-01 08:37:40 +00:00
$settings = instanceSettings ();
2023-08-15 13:39:15 +00:00
if ( $settings -> public_ipv4 ) {
return " $settings->public_ipv4 " ;
}
if ( $settings -> public_ipv6 ) {
return " $settings->public_ipv6 " ;
}
2024-06-10 20:43:34 +00:00
return 'localhost' ;
2023-06-16 11:35:35 +00:00
}
2024-06-10 20:43:34 +00:00
function getFqdnWithoutPort ( string $fqdn )
2023-09-24 10:10:36 +00:00
{
2023-12-28 16:53:47 +00:00
try {
$url = Url :: fromString ( $fqdn );
$host = $url -> getHost ();
$scheme = $url -> getScheme ();
$path = $url -> getPath ();
2024-06-10 20:43:34 +00:00
2023-12-28 16:53:47 +00:00
return " $scheme :// $host $path " ;
2024-10-31 14:19:37 +00:00
} catch ( \Throwable ) {
2023-12-28 16:53:47 +00:00
return $fqdn ;
}
2023-09-22 12:47:25 +00:00
}
2023-08-09 13:57:53 +00:00
/**
* If fqdn is set , return it , otherwise return public ip .
*/
function base_url ( bool $withPort = true ) : string
2023-06-13 13:01:11 +00:00
{
2024-10-01 08:37:40 +00:00
$settings = instanceSettings ();
2023-06-13 13:01:11 +00:00
if ( $settings -> fqdn ) {
return $settings -> fqdn ;
}
2023-06-14 07:05:13 +00:00
$port = config ( 'app.port' );
if ( $settings -> public_ipv4 ) {
2023-06-16 09:01:27 +00:00
if ( $withPort ) {
2023-08-27 13:23:47 +00:00
if ( isDev ()) {
2023-08-09 13:57:53 +00:00
return " http://localhost: $port " ;
2023-06-16 09:01:27 +00:00
}
2024-06-10 20:43:34 +00:00
2023-08-09 13:57:53 +00:00
return " http:// $settings->public_ipv4 : $port " ;
2023-06-16 09:01:27 +00:00
}
2023-08-27 13:23:47 +00:00
if ( isDev ()) {
2024-06-10 20:43:34 +00:00
return 'http://localhost' ;
2023-06-16 09:01:27 +00:00
}
2024-06-10 20:43:34 +00:00
2023-08-09 13:57:53 +00:00
return " http:// $settings->public_ipv4 " ;
2023-06-14 07:05:13 +00:00
}
if ( $settings -> public_ipv6 ) {
2023-06-16 09:01:27 +00:00
if ( $withPort ) {
2023-08-09 13:57:53 +00:00
return " http:// $settings->public_ipv6 : $port " ;
2023-06-16 09:01:27 +00:00
}
2024-06-10 20:43:34 +00:00
2023-08-09 13:57:53 +00:00
return " http:// $settings->public_ipv6 " ;
2023-06-14 07:05:13 +00:00
}
2024-06-10 20:43:34 +00:00
2023-06-14 07:05:13 +00:00
return url ( '/' );
2023-06-13 13:01:11 +00:00
}
2023-06-16 09:01:27 +00:00
2024-03-21 13:30:35 +00:00
function isSubscribed ()
{
2024-03-29 23:23:48 +00:00
return isSubscriptionActive () || auth () -> user () -> isInstanceAdmin ();
2024-03-21 13:30:35 +00:00
}
2024-10-01 07:31:01 +00:00
function isProduction () : bool
{
2024-12-09 14:38:21 +00:00
return ! isDev ();
2024-10-01 07:31:01 +00:00
}
2023-08-27 13:23:47 +00:00
function isDev () : bool
2023-06-16 09:01:27 +00:00
{
return config ( 'app.env' ) === 'local' ;
}
2023-08-08 09:51:36 +00:00
2023-08-31 07:56:37 +00:00
function isCloud () : bool
2023-07-13 13:07:42 +00:00
{
2024-11-13 17:41:23 +00:00
return ! config ( 'constants.coolify.self_hosted' );
2023-08-08 09:51:36 +00:00
}
2023-08-09 13:57:53 +00:00
2024-08-26 13:26:08 +00:00
function translate_cron_expression ( $expression_to_validate ) : string
{
if ( isset ( VALID_CRON_STRINGS [ $expression_to_validate ])) {
return VALID_CRON_STRINGS [ $expression_to_validate ];
}
return $expression_to_validate ;
}
2023-08-10 13:52:54 +00:00
function validate_cron_expression ( $expression_to_validate ) : bool
{
2024-10-30 13:54:27 +00:00
if ( empty ( $expression_to_validate )) {
return false ;
}
2023-08-10 13:52:54 +00:00
$isValid = false ;
$expression = new CronExpression ( $expression_to_validate );
$isValid = $expression -> isValid ();
if ( isset ( VALID_CRON_STRINGS [ $expression_to_validate ])) {
$isValid = true ;
}
2024-06-10 20:43:34 +00:00
2023-08-10 13:52:54 +00:00
return $isValid ;
}
2024-11-14 09:02:37 +00:00
function validate_timezone ( string $timezone ) : bool
{
return in_array ( $timezone , timezone_identifiers_list ());
}
2024-06-10 20:43:34 +00:00
2023-09-15 13:34:25 +00:00
function parseEnvFormatToArray ( $env_file_contents )
{
2024-06-10 20:43:34 +00:00
$env_array = [];
2023-09-08 14:16:59 +00:00
$lines = explode ( " \n " , $env_file_contents );
foreach ( $lines as $line ) {
if ( $line === '' || substr ( $line , 0 , 1 ) === '#' ) {
continue ;
}
$equals_pos = strpos ( $line , '=' );
if ( $equals_pos !== false ) {
$key = substr ( $line , 0 , $equals_pos );
$value = substr ( $line , $equals_pos + 1 );
if ( substr ( $value , 0 , 1 ) === '"' && substr ( $value , - 1 ) === '"' ) {
$value = substr ( $value , 1 , - 1 );
2023-09-15 13:34:25 +00:00
} elseif ( substr ( $value , 0 , 1 ) === " ' " && substr ( $value , - 1 ) === " ' " ) {
2023-09-08 14:16:59 +00:00
$value = substr ( $value , 1 , - 1 );
}
$env_array [ $key ] = $value ;
}
}
2024-06-10 20:43:34 +00:00
2023-09-08 14:16:59 +00:00
return $env_array ;
}
2023-09-27 10:45:53 +00:00
function data_get_str ( $data , $key , $default = null ) : Stringable
{
$str = data_get ( $data , $key , $default ) ? ? $default ;
2024-06-10 20:43:34 +00:00
2024-06-25 08:34:56 +00:00
return str ( $str );
2023-09-27 10:45:53 +00:00
}
2023-09-28 08:53:00 +00:00
2025-08-04 08:46:59 +00:00
function generateUrl ( Server $server , string $random , bool $forceHttps = false ) : string
2023-09-30 13:39:40 +00:00
{
$wildcard = data_get ( $server , 'settings.wildcard_domain' );
if ( is_null ( $wildcard ) || $wildcard === '' ) {
$wildcard = sslip ( $server );
}
$url = Url :: fromString ( $wildcard );
$host = $url -> getHost ();
$path = $url -> getPath () === '/' ? '' : $url -> getPath ();
$scheme = $url -> getScheme ();
2024-09-16 14:35:47 +00:00
if ( $forceHttps ) {
$scheme = 'https' ;
}
2024-06-10 20:43:34 +00:00
2024-10-31 17:20:11 +00:00
return " $scheme :// { $random } . $host $path " ;
2023-09-30 13:39:40 +00:00
}
2025-08-04 08:46:59 +00:00
function generateFqdn ( Server $server , string $random , bool $forceHttps = false , int $parserVersion = 4 ) : string
{
$wildcard = data_get ( $server , 'settings.wildcard_domain' );
if ( is_null ( $wildcard ) || $wildcard === '' ) {
$wildcard = sslip ( $server );
}
$url = Url :: fromString ( $wildcard );
$host = $url -> getHost ();
$path = $url -> getPath () === '/' ? '' : $url -> getPath ();
$scheme = $url -> getScheme ();
if ( $forceHttps ) {
$scheme = 'https' ;
}
if ( $parserVersion >= 5 && version_compare ( config ( 'constants.coolify.version' ), '4.0.0-beta.420.7' , '>=' )) {
return " { $random } . $host $path " ;
}
return " $scheme :// { $random } . $host $path " ;
}
2023-09-28 08:53:00 +00:00
function sslip ( Server $server )
{
2024-01-09 11:48:46 +00:00
if ( isDev () && $server -> id === 0 ) {
2024-06-10 20:43:34 +00:00
return 'http://127.0.0.1.sslip.io' ;
2023-09-28 08:53:00 +00:00
}
if ( $server -> ip === 'host.docker.internal' ) {
$baseIp = base_ip ();
2024-06-10 20:43:34 +00:00
2023-09-30 13:39:40 +00:00
return " http:// $baseIp .sslip.io " ;
2023-09-28 08:53:00 +00:00
}
2024-09-26 11:47:13 +00:00
// ipv6
if ( str ( $server -> ip ) -> contains ( ':' )) {
$ipv6 = str ( $server -> ip ) -> replace ( ':' , '-' );
return " http:// { $ipv6 } .sslip.io " ;
}
2024-06-10 20:43:34 +00:00
2023-09-30 13:39:40 +00:00
return " http:// { $server -> ip } .sslip.io " ;
2023-09-28 08:53:00 +00:00
}
2023-09-28 20:20:49 +00:00
2024-05-27 08:27:18 +00:00
function get_service_templates ( bool $force = false ) : Collection
{
2024-10-07 09:02:01 +00:00
2024-05-27 08:27:18 +00:00
if ( $force ) {
try {
2024-08-07 07:50:29 +00:00
$response = Http :: retry ( 3 , 1000 ) -> get ( config ( 'constants.services.official' ));
2024-05-27 08:27:18 +00:00
if ( $response -> failed ()) {
return collect ([]);
}
$services = $response -> json ();
2024-06-10 20:43:34 +00:00
2024-05-27 08:27:18 +00:00
return collect ( $services );
2024-10-31 14:19:37 +00:00
} catch ( \Throwable ) {
2025-08-10 08:10:14 +00:00
$services = File :: get ( base_path ( 'templates/' . config ( 'constants.services.file_name' )));
2024-06-10 20:43:34 +00:00
2024-05-27 08:27:18 +00:00
return collect ( json_decode ( $services )) -> sortKeys ();
}
} else {
2025-08-10 08:10:14 +00:00
$services = File :: get ( base_path ( 'templates/' . config ( 'constants.services.file_name' )));
2024-06-10 20:43:34 +00:00
2024-05-27 08:27:18 +00:00
return collect ( json_decode ( $services )) -> sortKeys ();
}
2023-09-28 20:20:49 +00:00
}
2023-10-20 12:51:01 +00:00
function getResourceByUuid ( string $uuid , ? int $teamId = null )
{
2024-04-10 13:00:46 +00:00
if ( is_null ( $teamId )) {
2023-10-20 12:51:01 +00:00
return null ;
2024-04-10 13:00:46 +00:00
}
$resource = queryResourcesByUuid ( $uuid );
2024-12-09 14:38:21 +00:00
if ( ! is_null ( $resource ) && $resource -> environment -> project -> team_id === $teamId ) {
2023-10-20 12:51:01 +00:00
return $resource ;
}
2024-06-10 20:43:34 +00:00
2024-04-10 13:00:46 +00:00
return null ;
2023-10-20 12:51:01 +00:00
}
2024-07-01 14:26:50 +00:00
function queryDatabaseByUuidWithinTeam ( string $uuid , string $teamId )
{
$postgresql = StandalonePostgresql :: whereUuid ( $uuid ) -> first ();
if ( $postgresql && $postgresql -> team () -> id == $teamId ) {
2025-05-20 13:08:04 +00:00
return $postgresql -> unsetRelation ( 'environment' );
2024-07-01 14:26:50 +00:00
}
$redis = StandaloneRedis :: whereUuid ( $uuid ) -> first ();
if ( $redis && $redis -> team () -> id == $teamId ) {
return $redis -> unsetRelation ( 'environment' );
}
$mongodb = StandaloneMongodb :: whereUuid ( $uuid ) -> first ();
if ( $mongodb && $mongodb -> team () -> id == $teamId ) {
return $mongodb -> unsetRelation ( 'environment' );
}
$mysql = StandaloneMysql :: whereUuid ( $uuid ) -> first ();
if ( $mysql && $mysql -> team () -> id == $teamId ) {
return $mysql -> unsetRelation ( 'environment' );
}
$mariadb = StandaloneMariadb :: whereUuid ( $uuid ) -> first ();
if ( $mariadb && $mariadb -> team () -> id == $teamId ) {
return $mariadb -> unsetRelation ( 'environment' );
}
$keydb = StandaloneKeydb :: whereUuid ( $uuid ) -> first ();
if ( $keydb && $keydb -> team () -> id == $teamId ) {
return $keydb -> unsetRelation ( 'environment' );
}
$dragonfly = StandaloneDragonfly :: whereUuid ( $uuid ) -> first ();
if ( $dragonfly && $dragonfly -> team () -> id == $teamId ) {
return $dragonfly -> unsetRelation ( 'environment' );
}
$clickhouse = StandaloneClickhouse :: whereUuid ( $uuid ) -> first ();
if ( $clickhouse && $clickhouse -> team () -> id == $teamId ) {
return $clickhouse -> unsetRelation ( 'environment' );
}
return null ;
}
2023-10-20 12:51:01 +00:00
function queryResourcesByUuid ( string $uuid )
{
$resource = null ;
$application = Application :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $application ) {
return $application ;
}
2023-10-20 12:51:01 +00:00
$service = Service :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $service ) {
return $service ;
}
2023-10-20 12:51:01 +00:00
$postgresql = StandalonePostgresql :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $postgresql ) {
return $postgresql ;
}
2023-10-20 12:51:01 +00:00
$redis = StandaloneRedis :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $redis ) {
return $redis ;
}
2023-10-20 12:51:01 +00:00
$mongodb = StandaloneMongodb :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $mongodb ) {
return $mongodb ;
}
2023-10-25 08:43:07 +00:00
$mysql = StandaloneMysql :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $mysql ) {
return $mysql ;
}
2023-10-25 08:43:07 +00:00
$mariadb = StandaloneMariadb :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $mariadb ) {
return $mariadb ;
}
2024-04-10 13:00:46 +00:00
$keydb = StandaloneKeydb :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $keydb ) {
return $keydb ;
}
2024-04-10 13:00:46 +00:00
$dragonfly = StandaloneDragonfly :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $dragonfly ) {
return $dragonfly ;
}
2024-04-10 13:00:46 +00:00
$clickhouse = StandaloneClickhouse :: whereUuid ( $uuid ) -> first ();
2024-06-10 20:43:34 +00:00
if ( $clickhouse ) {
return $clickhouse ;
}
2023-10-20 12:51:01 +00:00
return $resource ;
}
2024-10-25 08:59:12 +00:00
function generateTagDeployWebhook ( $tag_name )
2024-02-01 14:38:12 +00:00
{
$baseUrl = base_url ();
2024-12-09 14:38:21 +00:00
$api = Url :: fromString ( $baseUrl ) . '/api/v1' ;
2024-02-02 10:50:28 +00:00
$endpoint = " /deploy?tag= $tag_name " ;
2024-06-10 20:43:34 +00:00
2024-12-09 14:38:21 +00:00
return $api . $endpoint ;
2024-02-01 14:38:12 +00:00
}
2023-11-13 10:44:13 +00:00
function generateDeployWebhook ( $resource )
{
2023-10-25 08:43:07 +00:00
$baseUrl = base_url ();
2024-12-09 14:38:21 +00:00
$api = Url :: fromString ( $baseUrl ) . '/api/v1' ;
2023-10-25 08:43:07 +00:00
$endpoint = '/deploy' ;
$uuid = data_get ( $resource , 'uuid' );
2024-06-10 20:43:34 +00:00
2024-12-09 14:38:21 +00:00
return $api . $endpoint . " ?uuid= $uuid &force=false " ;
2023-10-25 08:43:07 +00:00
}
2023-11-24 14:48:23 +00:00
function generateGitManualWebhook ( $resource , $type )
{
2024-12-09 14:38:21 +00:00
if ( $resource -> source_id !== 0 && ! is_null ( $resource -> source_id )) {
2023-11-14 13:14:21 +00:00
return null ;
}
2024-10-28 13:56:13 +00:00
if ( $resource -> getMorphClass () === \App\Models\Application :: class ) {
2023-11-14 12:26:14 +00:00
$baseUrl = base_url ();
2024-06-10 20:43:34 +00:00
2024-12-09 14:38:21 +00:00
return Url :: fromString ( $baseUrl ) . " /webhooks/source/ $type /events/manual " ;
2023-11-14 12:26:14 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-14 12:26:14 +00:00
return null ;
}
2023-11-13 10:44:13 +00:00
function removeAnsiColors ( $text )
{
2023-11-07 13:40:58 +00:00
return preg_replace ( '/\e[[][A-Za-z0-9];?[0-9]*m?/' , '' , $text );
}
2023-11-24 14:48:23 +00:00
2023-11-27 08:58:31 +00:00
function getTopLevelNetworks ( Service | Application $resource )
{
2024-10-28 13:56:13 +00:00
if ( $resource -> getMorphClass () === \App\Models\Service :: class ) {
2023-11-27 08:58:31 +00:00
if ( $resource -> docker_compose_raw ) {
try {
$yaml = Yaml :: parse ( $resource -> docker_compose_raw );
} catch ( \Exception $e ) {
2025-07-07 08:20:54 +00:00
// If the docker-compose.yml file is not valid, we will return the network name as the key
$topLevelNetworks = collect ([
$resource -> uuid => [
'name' => $resource -> uuid ,
'external' => true ,
],
]);
return $topLevelNetworks -> keys ();
2023-11-27 08:58:31 +00:00
}
$services = data_get ( $yaml , 'services' );
$topLevelNetworks = collect ( data_get ( $yaml , 'networks' , []));
$definedNetwork = collect ([ $resource -> uuid ]);
$services = collect ( $services ) -> map ( function ( $service , $_ ) use ( $topLevelNetworks , $definedNetwork ) {
$serviceNetworks = collect ( data_get ( $service , 'networks' , []));
2024-02-14 14:00:24 +00:00
$hasHostNetworkMode = data_get ( $service , 'network_mode' ) === 'host' ? true : false ;
2023-11-27 08:58:31 +00:00
2024-02-14 14:00:24 +00:00
// Only add 'networks' key if 'network_mode' is not 'host'
2024-12-09 14:38:21 +00:00
if ( ! $hasHostNetworkMode ) {
2024-02-11 15:22:09 +00:00
// Collect/create/update networks
if ( $serviceNetworks -> count () > 0 ) {
foreach ( $serviceNetworks as $networkName => $networkDetails ) {
2024-05-31 08:21:38 +00:00
if ( $networkName === 'default' ) {
continue ;
}
// ignore alias
if ( $networkDetails [ 'aliases' ] ? ? false ) {
continue ;
}
2024-02-11 15:22:09 +00:00
$networkExists = $topLevelNetworks -> contains ( function ( $value , $key ) use ( $networkName ) {
return $value == $networkName || $key == $networkName ;
});
2024-12-09 14:38:21 +00:00
if ( ! $networkExists ) {
2024-10-03 13:04:40 +00:00
if ( is_string ( $networkDetails ) || is_int ( $networkDetails )) {
$topLevelNetworks -> put ( $networkDetails , null );
}
2024-02-11 15:22:09 +00:00
}
2023-11-27 08:58:31 +00:00
}
}
2024-02-11 15:22:09 +00:00
$definedNetworkExists = $topLevelNetworks -> contains ( function ( $value , $_ ) use ( $definedNetwork ) {
return $value == $definedNetwork ;
});
2024-12-09 14:38:21 +00:00
if ( ! $definedNetworkExists ) {
2024-02-11 15:22:09 +00:00
foreach ( $definedNetwork as $network ) {
2024-06-10 20:43:34 +00:00
$topLevelNetworks -> put ( $network , [
2024-02-11 15:22:09 +00:00
'name' => $network ,
2024-06-10 20:43:34 +00:00
'external' => true ,
2024-02-11 15:22:09 +00:00
]);
}
2023-11-27 08:58:31 +00:00
}
}
return $service ;
});
2024-06-10 20:43:34 +00:00
2023-11-27 08:58:31 +00:00
return $topLevelNetworks -> keys ();
}
2024-10-28 13:56:13 +00:00
} elseif ( $resource -> getMorphClass () === \App\Models\Application :: class ) {
2023-11-27 08:58:31 +00:00
try {
$yaml = Yaml :: parse ( $resource -> docker_compose_raw );
} catch ( \Exception $e ) {
2025-07-07 08:20:54 +00:00
// If the docker-compose.yml file is not valid, we will return the network name as the key
$topLevelNetworks = collect ([
$resource -> uuid => [
'name' => $resource -> uuid ,
'external' => true ,
],
]);
return $topLevelNetworks -> keys ();
2023-11-27 08:58:31 +00:00
}
$topLevelNetworks = collect ( data_get ( $yaml , 'networks' , []));
$services = data_get ( $yaml , 'services' );
$definedNetwork = collect ([ $resource -> uuid ]);
$services = collect ( $services ) -> map ( function ( $service , $_ ) use ( $topLevelNetworks , $definedNetwork ) {
$serviceNetworks = collect ( data_get ( $service , 'networks' , []));
// Collect/create/update networks
if ( $serviceNetworks -> count () > 0 ) {
foreach ( $serviceNetworks as $networkName => $networkDetails ) {
2024-05-31 08:21:38 +00:00
if ( $networkName === 'default' ) {
continue ;
}
// ignore alias
if ( $networkDetails [ 'aliases' ] ? ? false ) {
continue ;
}
2023-11-27 08:58:31 +00:00
$networkExists = $topLevelNetworks -> contains ( function ( $value , $key ) use ( $networkName ) {
return $value == $networkName || $key == $networkName ;
});
2024-12-09 14:38:21 +00:00
if ( ! $networkExists ) {
2024-10-03 13:04:40 +00:00
if ( is_string ( $networkDetails ) || is_int ( $networkDetails )) {
$topLevelNetworks -> put ( $networkDetails , null );
}
2023-11-27 08:58:31 +00:00
}
}
}
$definedNetworkExists = $topLevelNetworks -> contains ( function ( $value , $_ ) use ( $definedNetwork ) {
return $value == $definedNetwork ;
});
2024-12-09 14:38:21 +00:00
if ( ! $definedNetworkExists ) {
2023-11-27 08:58:31 +00:00
foreach ( $definedNetwork as $network ) {
2024-06-10 20:43:34 +00:00
$topLevelNetworks -> put ( $network , [
2023-11-27 08:58:31 +00:00
'name' => $network ,
2024-06-10 20:43:34 +00:00
'external' => true ,
2023-11-27 08:58:31 +00:00
]);
}
}
2024-06-10 20:43:34 +00:00
2023-11-27 08:58:31 +00:00
return $service ;
});
2024-06-10 20:43:34 +00:00
2023-11-27 08:58:31 +00:00
return $topLevelNetworks -> keys ();
}
}
2024-08-21 18:32:02 +00:00
function sourceIsLocal ( Stringable $source )
{
if ( $source -> startsWith ( './' ) || $source -> startsWith ( '/' ) || $source -> startsWith ( '~' ) || $source -> startsWith ( '..' ) || $source -> startsWith ( '~/' ) || $source -> startsWith ( '../' )) {
return true ;
}
return false ;
}
function replaceLocalSource ( Stringable $source , Stringable $replacedWith )
{
if ( $source -> startsWith ( '.' )) {
$source = $source -> replaceFirst ( '.' , $replacedWith -> value ());
}
if ( $source -> startsWith ( '~' )) {
$source = $source -> replaceFirst ( '~' , $replacedWith -> value ());
}
if ( $source -> startsWith ( '..' )) {
$source = $source -> replaceFirst ( '..' , $replacedWith -> value ());
}
2024-09-13 09:12:28 +00:00
if ( $source -> endsWith ( '/' ) && $source -> value () !== '/' ) {
2024-08-23 12:21:12 +00:00
$source = $source -> replaceLast ( '/' , '' );
2024-08-21 18:32:02 +00:00
}
2024-08-22 13:05:04 +00:00
2024-08-23 12:21:12 +00:00
return $source ;
2024-08-21 18:32:02 +00:00
}
2024-08-22 13:05:04 +00:00
function convertToArray ( $collection )
{
if ( $collection instanceof Collection ) {
return $collection -> map ( function ( $item ) {
return convertToArray ( $item );
}) -> toArray ();
} elseif ( $collection instanceof Stringable ) {
return ( string ) $collection ;
} elseif ( is_array ( $collection )) {
return array_map ( function ( $item ) {
return convertToArray ( $item );
}, $collection );
}
return $collection ;
}
2023-11-28 11:05:04 +00:00
2024-10-03 20:02:18 +00:00
function parseCommandFromMagicEnvVariable ( Str | string $key ) : Stringable
{
$value = str ( $key );
$count = substr_count ( $value -> value (), '_' );
2025-02-28 19:28:35 +00:00
$command = null ;
2024-10-03 20:02:18 +00:00
if ( $count === 2 ) {
if ( $value -> startsWith ( 'SERVICE_FQDN' ) || $value -> startsWith ( 'SERVICE_URL' )) {
// SERVICE_FQDN_UMAMI
$command = $value -> after ( 'SERVICE_' ) -> beforeLast ( '_' );
} else {
// SERVICE_BASE64_UMAMI
$command = $value -> after ( 'SERVICE_' ) -> beforeLast ( '_' );
}
}
if ( $count === 3 ) {
if ( $value -> startsWith ( 'SERVICE_FQDN' ) || $value -> startsWith ( 'SERVICE_URL' )) {
// SERVICE_FQDN_UMAMI_1000
$command = $value -> after ( 'SERVICE_' ) -> before ( '_' );
} else {
// SERVICE_BASE64_64_UMAMI
$command = $value -> after ( 'SERVICE_' ) -> beforeLast ( '_' );
}
}
return str ( $command );
}
2023-11-28 11:05:04 +00:00
function parseEnvVariable ( Str | string $value )
{
$value = str ( $value );
$count = substr_count ( $value -> value (), '_' );
$command = null ;
$forService = null ;
$generatedValue = null ;
$port = null ;
2024-03-21 14:36:37 +00:00
if ( $value -> startsWith ( 'SERVICE' )) {
if ( $count === 2 ) {
if ( $value -> startsWith ( 'SERVICE_FQDN' ) || $value -> startsWith ( 'SERVICE_URL' )) {
// SERVICE_FQDN_UMAMI
$command = $value -> after ( 'SERVICE_' ) -> beforeLast ( '_' );
$forService = $value -> afterLast ( '_' );
} else {
// SERVICE_BASE64_UMAMI
$command = $value -> after ( 'SERVICE_' ) -> beforeLast ( '_' );
}
2023-11-28 11:05:04 +00:00
}
2024-03-21 14:36:37 +00:00
if ( $count === 3 ) {
if ( $value -> startsWith ( 'SERVICE_FQDN' ) || $value -> startsWith ( 'SERVICE_URL' )) {
// SERVICE_FQDN_UMAMI_1000
$command = $value -> after ( 'SERVICE_' ) -> before ( '_' );
$forService = $value -> after ( 'SERVICE_' ) -> after ( '_' ) -> before ( '_' );
$port = $value -> afterLast ( '_' );
if ( filter_var ( $port , FILTER_VALIDATE_INT ) === false ) {
$port = null ;
}
} else {
// SERVICE_BASE64_64_UMAMI
$command = $value -> after ( 'SERVICE_' ) -> beforeLast ( '_' );
2023-12-28 16:53:47 +00:00
}
2023-11-28 11:05:04 +00:00
}
}
2024-06-10 20:43:34 +00:00
2023-11-28 11:05:04 +00:00
return [
'command' => $command ,
'forService' => $forService ,
'generatedValue' => $generatedValue ,
'port' => $port ,
];
}
2024-08-27 19:48:25 +00:00
function generateEnvValue ( string $command , Service | Application | null $service = null )
2023-11-28 11:05:04 +00:00
{
switch ( $command ) {
case 'PASSWORD' :
$generatedValue = Str :: password ( symbols : false );
break ;
case 'PASSWORD_64' :
$generatedValue = Str :: password ( length : 64 , symbols : false );
break ;
2024-12-12 10:06:00 +00:00
case 'PASSWORDWITHSYMBOLS' :
$generatedValue = Str :: password ( symbols : true );
break ;
case 'PASSWORDWITHSYMBOLS_64' :
$generatedValue = Str :: password ( length : 64 , symbols : true );
break ;
2024-12-09 14:38:21 +00:00
// This is not base64, it's just a random string
2023-11-28 11:05:04 +00:00
case 'BASE64_64' :
2024-03-04 11:50:56 +00:00
$generatedValue = Str :: random ( 64 );
2023-11-28 11:05:04 +00:00
break ;
case 'BASE64_128' :
2024-03-04 11:50:56 +00:00
$generatedValue = Str :: random ( 128 );
2023-11-28 11:05:04 +00:00
break ;
case 'BASE64' :
2024-03-04 11:46:37 +00:00
case 'BASE64_32' :
2024-03-04 11:50:56 +00:00
$generatedValue = Str :: random ( 32 );
break ;
2024-12-09 14:38:21 +00:00
// This is base64,
2024-03-04 11:50:56 +00:00
case 'REALBASE64_64' :
$generatedValue = base64_encode ( Str :: random ( 64 ));
break ;
case 'REALBASE64_128' :
$generatedValue = base64_encode ( Str :: random ( 128 ));
break ;
case 'REALBASE64' :
case 'REALBASE64_32' :
2024-03-04 11:46:37 +00:00
$generatedValue = base64_encode ( Str :: random ( 32 ));
2023-11-28 11:05:04 +00:00
break ;
2024-11-26 12:01:42 +00:00
case 'HEX_32' :
$generatedValue = bin2hex ( Str :: random ( 32 ));
break ;
case 'HEX_64' :
$generatedValue = bin2hex ( Str :: random ( 64 ));
break ;
case 'HEX_128' :
$generatedValue = bin2hex ( Str :: random ( 128 ));
break ;
2023-11-28 11:05:04 +00:00
case 'USER' :
$generatedValue = Str :: random ( 16 );
break ;
2024-02-28 12:48:39 +00:00
case 'SUPABASEANON' :
$signingKey = $service -> environment_variables () -> where ( 'key' , 'SERVICE_PASSWORD_JWT' ) -> first ();
if ( is_null ( $signingKey )) {
return ;
} else {
$signingKey = $signingKey -> value ;
}
$key = InMemory :: plainText ( $signingKey );
2024-07-24 19:11:12 +00:00
$algorithm = new Sha256 ;
$tokenBuilder = ( new Builder ( new JoseEncoder , ChainedFormatter :: default ()));
2024-10-31 15:47:08 +00:00
$now = CarbonImmutable :: now ();
2024-02-28 12:48:39 +00:00
$now = $now -> setTime ( $now -> format ( 'H' ), $now -> format ( 'i' ));
$token = $tokenBuilder
-> issuedBy ( 'supabase' )
-> issuedAt ( $now )
-> expiresAt ( $now -> modify ( '+100 year' ))
-> withClaim ( 'role' , 'anon' )
-> getToken ( $algorithm , $key );
$generatedValue = $token -> toString ();
break ;
case 'SUPABASESERVICE' :
$signingKey = $service -> environment_variables () -> where ( 'key' , 'SERVICE_PASSWORD_JWT' ) -> first ();
if ( is_null ( $signingKey )) {
return ;
} else {
$signingKey = $signingKey -> value ;
}
$key = InMemory :: plainText ( $signingKey );
2024-07-24 19:11:12 +00:00
$algorithm = new Sha256 ;
$tokenBuilder = ( new Builder ( new JoseEncoder , ChainedFormatter :: default ()));
2024-10-31 15:47:08 +00:00
$now = CarbonImmutable :: now ();
2024-02-28 12:48:39 +00:00
$now = $now -> setTime ( $now -> format ( 'H' ), $now -> format ( 'i' ));
$token = $tokenBuilder
-> issuedBy ( 'supabase' )
-> issuedAt ( $now )
-> expiresAt ( $now -> modify ( '+100 year' ))
-> withClaim ( 'role' , 'service_role' )
-> getToken ( $algorithm , $key );
$generatedValue = $token -> toString ();
break ;
2023-12-14 14:34:05 +00:00
default :
2024-08-27 19:48:25 +00:00
// $generatedValue = Str::random(16);
$generatedValue = null ;
2023-12-14 14:34:05 +00:00
break ;
2023-11-28 11:05:04 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-28 11:05:04 +00:00
return $generatedValue ;
}
2023-12-11 19:01:54 +00:00
2023-12-11 19:13:41 +00:00
function getRealtime ()
{
2024-11-12 14:53:05 +00:00
$envDefined = config ( 'constants.pusher.port' );
2023-12-11 19:22:31 +00:00
if ( empty ( $envDefined )) {
2023-12-11 19:13:41 +00:00
$url = Url :: fromString ( Request :: getSchemeAndHttpHost ());
$port = $url -> getPort ();
if ( $port ) {
return '6001' ;
} else {
return null ;
}
2023-12-11 19:01:54 +00:00
} else {
2023-12-11 19:13:41 +00:00
return $envDefined ;
2023-12-11 19:01:54 +00:00
}
}
2024-01-15 09:03:15 +00:00
function validate_dns_entry ( string $fqdn , Server $server )
{
2024-06-10 20:43:34 +00:00
// https://www.cloudflare.com/ips-v4/#
2024-01-15 10:37:26 +00:00
$cloudflare_ips = collect ([ '173.245.48.0/20' , '103.21.244.0/22' , '103.22.200.0/22' , '103.31.4.0/22' , '141.101.64.0/18' , '108.162.192.0/18' , '190.93.240.0/20' , '188.114.96.0/20' , '197.234.240.0/22' , '198.41.128.0/17' , '162.158.0.0/15' , '104.16.0.0/13' , '172.64.0.0/13' , '131.0.72.0/22' ]);
2024-01-15 09:03:15 +00:00
$url = Url :: fromString ( $fqdn );
$host = $url -> getHost ();
if ( str ( $host ) -> contains ( 'sslip.io' )) {
return true ;
}
2024-10-01 08:37:40 +00:00
$settings = instanceSettings ();
2024-01-15 09:03:15 +00:00
$is_dns_validation_enabled = data_get ( $settings , 'is_dns_validation_enabled' );
2024-12-09 14:38:21 +00:00
if ( ! $is_dns_validation_enabled ) {
2024-01-15 09:03:15 +00:00
return true ;
}
2024-01-15 10:37:26 +00:00
$dns_servers = data_get ( $settings , 'custom_dns_servers' );
$dns_servers = str ( $dns_servers ) -> explode ( ',' );
2024-01-15 09:03:15 +00:00
if ( $server -> id === 0 ) {
2024-05-06 10:33:22 +00:00
$ip = data_get ( $settings , 'public_ipv4' , data_get ( $settings , 'public_ipv6' , $server -> ip ));
2024-01-15 09:03:15 +00:00
} else {
$ip = $server -> ip ;
}
2024-01-15 10:37:26 +00:00
$found_matching_ip = false ;
2024-01-15 09:03:15 +00:00
$type = \PurplePixie\PhpDns\DNSTypes :: NAME_A ;
2024-01-15 10:37:26 +00:00
foreach ( $dns_servers as $dns_server ) {
2024-01-15 09:03:15 +00:00
try {
2024-01-15 10:37:26 +00:00
$query = new DNSQuery ( $dns_server );
2024-01-15 09:03:15 +00:00
$results = $query -> query ( $host , $type );
if ( $results === false || $query -> hasError ()) {
2024-12-09 14:38:21 +00:00
ray ( 'Error: ' . $query -> getLasterror ());
2024-01-15 09:03:15 +00:00
} else {
foreach ( $results as $result ) {
if ( $result -> getType () == $type ) {
2024-01-15 10:37:26 +00:00
if ( ip_match ( $result -> getData (), $cloudflare_ips -> toArray (), $match )) {
$found_matching_ip = true ;
break ;
}
2024-01-15 09:03:15 +00:00
if ( $result -> getData () === $ip ) {
2024-01-15 10:37:26 +00:00
$found_matching_ip = true ;
2024-01-15 09:03:15 +00:00
break ;
}
}
}
}
2024-10-31 14:19:37 +00:00
} catch ( \Exception ) {
2024-01-15 09:03:15 +00:00
}
}
2024-06-10 20:43:34 +00:00
2024-01-15 10:37:26 +00:00
return $found_matching_ip ;
}
function ip_match ( $ip , $cidrs , & $match = null )
{
foreach (( array ) $cidrs as $cidr ) {
2024-06-10 20:43:34 +00:00
[ $subnet , $mask ] = explode ( '/' , $cidr );
2024-01-15 10:37:26 +00:00
if ((( ip2long ( $ip ) & ( $mask = ~ (( 1 << ( 32 - $mask )) - 1 ))) == ( ip2long ( $subnet ) & $mask ))) {
$match = $cidr ;
2024-06-10 20:43:34 +00:00
2024-01-15 10:37:26 +00:00
return true ;
}
}
2024-06-10 20:43:34 +00:00
2024-01-15 10:37:26 +00:00
return false ;
2024-01-15 09:03:15 +00:00
}
2024-07-11 08:02:35 +00:00
function checkIfDomainIsAlreadyUsed ( Collection | array $domains , ? string $teamId = null , ? string $uuid = null )
2024-07-01 09:39:10 +00:00
{
if ( is_null ( $teamId )) {
return response () -> json ([ 'error' => 'Team ID is required.' ], 400 );
}
if ( is_array ( $domains )) {
$domains = collect ( $domains );
}
$domains = $domains -> map ( function ( $domain ) {
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
return str ( $domain );
});
2024-07-11 08:02:35 +00:00
$applications = Application :: ownedByCurrentTeamAPI ( $teamId ) -> get ([ 'fqdn' , 'uuid' ]);
$serviceApplications = ServiceApplication :: ownedByCurrentTeamAPI ( $teamId ) -> get ([ 'fqdn' , 'uuid' ]);
if ( $uuid ) {
2024-12-09 14:38:21 +00:00
$applications = $applications -> filter ( fn ( $app ) => $app -> uuid !== $uuid );
$serviceApplications = $serviceApplications -> filter ( fn ( $app ) => $app -> uuid !== $uuid );
2024-07-11 08:02:35 +00:00
}
2024-07-01 09:39:10 +00:00
$domainFound = false ;
foreach ( $applications as $app ) {
if ( is_null ( $app -> fqdn )) {
continue ;
}
2024-12-09 14:38:21 +00:00
$list_of_domains = collect ( explode ( ',' , $app -> fqdn )) -> filter ( fn ( $fqdn ) => $fqdn !== '' );
2024-07-01 09:39:10 +00:00
foreach ( $list_of_domains as $domain ) {
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
$naked_domain = str ( $domain ) -> value ();
if ( $domains -> contains ( $naked_domain )) {
$domainFound = true ;
break ;
}
}
}
if ( $domainFound ) {
return true ;
}
foreach ( $serviceApplications as $app ) {
2024-07-10 09:09:29 +00:00
if ( str ( $app -> fqdn ) -> isEmpty ()) {
2024-07-01 09:39:10 +00:00
continue ;
}
2024-12-09 14:38:21 +00:00
$list_of_domains = collect ( explode ( ',' , $app -> fqdn )) -> filter ( fn ( $fqdn ) => $fqdn !== '' );
2024-07-01 09:39:10 +00:00
foreach ( $list_of_domains as $domain ) {
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
$naked_domain = str ( $domain ) -> value ();
if ( $domains -> contains ( $naked_domain )) {
$domainFound = true ;
break ;
}
}
}
if ( $domainFound ) {
return true ;
}
2024-10-01 08:37:40 +00:00
$settings = instanceSettings ();
2024-07-01 09:39:10 +00:00
if ( data_get ( $settings , 'fqdn' )) {
$domain = data_get ( $settings , 'fqdn' );
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
$naked_domain = str ( $domain ) -> value ();
if ( $domains -> contains ( $naked_domain )) {
return true ;
}
}
}
2024-04-08 10:15:44 +00:00
function check_domain_usage ( ServiceApplication | Application | null $resource = null , ? string $domain = null )
2024-01-30 08:22:34 +00:00
{
2024-04-08 10:15:44 +00:00
if ( $resource ) {
2024-10-28 13:56:13 +00:00
if ( $resource -> getMorphClass () === \App\Models\Application :: class && $resource -> build_pack === 'dockercompose' ) {
2024-06-10 20:43:34 +00:00
$domains = data_get ( json_decode ( $resource -> docker_compose_domains , true ), '*.domain' );
2024-04-08 10:15:44 +00:00
$domains = collect ( $domains );
} else {
$domains = collect ( $resource -> fqdns );
}
2024-06-10 20:43:34 +00:00
} elseif ( $domain ) {
2024-04-08 10:15:44 +00:00
$domains = collect ( $domain );
} else {
2024-06-10 20:43:34 +00:00
throw new \RuntimeException ( 'No resource or FQDN provided.' );
2024-04-08 10:15:44 +00:00
}
$domains = $domains -> map ( function ( $domain ) {
2024-02-01 09:53:05 +00:00
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
2024-06-10 20:43:34 +00:00
2024-04-08 10:15:44 +00:00
return str ( $domain );
2024-01-30 08:22:34 +00:00
});
$apps = Application :: all ();
foreach ( $apps as $app ) {
2024-12-09 14:38:21 +00:00
$list_of_domains = collect ( explode ( ',' , $app -> fqdn )) -> filter ( fn ( $fqdn ) => $fqdn !== '' );
2024-01-30 08:22:34 +00:00
foreach ( $list_of_domains as $domain ) {
2024-02-01 09:53:05 +00:00
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
2024-04-08 10:15:44 +00:00
$naked_domain = str ( $domain ) -> value ();
2024-01-30 08:22:34 +00:00
if ( $domains -> contains ( $naked_domain )) {
2024-04-08 10:15:44 +00:00
if ( data_get ( $resource , 'uuid' )) {
if ( $resource -> uuid !== $app -> uuid ) {
2024-10-10 08:24:11 +00:00
throw new \RuntimeException ( " Domain $naked_domain is already in use by another resource: <br><br>Link: <a class='underline' target='_blank' href=' { $app -> link () } '> { $app -> name } </a> " );
2024-04-08 10:15:44 +00:00
}
2024-06-10 20:43:34 +00:00
} elseif ( $domain ) {
2024-10-10 08:24:11 +00:00
throw new \RuntimeException ( " Domain $naked_domain is already in use by another resource: <br><br>Link: <a class='underline' target='_blank' href=' { $app -> link () } '> { $app -> name } </a> " );
2024-01-30 08:22:34 +00:00
}
}
}
}
$apps = ServiceApplication :: all ();
foreach ( $apps as $app ) {
2024-12-09 14:38:21 +00:00
$list_of_domains = collect ( explode ( ',' , $app -> fqdn )) -> filter ( fn ( $fqdn ) => $fqdn !== '' );
2024-01-30 08:22:34 +00:00
foreach ( $list_of_domains as $domain ) {
2024-02-01 09:53:05 +00:00
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
2024-04-08 10:15:44 +00:00
$naked_domain = str ( $domain ) -> value ();
2024-01-30 08:22:34 +00:00
if ( $domains -> contains ( $naked_domain )) {
2024-04-08 10:15:44 +00:00
if ( data_get ( $resource , 'uuid' )) {
if ( $resource -> uuid !== $app -> uuid ) {
2024-10-10 08:24:11 +00:00
throw new \RuntimeException ( " Domain $naked_domain is already in use by another resource: <br><br>Link: <a class='underline' target='_blank' href=' { $app -> service -> link () } '> { $app -> service -> name } </a> " );
2024-04-08 10:15:44 +00:00
}
2024-06-10 20:43:34 +00:00
} elseif ( $domain ) {
2024-10-10 08:24:11 +00:00
throw new \RuntimeException ( " Domain $naked_domain is already in use by another resource: <br><br>Link: <a class='underline' target='_blank' href=' { $app -> service -> link () } '> { $app -> service -> name } </a> " );
2024-01-30 08:22:34 +00:00
}
}
}
}
2024-04-08 10:15:44 +00:00
if ( $resource ) {
2024-10-01 08:37:40 +00:00
$settings = instanceSettings ();
2024-04-08 10:15:44 +00:00
if ( data_get ( $settings , 'fqdn' )) {
$domain = data_get ( $settings , 'fqdn' );
if ( str ( $domain ) -> endsWith ( '/' )) {
$domain = str ( $domain ) -> beforeLast ( '/' );
}
$naked_domain = str ( $domain ) -> value ();
if ( $domains -> contains ( $naked_domain )) {
throw new \RuntimeException ( " Domain $naked_domain is already in use by this Coolify instance. " );
}
}
}
2024-01-30 08:22:34 +00:00
}
2024-04-16 18:57:54 +00:00
2024-04-16 19:22:57 +00:00
function parseCommandsByLineForSudo ( Collection $commands , Server $server ) : array
{
2024-04-16 18:57:54 +00:00
$commands = $commands -> map ( function ( $line ) {
2024-10-02 16:26:40 +00:00
if (
2024-12-09 14:38:21 +00:00
! str ( trim ( $line )) -> startsWith ([
2024-10-02 16:26:40 +00:00
'cd' ,
'command' ,
'echo' ,
'true' ,
'if' ,
'fi' ,
])
) {
2024-04-16 18:57:54 +00:00
return " sudo $line " ;
}
2024-06-10 20:43:34 +00:00
2024-09-30 21:21:58 +00:00
if ( str ( trim ( $line )) -> startsWith ( 'if' )) {
return str_replace ( 'if' , 'if sudo' , $line );
}
2024-04-16 18:57:54 +00:00
return $line ;
});
2024-09-30 21:21:58 +00:00
2024-04-16 18:57:54 +00:00
$commands = $commands -> map ( function ( $line ) use ( $server ) {
if ( Str :: startsWith ( $line , 'sudo mkdir -p' )) {
2024-12-09 14:38:21 +00:00
return " $line && sudo chown -R $server->user : $server->user " . Str :: after ( $line , 'sudo mkdir -p' ) . ' && sudo chmod -R o-rwx ' . Str :: after ( $line , 'sudo mkdir -p' );
2024-04-16 18:57:54 +00:00
}
2024-06-10 20:43:34 +00:00
2024-04-16 18:57:54 +00:00
return $line ;
});
2024-09-30 21:21:58 +00:00
2024-04-16 18:57:54 +00:00
$commands = $commands -> map ( function ( $line ) {
2024-04-17 08:49:34 +00:00
$line = str ( $line );
if ( str ( $line ) -> contains ( '$(' )) {
$line = $line -> replace ( '$(' , '$(sudo ' );
2024-04-16 18:57:54 +00:00
}
if ( str ( $line ) -> contains ( '||' )) {
2024-04-17 08:49:34 +00:00
$line = $line -> replace ( '||' , '|| sudo' );
2024-04-16 18:57:54 +00:00
}
if ( str ( $line ) -> contains ( '&&' )) {
2024-04-17 08:49:34 +00:00
$line = $line -> replace ( '&&' , '&& sudo' );
2024-04-16 18:57:54 +00:00
}
2024-04-17 08:49:34 +00:00
if ( str ( $line ) -> contains ( ' | ' )) {
$line = $line -> replace ( ' | ' , ' | sudo ' );
}
2024-06-10 20:43:34 +00:00
2024-04-17 08:49:34 +00:00
return $line -> value ();
2024-04-16 18:57:54 +00:00
});
return $commands -> toArray ();
}
2024-04-16 19:22:57 +00:00
function parseLineForSudo ( string $command , Server $server ) : string
{
2024-12-09 14:38:21 +00:00
if ( ! str ( $command ) -> startSwith ( 'cd' ) && ! str ( $command ) -> startSwith ( 'command' )) {
2024-04-16 19:22:57 +00:00
$command = " sudo $command " ;
}
if ( Str :: startsWith ( $command , 'sudo mkdir -p' )) {
2024-12-09 14:38:21 +00:00
$command = " $command && sudo chown -R $server->user : $server->user " . Str :: after ( $command , 'sudo mkdir -p' ) . ' && sudo chmod -R o-rwx ' . Str :: after ( $command , 'sudo mkdir -p' );
2024-04-16 19:22:57 +00:00
}
if ( str ( $command ) -> contains ( '$(' ) || str ( $command ) -> contains ( '`' )) {
$command = str ( $command ) -> replace ( '$(' , '$(sudo ' ) -> replace ( '`' , '`sudo ' ) -> value ();
}
if ( str ( $command ) -> contains ( '||' )) {
$command = str ( $command ) -> replace ( '||' , '|| sudo ' ) -> value ();
}
if ( str ( $command ) -> contains ( '&&' )) {
$command = str ( $command ) -> replace ( '&&' , '&& sudo ' ) -> value ();
}
return $command ;
}
2024-04-25 11:52:52 +00:00
function get_public_ips ()
{
try {
[ $first , $second ] = Process :: concurrently ( function ( Pool $pool ) {
$pool -> path ( __DIR__ ) -> command ( 'curl -4s https://ifconfig.io' );
$pool -> path ( __DIR__ ) -> command ( 'curl -6s https://ifconfig.io' );
});
$ipv4 = $first -> output ();
if ( $ipv4 ) {
$ipv4 = trim ( $ipv4 );
$validate_ipv4 = filter_var ( $ipv4 , FILTER_VALIDATE_IP );
if ( $validate_ipv4 == false ) {
echo " Invalid ipv4: $ipv4\n " ;
2024-06-10 20:43:34 +00:00
2024-04-25 11:52:52 +00:00
return ;
}
2024-09-29 18:12:30 +00:00
InstanceSettings :: get () -> update ([ 'public_ipv4' => $ipv4 ]);
2024-04-25 11:52:52 +00:00
}
2024-09-23 18:31:50 +00:00
} catch ( \Exception $e ) {
echo " Error: { $e -> getMessage () } \n " ;
}
try {
2024-04-25 11:52:52 +00:00
$ipv6 = $second -> output ();
if ( $ipv6 ) {
$ipv6 = trim ( $ipv6 );
$validate_ipv6 = filter_var ( $ipv6 , FILTER_VALIDATE_IP );
if ( $validate_ipv6 == false ) {
echo " Invalid ipv6: $ipv6\n " ;
2024-06-10 20:43:34 +00:00
2024-04-25 11:52:52 +00:00
return ;
}
2024-09-29 18:12:30 +00:00
InstanceSettings :: get () -> update ([ 'public_ipv6' => $ipv6 ]);
2024-04-25 11:52:52 +00:00
}
} catch ( \Throwable $e ) {
echo " Error: { $e -> getMessage () } \n " ;
}
}
2024-06-11 10:37:39 +00:00
2024-06-11 10:38:24 +00:00
function isAnyDeploymentInprogress ()
{
2025-01-10 17:34:16 +00:00
$runningJobs = ApplicationDeploymentQueue :: where ( 'horizon_job_worker' , gethostname ()) -> where ( 'status' , ApplicationDeploymentStatus :: IN_PROGRESS -> value ) -> get ();
2025-03-28 14:30:21 +00:00
$basicDetails = $runningJobs -> map ( function ( $job ) {
return [
'id' => $job -> id ,
'created_at' => $job -> created_at ,
'application_id' => $job -> application_id ,
'server_id' => $job -> server_id ,
'horizon_job_id' => $job -> horizon_job_id ,
'status' => $job -> status ,
];
});
echo 'Running jobs: ' . json_encode ( $basicDetails ) . " \n " ;
2025-01-10 17:34:16 +00:00
$horizonJobIds = [];
foreach ( $runningJobs as $runningJob ) {
2025-01-10 18:53:13 +00:00
$horizonJobStatus = getJobStatus ( $runningJob -> horizon_job_id );
2025-03-28 14:42:25 +00:00
if ( $horizonJobStatus === 'unknown' || $horizonJobStatus === 'reserved' ) {
2025-03-28 14:09:38 +00:00
$horizonJobIds [] = $runningJob -> horizon_job_id ;
2025-01-10 17:34:16 +00:00
}
}
if ( count ( $horizonJobIds ) === 0 ) {
echo " No deployments in progress. \n " ;
exit ( 0 );
}
$horizonJobIds = collect ( $horizonJobIds ) -> unique () -> toArray ();
echo 'There are ' . count ( $horizonJobIds ) . " deployments in progress. \n " ;
exit ( 1 );
2024-06-11 10:37:39 +00:00
}
2024-06-18 14:42:42 +00:00
2024-06-25 19:22:23 +00:00
function isBase64Encoded ( $strValue )
{
return base64_encode ( base64_decode ( $strValue , true )) === $strValue ;
}
2024-06-26 11:32:36 +00:00
function customApiValidator ( Collection | array $item , array $rules )
{
if ( is_array ( $item )) {
$item = collect ( $item );
}
return Validator :: make ( $item -> toArray (), $rules , [
'required' => 'This field is required.' ,
]);
}
2024-08-12 14:06:24 +00:00
function parseServiceVolumes ( $serviceVolumes , $resource , $topLevelVolumes , $pull_request_id = 0 )
{
2024-08-13 10:43:17 +00:00
$serviceVolumes = $serviceVolumes -> map ( function ( $volume ) use ( $resource , $topLevelVolumes , $pull_request_id ) {
2024-08-12 14:06:24 +00:00
$type = null ;
$source = null ;
$target = null ;
$content = null ;
$isDirectory = false ;
if ( is_string ( $volume )) {
$source = str ( $volume ) -> before ( ':' );
$target = str ( $volume ) -> after ( ':' ) -> beforeLast ( ':' );
2024-08-21 12:31:17 +00:00
$foundConfig = $resource -> fileStorages () -> whereMountPath ( $target ) -> first ();
2024-08-12 14:06:24 +00:00
if ( $source -> startsWith ( './' ) || $source -> startsWith ( '/' ) || $source -> startsWith ( '~' )) {
$type = str ( 'bind' );
2024-08-21 12:31:17 +00:00
if ( $foundConfig ) {
$contentNotNull = data_get ( $foundConfig , 'content' );
if ( $contentNotNull ) {
$content = $contentNotNull ;
}
$isDirectory = data_get ( $foundConfig , 'is_directory' );
} else {
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
$isDirectory = true ;
}
2024-08-12 14:06:24 +00:00
} else {
$type = str ( 'volume' );
}
} elseif ( is_array ( $volume )) {
$type = data_get_str ( $volume , 'type' );
$source = data_get_str ( $volume , 'source' );
$target = data_get_str ( $volume , 'target' );
$content = data_get ( $volume , 'content' );
$isDirectory = ( bool ) data_get ( $volume , 'isDirectory' , null ) || ( bool ) data_get ( $volume , 'is_directory' , null );
$foundConfig = $resource -> fileStorages () -> whereMountPath ( $target ) -> first ();
if ( $foundConfig ) {
$contentNotNull = data_get ( $foundConfig , 'content' );
if ( $contentNotNull ) {
$content = $contentNotNull ;
}
2024-08-21 12:31:17 +00:00
$isDirectory = data_get ( $foundConfig , 'is_directory' );
} else {
2024-08-12 14:06:24 +00:00
$isDirectory = ( bool ) data_get ( $volume , 'isDirectory' , null ) || ( bool ) data_get ( $volume , 'is_directory' , null );
2024-12-09 14:38:21 +00:00
if (( is_null ( $isDirectory ) || ! $isDirectory ) && is_null ( $content )) {
2024-08-21 12:31:17 +00:00
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
$isDirectory = true ;
}
2024-08-12 14:06:24 +00:00
}
}
if ( $type ? -> value () === 'bind' ) {
if ( $source -> value () === '/var/run/docker.sock' ) {
return $volume ;
}
if ( $source -> value () === '/tmp' || $source -> value () === '/tmp/' ) {
return $volume ;
}
2024-10-28 13:56:13 +00:00
if ( get_class ( $resource ) === \App\Models\Application :: class ) {
2024-12-09 14:38:21 +00:00
$dir = base_configuration_dir () . '/applications/' . $resource -> uuid ;
2024-08-12 14:06:24 +00:00
} else {
2024-12-09 14:38:21 +00:00
$dir = base_configuration_dir () . '/services/' . $resource -> service -> uuid ;
2024-08-12 14:06:24 +00:00
}
if ( $source -> startsWith ( '.' )) {
$source = $source -> replaceFirst ( '.' , $dir );
}
if ( $source -> startsWith ( '~' )) {
$source = $source -> replaceFirst ( '~' , $dir );
}
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$source = $source . " -pr- $pull_request_id " ;
2024-08-12 14:06:24 +00:00
}
2024-12-09 14:38:21 +00:00
if ( ! $resource ? -> settings ? -> is_preserve_repository_enabled || $foundConfig ? -> is_based_on_git ) {
2025-03-29 21:16:12 +00:00
LocalFileVolume :: updateOrCreate (
[
'mount_path' => $target ,
'resource_id' => $resource -> id ,
'resource_type' => get_class ( $resource ),
],
[
'fs_path' => $source ,
'mount_path' => $target ,
'content' => $content ,
'is_directory' => $isDirectory ,
'resource_id' => $resource -> id ,
'resource_type' => get_class ( $resource ),
]
);
2024-08-21 12:31:17 +00:00
}
2024-08-12 14:06:24 +00:00
} elseif ( $type -> value () === 'volume' ) {
if ( $topLevelVolumes -> has ( $source -> value ())) {
$v = $topLevelVolumes -> get ( $source -> value ());
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
return $volume ;
}
}
$slugWithoutUuid = Str :: slug ( $source , '-' );
2024-10-28 13:56:13 +00:00
if ( get_class ( $resource ) === \App\Models\Application :: class ) {
2024-08-12 14:06:24 +00:00
$name = " { $resource -> uuid } _ { $slugWithoutUuid } " ;
} else {
$name = " { $resource -> service -> uuid } _ { $slugWithoutUuid } " ;
}
if ( is_string ( $volume )) {
$source = str ( $volume ) -> before ( ':' );
$target = str ( $volume ) -> after ( ':' ) -> beforeLast ( ':' );
$source = $name ;
$volume = " $source : $target " ;
} elseif ( is_array ( $volume )) {
data_set ( $volume , 'source' , $name );
}
$topLevelVolumes -> put ( $name , [
'name' => $name ,
]);
LocalPersistentVolume :: updateOrCreate (
[
'mount_path' => $target ,
'resource_id' => $resource -> id ,
'resource_type' => get_class ( $resource ),
],
[
'name' => $name ,
'mount_path' => $target ,
'resource_id' => $resource -> id ,
'resource_type' => get_class ( $resource ),
]
);
}
dispatch ( new ServerFilesFromServerJob ( $resource ));
2024-08-13 10:43:17 +00:00
return $volume ;
2024-08-12 14:06:24 +00:00
});
2024-08-13 10:43:17 +00:00
return [
'serviceVolumes' => $serviceVolumes ,
'topLevelVolumes' => $topLevelVolumes ,
];
2024-08-12 14:06:24 +00:00
}
2024-08-23 18:54:38 +00:00
function parseDockerComposeFile ( Service | Application $resource , bool $isNew = false , int $pull_request_id = 0 , ? int $preview_id = null )
{
2024-10-28 13:56:13 +00:00
if ( $resource -> getMorphClass () === \App\Models\Service :: class ) {
2024-08-23 18:54:38 +00:00
if ( $resource -> docker_compose_raw ) {
try {
$yaml = Yaml :: parse ( $resource -> docker_compose_raw );
} catch ( \Exception $e ) {
2025-05-29 08:49:55 +00:00
throw new \RuntimeException ( $e -> getMessage ());
2024-08-23 18:54:38 +00:00
}
$allServices = get_service_templates ();
$topLevelVolumes = collect ( data_get ( $yaml , 'volumes' , []));
$topLevelNetworks = collect ( data_get ( $yaml , 'networks' , []));
$topLevelConfigs = collect ( data_get ( $yaml , 'configs' , []));
$topLevelSecrets = collect ( data_get ( $yaml , 'secrets' , []));
$services = data_get ( $yaml , 'services' );
$generatedServiceFQDNS = collect ([]);
if ( is_null ( $resource -> destination )) {
$destination = $resource -> server -> destinations () -> first ();
if ( $destination ) {
$resource -> destination () -> associate ( $destination );
$resource -> save ();
}
}
$definedNetwork = collect ([ $resource -> uuid ]);
if ( $topLevelVolumes -> count () > 0 ) {
$tempTopLevelVolumes = collect ([]);
foreach ( $topLevelVolumes as $volumeName => $volume ) {
if ( is_null ( $volume )) {
continue ;
}
$tempTopLevelVolumes -> put ( $volumeName , $volume );
}
$topLevelVolumes = collect ( $tempTopLevelVolumes );
}
$services = collect ( $services ) -> map ( function ( $service , $serviceName ) use ( $topLevelVolumes , $topLevelNetworks , $definedNetwork , $isNew , $generatedServiceFQDNS , $resource , $allServices ) {
// Workarounds for beta users.
if ( $serviceName === 'registry' ) {
$tempServiceName = 'docker-registry' ;
} else {
$tempServiceName = $serviceName ;
}
if ( str ( data_get ( $service , 'image' )) -> contains ( 'glitchtip' )) {
$tempServiceName = 'glitchtip' ;
}
if ( $serviceName === 'supabase-kong' ) {
$tempServiceName = 'supabase' ;
}
$serviceDefinition = data_get ( $allServices , $tempServiceName );
$predefinedPort = data_get ( $serviceDefinition , 'port' );
if ( $serviceName === 'plausible' ) {
$predefinedPort = '8000' ;
}
// End of workarounds for beta users.
$serviceVolumes = collect ( data_get ( $service , 'volumes' , []));
$servicePorts = collect ( data_get ( $service , 'ports' , []));
$serviceNetworks = collect ( data_get ( $service , 'networks' , []));
$serviceVariables = collect ( data_get ( $service , 'environment' , []));
$serviceLabels = collect ( data_get ( $service , 'labels' , []));
$hasHostNetworkMode = data_get ( $service , 'network_mode' ) === 'host' ? true : false ;
if ( $serviceLabels -> count () > 0 ) {
$removedLabels = collect ([]);
$serviceLabels = $serviceLabels -> filter ( function ( $serviceLabel , $serviceLabelName ) use ( $removedLabels ) {
2024-12-09 14:38:21 +00:00
if ( ! str ( $serviceLabel ) -> contains ( '=' )) {
2024-08-23 18:54:38 +00:00
$removedLabels -> put ( $serviceLabelName , $serviceLabel );
return false ;
}
return $serviceLabel ;
});
foreach ( $removedLabels as $removedLabelName => $removedLabel ) {
$serviceLabels -> push ( " $removedLabelName = $removedLabel " );
}
}
$containerName = " $serviceName - { $resource -> uuid } " ;
// Decide if the service is a database
$image = data_get_str ( $service , 'image' );
2025-06-04 09:44:37 +00:00
$isDatabase = isDatabaseImage ( $image , $service );
2024-08-23 18:54:38 +00:00
data_set ( $service , 'is_database' , $isDatabase );
// Create new serviceApplication or serviceDatabase
if ( $isDatabase ) {
if ( $isNew ) {
$savedService = ServiceDatabase :: create ([
'name' => $serviceName ,
'image' => $image ,
'service_id' => $resource -> id ,
]);
} else {
$savedService = ServiceDatabase :: where ([
'name' => $serviceName ,
'service_id' => $resource -> id ,
]) -> first ();
}
} else {
if ( $isNew ) {
$savedService = ServiceApplication :: create ([
'name' => $serviceName ,
'image' => $image ,
'service_id' => $resource -> id ,
]);
} else {
$savedService = ServiceApplication :: where ([
'name' => $serviceName ,
'service_id' => $resource -> id ,
]) -> first ();
}
}
if ( is_null ( $savedService )) {
if ( $isDatabase ) {
$savedService = ServiceDatabase :: create ([
'name' => $serviceName ,
'image' => $image ,
'service_id' => $resource -> id ,
]);
} else {
$savedService = ServiceApplication :: create ([
'name' => $serviceName ,
'image' => $image ,
'service_id' => $resource -> id ,
]);
}
}
// Check if image changed
if ( $savedService -> image !== $image ) {
$savedService -> image = $image ;
$savedService -> save ();
}
// Collect/create/update networks
if ( $serviceNetworks -> count () > 0 ) {
foreach ( $serviceNetworks as $networkName => $networkDetails ) {
if ( $networkName === 'default' ) {
continue ;
}
// ignore alias
if ( $networkDetails [ 'aliases' ] ? ? false ) {
continue ;
}
$networkExists = $topLevelNetworks -> contains ( function ( $value , $key ) use ( $networkName ) {
return $value == $networkName || $key == $networkName ;
});
2024-12-09 14:38:21 +00:00
if ( ! $networkExists ) {
2024-10-03 13:04:40 +00:00
if ( is_string ( $networkDetails ) || is_int ( $networkDetails )) {
$topLevelNetworks -> put ( $networkDetails , null );
}
2024-08-23 18:54:38 +00:00
}
}
}
// Collect/create/update ports
$collectedPorts = collect ([]);
if ( $servicePorts -> count () > 0 ) {
foreach ( $servicePorts as $sport ) {
if ( is_string ( $sport ) || is_numeric ( $sport )) {
$collectedPorts -> push ( $sport );
}
if ( is_array ( $sport )) {
$target = data_get ( $sport , 'target' );
$published = data_get ( $sport , 'published' );
$protocol = data_get ( $sport , 'protocol' );
$collectedPorts -> push ( " $target : $published / $protocol " );
}
}
}
$savedService -> ports = $collectedPorts -> implode ( ',' );
$savedService -> save ();
2024-12-09 14:38:21 +00:00
if ( ! $hasHostNetworkMode ) {
2024-08-23 18:54:38 +00:00
// Add Coolify specific networks
$definedNetworkExists = $topLevelNetworks -> contains ( function ( $value , $_ ) use ( $definedNetwork ) {
return $value == $definedNetwork ;
});
2024-12-09 14:38:21 +00:00
if ( ! $definedNetworkExists ) {
2024-08-23 18:54:38 +00:00
foreach ( $definedNetwork as $network ) {
$topLevelNetworks -> put ( $network , [
'name' => $network ,
'external' => true ,
]);
}
}
$networks = collect ();
foreach ( $serviceNetworks as $key => $serviceNetwork ) {
if ( gettype ( $serviceNetwork ) === 'string' ) {
// networks:
// - appwrite
$networks -> put ( $serviceNetwork , null );
} elseif ( gettype ( $serviceNetwork ) === 'array' ) {
// networks:
// default:
// ipv4_address: 192.168.203.254
// $networks->put($serviceNetwork, null);
$networks -> put ( $key , $serviceNetwork );
}
}
foreach ( $definedNetwork as $key => $network ) {
$networks -> put ( $network , null );
}
data_set ( $service , 'networks' , $networks -> toArray ());
}
// Collect/create/update volumes
if ( $serviceVolumes -> count () > 0 ) {
$serviceVolumes = $serviceVolumes -> map ( function ( $volume ) use ( $savedService , $topLevelVolumes ) {
$type = null ;
$source = null ;
$target = null ;
$content = null ;
$isDirectory = false ;
if ( is_string ( $volume )) {
$source = str ( $volume ) -> before ( ':' );
$target = str ( $volume ) -> after ( ':' ) -> beforeLast ( ':' );
if ( $source -> startsWith ( './' ) || $source -> startsWith ( '/' ) || $source -> startsWith ( '~' )) {
$type = str ( 'bind' );
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
$isDirectory = true ;
} else {
$type = str ( 'volume' );
}
} elseif ( is_array ( $volume )) {
$type = data_get_str ( $volume , 'type' );
$source = data_get_str ( $volume , 'source' );
$target = data_get_str ( $volume , 'target' );
$content = data_get ( $volume , 'content' );
$isDirectory = ( bool ) data_get ( $volume , 'isDirectory' , null ) || ( bool ) data_get ( $volume , 'is_directory' , null );
$foundConfig = $savedService -> fileStorages () -> whereMountPath ( $target ) -> first ();
if ( $foundConfig ) {
$contentNotNull = data_get ( $foundConfig , 'content' );
if ( $contentNotNull ) {
$content = $contentNotNull ;
}
$isDirectory = ( bool ) data_get ( $volume , 'isDirectory' , null ) || ( bool ) data_get ( $volume , 'is_directory' , null );
}
if ( is_null ( $isDirectory ) && is_null ( $content )) {
// if isDirectory is not set & content is also not set, we assume it is a directory
$isDirectory = true ;
}
}
if ( $type ? -> value () === 'bind' ) {
if ( $source -> value () === '/var/run/docker.sock' ) {
return $volume ;
}
if ( $source -> value () === '/tmp' || $source -> value () === '/tmp/' ) {
return $volume ;
}
2025-03-28 21:10:33 +00:00
2025-03-29 21:16:12 +00:00
LocalFileVolume :: updateOrCreate (
[
2024-08-23 18:54:38 +00:00
'mount_path' => $target ,
'resource_id' => $savedService -> id ,
'resource_type' => get_class ( $savedService ),
2025-03-29 21:16:12 +00:00
],
[
2024-08-23 18:54:38 +00:00
'fs_path' => $source ,
'mount_path' => $target ,
'content' => $content ,
'is_directory' => $isDirectory ,
'resource_id' => $savedService -> id ,
'resource_type' => get_class ( $savedService ),
2025-03-29 21:16:12 +00:00
]
);
2024-08-23 18:54:38 +00:00
} elseif ( $type -> value () === 'volume' ) {
if ( $topLevelVolumes -> has ( $source -> value ())) {
$v = $topLevelVolumes -> get ( $source -> value ());
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
return $volume ;
}
}
$slugWithoutUuid = Str :: slug ( $source , '-' );
$name = " { $savedService -> service -> uuid } _ { $slugWithoutUuid } " ;
if ( is_string ( $volume )) {
$source = str ( $volume ) -> before ( ':' );
$target = str ( $volume ) -> after ( ':' ) -> beforeLast ( ':' );
$source = $name ;
$volume = " $source : $target " ;
} elseif ( is_array ( $volume )) {
data_set ( $volume , 'source' , $name );
}
$topLevelVolumes -> put ( $name , [
'name' => $name ,
]);
LocalPersistentVolume :: updateOrCreate (
[
'mount_path' => $target ,
'resource_id' => $savedService -> id ,
'resource_type' => get_class ( $savedService ),
],
[
'name' => $name ,
'mount_path' => $target ,
'resource_id' => $savedService -> id ,
'resource_type' => get_class ( $savedService ),
]
);
}
dispatch ( new ServerFilesFromServerJob ( $savedService ));
return $volume ;
});
data_set ( $service , 'volumes' , $serviceVolumes -> toArray ());
}
2024-08-24 09:00:27 +00:00
// convert - SESSION_SECRET: 123 to - SESSION_SECRET=123
$convertedServiceVariables = collect ([]);
foreach ( $serviceVariables as $variableName => $variable ) {
if ( is_numeric ( $variableName )) {
if ( is_array ( $variable )) {
$key = str ( collect ( $variable ) -> keys () -> first ());
$value = str ( collect ( $variable ) -> values () -> first ());
$variable = " $key = $value " ;
$convertedServiceVariables -> put ( $variableName , $variable );
} elseif ( is_string ( $variable )) {
$convertedServiceVariables -> put ( $variableName , $variable );
}
2024-08-27 14:02:52 +00:00
} elseif ( is_string ( $variableName )) {
$convertedServiceVariables -> put ( $variableName , $variable );
2024-08-24 09:00:27 +00:00
}
}
$serviceVariables = $convertedServiceVariables ;
2024-08-23 18:54:38 +00:00
// Get variables from the service
foreach ( $serviceVariables as $variableName => $variable ) {
if ( is_numeric ( $variableName )) {
if ( is_array ( $variable )) {
// - SESSION_SECRET: 123
// - SESSION_SECRET:
$key = str ( collect ( $variable ) -> keys () -> first ());
$value = str ( collect ( $variable ) -> values () -> first ());
} else {
$variable = str ( $variable );
if ( $variable -> contains ( '=' )) {
// - SESSION_SECRET=123
// - SESSION_SECRET=
$key = $variable -> before ( '=' );
$value = $variable -> after ( '=' );
} else {
// - SESSION_SECRET
$key = $variable ;
$value = null ;
}
}
} else {
// SESSION_SECRET: 123
// SESSION_SECRET:
$key = str ( $variableName );
$value = str ( $variable );
}
if ( $key -> startsWith ( 'SERVICE_FQDN' )) {
if ( $isNew || $savedService -> fqdn === null ) {
$name = $key -> after ( 'SERVICE_FQDN_' ) -> beforeLast ( '_' ) -> lower ();
$fqdn = generateFqdn ( $resource -> server , " { $name -> value () } - { $resource -> uuid } " );
if ( substr_count ( $key -> value (), '_' ) === 3 ) {
// SERVICE_FQDN_UMAMI_1000
$port = $key -> afterLast ( '_' );
} else {
$last = $key -> afterLast ( '_' );
if ( is_numeric ( $last -> value ())) {
// SERVICE_FQDN_3001
$port = $last ;
} else {
// SERVICE_FQDN_UMAMI
$port = null ;
}
}
if ( $port ) {
$fqdn = " $fqdn : $port " ;
}
if ( substr_count ( $key -> value (), '_' ) >= 2 ) {
if ( $value ) {
$path = $value -> value ();
} else {
$path = null ;
}
if ( $generatedServiceFQDNS -> count () > 0 ) {
$alreadyGenerated = $generatedServiceFQDNS -> has ( $key -> value ());
if ( $alreadyGenerated ) {
$fqdn = $generatedServiceFQDNS -> get ( $key -> value ());
} else {
$generatedServiceFQDNS -> put ( $key -> value (), $fqdn );
}
} else {
$generatedServiceFQDNS -> put ( $key -> value (), $fqdn );
}
$fqdn = " $fqdn $path " ;
}
2024-12-09 14:38:21 +00:00
if ( ! $isDatabase ) {
2024-08-23 18:54:38 +00:00
if ( $savedService -> fqdn ) {
2024-12-09 14:38:21 +00:00
data_set ( $savedService , 'fqdn' , $savedService -> fqdn . ',' . $fqdn );
2024-08-23 18:54:38 +00:00
} else {
data_set ( $savedService , 'fqdn' , $fqdn );
}
$savedService -> save ();
}
EnvironmentVariable :: create ([
'key' => $key ,
'value' => $fqdn ,
'is_build_time' => false ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
// Caddy needs exact port in some cases.
2024-12-09 14:38:21 +00:00
if ( $predefinedPort && ! $key -> endsWith ( " _ { $predefinedPort } " )) {
2024-08-23 18:54:38 +00:00
$fqdns_exploded = str ( $savedService -> fqdn ) -> explode ( ',' );
if ( $fqdns_exploded -> count () > 1 ) {
continue ;
}
$env = EnvironmentVariable :: where ([
'key' => $key ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
]) -> first ();
if ( $env ) {
$env_url = Url :: fromString ( $savedService -> fqdn );
$env_port = $env_url -> getPort ();
if ( $env_port !== $predefinedPort ) {
$env_url = $env_url -> withPort ( $predefinedPort );
$savedService -> fqdn = $env_url -> __toString ();
$savedService -> save ();
}
}
}
// data_forget($service, "environment.$variableName");
// $yaml = data_forget($yaml, "services.$serviceName.environment.$variableName");
// if (count(data_get($yaml, 'services.' . $serviceName . '.environment')) === 0) {
// $yaml = data_forget($yaml, "services.$serviceName.environment");
// }
continue ;
}
if ( $value ? -> startsWith ( '$' )) {
$foundEnv = EnvironmentVariable :: where ([
'key' => $key ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
]) -> first ();
2024-09-09 13:04:51 +00:00
$value = replaceVariables ( $value );
2024-08-23 18:54:38 +00:00
$key = $value ;
if ( $value -> startsWith ( 'SERVICE_' )) {
$foundEnv = EnvironmentVariable :: where ([
'key' => $key ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
]) -> first ();
[ 'command' => $command , 'forService' => $forService , 'generatedValue' => $generatedValue , 'port' => $port ] = parseEnvVariable ( $value );
2024-12-09 14:38:21 +00:00
if ( ! is_null ( $command )) {
2024-08-23 18:54:38 +00:00
if ( $command ? -> value () === 'FQDN' || $command ? -> value () === 'URL' ) {
if ( Str :: lower ( $forService ) === $serviceName ) {
$fqdn = generateFqdn ( $resource -> server , $containerName );
} else {
2024-12-09 14:38:21 +00:00
$fqdn = generateFqdn ( $resource -> server , Str :: lower ( $forService ) . '-' . $resource -> uuid );
2024-08-23 18:54:38 +00:00
}
if ( $port ) {
$fqdn = " $fqdn : $port " ;
}
if ( $foundEnv ) {
$fqdn = data_get ( $foundEnv , 'value' );
// if ($savedService->fqdn) {
// $savedServiceFqdn = Url::fromString($savedService->fqdn);
// $parsedFqdn = Url::fromString($fqdn);
// $savedServicePath = $savedServiceFqdn->getPath();
// $parsedFqdnPath = $parsedFqdn->getPath();
// if ($savedServicePath != $parsedFqdnPath) {
// $fqdn = $parsedFqdn->withPath($savedServicePath)->__toString();
// $foundEnv->value = $fqdn;
// $foundEnv->save();
// }
// }
} else {
if ( $command -> value () === 'URL' ) {
$fqdn = str ( $fqdn ) -> after ( '://' ) -> value ();
}
EnvironmentVariable :: create ([
'key' => $key ,
'value' => $fqdn ,
'is_build_time' => false ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
2024-12-09 14:38:21 +00:00
if ( ! $isDatabase ) {
if ( $command -> value () === 'FQDN' && is_null ( $savedService -> fqdn ) && ! $foundEnv ) {
2024-08-23 18:54:38 +00:00
$savedService -> fqdn = $fqdn ;
$savedService -> save ();
}
// Caddy needs exact port in some cases.
2024-12-09 14:38:21 +00:00
if ( $predefinedPort && ! $key -> endsWith ( " _ { $predefinedPort } " ) && $command ? -> value () === 'FQDN' && $resource -> server -> proxyType () === 'CADDY' ) {
2024-08-23 18:54:38 +00:00
$fqdns_exploded = str ( $savedService -> fqdn ) -> explode ( ',' );
if ( $fqdns_exploded -> count () > 1 ) {
continue ;
}
$env = EnvironmentVariable :: where ([
'key' => $key ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
]) -> first ();
if ( $env ) {
$env_url = Url :: fromString ( $env -> value );
$env_port = $env_url -> getPort ();
if ( $env_port !== $predefinedPort ) {
$env_url = $env_url -> withPort ( $predefinedPort );
$savedService -> fqdn = $env_url -> __toString ();
$savedService -> save ();
}
}
}
}
} else {
$generatedValue = generateEnvValue ( $command , $resource );
2024-12-09 14:38:21 +00:00
if ( ! $foundEnv ) {
2024-08-23 18:54:38 +00:00
EnvironmentVariable :: create ([
'key' => $key ,
'value' => $generatedValue ,
'is_build_time' => false ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
}
}
} else {
if ( $value -> contains ( ':-' )) {
$key = $value -> before ( ':' );
$defaultValue = $value -> after ( ':-' );
} elseif ( $value -> contains ( '-' )) {
$key = $value -> before ( '-' );
$defaultValue = $value -> after ( '-' );
} elseif ( $value -> contains ( ':?' )) {
$key = $value -> before ( ':' );
$defaultValue = $value -> after ( ':?' );
} elseif ( $value -> contains ( '?' )) {
$key = $value -> before ( '?' );
$defaultValue = $value -> after ( '?' );
} else {
$key = $value ;
$defaultValue = null ;
}
$foundEnv = EnvironmentVariable :: where ([
'key' => $key ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
]) -> first ();
if ( $foundEnv ) {
$defaultValue = data_get ( $foundEnv , 'value' );
}
EnvironmentVariable :: updateOrCreate ([
'key' => $key ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
], [
'value' => $defaultValue ,
'is_build_time' => false ,
2024-12-17 09:38:32 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
}
}
// Add labels to the service
if ( $savedService -> serviceType ()) {
$fqdns = generateServiceSpecificFqdns ( $savedService );
} else {
$fqdns = collect ( data_get ( $savedService , 'fqdns' )) -> filter ();
}
2024-11-17 21:49:44 +00:00
$defaultLabels = defaultLabels (
id : $resource -> id ,
name : $containerName ,
projectName : $resource -> project () -> name ,
resourceName : $resource -> name ,
type : 'service' ,
2025-01-20 12:58:52 +00:00
subType : $isDatabase ? 'database' : 'application' ,
2024-11-17 21:49:44 +00:00
subId : $savedService -> id ,
subName : $savedService -> name ,
2024-12-02 17:38:21 +00:00
environment : $resource -> environment -> name ,
2024-11-17 21:49:44 +00:00
);
2024-08-23 18:54:38 +00:00
$serviceLabels = $serviceLabels -> merge ( $defaultLabels );
2024-12-09 14:38:21 +00:00
if ( ! $isDatabase && $fqdns -> count () > 0 ) {
2024-08-23 18:54:38 +00:00
if ( $fqdns ) {
$shouldGenerateLabelsExactly = $resource -> server -> settings -> generate_exact_labels ;
if ( $shouldGenerateLabelsExactly ) {
switch ( $resource -> server -> proxyType ()) {
case ProxyTypes :: TRAEFIK -> value :
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge ( fqdnLabelsForTraefik (
2024-08-23 18:54:38 +00:00
uuid : $resource -> uuid ,
domains : $fqdns ,
is_force_https_enabled : true ,
serviceLabels : $serviceLabels ,
is_gzip_enabled : $savedService -> isGzipEnabled (),
is_stripprefix_enabled : $savedService -> isStripprefixEnabled (),
service_name : $serviceName ,
image : data_get ( $service , 'image' )
2025-02-28 19:25:19 +00:00
));
2024-08-23 18:54:38 +00:00
break ;
case ProxyTypes :: CADDY -> value :
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge ( fqdnLabelsForCaddy (
2024-08-23 18:54:38 +00:00
network : $resource -> destination -> network ,
uuid : $resource -> uuid ,
domains : $fqdns ,
is_force_https_enabled : true ,
serviceLabels : $serviceLabels ,
is_gzip_enabled : $savedService -> isGzipEnabled (),
is_stripprefix_enabled : $savedService -> isStripprefixEnabled (),
service_name : $serviceName ,
image : data_get ( $service , 'image' )
2025-02-28 19:25:19 +00:00
));
2024-08-23 18:54:38 +00:00
break ;
}
} else {
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge ( fqdnLabelsForTraefik (
2024-08-23 18:54:38 +00:00
uuid : $resource -> uuid ,
domains : $fqdns ,
is_force_https_enabled : true ,
serviceLabels : $serviceLabels ,
is_gzip_enabled : $savedService -> isGzipEnabled (),
is_stripprefix_enabled : $savedService -> isStripprefixEnabled (),
service_name : $serviceName ,
image : data_get ( $service , 'image' )
2025-02-28 19:25:19 +00:00
));
$serviceLabels = $serviceLabels -> merge ( fqdnLabelsForCaddy (
2024-08-23 18:54:38 +00:00
network : $resource -> destination -> network ,
uuid : $resource -> uuid ,
domains : $fqdns ,
is_force_https_enabled : true ,
serviceLabels : $serviceLabels ,
is_gzip_enabled : $savedService -> isGzipEnabled (),
is_stripprefix_enabled : $savedService -> isStripprefixEnabled (),
service_name : $serviceName ,
image : data_get ( $service , 'image' )
2025-02-28 19:25:19 +00:00
));
2024-08-23 18:54:38 +00:00
}
}
}
if ( $resource -> server -> isLogDrainEnabled () && $savedService -> isLogDrainEnabled ()) {
2024-08-28 09:11:14 +00:00
data_set ( $service , 'logging' , generate_fluentd_configuration ());
2024-08-23 18:54:38 +00:00
}
if ( $serviceLabels -> count () > 0 ) {
if ( $resource -> is_container_label_escape_enabled ) {
$serviceLabels = $serviceLabels -> map ( function ( $value , $key ) {
return escapeDollarSign ( $value );
});
}
}
data_set ( $service , 'labels' , $serviceLabels -> toArray ());
data_forget ( $service , 'is_database' );
2024-12-09 14:38:21 +00:00
if ( ! data_get ( $service , 'restart' )) {
2024-08-23 18:54:38 +00:00
data_set ( $service , 'restart' , RESTART_MODE );
}
if ( data_get ( $service , 'restart' ) === 'no' || data_get ( $service , 'exclude_from_hc' )) {
$savedService -> update ([ 'exclude_from_status' => true ]);
}
data_set ( $service , 'container_name' , $containerName );
data_forget ( $service , 'volumes.*.content' );
data_forget ( $service , 'volumes.*.isDirectory' );
data_forget ( $service , 'volumes.*.is_directory' );
data_forget ( $service , 'exclude_from_hc' );
data_set ( $service , 'environment' , $serviceVariables -> toArray ());
updateCompose ( $savedService );
return $service ;
});
$envs_from_coolify = $resource -> environment_variables () -> get ();
$services = collect ( $services ) -> map ( function ( $service , $serviceName ) use ( $resource , $envs_from_coolify ) {
$serviceVariables = collect ( data_get ( $service , 'environment' , []));
$parsedServiceVariables = collect ([]);
foreach ( $serviceVariables as $key => $value ) {
if ( is_numeric ( $key )) {
$value = str ( $value );
if ( $value -> contains ( '=' )) {
$key = $value -> before ( '=' ) -> value ();
$value = $value -> after ( '=' ) -> value ();
} else {
$key = $value -> value ();
$value = null ;
}
$parsedServiceVariables -> put ( $key , $value );
} else {
$parsedServiceVariables -> put ( $key , $value );
}
}
2025-01-24 11:04:34 +00:00
$parsedServiceVariables -> put ( 'COOLIFY_RESOURCE_UUID' , " { $resource -> uuid } " );
2024-08-23 18:54:38 +00:00
$parsedServiceVariables -> put ( 'COOLIFY_CONTAINER_NAME' , " $serviceName - { $resource -> uuid } " );
2024-08-27 17:36:22 +00:00
// TODO: move this in a shared function
2024-12-09 14:38:21 +00:00
if ( ! $parsedServiceVariables -> has ( 'COOLIFY_APP_NAME' )) {
2024-09-13 06:23:05 +00:00
$parsedServiceVariables -> put ( 'COOLIFY_APP_NAME' , " \" { $resource -> name } \" " );
2024-08-27 17:36:22 +00:00
}
2024-12-09 14:38:21 +00:00
if ( ! $parsedServiceVariables -> has ( 'COOLIFY_SERVER_IP' )) {
2024-09-13 06:23:05 +00:00
$parsedServiceVariables -> put ( 'COOLIFY_SERVER_IP' , " \" { $resource -> destination -> server -> ip } \" " );
2024-08-27 17:36:22 +00:00
}
2024-12-09 14:38:21 +00:00
if ( ! $parsedServiceVariables -> has ( 'COOLIFY_ENVIRONMENT_NAME' )) {
2024-09-13 06:23:05 +00:00
$parsedServiceVariables -> put ( 'COOLIFY_ENVIRONMENT_NAME' , " \" { $resource -> environment -> name } \" " );
2024-08-27 17:36:22 +00:00
}
2024-12-09 14:38:21 +00:00
if ( ! $parsedServiceVariables -> has ( 'COOLIFY_PROJECT_NAME' )) {
2024-09-13 06:23:05 +00:00
$parsedServiceVariables -> put ( 'COOLIFY_PROJECT_NAME' , " \" { $resource -> project () -> name } \" " );
2024-08-27 17:36:22 +00:00
}
2024-08-23 18:54:38 +00:00
$parsedServiceVariables = $parsedServiceVariables -> map ( function ( $value , $key ) use ( $envs_from_coolify ) {
2024-12-09 14:38:21 +00:00
if ( ! str ( $value ) -> startsWith ( '$' )) {
2024-08-23 18:54:38 +00:00
$found_env = $envs_from_coolify -> where ( 'key' , $key ) -> first ();
if ( $found_env ) {
return $found_env -> value ;
}
}
return $value ;
});
data_set ( $service , 'environment' , $parsedServiceVariables -> toArray ());
return $service ;
});
$finalServices = [
'services' => $services -> toArray (),
'volumes' => $topLevelVolumes -> toArray (),
'networks' => $topLevelNetworks -> toArray (),
'configs' => $topLevelConfigs -> toArray (),
'secrets' => $topLevelSecrets -> toArray (),
];
$yaml = data_forget ( $yaml , 'services.*.volumes.*.content' );
$resource -> docker_compose_raw = Yaml :: dump ( $yaml , 10 , 2 );
$resource -> docker_compose = Yaml :: dump ( $finalServices , 10 , 2 );
2024-08-26 08:51:01 +00:00
2024-08-23 18:54:38 +00:00
$resource -> save ();
$resource -> saveComposeConfigs ();
return collect ( $finalServices );
} else {
return collect ([]);
}
2024-10-28 13:56:13 +00:00
} elseif ( $resource -> getMorphClass () === \App\Models\Application :: class ) {
2024-08-23 18:54:38 +00:00
try {
$yaml = Yaml :: parse ( $resource -> docker_compose_raw );
2024-10-31 14:19:37 +00:00
} catch ( \Exception ) {
2024-08-23 18:54:38 +00:00
return ;
}
$server = $resource -> destination -> server ;
$topLevelVolumes = collect ( data_get ( $yaml , 'volumes' , []));
if ( $pull_request_id !== 0 ) {
$topLevelVolumes = collect ([]);
}
if ( $topLevelVolumes -> count () > 0 ) {
$tempTopLevelVolumes = collect ([]);
foreach ( $topLevelVolumes as $volumeName => $volume ) {
if ( is_null ( $volume )) {
continue ;
}
$tempTopLevelVolumes -> put ( $volumeName , $volume );
}
$topLevelVolumes = collect ( $tempTopLevelVolumes );
}
$topLevelNetworks = collect ( data_get ( $yaml , 'networks' , []));
$topLevelConfigs = collect ( data_get ( $yaml , 'configs' , []));
$topLevelSecrets = collect ( data_get ( $yaml , 'secrets' , []));
$services = data_get ( $yaml , 'services' );
$generatedServiceFQDNS = collect ([]);
if ( is_null ( $resource -> destination )) {
$destination = $server -> destinations () -> first ();
if ( $destination ) {
$resource -> destination () -> associate ( $destination );
$resource -> save ();
}
}
$definedNetwork = collect ([ $resource -> uuid ]);
if ( $pull_request_id !== 0 ) {
$definedNetwork = collect ([ " { $resource -> uuid } - $pull_request_id " ]);
}
$services = collect ( $services ) -> map ( function ( $service , $serviceName ) use ( $topLevelVolumes , $topLevelNetworks , $definedNetwork , $isNew , $generatedServiceFQDNS , $resource , $server , $pull_request_id , $preview_id ) {
$serviceVolumes = collect ( data_get ( $service , 'volumes' , []));
$servicePorts = collect ( data_get ( $service , 'ports' , []));
$serviceNetworks = collect ( data_get ( $service , 'networks' , []));
$serviceVariables = collect ( data_get ( $service , 'environment' , []));
$serviceDependencies = collect ( data_get ( $service , 'depends_on' , []));
$serviceLabels = collect ( data_get ( $service , 'labels' , []));
$serviceBuildVariables = collect ( data_get ( $service , 'build.args' , []));
$serviceVariables = $serviceVariables -> merge ( $serviceBuildVariables );
if ( $serviceLabels -> count () > 0 ) {
$removedLabels = collect ([]);
$serviceLabels = $serviceLabels -> filter ( function ( $serviceLabel , $serviceLabelName ) use ( $removedLabels ) {
2024-12-09 14:38:21 +00:00
if ( ! str ( $serviceLabel ) -> contains ( '=' )) {
2024-08-23 18:54:38 +00:00
$removedLabels -> put ( $serviceLabelName , $serviceLabel );
return false ;
}
return $serviceLabel ;
});
foreach ( $removedLabels as $removedLabelName => $removedLabel ) {
$serviceLabels -> push ( " $removedLabelName = $removedLabel " );
}
}
$baseName = generateApplicationContainerName ( $resource , $pull_request_id );
$containerName = " $serviceName - $baseName " ;
if ( $resource -> compose_parsing_version === '1' ) {
if ( count ( $serviceVolumes ) > 0 ) {
$serviceVolumes = $serviceVolumes -> map ( function ( $volume ) use ( $resource , $topLevelVolumes , $pull_request_id ) {
if ( is_string ( $volume )) {
$volume = str ( $volume );
2024-12-09 14:38:21 +00:00
if ( $volume -> contains ( ':' ) && ! $volume -> startsWith ( '/' )) {
2024-08-23 18:54:38 +00:00
$name = $volume -> before ( ':' );
$mount = $volume -> after ( ':' );
if ( $name -> startsWith ( '.' ) || $name -> startsWith ( '~' )) {
2024-12-09 14:38:21 +00:00
$dir = base_configuration_dir () . '/applications/' . $resource -> uuid ;
2024-08-23 18:54:38 +00:00
if ( $name -> startsWith ( '.' )) {
$name = $name -> replaceFirst ( '.' , $dir );
}
if ( $name -> startsWith ( '~' )) {
$name = $name -> replaceFirst ( '~' , $dir );
}
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$name = $name . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
$volume = str ( " $name : $mount " );
} else {
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$name = $name . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
$volume = str ( " $name : $mount " );
if ( $topLevelVolumes -> has ( $name )) {
$v = $topLevelVolumes -> get ( $name );
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
// Do nothing
} else {
if ( is_null ( data_get ( $v , 'name' ))) {
data_set ( $v , 'name' , $name );
data_set ( $topLevelVolumes , $name , $v );
}
}
} else {
$topLevelVolumes -> put ( $name , [
'name' => $name ,
]);
}
} else {
if ( $topLevelVolumes -> has ( $name -> value ())) {
$v = $topLevelVolumes -> get ( $name -> value ());
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
// Do nothing
} else {
if ( is_null ( data_get ( $v , 'name' ))) {
data_set ( $topLevelVolumes , $name -> value (), $v );
}
}
} else {
$topLevelVolumes -> put ( $name -> value (), [
'name' => $name -> value (),
]);
}
}
}
} else {
if ( $volume -> startsWith ( '/' )) {
$name = $volume -> before ( ':' );
$mount = $volume -> after ( ':' );
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$name = $name . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
$volume = str ( " $name : $mount " );
}
}
} elseif ( is_array ( $volume )) {
$source = data_get ( $volume , 'source' );
$target = data_get ( $volume , 'target' );
$read_only = data_get ( $volume , 'read_only' );
if ( $source && $target ) {
if (( str ( $source ) -> startsWith ( '.' ) || str ( $source ) -> startsWith ( '~' ))) {
2024-12-09 14:38:21 +00:00
$dir = base_configuration_dir () . '/applications/' . $resource -> uuid ;
2024-08-23 18:54:38 +00:00
if ( str ( $source , '.' )) {
$source = str ( $source ) -> replaceFirst ( '.' , $dir );
}
if ( str ( $source , '~' )) {
$source = str ( $source ) -> replaceFirst ( '~' , $dir );
}
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$source = $source . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
if ( $read_only ) {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target . ':ro' );
2024-08-23 18:54:38 +00:00
} else {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target );
2024-08-23 18:54:38 +00:00
}
} else {
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$source = $source . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
if ( $read_only ) {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target . ':ro' );
2024-08-23 18:54:38 +00:00
} else {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target );
2024-08-23 18:54:38 +00:00
}
2024-12-09 14:38:21 +00:00
if ( ! str ( $source ) -> startsWith ( '/' )) {
2024-08-23 18:54:38 +00:00
if ( $topLevelVolumes -> has ( $source )) {
$v = $topLevelVolumes -> get ( $source );
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
// Do nothing
} else {
if ( is_null ( data_get ( $v , 'name' ))) {
data_set ( $v , 'name' , $source );
data_set ( $topLevelVolumes , $source , $v );
}
}
} else {
$topLevelVolumes -> put ( $source , [
'name' => $source ,
]);
}
}
}
}
}
if ( is_array ( $volume )) {
return data_get ( $volume , 'source' );
}
return $volume -> value ();
});
data_set ( $service , 'volumes' , $serviceVolumes -> toArray ());
}
} elseif ( $resource -> compose_parsing_version === '2' ) {
if ( count ( $serviceVolumes ) > 0 ) {
$serviceVolumes = $serviceVolumes -> map ( function ( $volume ) use ( $resource , $topLevelVolumes , $pull_request_id ) {
if ( is_string ( $volume )) {
$volume = str ( $volume );
2024-12-09 14:38:21 +00:00
if ( $volume -> contains ( ':' ) && ! $volume -> startsWith ( '/' )) {
2024-08-23 18:54:38 +00:00
$name = $volume -> before ( ':' );
$mount = $volume -> after ( ':' );
if ( $name -> startsWith ( '.' ) || $name -> startsWith ( '~' )) {
2024-12-09 14:38:21 +00:00
$dir = base_configuration_dir () . '/applications/' . $resource -> uuid ;
2024-08-23 18:54:38 +00:00
if ( $name -> startsWith ( '.' )) {
$name = $name -> replaceFirst ( '.' , $dir );
}
if ( $name -> startsWith ( '~' )) {
$name = $name -> replaceFirst ( '~' , $dir );
}
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$name = $name . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
$volume = str ( " $name : $mount " );
} else {
if ( $pull_request_id !== 0 ) {
$uuid = $resource -> uuid ;
2024-12-09 14:38:21 +00:00
$name = $uuid . " - $name -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
$volume = str ( " $name : $mount " );
if ( $topLevelVolumes -> has ( $name )) {
$v = $topLevelVolumes -> get ( $name );
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
// Do nothing
} else {
if ( is_null ( data_get ( $v , 'name' ))) {
data_set ( $v , 'name' , $name );
data_set ( $topLevelVolumes , $name , $v );
}
}
} else {
$topLevelVolumes -> put ( $name , [
'name' => $name ,
]);
}
} else {
$uuid = $resource -> uuid ;
2024-12-09 14:38:21 +00:00
$name = str ( $uuid . " - $name " );
2024-08-23 18:54:38 +00:00
$volume = str ( " $name : $mount " );
if ( $topLevelVolumes -> has ( $name -> value ())) {
$v = $topLevelVolumes -> get ( $name -> value ());
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
// Do nothing
} else {
if ( is_null ( data_get ( $v , 'name' ))) {
data_set ( $topLevelVolumes , $name -> value (), $v );
}
}
} else {
$topLevelVolumes -> put ( $name -> value (), [
'name' => $name -> value (),
]);
}
}
}
} else {
if ( $volume -> startsWith ( '/' )) {
$name = $volume -> before ( ':' );
$mount = $volume -> after ( ':' );
if ( $pull_request_id !== 0 ) {
2024-12-09 14:38:21 +00:00
$name = $name . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
$volume = str ( " $name : $mount " );
}
}
} elseif ( is_array ( $volume )) {
$source = data_get ( $volume , 'source' );
$target = data_get ( $volume , 'target' );
$read_only = data_get ( $volume , 'read_only' );
if ( $source && $target ) {
$uuid = $resource -> uuid ;
if (( str ( $source ) -> startsWith ( '.' ) || str ( $source ) -> startsWith ( '~' ) || str ( $source ) -> startsWith ( '/' ))) {
2024-12-09 14:38:21 +00:00
$dir = base_configuration_dir () . '/applications/' . $resource -> uuid ;
2024-08-23 18:54:38 +00:00
if ( str ( $source , '.' )) {
$source = str ( $source ) -> replaceFirst ( '.' , $dir );
}
if ( str ( $source , '~' )) {
$source = str ( $source ) -> replaceFirst ( '~' , $dir );
}
if ( $read_only ) {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target . ':ro' );
2024-08-23 18:54:38 +00:00
} else {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target );
2024-08-23 18:54:38 +00:00
}
} else {
if ( $pull_request_id === 0 ) {
2024-12-09 14:38:21 +00:00
$source = $uuid . " - $source " ;
2024-08-23 18:54:38 +00:00
} else {
2024-12-09 14:38:21 +00:00
$source = $uuid . " - $source -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
}
if ( $read_only ) {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target . ':ro' );
2024-08-23 18:54:38 +00:00
} else {
2024-12-09 14:38:21 +00:00
data_set ( $volume , 'source' , $source . ':' . $target );
2024-08-23 18:54:38 +00:00
}
2024-12-09 14:38:21 +00:00
if ( ! str ( $source ) -> startsWith ( '/' )) {
2024-08-23 18:54:38 +00:00
if ( $topLevelVolumes -> has ( $source )) {
$v = $topLevelVolumes -> get ( $source );
if ( data_get ( $v , 'driver_opts.type' ) === 'cifs' ) {
// Do nothing
} else {
if ( is_null ( data_get ( $v , 'name' ))) {
data_set ( $v , 'name' , $source );
data_set ( $topLevelVolumes , $source , $v );
}
}
} else {
$topLevelVolumes -> put ( $source , [
'name' => $source ,
]);
}
}
}
}
}
if ( is_array ( $volume )) {
return data_get ( $volume , 'source' );
}
dispatch ( new ServerFilesFromServerJob ( $resource ));
return $volume -> value ();
});
data_set ( $service , 'volumes' , $serviceVolumes -> toArray ());
}
}
if ( $pull_request_id !== 0 && count ( $serviceDependencies ) > 0 ) {
$serviceDependencies = $serviceDependencies -> map ( function ( $dependency ) use ( $pull_request_id ) {
2024-12-09 14:38:21 +00:00
return $dependency . " -pr- $pull_request_id " ;
2024-08-23 18:54:38 +00:00
});
data_set ( $service , 'depends_on' , $serviceDependencies -> toArray ());
}
// Decide if the service is a database
2025-06-04 09:44:37 +00:00
$image = data_get_str ( $service , 'image' );
$isDatabase = isDatabaseImage ( $image , $service );
2024-08-23 18:54:38 +00:00
data_set ( $service , 'is_database' , $isDatabase );
// Collect/create/update networks
if ( $serviceNetworks -> count () > 0 ) {
foreach ( $serviceNetworks as $networkName => $networkDetails ) {
if ( $networkName === 'default' ) {
continue ;
}
// ignore alias
if ( $networkDetails [ 'aliases' ] ? ? false ) {
continue ;
}
$networkExists = $topLevelNetworks -> contains ( function ( $value , $key ) use ( $networkName ) {
return $value == $networkName || $key == $networkName ;
});
2024-12-09 14:38:21 +00:00
if ( ! $networkExists ) {
2024-10-03 13:04:40 +00:00
if ( is_string ( $networkDetails ) || is_int ( $networkDetails )) {
$topLevelNetworks -> put ( $networkDetails , null );
}
2024-08-23 18:54:38 +00:00
}
}
}
// Collect/create/update ports
$collectedPorts = collect ([]);
if ( $servicePorts -> count () > 0 ) {
foreach ( $servicePorts as $sport ) {
if ( is_string ( $sport ) || is_numeric ( $sport )) {
$collectedPorts -> push ( $sport );
}
if ( is_array ( $sport )) {
$target = data_get ( $sport , 'target' );
$published = data_get ( $sport , 'published' );
$protocol = data_get ( $sport , 'protocol' );
$collectedPorts -> push ( " $target : $published / $protocol " );
}
}
}
$definedNetworkExists = $topLevelNetworks -> contains ( function ( $value , $_ ) use ( $definedNetwork ) {
return $value == $definedNetwork ;
});
2024-12-09 14:38:21 +00:00
if ( ! $definedNetworkExists ) {
2024-08-23 18:54:38 +00:00
foreach ( $definedNetwork as $network ) {
if ( $pull_request_id !== 0 ) {
$topLevelNetworks -> put ( $network , [
'name' => $network ,
'external' => true ,
]);
} else {
$topLevelNetworks -> put ( $network , [
'name' => $network ,
'external' => true ,
]);
}
}
}
$networks = collect ();
foreach ( $serviceNetworks as $key => $serviceNetwork ) {
if ( gettype ( $serviceNetwork ) === 'string' ) {
// networks:
// - appwrite
$networks -> put ( $serviceNetwork , null );
} elseif ( gettype ( $serviceNetwork ) === 'array' ) {
// networks:
// default:
// ipv4_address: 192.168.203.254
// $networks->put($serviceNetwork, null);
$networks -> put ( $key , $serviceNetwork );
}
}
foreach ( $definedNetwork as $key => $network ) {
$networks -> put ( $network , null );
}
if ( data_get ( $resource , 'settings.connect_to_docker_network' )) {
$network = $resource -> destination -> network ;
$networks -> put ( $network , null );
$topLevelNetworks -> put ( $network , [
'name' => $network ,
'external' => true ,
]);
}
data_set ( $service , 'networks' , $networks -> toArray ());
// Get variables from the service
foreach ( $serviceVariables as $variableName => $variable ) {
if ( is_numeric ( $variableName )) {
if ( is_array ( $variable )) {
// - SESSION_SECRET: 123
// - SESSION_SECRET:
$key = str ( collect ( $variable ) -> keys () -> first ());
$value = str ( collect ( $variable ) -> values () -> first ());
} else {
$variable = str ( $variable );
if ( $variable -> contains ( '=' )) {
// - SESSION_SECRET=123
// - SESSION_SECRET=
$key = $variable -> before ( '=' );
$value = $variable -> after ( '=' );
} else {
// - SESSION_SECRET
$key = $variable ;
$value = null ;
}
}
} else {
// SESSION_SECRET: 123
// SESSION_SECRET:
$key = str ( $variableName );
$value = str ( $variable );
}
if ( $key -> startsWith ( 'SERVICE_FQDN' )) {
if ( $isNew ) {
$name = $key -> after ( 'SERVICE_FQDN_' ) -> beforeLast ( '_' ) -> lower ();
$fqdn = generateFqdn ( $server , " { $name -> value () } - { $resource -> uuid } " );
if ( substr_count ( $key -> value (), '_' ) === 3 ) {
// SERVICE_FQDN_UMAMI_1000
$port = $key -> afterLast ( '_' );
} else {
// SERVICE_FQDN_UMAMI
$port = null ;
}
if ( $port ) {
$fqdn = " $fqdn : $port " ;
}
if ( substr_count ( $key -> value (), '_' ) >= 2 ) {
if ( $value ) {
$path = $value -> value ();
} else {
$path = null ;
}
if ( $generatedServiceFQDNS -> count () > 0 ) {
$alreadyGenerated = $generatedServiceFQDNS -> has ( $key -> value ());
if ( $alreadyGenerated ) {
$fqdn = $generatedServiceFQDNS -> get ( $key -> value ());
} else {
$generatedServiceFQDNS -> put ( $key -> value (), $fqdn );
}
} else {
$generatedServiceFQDNS -> put ( $key -> value (), $fqdn );
}
$fqdn = " $fqdn $path " ;
}
}
continue ;
}
if ( $value ? -> startsWith ( '$' )) {
$foundEnv = EnvironmentVariable :: where ([
'key' => $key ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]) -> first ();
2024-09-09 13:04:51 +00:00
$value = replaceVariables ( $value );
2024-08-23 18:54:38 +00:00
$key = $value ;
if ( $value -> startsWith ( 'SERVICE_' )) {
$foundEnv = EnvironmentVariable :: where ([
'key' => $key ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
]) -> first ();
[ 'command' => $command , 'forService' => $forService , 'generatedValue' => $generatedValue , 'port' => $port ] = parseEnvVariable ( $value );
2024-12-09 14:38:21 +00:00
if ( ! is_null ( $command )) {
2024-08-23 18:54:38 +00:00
if ( $command ? -> value () === 'FQDN' || $command ? -> value () === 'URL' ) {
if ( Str :: lower ( $forService ) === $serviceName ) {
$fqdn = generateFqdn ( $server , $containerName );
} else {
2024-12-09 14:38:21 +00:00
$fqdn = generateFqdn ( $server , Str :: lower ( $forService ) . '-' . $resource -> uuid );
2024-08-23 18:54:38 +00:00
}
if ( $port ) {
$fqdn = " $fqdn : $port " ;
}
if ( $foundEnv ) {
$fqdn = data_get ( $foundEnv , 'value' );
} else {
if ( $command ? -> value () === 'URL' ) {
$fqdn = str ( $fqdn ) -> after ( '://' ) -> value ();
}
EnvironmentVariable :: create ([
'key' => $key ,
'value' => $fqdn ,
'is_build_time' => false ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
} else {
$generatedValue = generateEnvValue ( $command );
2024-12-09 14:38:21 +00:00
if ( ! $foundEnv ) {
2024-08-23 18:54:38 +00:00
EnvironmentVariable :: create ([
'key' => $key ,
'value' => $generatedValue ,
'is_build_time' => false ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
}
}
} else {
if ( $value -> contains ( ':-' )) {
$key = $value -> before ( ':' );
$defaultValue = $value -> after ( ':-' );
} elseif ( $value -> contains ( '-' )) {
$key = $value -> before ( '-' );
$defaultValue = $value -> after ( '-' );
} elseif ( $value -> contains ( ':?' )) {
$key = $value -> before ( ':' );
$defaultValue = $value -> after ( ':?' );
} elseif ( $value -> contains ( '?' )) {
$key = $value -> before ( '?' );
$defaultValue = $value -> after ( '?' );
} else {
$key = $value ;
$defaultValue = null ;
}
$foundEnv = EnvironmentVariable :: where ([
'key' => $key ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]) -> first ();
if ( $foundEnv ) {
$defaultValue = data_get ( $foundEnv , 'value' );
}
$isBuildTime = data_get ( $foundEnv , 'is_build_time' , false );
if ( $foundEnv ) {
$foundEnv -> update ([
'key' => $key ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_build_time' => $isBuildTime ,
'value' => $defaultValue ,
]);
} else {
EnvironmentVariable :: create ([
'key' => $key ,
'value' => $defaultValue ,
'is_build_time' => $isBuildTime ,
2025-01-22 09:21:41 +00:00
'resourceable_type' => get_class ( $resource ),
'resourceable_id' => $resource -> id ,
2024-08-23 18:54:38 +00:00
'is_preview' => false ,
]);
}
}
}
}
// Add labels to the service
if ( $resource -> serviceType ()) {
$fqdns = generateServiceSpecificFqdns ( $resource );
} else {
$domains = collect ( json_decode ( $resource -> docker_compose_domains )) ? ? [];
if ( $domains ) {
$fqdns = data_get ( $domains , " $serviceName .domain " );
if ( $fqdns ) {
$fqdns = str ( $fqdns ) -> explode ( ',' );
if ( $pull_request_id !== 0 ) {
$preview = $resource -> previews () -> find ( $preview_id );
$docker_compose_domains = collect ( json_decode ( data_get ( $preview , 'docker_compose_domains' )));
if ( $docker_compose_domains -> count () > 0 ) {
$found_fqdn = data_get ( $docker_compose_domains , " $serviceName .domain " );
if ( $found_fqdn ) {
$fqdns = collect ( $found_fqdn );
} else {
$fqdns = collect ([]);
}
} else {
$fqdns = $fqdns -> map ( function ( $fqdn ) use ( $pull_request_id , $resource ) {
$preview = ApplicationPreview :: findPreviewByApplicationAndPullId ( $resource -> id , $pull_request_id );
$url = Url :: fromString ( $fqdn );
$template = $resource -> preview_url_template ;
$host = $url -> getHost ();
$schema = $url -> getScheme ();
$random = new Cuid2 ;
$preview_fqdn = str_replace ( '{{random}}' , $random , $template );
$preview_fqdn = str_replace ( '{{domain}}' , $host , $preview_fqdn );
$preview_fqdn = str_replace ( '{{pr_id}}' , $pull_request_id , $preview_fqdn );
$preview_fqdn = " $schema :// $preview_fqdn " ;
$preview -> fqdn = $preview_fqdn ;
$preview -> save ();
return $preview_fqdn ;
});
}
}
$shouldGenerateLabelsExactly = $server -> settings -> generate_exact_labels ;
if ( $shouldGenerateLabelsExactly ) {
switch ( $server -> proxyType ()) {
case ProxyTypes :: TRAEFIK -> value :
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge (
fqdnLabelsForTraefik (
uuid : $resource -> uuid ,
domains : $fqdns ,
serviceLabels : $serviceLabels ,
generate_unique_uuid : $resource -> build_pack === 'dockercompose' ,
image : data_get ( $service , 'image' ),
is_force_https_enabled : $resource -> isForceHttpsEnabled (),
is_gzip_enabled : $resource -> isGzipEnabled (),
is_stripprefix_enabled : $resource -> isStripprefixEnabled (),
)
2024-08-23 18:54:38 +00:00
);
break ;
case ProxyTypes :: CADDY -> value :
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge (
fqdnLabelsForCaddy (
network : $resource -> destination -> network ,
uuid : $resource -> uuid ,
domains : $fqdns ,
serviceLabels : $serviceLabels ,
image : data_get ( $service , 'image' ),
is_force_https_enabled : $resource -> isForceHttpsEnabled (),
is_gzip_enabled : $resource -> isGzipEnabled (),
is_stripprefix_enabled : $resource -> isStripprefixEnabled (),
)
2024-08-23 18:54:38 +00:00
);
break ;
}
} else {
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge (
fqdnLabelsForTraefik (
uuid : $resource -> uuid ,
domains : $fqdns ,
serviceLabels : $serviceLabels ,
generate_unique_uuid : $resource -> build_pack === 'dockercompose' ,
image : data_get ( $service , 'image' ),
is_force_https_enabled : $resource -> isForceHttpsEnabled (),
is_gzip_enabled : $resource -> isGzipEnabled (),
is_stripprefix_enabled : $resource -> isStripprefixEnabled (),
)
2024-08-23 18:54:38 +00:00
);
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge (
2024-08-23 18:54:38 +00:00
fqdnLabelsForCaddy (
network : $resource -> destination -> network ,
uuid : $resource -> uuid ,
domains : $fqdns ,
serviceLabels : $serviceLabels ,
image : data_get ( $service , 'image' ),
is_force_https_enabled : $resource -> isForceHttpsEnabled (),
is_gzip_enabled : $resource -> isGzipEnabled (),
is_stripprefix_enabled : $resource -> isStripprefixEnabled (),
2025-02-28 19:25:19 +00:00
)
);
2024-08-23 18:54:38 +00:00
}
}
}
}
2024-11-17 21:49:44 +00:00
$defaultLabels = defaultLabels (
id : $resource -> id ,
name : $containerName ,
projectName : $resource -> project () -> name ,
resourceName : $resource -> name ,
2024-12-02 17:38:21 +00:00
environment : $resource -> environment -> name ,
2024-11-17 21:49:44 +00:00
pull_request_id : $pull_request_id ,
type : 'application'
);
2025-02-28 19:25:19 +00:00
$serviceLabels = $serviceLabels -> merge ( $defaultLabels );
2024-08-23 18:54:38 +00:00
2024-09-02 08:57:03 +00:00
if ( $server -> isLogDrainEnabled ()) {
if ( $resource instanceof Application && $resource -> isLogDrainEnabled ()) {
data_set ( $service , 'logging' , generate_fluentd_configuration ());
}
2024-08-23 18:54:38 +00:00
}
if ( $serviceLabels -> count () > 0 ) {
if ( $resource -> settings -> is_container_label_escape_enabled ) {
$serviceLabels = $serviceLabels -> map ( function ( $value , $key ) {
return escapeDollarSign ( $value );
});
}
}
data_set ( $service , 'labels' , $serviceLabels -> toArray ());
data_forget ( $service , 'is_database' );
2024-12-09 14:38:21 +00:00
if ( ! data_get ( $service , 'restart' )) {
2024-08-23 18:54:38 +00:00
data_set ( $service , 'restart' , RESTART_MODE );
}
data_set ( $service , 'container_name' , $containerName );
data_forget ( $service , 'volumes.*.content' );
data_forget ( $service , 'volumes.*.isDirectory' );
2024-12-17 09:38:32 +00:00
data_forget ( $service , 'volumes.*.is_directory' );
data_forget ( $service , 'exclude_from_hc' );
data_set ( $service , 'environment' , $serviceVariables -> toArray ());
2024-08-23 18:54:38 +00:00
return $service ;
});
if ( $pull_request_id !== 0 ) {
$services -> each ( function ( $service , $serviceName ) use ( $pull_request_id , $services ) {
2024-12-09 14:38:21 +00:00
$services [ $serviceName . " -pr- $pull_request_id " ] = $service ;
2024-08-23 18:54:38 +00:00
data_forget ( $services , $serviceName );
});
}
$finalServices = [
'services' => $services -> toArray (),
'volumes' => $topLevelVolumes -> toArray (),
'networks' => $topLevelNetworks -> toArray (),
'configs' => $topLevelConfigs -> toArray (),
'secrets' => $topLevelSecrets -> toArray (),
];
2024-08-26 08:51:01 +00:00
$resource -> docker_compose_raw = Yaml :: dump ( $yaml , 10 , 2 );
$resource -> docker_compose = Yaml :: dump ( $finalServices , 10 , 2 );
data_forget ( $resource , 'environment_variables' );
data_forget ( $resource , 'environment_variables_preview' );
2024-08-23 18:54:38 +00:00
$resource -> save ();
return collect ( $finalServices );
}
}
2024-09-07 18:56:26 +00:00
2025-08-04 08:46:59 +00:00
// function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection
// {
// $isApplication = $resource instanceof Application;
// $isService = $resource instanceof Service;
// if ($isApplication) {
// return applicationParser($resource, $pull_request_id, $preview_id);
// }
// if ($isService) {
// return serviceParser($resource);
// }
// return collect([]);
// $uuid = data_get($resource, 'uuid');
// $compose = data_get($resource, 'docker_compose_raw');
// if (! $compose) {
// return collect([]);
// }
// if ($isApplication) {
// $pullRequestId = $pull_request_id;
// $isPullRequest = $pullRequestId == 0 ? false : true;
// $server = data_get($resource, 'destination.server');
// $fileStorages = $resource->fileStorages();
// } elseif ($isService) {
// $server = data_get($resource, 'server');
// $allServices = get_service_templates();
// } else {
// return collect([]);
// }
// try {
// $yaml = Yaml::parse($compose);
// } catch (\Exception) {
// return collect([]);
// }
// $services = data_get($yaml, 'services', collect([]));
// $topLevel = collect([
// 'volumes' => collect(data_get($yaml, 'volumes', [])),
// 'networks' => collect(data_get($yaml, 'networks', [])),
// 'configs' => collect(data_get($yaml, 'configs', [])),
// 'secrets' => collect(data_get($yaml, 'secrets', [])),
// ]);
// // If there are predefined volumes, make sure they are not null
// if ($topLevel->get('volumes')->count() > 0) {
// $temp = collect([]);
// foreach ($topLevel['volumes'] as $volumeName => $volume) {
// if (is_null($volume)) {
// continue;
// }
// $temp->put($volumeName, $volume);
// }
// $topLevel['volumes'] = $temp;
// }
// // Get the base docker network
// $baseNetwork = collect([$uuid]);
// if ($isApplication && $isPullRequest) {
// $baseNetwork = collect(["{$uuid}-{$pullRequestId}"]);
// }
// $parsedServices = collect([]);
// $allMagicEnvironments = collect([]);
// foreach ($services as $serviceName => $service) {
// $predefinedPort = null;
// $magicEnvironments = collect([]);
// $image = data_get_str($service, 'image');
// $environment = collect(data_get($service, 'environment', []));
// $buildArgs = collect(data_get($service, 'build.args', []));
// $environment = $environment->merge($buildArgs);
// $isDatabase = isDatabaseImage($image, $service);
// if ($isService) {
// $containerName = "$serviceName-{$resource->uuid}";
// if ($serviceName === 'registry') {
// $tempServiceName = 'docker-registry';
// } else {
// $tempServiceName = $serviceName;
// }
// if (str(data_get($service, 'image'))->contains('glitchtip')) {
// $tempServiceName = 'glitchtip';
// }
// if ($serviceName === 'supabase-kong') {
// $tempServiceName = 'supabase';
// }
// $serviceDefinition = data_get($allServices, $tempServiceName);
// $predefinedPort = data_get($serviceDefinition, 'port');
// if ($serviceName === 'plausible') {
// $predefinedPort = '8000';
// }
// if ($isDatabase) {
// $applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first();
// if ($applicationFound) {
// $savedService = $applicationFound;
// } else {
// $savedService = ServiceDatabase::firstOrCreate([
// 'name' => $serviceName,
// 'service_id' => $resource->id,
// ]);
// }
// } else {
// $savedService = ServiceApplication::firstOrCreate([
// 'name' => $serviceName,
// 'service_id' => $resource->id,
// ], [
// 'is_gzip_enabled' => true,
// ]);
// }
// // Check if image changed
// if ($savedService->image !== $image) {
// $savedService->image = $image;
// $savedService->save();
// }
// // Pocketbase does not need gzip for SSE.
// if (str($savedService->image)->contains('pocketbase') && $savedService->is_gzip_enabled) {
// $savedService->is_gzip_enabled = false;
// $savedService->save();
// }
// }
// $environment = collect(data_get($service, 'environment', []));
// $buildArgs = collect(data_get($service, 'build.args', []));
// $environment = $environment->merge($buildArgs);
// // convert environment variables to one format
// $environment = convertToKeyValueCollection($environment);
// // Add Coolify defined environments
// $allEnvironments = $resource->environment_variables()->get(['key', 'value']);
// $allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
// return [$item['key'] => $item['value']];
// });
// // filter and add magic environments
// foreach ($environment as $key => $value) {
// // Get all SERVICE_ variables from keys and values
// $key = str($key);
// $value = str($value);
// $regex = '/\$(\{?([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\}?)/';
// preg_match_all($regex, $value, $valueMatches);
// if (count($valueMatches[1]) > 0) {
// foreach ($valueMatches[1] as $match) {
// $match = replaceVariables($match);
// if ($match->startsWith('SERVICE_')) {
// if ($magicEnvironments->has($match->value())) {
// continue;
// }
// $magicEnvironments->put($match->value(), '');
// }
// }
// }
// // Get magic environments where we need to preset the FQDN
// if ($key->startsWith('SERVICE_FQDN_')) {
// // SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000
// if (substr_count(str($key)->value(), '_') === 3) {
// $fqdnFor = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value();
// $port = $key->afterLast('_')->value();
// } else {
// $fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
// $port = null;
// }
// if ($isApplication) {
// $fqdn = $resource->fqdn;
// if (blank($resource->fqdn)) {
// $fqdn = generateFqdn($server, "$uuid");
// }
// } elseif ($isService) {
// if ($isOneClick) {
// if (blank($savedService->fqdn)) {
// if ($fqdnFor) {
// $fqdn = generateFqdn($server, "$fqdnFor-$uuid");
// } else {
// $fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
// }
// } else {
// $fqdn = str($savedService->fqdn)->after('://')->before(':')->prepend(str($savedService->fqdn)->before('://')->append('://'))->value();
// }
// } else {
// // For services which are not one-click, if no explicit FQDN is set, leave SERVICE_FQDN_ variables empty
// if (blank($savedService->fqdn)) {
// $fqdn = '';
// } else {
// $fqdn = str($savedService->fqdn)->after('://')->before(':')->prepend(str($savedService->fqdn)->before('://')->append('://'))->value();
// }
// ray($fqdn);
// }
// }
// if ($value && get_class($value) === \Illuminate\Support\Stringable::class && $value->startsWith('/')) {
// $path = $value->value();
// if ($path !== '/') {
// $fqdn = "$fqdn$path";
// }
// }
// $fqdnWithPort = $fqdn;
// if ($port) {
// $fqdnWithPort = "$fqdn:$port";
// }
// if ($isApplication && is_null($resource->fqdn)) {
// data_forget($resource, 'environment_variables');
// data_forget($resource, 'environment_variables_preview');
// $resource->fqdn = $fqdnWithPort;
// $resource->save();
// } elseif ($isService && is_null($savedService->fqdn)) {
// $savedService->fqdn = $fqdnWithPort;
// $savedService->save();
// }
// if (substr_count(str($key)->value(), '_') === 2) {
// $resource->environment_variables()->updateOrCreate([
// 'key' => $key->value(),
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $fqdn,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// }
// if (substr_count(str($key)->value(), '_') === 3) {
// $newKey = str($key)->beforeLast('_');
// $resource->environment_variables()->updateOrCreate([
// 'key' => $newKey->value(),
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $fqdn,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// }
// }
// }
// $allMagicEnvironments = $allMagicEnvironments->merge($magicEnvironments);
// if ($magicEnvironments->count() > 0) {
// foreach ($magicEnvironments as $key => $value) {
// $key = str($key);
// $value = replaceVariables($value);
// $command = parseCommandFromMagicEnvVariable($key);
// if ($command->value() === 'FQDN') {
// if ($isOneClick) {
// $fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
// if (str($fqdnFor)->contains('_')) {
// $fqdnFor = str($fqdnFor)->before('_');
// }
// $fqdn = generateFqdn($server, "$fqdnFor-$uuid");
// $resource->environment_variables()->firstOrCreate([
// 'key' => $key->value(),
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $fqdn,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// }
// } elseif ($command->value() === 'URL') {
// if ($isOneClick) {
// $fqdnFor = $key->after('SERVICE_URL_')->lower()->value();
// if (str($fqdnFor)->contains('_')) {
// $fqdnFor = str($fqdnFor)->before('_');
// }
// $fqdn = generateFqdn($server, "$fqdnFor-$uuid");
// $fqdn = str($fqdn)->replace('http://', '')->replace('https://', '');
// $resource->environment_variables()->firstOrCreate([
// 'key' => $key->value(),
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $fqdn,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// }
// } else {
// $value = generateEnvValue($command, $resource);
// $resource->environment_variables()->firstOrCreate([
// 'key' => $key->value(),
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $value,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// }
// }
// }
// }
// $serviceAppsLogDrainEnabledMap = collect([]);
// if ($resource instanceof Service) {
// $serviceAppsLogDrainEnabledMap = $resource->applications()->get()->keyBy('name')->map(function ($app) {
// return $app->isLogDrainEnabled();
// });
// }
// // Parse the rest of the services
// foreach ($services as $serviceName => $service) {
// $image = data_get_str($service, 'image');
// $restart = data_get_str($service, 'restart', RESTART_MODE);
// $logging = data_get($service, 'logging');
// if ($server->isLogDrainEnabled()) {
// if ($resource instanceof Application && $resource->isLogDrainEnabled()) {
// $logging = generate_fluentd_configuration();
// }
// if ($resource instanceof Service && $serviceAppsLogDrainEnabledMap->get($serviceName)) {
// $logging = generate_fluentd_configuration();
// }
// }
// $volumes = collect(data_get($service, 'volumes', []));
// $networks = collect(data_get($service, 'networks', []));
// $use_network_mode = data_get($service, 'network_mode') !== null;
// $depends_on = collect(data_get($service, 'depends_on', []));
// $labels = collect(data_get($service, 'labels', []));
// if ($labels->count() > 0) {
// if (isAssociativeArray($labels)) {
// $newLabels = collect([]);
// $labels->each(function ($value, $key) use ($newLabels) {
// $newLabels->push("$key=$value");
// });
// $labels = $newLabels;
// }
// }
// $environment = collect(data_get($service, 'environment', []));
// $ports = collect(data_get($service, 'ports', []));
// $buildArgs = collect(data_get($service, 'build.args', []));
// $environment = $environment->merge($buildArgs);
// $environment = convertToKeyValueCollection($environment);
// $coolifyEnvironments = collect([]);
// $isDatabase = isDatabaseImage($image, $service);
// $volumesParsed = collect([]);
// if ($isApplication) {
// $baseName = generateApplicationContainerName(
// application: $resource,
// pull_request_id: $pullRequestId
// );
// $containerName = "$serviceName-$baseName";
// $predefinedPort = null;
// } elseif ($isService) {
// $containerName = "$serviceName-{$resource->uuid}";
// if ($serviceName === 'registry') {
// $tempServiceName = 'docker-registry';
// } else {
// $tempServiceName = $serviceName;
// }
// if (str(data_get($service, 'image'))->contains('glitchtip')) {
// $tempServiceName = 'glitchtip';
// }
// if ($serviceName === 'supabase-kong') {
// $tempServiceName = 'supabase';
// }
// $serviceDefinition = data_get($allServices, $tempServiceName);
// $predefinedPort = data_get($serviceDefinition, 'port');
// if ($serviceName === 'plausible') {
// $predefinedPort = '8000';
// }
// if ($isDatabase) {
// $applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
// if ($applicationFound) {
// $savedService = $applicationFound;
// } else {
// $savedService = ServiceDatabase::firstOrCreate([
// 'name' => $serviceName,
// 'image' => $image,
// 'service_id' => $resource->id,
// ]);
// }
// } else {
// $savedService = ServiceApplication::firstOrCreate([
// 'name' => $serviceName,
// 'image' => $image,
// 'service_id' => $resource->id,
// ]);
// }
// $fileStorages = $savedService->fileStorages();
// if ($savedService->image !== $image) {
// $savedService->image = $image;
// $savedService->save();
// }
// }
// $originalResource = $isApplication ? $resource : $savedService;
// if ($volumes->count() > 0) {
// foreach ($volumes as $index => $volume) {
// $type = null;
// $source = null;
// $target = null;
// $content = null;
// $isDirectory = false;
// if (is_string($volume)) {
// $source = str($volume)->before(':');
// $target = str($volume)->after(':')->beforeLast(':');
// $foundConfig = $fileStorages->whereMountPath($target)->first();
// if (sourceIsLocal($source)) {
// $type = str('bind');
// if ($foundConfig) {
// $contentNotNull_temp = data_get($foundConfig, 'content');
// if ($contentNotNull_temp) {
// $content = $contentNotNull_temp;
// }
// $isDirectory = data_get($foundConfig, 'is_directory');
// } else {
// // By default, we cannot determine if the bind is a directory or not, so we set it to directory
// $isDirectory = true;
// }
// } else {
// $type = str('volume');
// }
// } elseif (is_array($volume)) {
// $type = data_get_str($volume, 'type');
// $source = data_get_str($volume, 'source');
// $target = data_get_str($volume, 'target');
// $content = data_get($volume, 'content');
// $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
// $foundConfig = $fileStorages->whereMountPath($target)->first();
// if ($foundConfig) {
// $contentNotNull_temp = data_get($foundConfig, 'content');
// if ($contentNotNull_temp) {
// $content = $contentNotNull_temp;
// }
// $isDirectory = data_get($foundConfig, 'is_directory');
// } else {
// // if isDirectory is not set (or false) & content is also not set, we assume it is a directory
// if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
// $isDirectory = true;
// }
// }
// }
// if ($type->value() === 'bind') {
// if ($source->value() === '/var/run/docker.sock') {
// $volume = $source->value().':'.$target->value();
// } elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
// $volume = $source->value().':'.$target->value();
// } else {
// if ((int) $resource->compose_parsing_version >= 4) {
// if ($isApplication) {
// $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
// } elseif ($isService) {
// $mainDirectory = str(base_configuration_dir().'/services/'.$uuid);
// }
// } else {
// $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
// }
// $source = replaceLocalSource($source, $mainDirectory);
// if ($isApplication && $isPullRequest) {
// $source = $source."-pr-$pullRequestId";
// }
// LocalFileVolume::updateOrCreate(
// [
// 'mount_path' => $target,
// 'resource_id' => $originalResource->id,
// 'resource_type' => get_class($originalResource),
// ],
// [
// 'fs_path' => $source,
// 'mount_path' => $target,
// 'content' => $content,
// 'is_directory' => $isDirectory,
// 'resource_id' => $originalResource->id,
// 'resource_type' => get_class($originalResource),
// ]
// );
// if (isDev()) {
// if ((int) $resource->compose_parsing_version >= 4) {
// if ($isApplication) {
// $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
// } elseif ($isService) {
// $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/'.$uuid);
// }
// } else {
// $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
// }
// }
// $volume = "$source:$target";
// }
// } elseif ($type->value() === 'volume') {
// if ($topLevel->get('volumes')->has($source->value())) {
// $temp = $topLevel->get('volumes')->get($source->value());
// if (data_get($temp, 'driver_opts.type') === 'cifs') {
// continue;
// }
// if (data_get($temp, 'driver_opts.type') === 'nfs') {
// continue;
// }
// }
// $slugWithoutUuid = Str::slug($source, '-');
// $name = "{$uuid}_{$slugWithoutUuid}";
// if ($isApplication && $isPullRequest) {
// $name = "{$name}-pr-$pullRequestId";
// }
// if (is_string($volume)) {
// $source = str($volume)->before(':');
// $target = str($volume)->after(':')->beforeLast(':');
// $source = $name;
// $volume = "$source:$target";
// } elseif (is_array($volume)) {
// data_set($volume, 'source', $name);
// }
// $topLevel->get('volumes')->put($name, [
// 'name' => $name,
// ]);
// LocalPersistentVolume::updateOrCreate(
// [
// 'name' => $name,
// 'resource_id' => $originalResource->id,
// 'resource_type' => get_class($originalResource),
// ],
// [
// 'name' => $name,
// 'mount_path' => $target,
// 'resource_id' => $originalResource->id,
// 'resource_type' => get_class($originalResource),
// ]
// );
// }
// dispatch(new ServerFilesFromServerJob($originalResource));
// $volumesParsed->put($index, $volume);
// }
// }
// if ($depends_on?->count() > 0) {
// if ($isApplication && $isPullRequest) {
// $newDependsOn = collect([]);
// $depends_on->each(function ($dependency, $condition) use ($pullRequestId, $newDependsOn) {
// if (is_numeric($condition)) {
// $dependency = "$dependency-pr-$pullRequestId";
// $newDependsOn->put($condition, $dependency);
// } else {
// $condition = "$condition-pr-$pullRequestId";
// $newDependsOn->put($condition, $dependency);
// }
// });
// $depends_on = $newDependsOn;
// }
// }
// if (! $use_network_mode) {
// if ($topLevel->get('networks')?->count() > 0) {
// foreach ($topLevel->get('networks') as $networkName => $network) {
// if ($networkName === 'default') {
// continue;
// }
// // ignore aliases
// if ($network['aliases'] ?? false) {
// continue;
// }
// $networkExists = $networks->contains(function ($value, $key) use ($networkName) {
// return $value == $networkName || $key == $networkName;
// });
// if (! $networkExists) {
// $networks->put($networkName, null);
// }
// }
// }
// $baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
// return $value == $baseNetwork;
// });
// if (! $baseNetworkExists) {
// foreach ($baseNetwork as $network) {
// $topLevel->get('networks')->put($network, [
// 'name' => $network,
// 'external' => true,
// ]);
// }
// }
// }
// // Collect/create/update ports
// $collectedPorts = collect([]);
// if ($ports->count() > 0) {
// foreach ($ports as $sport) {
// if (is_string($sport) || is_numeric($sport)) {
// $collectedPorts->push($sport);
// }
// if (is_array($sport)) {
// $target = data_get($sport, 'target');
// $published = data_get($sport, 'published');
// $protocol = data_get($sport, 'protocol');
// $collectedPorts->push("$target:$published/$protocol");
// }
// }
// }
// if ($isService) {
// $originalResource->ports = $collectedPorts->implode(',');
// $originalResource->save();
// }
// $networks_temp = collect();
// if (! $use_network_mode) {
// foreach ($networks as $key => $network) {
// if (gettype($network) === 'string') {
// // networks:
// // - appwrite
// $networks_temp->put($network, null);
// } elseif (gettype($network) === 'array') {
// // networks:
// // default:
// // ipv4_address: 192.168.203.254
// $networks_temp->put($key, $network);
// }
// }
// foreach ($baseNetwork as $key => $network) {
// $networks_temp->put($network, null);
// }
// if ($isApplication) {
// if (data_get($resource, 'settings.connect_to_docker_network')) {
// $network = $resource->destination->network;
// $networks_temp->put($network, null);
// $topLevel->get('networks')->put($network, [
// 'name' => $network,
// 'external' => true,
// ]);
// }
// }
// }
// $normalEnvironments = $environment->diffKeys($allMagicEnvironments);
// $normalEnvironments = $normalEnvironments->filter(function ($value, $key) {
// return ! str($value)->startsWith('SERVICE_');
// });
// foreach ($normalEnvironments as $key => $value) {
// $key = str($key);
// $value = str($value);
// $originalValue = $value;
// $parsedValue = replaceVariables($value);
// if ($value->startsWith('$SERVICE_')) {
// $resource->environment_variables()->firstOrCreate([
// 'key' => $key,
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $value,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// continue;
// }
// if (! $value->startsWith('$')) {
// continue;
// }
// if ($key->value() === $parsedValue->value()) {
// $value = null;
// $resource->environment_variables()->firstOrCreate([
// 'key' => $key,
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $value,
// 'is_build_time' => false,
// 'is_preview' => false,
// ]);
// } else {
// if ($value->startsWith('$')) {
// $isRequired = false;
// if ($value->contains(':-')) {
// $value = replaceVariables($value);
// $key = $value->before(':');
// $value = $value->after(':-');
// } elseif ($value->contains('-')) {
// $value = replaceVariables($value);
// $key = $value->before('-');
// $value = $value->after('-');
// } elseif ($value->contains(':?')) {
// $value = replaceVariables($value);
// $key = $value->before(':');
// $value = $value->after(':?');
// $isRequired = true;
// } elseif ($value->contains('?')) {
// $value = replaceVariables($value);
// $key = $value->before('?');
// $value = $value->after('?');
// $isRequired = true;
// }
// if ($originalValue->value() === $value->value()) {
// // This means the variable does not have a default value, so it needs to be created in Coolify
// $parsedKeyValue = replaceVariables($value);
// $resource->environment_variables()->firstOrCreate([
// 'key' => $parsedKeyValue,
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'is_build_time' => false,
// 'is_preview' => false,
// 'is_required' => $isRequired,
// ]);
// // Add the variable to the environment so it will be shown in the deployable compose file
// // $environment[$parsedKeyValue->value()] = $resource->environment_variables()->where('key', $parsedKeyValue)->where('resourceable_type', get_class($resource))->where('resourceable_id', $resource->id)->first()->real_value;
// $environment[$parsedKeyValue->value()] = $value;
// continue;
// }
// $resource->environment_variables()->firstOrCreate([
// 'key' => $key,
// 'resourceable_type' => get_class($resource),
// 'resourceable_id' => $resource->id,
// ], [
// 'value' => $value,
// 'is_build_time' => false,
// 'is_preview' => false,
// 'is_required' => $isRequired,
// ]);
// }
// }
// }
// if ($isApplication) {
// $branch = $originalResource->git_branch;
// if ($pullRequestId !== 0) {
// $branch = "pull/{$pullRequestId}/head";
// }
// if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
// $coolifyEnvironments->put('COOLIFY_BRANCH', "\"{$branch}\"");
// }
// }
// // Add COOLIFY_RESOURCE_UUID to environment
// if ($resource->environment_variables->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
// $coolifyEnvironments->put('COOLIFY_RESOURCE_UUID', "{$resource->uuid}");
// }
// // Add COOLIFY_CONTAINER_NAME to environment
// if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
// $coolifyEnvironments->put('COOLIFY_CONTAINER_NAME', "{$containerName}");
// }
// if ($isApplication) {
// if ($isPullRequest) {
// $preview = $resource->previews()->find($preview_id);
// $domains = collect(json_decode(data_get($preview, 'docker_compose_domains'))) ?? collect([]);
// } else {
// $domains = collect(json_decode($resource->docker_compose_domains)) ?? collect([]);
// }
// $fqdns = data_get($domains, "$serviceName.domain");
// // Generate SERVICE_FQDN & SERVICE_URL for dockercompose
// if ($resource->build_pack === 'dockercompose') {
// foreach ($domains as $forServiceName => $domain) {
// $parsedDomain = data_get($domain, 'domain');
// if (filled($parsedDomain)) {
// $parsedDomain = str($parsedDomain)->explode(',')->first();
// $coolifyUrl = Url::fromString($parsedDomain);
// $coolifyScheme = $coolifyUrl->getScheme();
// $coolifyFqdn = $coolifyUrl->getHost();
// $coolifyUrl = $coolifyUrl->withScheme($coolifyScheme)->withHost($coolifyFqdn)->withPort(null);
// $coolifyEnvironments->put('SERVICE_URL_'.str($forServiceName)->upper()->replace('-', '_'), $coolifyUrl->__toString());
// $coolifyEnvironments->put('SERVICE_FQDN_'.str($forServiceName)->upper()->replace('-', '_'), $coolifyFqdn);
// }
// }
// }
// // If the domain is set, we need to generate the FQDNs for the preview
// if (filled($fqdns)) {
// $fqdns = str($fqdns)->explode(',');
// if ($isPullRequest) {
// $preview = $resource->previews()->find($preview_id);
// $docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
// if ($docker_compose_domains->count() > 0) {
// $found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
// if ($found_fqdn) {
// $fqdns = collect($found_fqdn);
// } else {
// $fqdns = collect([]);
// }
// } else {
// $fqdns = $fqdns->map(function ($fqdn) use ($pullRequestId, $resource) {
// $preview = ApplicationPreview::findPreviewByApplicationAndPullId($resource->id, $pullRequestId);
// $url = Url::fromString($fqdn);
// $template = $resource->preview_url_template;
// $host = $url->getHost();
// $schema = $url->getScheme();
// $random = new Cuid2;
// $preview_fqdn = str_replace('{{random}}', $random, $template);
// $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
// $preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
// $preview_fqdn = "$schema://$preview_fqdn";
// $preview->fqdn = $preview_fqdn;
// $preview->save();
// return $preview_fqdn;
// });
// }
// }
// }
// $defaultLabels = defaultLabels(
// id: $resource->id,
// name: $containerName,
// projectName: $resource->project()->name,
// resourceName: $resource->name,
// pull_request_id: $pullRequestId,
// type: 'application',
// environment: $resource->environment->name,
// );
// } elseif ($isService) {
// if ($savedService->serviceType()) {
// $fqdns = generateServiceSpecificFqdns($savedService);
// } else {
// $fqdns = collect(data_get($savedService, 'fqdns'))->filter();
// }
// $defaultLabels = defaultLabels(
// id: $resource->id,
// name: $containerName,
// projectName: $resource->project()->name,
// resourceName: $resource->name,
// type: 'service',
// subType: $isDatabase ? 'database' : 'application',
// subId: $savedService->id,
// subName: $savedService->human_name ?? $savedService->name,
// environment: $resource->environment->name,
// );
// }
// // Add COOLIFY_FQDN & COOLIFY_URL to environment
// if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
// $fqdnsWithoutPort = $fqdns->map(function ($fqdn) {
// return str($fqdn)->after('://')->before(':')->prepend(str($fqdn)->before('://')->append('://'));
// });
// $coolifyEnvironments->put('COOLIFY_URL', $fqdnsWithoutPort->implode(','));
// $urls = $fqdns->map(function ($fqdn) {
// return str($fqdn)->replace('http://', '')->replace('https://', '')->before(':');
// });
// $coolifyEnvironments->put('COOLIFY_FQDN', $urls->implode(','));
// }
// add_coolify_default_environment_variables($resource, $coolifyEnvironments, $resource->environment_variables);
// if ($environment->count() > 0) {
// $environment = $environment->filter(function ($value, $key) {
// return ! str($key)->startsWith('SERVICE_FQDN_');
// })->map(function ($value, $key) use ($resource) {
// // if value is empty, set it to null so if you set the environment variable in the .env file (Coolify's UI), it will used
// if (str($value)->isEmpty()) {
// if ($resource->environment_variables()->where('key', $key)->exists()) {
// $value = $resource->environment_variables()->where('key', $key)->first()->value;
// } else {
// $value = null;
// }
// }
// return $value;
// });
// }
// $serviceLabels = $labels->merge($defaultLabels);
// if ($serviceLabels->count() > 0) {
// if ($isApplication) {
// $isContainerLabelEscapeEnabled = data_get($resource, 'settings.is_container_label_escape_enabled');
// } else {
// $isContainerLabelEscapeEnabled = data_get($resource, 'is_container_label_escape_enabled');
// }
// if ($isContainerLabelEscapeEnabled) {
// $serviceLabels = $serviceLabels->map(function ($value, $key) {
// return escapeDollarSign($value);
// });
// }
// }
// if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
// if ($isApplication) {
// $shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
// $uuid = $resource->uuid;
// $network = data_get($resource, 'destination.network');
// if ($isPullRequest) {
// $uuid = "{$resource->uuid}-{$pullRequestId}";
// }
// if ($isPullRequest) {
// $network = "{$resource->destination->network}-{$pullRequestId}";
// }
// } else {
// $shouldGenerateLabelsExactly = $resource->server->settings->generate_exact_labels;
// $uuid = $resource->uuid;
// $network = data_get($resource, 'destination.network');
// }
// if ($shouldGenerateLabelsExactly) {
// switch ($server->proxyType()) {
// case ProxyTypes::TRAEFIK->value:
// $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
// uuid: $uuid,
// domains: $fqdns,
// is_force_https_enabled: true,
// serviceLabels: $serviceLabels,
// is_gzip_enabled: $originalResource->isGzipEnabled(),
// is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
// service_name: $serviceName,
// image: $image
// ));
// break;
// case ProxyTypes::CADDY->value:
// $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
// network: $network,
// uuid: $uuid,
// domains: $fqdns,
// is_force_https_enabled: true,
// serviceLabels: $serviceLabels,
// is_gzip_enabled: $originalResource->isGzipEnabled(),
// is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
// service_name: $serviceName,
// image: $image,
// predefinedPort: $predefinedPort
// ));
// break;
// }
// } else {
// $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
// uuid: $uuid,
// domains: $fqdns,
// is_force_https_enabled: true,
// serviceLabels: $serviceLabels,
// is_gzip_enabled: $originalResource->isGzipEnabled(),
// is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
// service_name: $serviceName,
// image: $image
// ));
// $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
// network: $network,
// uuid: $uuid,
// domains: $fqdns,
// is_force_https_enabled: true,
// serviceLabels: $serviceLabels,
// is_gzip_enabled: $originalResource->isGzipEnabled(),
// is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
// service_name: $serviceName,
// image: $image,
// predefinedPort: $predefinedPort
// ));
// }
// }
// if ($isService) {
// if (data_get($service, 'restart') === 'no' || data_get($service, 'exclude_from_hc')) {
// $savedService->update(['exclude_from_status' => true]);
// }
// }
// data_forget($service, 'volumes.*.content');
// data_forget($service, 'volumes.*.isDirectory');
// data_forget($service, 'volumes.*.is_directory');
// data_forget($service, 'exclude_from_hc');
// $volumesParsed = $volumesParsed->map(function ($volume) {
// data_forget($volume, 'content');
// data_forget($volume, 'is_directory');
// data_forget($volume, 'isDirectory');
// return $volume;
// });
// $payload = collect($service)->merge([
// 'container_name' => $containerName,
// 'restart' => $restart->value(),
// 'labels' => $serviceLabels,
// ]);
// if (! $use_network_mode) {
// $payload['networks'] = $networks_temp;
// }
// if ($ports->count() > 0) {
// $payload['ports'] = $ports;
// }
// if ($volumesParsed->count() > 0) {
// $payload['volumes'] = $volumesParsed;
// }
// if ($environment->count() > 0 || $coolifyEnvironments->count() > 0) {
// $payload['environment'] = $environment->merge($coolifyEnvironments);
// }
// if ($logging) {
// $payload['logging'] = $logging;
// }
// if ($depends_on->count() > 0) {
// $payload['depends_on'] = $depends_on;
// }
// if ($isApplication && $isPullRequest) {
// $serviceName = "{$serviceName}-pr-{$pullRequestId}";
// }
// $parsedServices->put($serviceName, $payload);
// }
// $topLevel->put('services', $parsedServices);
// $customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
// $topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
// return array_search($key, $customOrder);
// });
// $resource->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
// data_forget($resource, 'environment_variables');
// data_forget($resource, 'environment_variables_preview');
// $resource->save();
// return $topLevel;
// }
2024-08-28 09:11:14 +00:00
2024-08-29 13:11:54 +00:00
function generate_fluentd_configuration () : array
{
2024-08-28 09:11:14 +00:00
return [
'driver' => 'fluentd' ,
'options' => [
'fluentd-address' => 'tcp://127.0.0.1:24224' ,
'fluentd-async' => 'true' ,
'fluentd-sub-second-precision' => 'true' ,
// env vars are used in the LogDrain configurations
'env' => 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME' ,
2024-08-29 13:11:54 +00:00
],
2024-08-28 09:11:14 +00:00
];
}
2024-08-28 11:30:59 +00:00
2024-09-03 15:04:56 +00:00
function isAssociativeArray ( $array )
{
if ( $array instanceof Collection ) {
$array = $array -> toArray ();
}
2024-12-09 14:38:21 +00:00
if ( ! is_array ( $array )) {
2024-09-03 15:04:56 +00:00
throw new \InvalidArgumentException ( 'Input must be an array or a Collection.' );
}
if ( $array === []) {
return false ;
}
return array_keys ( $array ) !== range ( 0 , count ( $array ) - 1 );
}
2024-08-28 11:30:59 +00:00
/**
2024-08-29 13:11:54 +00:00
* This method adds the default environment variables to the resource .
* - COOLIFY_APP_NAME
* - COOLIFY_PROJECT_NAME
* - COOLIFY_SERVER_IP
* - COOLIFY_ENVIRONMENT_NAME
*
* Theses variables are added in place to the $where_to_add array .
*/
function add_coolify_default_environment_variables ( StandaloneRedis | StandalonePostgresql | StandaloneMongodb | StandaloneMysql | StandaloneMariadb | StandaloneKeydb | StandaloneDragonfly | StandaloneClickhouse | Application | Service $resource , Collection & $where_to_add , ? Collection $where_to_check = null )
{
2024-10-11 09:04:44 +00:00
// Currently disabled
return ;
2024-09-03 15:04:56 +00:00
if ( $resource instanceof Service ) {
$ip = $resource -> server -> ip ;
} else {
$ip = $resource -> destination -> server -> ip ;
}
if ( isAssociativeArray ( $where_to_add )) {
$isAssociativeArray = true ;
} else {
$isAssociativeArray = false ;
}
2024-08-28 11:30:59 +00:00
if ( $where_to_check != null && $where_to_check -> where ( 'key' , 'COOLIFY_APP_NAME' ) -> isEmpty ()) {
2024-09-03 15:04:56 +00:00
if ( $isAssociativeArray ) {
2024-09-13 06:23:05 +00:00
$where_to_add -> put ( 'COOLIFY_APP_NAME' , " \" { $resource -> name } \" " );
2024-08-29 13:11:54 +00:00
} else {
2024-09-13 06:23:05 +00:00
$where_to_add -> push ( " COOLIFY_APP_NAME= \" { $resource -> name } \" " );
2024-08-29 13:11:54 +00:00
}
2024-08-28 11:30:59 +00:00
}
if ( $where_to_check != null && $where_to_check -> where ( 'key' , 'COOLIFY_SERVER_IP' ) -> isEmpty ()) {
2024-09-03 15:04:56 +00:00
if ( $isAssociativeArray ) {
2024-09-13 06:23:05 +00:00
$where_to_add -> put ( 'COOLIFY_SERVER_IP' , " \" { $ip } \" " );
2024-08-29 13:11:54 +00:00
} else {
2024-09-13 06:23:05 +00:00
$where_to_add -> push ( " COOLIFY_SERVER_IP= \" { $ip } \" " );
2024-08-29 13:11:54 +00:00
}
2024-08-28 11:30:59 +00:00
}
if ( $where_to_check != null && $where_to_check -> where ( 'key' , 'COOLIFY_ENVIRONMENT_NAME' ) -> isEmpty ()) {
2024-09-03 15:04:56 +00:00
if ( $isAssociativeArray ) {
2024-09-13 06:23:05 +00:00
$where_to_add -> put ( 'COOLIFY_ENVIRONMENT_NAME' , " \" { $resource -> environment -> name } \" " );
2024-08-29 13:11:54 +00:00
} else {
2024-09-13 06:23:05 +00:00
$where_to_add -> push ( " COOLIFY_ENVIRONMENT_NAME= \" { $resource -> environment -> name } \" " );
2024-08-29 13:11:54 +00:00
}
2024-08-28 11:30:59 +00:00
}
if ( $where_to_check != null && $where_to_check -> where ( 'key' , 'COOLIFY_PROJECT_NAME' ) -> isEmpty ()) {
2024-09-03 15:04:56 +00:00
if ( $isAssociativeArray ) {
2024-09-13 06:23:05 +00:00
$where_to_add -> put ( 'COOLIFY_PROJECT_NAME' , " \" { $resource -> project () -> name } \" " );
2024-08-29 13:11:54 +00:00
} else {
2024-09-13 06:23:05 +00:00
$where_to_add -> push ( " COOLIFY_PROJECT_NAME= \" { $resource -> project () -> name } \" " );
2024-08-29 13:11:54 +00:00
}
2024-08-28 11:30:59 +00:00
}
2024-08-29 13:11:54 +00:00
}
2024-08-29 12:35:04 +00:00
2025-02-27 10:29:04 +00:00
function convertToKeyValueCollection ( $environment )
2024-08-28 13:45:11 +00:00
{
$convertedServiceVariables = collect ([]);
2024-09-04 11:37:15 +00:00
if ( isAssociativeArray ( $environment )) {
2024-10-02 16:26:40 +00:00
// Example: $environment = ['FOO' => 'bar', 'BAZ' => 'qux'];
2024-09-30 09:15:23 +00:00
if ( $environment instanceof Collection ) {
$changedEnvironment = collect ([]);
$environment -> each ( function ( $value , $key ) use ( $changedEnvironment ) {
2024-10-02 16:26:40 +00:00
if ( is_numeric ( $key )) {
$parts = explode ( '=' , $value , 2 );
if ( count ( $parts ) === 2 ) {
$key = $parts [ 0 ];
$realValue = $parts [ 1 ] ? ? '' ;
$changedEnvironment -> put ( $key , $realValue );
} else {
$changedEnvironment -> put ( $key , $value );
}
2024-09-30 09:15:23 +00:00
} else {
$changedEnvironment -> put ( $key , $value );
}
});
return $changedEnvironment ;
}
2024-09-04 11:37:15 +00:00
$convertedServiceVariables = $environment ;
} else {
2024-10-02 16:26:40 +00:00
// Example: $environment = ['FOO=bar', 'BAZ=qux'];
2024-09-04 11:37:15 +00:00
foreach ( $environment as $value ) {
2024-10-03 13:04:40 +00:00
if ( is_string ( $value )) {
$parts = explode ( '=' , $value , 2 );
$key = $parts [ 0 ];
$realValue = $parts [ 1 ] ? ? '' ;
if ( $key ) {
$convertedServiceVariables -> put ( $key , $realValue );
}
2024-08-28 13:45:11 +00:00
}
}
}
return $convertedServiceVariables ;
2024-08-28 11:30:59 +00:00
}
2024-10-01 08:33:56 +00:00
function instanceSettings ()
{
return InstanceSettings :: get ();
}
2024-10-08 13:11:19 +00:00
2024-10-17 19:48:47 +00:00
function loadConfigFromGit ( string $repository , string $branch , string $base_directory , int $server_id , int $team_id )
{
2024-10-08 13:11:19 +00:00
$server = Server :: find ( $server_id ) -> where ( 'team_id' , $team_id ) -> first ();
2024-12-09 14:38:21 +00:00
if ( ! $server ) {
2024-10-08 13:11:19 +00:00
return ;
}
2024-10-17 19:48:47 +00:00
$uuid = new Cuid2 ;
2024-10-08 13:11:19 +00:00
$cloneCommand = " git clone --no-checkout -b $branch $repository . " ;
$workdir = rtrim ( $base_directory , '/' );
$fileList = collect ([ " . $workdir /coolify.json " ]);
$commands = collect ([
" rm -rf /tmp/ { $uuid } " ,
" mkdir -p /tmp/ { $uuid } " ,
" cd /tmp/ { $uuid } " ,
$cloneCommand ,
'git sparse-checkout init --cone' ,
" git sparse-checkout set { $fileList -> implode ( ' ' ) } " ,
'git read-tree -mu HEAD' ,
" cat . $workdir /coolify.json " ,
'rm -rf /tmp/{$uuid}' ,
]);
try {
return instant_remote_process ( $commands , $server );
2024-10-31 14:19:37 +00:00
} catch ( \Exception ) {
2024-10-17 19:48:47 +00:00
// continue
2024-10-08 13:11:19 +00:00
}
}
2024-10-17 08:04:49 +00:00
2024-10-25 15:49:16 +00:00
function loggy ( $message = null , array $context = [])
2024-10-17 08:04:49 +00:00
{
2024-12-09 14:38:21 +00:00
if ( ! isDev ()) {
2024-10-17 08:04:49 +00:00
return ;
}
if ( function_exists ( 'ray' ) && config ( 'app.debug' )) {
ray ( $message , $context );
}
if ( is_null ( $message )) {
return app ( 'log' );
}
return app ( 'log' ) -> debug ( $message , $context );
}
2024-10-20 20:15:31 +00:00
function sslipDomainWarning ( string $domains )
{
$domains = str ( $domains ) -> trim () -> explode ( ',' );
$showSslipHttpsWarning = false ;
$domains -> each ( function ( $domain ) use ( & $showSslipHttpsWarning ) {
if ( str ( $domain ) -> contains ( 'https' ) && str ( $domain ) -> contains ( 'sslip' )) {
$showSslipHttpsWarning = true ;
}
});
return $showSslipHttpsWarning ;
}
2024-10-25 13:13:23 +00:00
function isEmailRateLimited ( string $limiterKey , int $decaySeconds = 3600 , ? callable $callbackOnSuccess = null ) : bool
{
if ( isDev ()) {
$decaySeconds = 120 ;
}
$rateLimited = false ;
$executed = RateLimiter :: attempt (
$limiterKey ,
$maxAttempts = 0 ,
function () use ( & $rateLimited , & $limiterKey , $callbackOnSuccess ) {
2024-12-09 14:38:21 +00:00
isDev () && loggy ( 'Rate limit not reached for ' . $limiterKey );
2024-10-25 13:13:23 +00:00
$rateLimited = false ;
if ( $callbackOnSuccess ) {
$callbackOnSuccess ();
}
},
$decaySeconds ,
);
2024-12-09 14:38:21 +00:00
if ( ! $executed ) {
isDev () && loggy ( 'Rate limit reached for ' . $limiterKey . '. Rate limiter will be disabled for ' . $decaySeconds . ' seconds.' );
2024-10-25 13:13:23 +00:00
$rateLimited = true ;
}
return $rateLimited ;
}
2024-11-11 13:37:19 +00:00
2025-03-31 13:10:50 +00:00
function defaultNginxConfiguration ( string $type = 'static' ) : string
2024-11-11 13:37:19 +00:00
{
2025-03-31 13:10:50 +00:00
if ( $type === 'spa' ) {
return <<< 'NGINX'
server {
location / {
root / usr / share / nginx / html ;
index index . html ;
try_files $uri $uri / / index . html ;
}
# Handle 404 errors
error_page 404 / 404. html ;
location = / 404. html {
root / usr / share / nginx / html ;
internal ;
}
# Handle server errors (50x)
error_page 500 502 503 504 / 50 x . html ;
location = / 50 x . html {
root / usr / share / nginx / html ;
internal ;
}
}
NGINX ;
} else {
return <<< 'NGINX'
server {
2024-11-11 13:37:19 +00:00
location / {
2025-03-13 19:30:22 +00:00
root / usr / share / nginx / html ;
index index . html index . htm ;
try_files $uri $uri . html $uri / index . html $uri / index . htm $uri / = 404 ;
2024-11-11 13:37:19 +00:00
}
2025-03-13 19:30:22 +00:00
# Handle 404 errors
error_page 404 / 404. html ;
location = / 404. html {
2024-11-11 13:45:34 +00:00
root / usr / share / nginx / html ;
internal ;
2024-11-11 13:37:19 +00:00
}
2025-03-13 19:30:22 +00:00
# Handle server errors (50x)
error_page 500 502 503 504 / 50 x . html ;
location = / 50 x . html {
2024-11-11 13:37:19 +00:00
root / usr / share / nginx / html ;
internal ;
}
2025-03-31 13:10:50 +00:00
}
NGINX ;
}
2024-11-11 13:37:19 +00:00
}
2024-11-12 10:32:18 +00:00
function convertGitUrl ( string $gitRepository , string $deploymentType , ? GithubApp $source = null ) : array
{
$repository = $gitRepository ;
$providerInfo = [
'host' => null ,
'user' => 'git' ,
'port' => 22 ,
'repository' => $gitRepository ,
];
$sshMatches = [];
$matches = [];
// Let's try and parse the string to detect if it's a valid SSH string or not
preg_match ( '/((.*?)\:\/\/)?(.*@.*:.*)/' , $gitRepository , $sshMatches );
if ( $deploymentType === 'deploy_key' && empty ( $sshMatches ) && $source ) {
// If this happens, the user may have provided an HTTP URL when they needed an SSH one
// Let's try and fix that for known Git providers
switch ( $source -> getMorphClass ()) {
case \App\Models\GithubApp :: class :
$providerInfo [ 'host' ] = Url :: fromString ( $source -> html_url ) -> getHost ();
$providerInfo [ 'port' ] = $source -> custom_port ;
$providerInfo [ 'user' ] = $source -> custom_user ;
break ;
}
if ( ! empty ( $providerInfo [ 'host' ])) {
// Until we do not support more providers with App (like GithubApp), this will be always true, port will be 22
if ( $providerInfo [ 'port' ] === 22 ) {
$repository = " { $providerInfo [ 'user' ] } @ { $providerInfo [ 'host' ] } : { $providerInfo [ 'repository' ] } " ;
} else {
$repository = " ssh:// { $providerInfo [ 'user' ] } @ { $providerInfo [ 'host' ] } : { $providerInfo [ 'port' ] } / { $providerInfo [ 'repository' ] } " ;
}
}
}
preg_match ( '/(?<=:)\d+(?=\/)/' , $gitRepository , $matches );
if ( count ( $matches ) === 1 ) {
$providerInfo [ 'port' ] = $matches [ 0 ];
$gitHost = str ( $gitRepository ) -> before ( ':' );
$gitRepo = str ( $gitRepository ) -> after ( '/' );
$repository = " $gitHost : $gitRepo " ;
}
return [
'repository' => $repository ,
'port' => $providerInfo [ 'port' ],
];
}
2025-01-10 17:27:48 +00:00
2025-01-10 18:53:13 +00:00
function getJobStatus ( ? string $jobId = null )
2025-01-10 17:27:48 +00:00
{
2025-01-10 18:53:13 +00:00
if ( blank ( $jobId )) {
return 'unknown' ;
}
2025-01-10 17:27:48 +00:00
$jobFound = app ( JobRepository :: class ) -> getJobs ([ $jobId ]);
if ( $jobFound -> isEmpty ()) {
return 'unknown' ;
}
return $jobFound -> first () -> status ;
}
2025-03-24 10:43:10 +00:00
function parseDockerfileInterval ( string $something )
{
$value = preg_replace ( '/[^0-9]/' , '' , $something );
$unit = preg_replace ( '/[0-9]/' , '' , $something );
// Default to seconds if no unit specified
$unit = $unit ? : 's' ;
// Convert to seconds based on unit
$seconds = ( int ) $value ;
switch ( $unit ) {
case 'ns' :
$seconds = ( int ) ( $value / 1000000000 );
break ;
case 'us' :
case 'µs' :
$seconds = ( int ) ( $value / 1000000 );
break ;
case 'ms' :
$seconds = ( int ) ( $value / 1000 );
break ;
case 'm' :
$seconds = ( int ) ( $value * 60 );
break ;
case 'h' :
$seconds = ( int ) ( $value * 3600 );
break ;
}
return $seconds ;
}