Merge remote-tracking branch 'origin/next' into feat/railpack
This commit is contained in:
commit
451b7376ed
49 changed files with 631 additions and 289 deletions
|
|
@ -88,6 +88,14 @@ private function processFile(string $file): false|array
|
|||
$payload['envs'] = base64_encode($envFileContent);
|
||||
}
|
||||
|
||||
if (str($data->get('amd_only'))->toBoolean()) {
|
||||
$payload['amd_only'] = true;
|
||||
}
|
||||
|
||||
if (str($data->get('arm_only'))->toBoolean()) {
|
||||
$payload['arm_only'] = true;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +168,14 @@ private function processFileWithFqdn(string $file): false|array
|
|||
$payload['envs'] = base64_encode($modifiedEnvContent);
|
||||
}
|
||||
|
||||
if (str($data->get('amd_only'))->toBoolean()) {
|
||||
$payload['amd_only'] = true;
|
||||
}
|
||||
|
||||
if (str($data->get('arm_only'))->toBoolean()) {
|
||||
$payload['arm_only'] = true;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
|
|
@ -229,6 +245,14 @@ private function processFileWithFqdnRaw(string $file): false|array
|
|||
$payload['envs'] = $modifiedEnvContent;
|
||||
}
|
||||
|
||||
if (str($data->get('amd_only'))->toBoolean()) {
|
||||
$payload['amd_only'] = true;
|
||||
}
|
||||
|
||||
if (str($data->get('arm_only'))->toBoolean()) {
|
||||
$payload['arm_only'] = true;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ public function applications(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
@ -383,7 +383,7 @@ public function create_public_application(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
@ -549,7 +549,7 @@ public function create_private_gh_app_application(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
@ -2397,7 +2397,7 @@ public function delete_by_uuid(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'],
|
||||
'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ public function services(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'],
|
||||
'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").'],
|
||||
'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").'],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
@ -843,7 +843,7 @@ public function delete_by_uuid(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'],
|
||||
'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").'],
|
||||
'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").'],
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -3198,7 +3198,7 @@ private function generate_healthcheck_commands()
|
|||
$scheme = $this->sanitizeHealthCheckValue($this->application->health_check_scheme, '/^https?$/', 'http');
|
||||
$host = $this->sanitizeHealthCheckValue($this->application->health_check_host, '/^[a-zA-Z0-9.\-_]+$/', 'localhost');
|
||||
$path = $this->application->health_check_path
|
||||
? $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%]+$#', '/')
|
||||
? $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%,;]+$#', '/')
|
||||
: null;
|
||||
|
||||
$url = escapeshellarg("{$scheme}://{$host}:{$health_check_port}".($path ?? '/'));
|
||||
|
|
|
|||
|
|
@ -1496,7 +1496,10 @@ public function getServicesProperty()
|
|||
'category' => 'Services',
|
||||
'resourceType' => 'service',
|
||||
'logo' => data_get($service, 'logo'),
|
||||
]);
|
||||
] + array_filter([
|
||||
'amd_only' => data_get($service, 'amd_only') ? true : null,
|
||||
'arm_only' => data_get($service, 'arm_only') ? true : null,
|
||||
]));
|
||||
}
|
||||
|
||||
$cachedServices = $items->toArray();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class HealthChecks extends Component
|
|||
#[Validate(['nullable', 'integer', 'min:1', 'max:65535'])]
|
||||
public ?string $healthCheckPort = null;
|
||||
|
||||
#[Validate(['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'])]
|
||||
#[Validate(['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%,;]+$#'])]
|
||||
public string $healthCheckPath;
|
||||
|
||||
#[Validate(['integer'])]
|
||||
|
|
@ -62,7 +62,7 @@ class HealthChecks extends Component
|
|||
'healthCheckEnabled' => 'boolean',
|
||||
'healthCheckType' => 'string|in:http,cmd',
|
||||
'healthCheckCommand' => ['nullable', 'string', 'max:1000', 'regex:/^[a-zA-Z0-9 \-_.\/:=@,+]+$/'],
|
||||
'healthCheckPath' => ['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'],
|
||||
'healthCheckPath' => ['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%,;]+$#'],
|
||||
'healthCheckPort' => 'nullable|integer|min:1|max:65535',
|
||||
'healthCheckHost' => ['required', 'string', 'regex:/^[a-zA-Z0-9.\-_]+$/'],
|
||||
'healthCheckMethod' => 'required|string|in:GET,HEAD,POST,OPTIONS',
|
||||
|
|
|
|||
|
|
@ -233,6 +233,9 @@ public function subscriptionEnded()
|
|||
'is_reachable' => false,
|
||||
]);
|
||||
ServerReachabilityChanged::dispatch($server);
|
||||
$server->unreachable_count = 3;
|
||||
$server->unreachable_notification_sent = true;
|
||||
$server->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -344,5 +347,4 @@ public function webhookNotificationSettings()
|
|||
{
|
||||
return $this->hasOne(WebhookNotificationSettings::class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,10 +203,24 @@ public static function volumeNameMessages(string $field = 'name'): array
|
|||
}
|
||||
|
||||
/**
|
||||
* Pattern for port mappings (e.g. 3000:3000, 8080:80, 8000-8010:8000-8010)
|
||||
* Each entry requires host:container format, where each side can be a number or a range (number-number)
|
||||
* Pattern for port mappings with optional IP binding and protocol suffix on either side.
|
||||
* Format: [ip:]port[:ip:port] where IP is IPv4 or [IPv6], port can be a range, protocol suffix optional.
|
||||
* Examples: 8080:80, 127.0.0.1:8080:80, [::1]::80/udp, 127.0.0.1:8080:80/tcp
|
||||
*/
|
||||
public const PORT_MAPPINGS_PATTERN = '/^(\d+(-\d+)?:\d+(-\d+)?)(,\d+(-\d+)?:\d+(-\d+)?)*$/';
|
||||
public const PORT_MAPPINGS_PATTERN = '/^
|
||||
(?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)? # optional IP
|
||||
(?:\d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))?)? # optional host port
|
||||
:
|
||||
(?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)? # optional IP
|
||||
\d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))? # container port
|
||||
(?:,
|
||||
(?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)?
|
||||
(?:\d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))?)?
|
||||
:
|
||||
(?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)?
|
||||
\d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))?
|
||||
)*
|
||||
$/x';
|
||||
|
||||
/**
|
||||
* Get validation rules for container name fields
|
||||
|
|
@ -230,7 +244,7 @@ public static function portMappingRules(): array
|
|||
public static function portMappingMessages(string $field = 'portsMappings'): array
|
||||
{
|
||||
return [
|
||||
"{$field}.regex" => 'Port mappings must be a comma-separated list of port pairs or ranges (e.g. 3000:3000,8080:80,8000-8010:8000-8010).',
|
||||
"{$field}.regex" => 'Port mappings must be a comma-separated list of port pairs or ranges with optional IP and protocol (e.g. 3000:3000, 8080:80/udp, 127.0.0.1:8080:80, [::1]::80).',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ function sharedDataApplications()
|
|||
'health_check_enabled' => 'boolean',
|
||||
'health_check_type' => 'string|in:http,cmd',
|
||||
'health_check_command' => ['nullable', 'string', 'max:1000', 'regex:/^[a-zA-Z0-9 \-_.\/:=@,+]+$/'],
|
||||
'health_check_path' => ['string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'],
|
||||
'health_check_path' => ['string', 'regex:#^[a-zA-Z0-9/\-_.~%,;]+$#'],
|
||||
'health_check_port' => 'integer|nullable|min:1|max:65535',
|
||||
'health_check_host' => ['string', 'regex:/^[a-zA-Z0-9.\-_]+$/'],
|
||||
'health_check_method' => 'string|in:GET,HEAD,POST,OPTIONS',
|
||||
|
|
|
|||
|
|
@ -3660,13 +3660,21 @@ function convertGitUrl(string $gitRepository, string $deploymentType, GithubApp|
|
|||
}
|
||||
}
|
||||
|
||||
preg_match('/(?<=:)\d+(?=\/)/', $gitRepository, $matches);
|
||||
$normalizedRepository = $repository;
|
||||
|
||||
if (count($matches) === 1) {
|
||||
$providerInfo['port'] = $matches[0];
|
||||
$gitHost = str($gitRepository)->before(':');
|
||||
$gitRepo = str($gitRepository)->after('/');
|
||||
$repository = "$gitHost:$gitRepo";
|
||||
if (str($normalizedRepository)->contains('://')) {
|
||||
$parsedRepository = parse_url($normalizedRepository);
|
||||
|
||||
if ($parsedRepository !== false && array_key_exists('port', $parsedRepository)) {
|
||||
$providerInfo['port'] = (string) $parsedRepository['port'];
|
||||
}
|
||||
} else {
|
||||
preg_match('/^(?<host>[^:]+):(?<port>\d+)\/(?<path>.+)$/', $normalizedRepository, $matches);
|
||||
|
||||
if (! empty($matches['port'])) {
|
||||
$providerInfo['port'] = $matches['port'];
|
||||
$repository = "{$matches['host']}:{$matches['path']}";
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
|
|
|
|||
14
composer.lock
generated
14
composer.lock
generated
|
|
@ -72,7 +72,6 @@
|
|||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/67b6b6210af47319c74c5666388d71bc1bc58276",
|
||||
"reference": "67b6b6210af47319c74c5666388d71bc1bc58276",
|
||||
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -157,7 +156,6 @@
|
|||
"source": "https://github.com/aws/aws-sdk-php/tree/3.374.2"
|
||||
},
|
||||
"time": "2026-03-27T18:05:55+00:00"
|
||||
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
|
|
@ -5158,16 +5156,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.50",
|
||||
"version": "3.0.51",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b"
|
||||
"reference": "d59c94077f9c9915abb51ddb52ce85188ece1748"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
|
||||
"reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d59c94077f9c9915abb51ddb52ce85188ece1748",
|
||||
"reference": "d59c94077f9c9915abb51ddb52ce85188ece1748",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5248,7 +5246,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.50"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.51"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5264,7 +5262,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-19T02:57:58+00:00"
|
||||
"time": "2026-04-10T01:33:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.473',
|
||||
'version' => '4.0.0-beta.474',
|
||||
'helper_version' => '1.0.13',
|
||||
'realtime_version' => '1.0.12',
|
||||
'realtime_version' => '1.0.13',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
'autoupdate' => env('AUTOUPDATE'),
|
||||
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Server::query()->chunk(100, function ($servers) {
|
||||
Server::query()->whereHas('team')->chunk(100, function ($servers) {
|
||||
foreach ($servers as $server) {
|
||||
$existingKeys = SharedEnvironmentVariable::where('type', 'server')
|
||||
->where('server_id', $server->id)
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@
|
|||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('local_persistent_volumes', function (Blueprint $table) {
|
||||
$table->string('uuid')->nullable()->after('id');
|
||||
});
|
||||
if (! Schema::hasColumn('local_persistent_volumes', 'uuid')) {
|
||||
Schema::table('local_persistent_volumes', function (Blueprint $table) {
|
||||
$table->string('uuid')->nullable()->after('id');
|
||||
});
|
||||
}
|
||||
|
||||
DB::table('local_persistent_volumes')
|
||||
->whereNull('uuid')
|
||||
->orderBy('id')
|
||||
->chunk(1000, function ($volumes) {
|
||||
->chunkById(1000, function ($volumes) {
|
||||
foreach ($volumes as $volume) {
|
||||
DB::table('local_persistent_volumes')
|
||||
->where('id', $volume->id)
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ services:
|
|||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- dev_coolify_data:/data/coolify
|
||||
- dev_coolify_data:/var/lib/docker/volumes/coolify_dev_coolify_data/_data
|
||||
- dev_backups_data:/data/coolify/backups
|
||||
- dev_postgres_data:/data/coolify/_volumes/database
|
||||
- dev_redis_data:/data/coolify/_volumes/redis
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ services:
|
|||
retries: 10
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.12'
|
||||
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.13'
|
||||
ports:
|
||||
- "${SOKETI_PORT:-6001}:6001"
|
||||
- "6002:6002"
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ services:
|
|||
retries: 10
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.12'
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.13'
|
||||
pull_policy: always
|
||||
container_name: coolify-realtime
|
||||
restart: always
|
||||
|
|
|
|||
21
docker/coolify-realtime/package-lock.json
generated
21
docker/coolify-realtime/package-lock.json
generated
|
|
@ -7,7 +7,7 @@
|
|||
"dependencies": {
|
||||
"@xterm/addon-fit": "0.11.0",
|
||||
"@xterm/xterm": "6.0.0",
|
||||
"axios": "1.13.6",
|
||||
"axios": "1.15.0",
|
||||
"cookie": "1.1.1",
|
||||
"dotenv": "17.3.1",
|
||||
"node-pty": "1.1.0",
|
||||
|
|
@ -36,14 +36,14 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
||||
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
|
||||
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
"proxy-from-env": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
|
|
@ -344,10 +344,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
|
||||
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"@xterm/addon-fit": "0.11.0",
|
||||
"@xterm/xterm": "6.0.0",
|
||||
"cookie": "1.1.1",
|
||||
"axios": "1.13.6",
|
||||
"axios": "1.15.0",
|
||||
"dotenv": "17.3.1",
|
||||
"node-pty": "1.1.0",
|
||||
"ws": "8.19.0"
|
||||
|
|
|
|||
12
openapi.json
12
openapi.json
|
|
@ -362,7 +362,7 @@
|
|||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
"description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -813,7 +813,7 @@
|
|||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
"description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -1264,7 +1264,7 @@
|
|||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
"description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -2697,7 +2697,7 @@
|
|||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
"description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -10816,7 +10816,7 @@
|
|||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")."
|
||||
"description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -11147,7 +11147,7 @@
|
|||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")."
|
||||
"description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
|
|||
12
openapi.yaml
12
openapi.yaml
|
|
@ -258,7 +258,7 @@ paths:
|
|||
docker_compose_domains:
|
||||
type: array
|
||||
description: 'Array of URLs to be applied to containers of a dockercompose application.'
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
|
|
@ -546,7 +546,7 @@ paths:
|
|||
docker_compose_domains:
|
||||
type: array
|
||||
description: 'Array of URLs to be applied to containers of a dockercompose application.'
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
|
|
@ -834,7 +834,7 @@ paths:
|
|||
docker_compose_domains:
|
||||
type: array
|
||||
description: 'Array of URLs to be applied to containers of a dockercompose application.'
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
|
|
@ -1735,7 +1735,7 @@ paths:
|
|||
docker_compose_domains:
|
||||
type: array
|
||||
description: 'Array of URLs to be applied to containers of a dockercompose application.'
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object }
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
|
|
@ -6886,7 +6886,7 @@ paths:
|
|||
urls:
|
||||
type: array
|
||||
description: 'Array of URLs to be applied to containers of a service.'
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").' } }, type: object }
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").' } }, type: object }
|
||||
force_domain_override:
|
||||
type: boolean
|
||||
default: false
|
||||
|
|
@ -7075,7 +7075,7 @@ paths:
|
|||
urls:
|
||||
type: array
|
||||
description: 'Array of URLs to be applied to containers of a service.'
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").' } }, type: object }
|
||||
items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").' } }, type: object }
|
||||
force_domain_override:
|
||||
type: boolean
|
||||
default: false
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ services:
|
|||
retries: 10
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.12'
|
||||
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.13'
|
||||
ports:
|
||||
- "${SOKETI_PORT:-6001}:6001"
|
||||
- "6002:6002"
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ services:
|
|||
retries: 10
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.12'
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.13'
|
||||
pull_policy: always
|
||||
container_name: coolify-realtime
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -539,6 +539,15 @@ install_docker_manually() {
|
|||
echo "Docker installed successfully."
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker_from_rhel_repo() {
|
||||
echo " - Installing Docker from the RHEL repository for Rocky Linux..."
|
||||
rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo
|
||||
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
|
||||
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl --now enable docker
|
||||
}
|
||||
|
||||
log_section "Step 3/9: Checking Docker installation"
|
||||
echo "3/9 Checking Docker installation..."
|
||||
if ! [ -x "$(command -v docker)" ]; then
|
||||
|
|
@ -579,6 +588,13 @@ if ! [ -x "$(command -v docker)" ]; then
|
|||
exit 1
|
||||
fi
|
||||
;;
|
||||
"rocky")
|
||||
install_docker_from_rhel_repo
|
||||
if ! [ -x "$(command -v docker)" ]; then
|
||||
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
"almalinux" | "tencentos")
|
||||
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo >/dev/null 2>&1
|
||||
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.473"
|
||||
"version": "4.0.0-beta.474"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0"
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
"version": "1.0.13"
|
||||
},
|
||||
"realtime": {
|
||||
"version": "1.0.12"
|
||||
"version": "1.0.13"
|
||||
},
|
||||
"sentinel": {
|
||||
"version": "0.0.21"
|
||||
|
|
|
|||
25
package-lock.json
generated
25
package-lock.json
generated
|
|
@ -16,7 +16,7 @@
|
|||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "4.1.18",
|
||||
"@vitejs/plugin-vue": "6.0.3",
|
||||
"axios": "1.13.2",
|
||||
"axios": "1.15.0",
|
||||
"laravel-echo": "2.2.7",
|
||||
"laravel-vite-plugin": "2.0.1",
|
||||
"postcss": "8.5.6",
|
||||
|
|
@ -1474,15 +1474,15 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
|
||||
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
|
||||
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
|
|
@ -2501,11 +2501,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
|
||||
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/pusher-js": {
|
||||
"version": "8.4.0",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "4.1.18",
|
||||
"@vitejs/plugin-vue": "6.0.3",
|
||||
"axios": "1.13.2",
|
||||
"axios": "1.15.0",
|
||||
"laravel-echo": "2.2.7",
|
||||
"laravel-vite-plugin": "2.0.1",
|
||||
"postcss": "8.5.6",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ
|
|||
</a>
|
||||
<a class="sub-menu-item" wire:current.exact="menu-item-active" {{ wireNavigate() }}
|
||||
href="{{ route('project.service.index', $parameters) }}"><span class="menu-item-label">General</span></a>
|
||||
<a class="sub-menu-item" wire:current.exact="menu-item-active" {{ wireNavigate() }}
|
||||
href="{{ route('project.service.index.advanced', $parameters) }}"><span class="menu-item-label">Advanced</span></a>
|
||||
@if ($serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated)
|
||||
<a class="sub-menu-item" wire:current.exact="menu-item-active" {{ wireNavigate() }}
|
||||
href="{{ route('project.service.database.backups', $parameters) }}"><span class="menu-item-label">Backups</span></a>
|
||||
|
|
|
|||
|
|
@ -835,6 +835,20 @@ class="h-5 w-5 text-warning-600 dark:text-warning-400"
|
|||
<div class="font-medium text-neutral-900 dark:text-white truncate"
|
||||
x-text="item.name">
|
||||
</div>
|
||||
<template x-if="item.amd_only">
|
||||
<span
|
||||
class="px-2 py-0.5 text-xs rounded-full bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200 shrink-0"
|
||||
title="This service only supports AMD64/x86_64 architecture">
|
||||
AMD only
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="item.arm_only">
|
||||
<span
|
||||
class="px-2 py-0.5 text-xs rounded-full bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200 shrink-0"
|
||||
title="This service only supports ARM64/aarch64 architecture">
|
||||
ARM only
|
||||
</span>
|
||||
</template>
|
||||
<span
|
||||
class="text-xs text-neutral-500 dark:text-neutral-400 shrink-0"
|
||||
x-text="item.quickcommand"
|
||||
|
|
|
|||
|
|
@ -5,19 +5,7 @@
|
|||
</div>
|
||||
<div>Advanced configuration for your application.</div>
|
||||
<div class="flex flex-col gap-1 pt-4">
|
||||
<h3>General</h3>
|
||||
@if ($application->git_based())
|
||||
<x-forms.checkbox helper="Automatically deploy new commits based on Git webhooks." instantSave
|
||||
id="isAutoDeployEnabled" label="Auto Deploy" canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox
|
||||
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
||||
instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" canGate="update"
|
||||
:canResource="$application" />
|
||||
<x-forms.checkbox
|
||||
helper="When enabled, anyone can trigger PR deployments. When disabled, only repository members, collaborators, and contributors can trigger PR deployments."
|
||||
instantSave id="isPrDeploymentsPublicEnabled" label="Allow Public PR Deployments" canGate="update"
|
||||
:canResource="$application" :disabled="!$isPreviewDeploymentsEnabled" />
|
||||
@endif
|
||||
<h3>Build</h3>
|
||||
<x-forms.checkbox helper="Disable Docker build cache on every deployment." instantSave
|
||||
id="disableBuildCache" label="Disable Build Cache" canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox
|
||||
|
|
@ -29,6 +17,55 @@
|
|||
instantSave id="includeSourceCommitInBuild" label="Include Source Commit in Build" canGate="update"
|
||||
:canResource="$application" />
|
||||
|
||||
<h3 class="pt-4">Container</h3>
|
||||
<x-forms.checkbox
|
||||
helper="The deployed container will have the same name ({{ $application->uuid }}). <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
|
||||
instantSave id="isConsistentContainerNameEnabled" label="Consistent Container Names" canGate="update"
|
||||
:canResource="$application" />
|
||||
@if ($isConsistentContainerNameEnabled === false)
|
||||
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomName'>
|
||||
<x-forms.input
|
||||
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
|
||||
instantSave id="customInternalName" label="Custom Container Name" canGate="update"
|
||||
:canResource="$application" />
|
||||
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@if ($application->git_based())
|
||||
<h3 class="pt-4">Deployment</h3>
|
||||
<x-forms.checkbox helper="Automatically deploy new commits based on Git webhooks." instantSave
|
||||
id="isAutoDeployEnabled" label="Auto Deploy" canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox
|
||||
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
||||
instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" canGate="update"
|
||||
:canResource="$application" />
|
||||
<x-forms.checkbox
|
||||
helper="When enabled, anyone can trigger PR deployments. When disabled, only repository members, collaborators, and contributors can trigger PR deployments."
|
||||
instantSave id="isPrDeploymentsPublicEnabled" label="Allow Public PR Deployments" canGate="update"
|
||||
:canResource="$application" :disabled="!$isPreviewDeploymentsEnabled" />
|
||||
|
||||
<h3 class="pt-4">Git</h3>
|
||||
<x-forms.checkbox instantSave id="isGitSubmodulesEnabled" label="Submodules"
|
||||
helper="Allow Git Submodules during build process." canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox instantSave id="isGitLfsEnabled" label="LFS"
|
||||
helper="Allow Git LFS during build process." canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox instantSave id="isGitShallowCloneEnabled" label="Shallow Clone"
|
||||
helper="Use shallow cloning (--depth=1) to speed up deployments by only fetching the latest commit history. This reduces clone time and resource usage, especially for large repositories."
|
||||
canGate="update" :canResource="$application" />
|
||||
@endif
|
||||
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<h3 class="pt-4">Docker Compose</h3>
|
||||
<x-forms.checkbox instantSave id="isRawComposeDeploymentEnabled" label="Raw Compose Deployment"
|
||||
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation.</a>"
|
||||
canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox instantSave id="isConnectToDockerNetworkEnabled" label="Connect To Predefined Network"
|
||||
helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>this</a>."
|
||||
canGate="update" :canResource="$application" />
|
||||
@endif
|
||||
|
||||
<h3 class="pt-4">Proxy</h3>
|
||||
@if ($application->settings->is_container_label_readonly_enabled)
|
||||
<x-forms.checkbox
|
||||
helper="Your application will be available only on https if your domain starts with https://..."
|
||||
|
|
@ -49,45 +86,10 @@
|
|||
helper="Readonly labels are disabled. You need to set the labels in the labels section." disabled
|
||||
instantSave id="isStripprefixEnabled" label="Strip Prefixes" canGate="update" :canResource="$application" />
|
||||
@endif
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<h3>Docker Compose</h3>
|
||||
<x-forms.checkbox instantSave id="isRawComposeDeploymentEnabled" label="Raw Compose Deployment"
|
||||
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation.</a>"
|
||||
canGate="update" :canResource="$application" />
|
||||
@endif
|
||||
<h3 class="pt-4">Container Names</h3>
|
||||
<x-forms.checkbox
|
||||
helper="The deployed container will have the same name ({{ $application->uuid }}). <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
|
||||
instantSave id="isConsistentContainerNameEnabled" label="Consistent Container Names" canGate="update"
|
||||
:canResource="$application" />
|
||||
@if ($isConsistentContainerNameEnabled === false)
|
||||
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomName'>
|
||||
<x-forms.input
|
||||
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
|
||||
instantSave id="customInternalName" label="Custom Container Name" canGate="update"
|
||||
:canResource="$application" />
|
||||
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<h3 class="pt-4">Network</h3>
|
||||
<x-forms.checkbox instantSave id="isConnectToDockerNetworkEnabled" label="Connect To Predefined Network"
|
||||
helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>this</a>."
|
||||
canGate="update" :canResource="$application" />
|
||||
@endif
|
||||
|
||||
<h3 class="pt-4">Logs</h3>
|
||||
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||
instantSave id="isLogDrainEnabled" label="Drain Logs" canGate="update" :canResource="$application" />
|
||||
@if ($application->git_based())
|
||||
<h3>Git</h3>
|
||||
<x-forms.checkbox instantSave id="isGitSubmodulesEnabled" label="Submodules"
|
||||
helper="Allow Git Submodules during build process." canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox instantSave id="isGitLfsEnabled" label="LFS"
|
||||
helper="Allow Git LFS during build process." canGate="update" :canResource="$application" />
|
||||
<x-forms.checkbox instantSave id="isGitShallowCloneEnabled" label="Shallow Clone"
|
||||
helper="Use shallow cloning (--depth=1) to speed up deployments by only fetching the latest commit history. This reduces clone time and resource usage, especially for large repositories."
|
||||
canGate="update" :canResource="$application" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
@if (!isDatabaseImage(data_get($service, 'image')))
|
||||
<div class="flex items-end gap-2">
|
||||
<x-forms.input
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- https://app.coolify.io,https://cloud.coolify.io/dashboard<br>- https://app.coolify.io/api/v3<br>- https://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container.<br>- https://app.coolify.io:8080/api -> app.coolify.io/api will point to port 8080 inside the container."
|
||||
label="Domains for {{ $serviceName }}"
|
||||
id="parsedServiceDomains.{{ str($serviceName)->replace('-', '_')->replace('.', '_') }}.domain"
|
||||
x-bind:disabled="shouldDisable()"></x-forms.input>
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
x-bind:disabled="!canUpdate" />
|
||||
@else
|
||||
<x-forms.input placeholder="https://coolify.io" wire:model="fqdn" label="Domains"
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- https://app.coolify.io,https://cloud.coolify.io/dashboard<br>- https://app.coolify.io/api/v3<br>- https://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container.<br>- https://app.coolify.io:8080/api -> app.coolify.io/api will point to port 8080 inside the container."
|
||||
x-bind:disabled="!canUpdate" />
|
||||
@can('update', $application)
|
||||
<x-forms.button wire:click="getWildcardDomain">Generate Domain
|
||||
|
|
|
|||
|
|
@ -173,6 +173,34 @@ class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/1
|
|||
</template>
|
||||
</x-slot:logo>
|
||||
</x-resource-view>
|
||||
<template x-if="service.amd_only">
|
||||
<div class="absolute top-2 right-10 group">
|
||||
<span
|
||||
class="px-2 py-0.5 text-xs rounded bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200 cursor-pointer">
|
||||
AMD only
|
||||
</span>
|
||||
<div class="info-helper-popup right-0 w-sm">
|
||||
<div class="p-4">
|
||||
This service only supports AMD64/x86_64 architecture. It will not work
|
||||
on ARM-based servers (e.g., Apple Silicon, Raspberry Pi, AWS Graviton).
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="service.arm_only">
|
||||
<div class="absolute top-2 right-10 group">
|
||||
<span
|
||||
class="px-2 py-0.5 text-xs rounded-full bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200 cursor-pointer">
|
||||
ARM only
|
||||
</span>
|
||||
<div class="info-helper-popup right-0 w-sm">
|
||||
<div class="p-4">
|
||||
This service only supports ARM64/aarch64 architecture. It will not work
|
||||
on AMD64/x86_64-based servers.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="shouldShowDocIcon(service)">
|
||||
<a :href="getDocLink(service) || coolifyDocsUrl(service.name)" target="_blank"
|
||||
@click.stop @mouseenter="resolveDocLink(service)"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
<div class="w-full">
|
||||
<form wire:submit.prevent='submit' class="flex flex-col w-full gap-2">
|
||||
@if($requiredPort)
|
||||
<x-callout type="warning" title="Required Port: {{ $requiredPort }}" class="mb-2">
|
||||
<x-callout type="info" title="Required Port: {{ $requiredPort }}" class="mb-2">
|
||||
This service requires port <strong>{{ $requiredPort }}</strong> to function correctly. All domains must include this port number (or any other port if you know what you're doing).
|
||||
<br><br>
|
||||
<strong>Example:</strong> http://app.coolify.io:{{ $requiredPort }}
|
||||
<strong>Example:</strong> https://app.coolify.io:{{ $requiredPort }},https://www.app.coolify.io:{{ $requiredPort }}
|
||||
</x-callout>
|
||||
@endif
|
||||
|
||||
<x-forms.input canGate="update" :canResource="$application" placeholder="https://app.coolify.io" label="Domains"
|
||||
id="fqdn"
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- https://app.coolify.io,https://cloud.coolify.io/dashboard<br>- https://app.coolify.io/api/v3<br>- https://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container.<br>- https://app.coolify.io:8080/api -> app.coolify.io/api will point to port 8080 inside the container."></x-forms.input>
|
||||
<x-forms.button canGate="update" :canResource="$application" type="submit">Save</x-forms.button>
|
||||
</form>
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ
|
|||
</svg>
|
||||
<span class="menu-item-label">Back</span>
|
||||
</a>
|
||||
<a class="sub-menu-item menu-item-active" href="#"><span class="menu-item-label">General</span></a>
|
||||
<a class="sub-menu-item" wire:current.exact="menu-item-active" {{ wireNavigate() }}
|
||||
href="{{ route('project.service.index', $parameters) }}"><span class="menu-item-label">General</span></a>
|
||||
<a class="sub-menu-item" wire:current.exact="menu-item-active" {{ wireNavigate() }}
|
||||
href="{{ route('project.service.index.advanced', $parameters) }}"><span class="menu-item-label">Advanced</span></a>
|
||||
</div>
|
||||
@endif
|
||||
<div class="w-full">
|
||||
|
|
@ -23,63 +26,9 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ
|
|||
{{ data_get_str($service, 'name')->limit(10) }} >
|
||||
{{ data_get_str($serviceApplication, 'name')->limit(10) }} | Coolify
|
||||
</x-slot>
|
||||
<form wire:submit='submitApplication'>
|
||||
<div class="flex items-center gap-2 pb-4">
|
||||
@if ($serviceApplication->human_name)
|
||||
<h2>{{ Str::headline($serviceApplication->human_name) }}</h2>
|
||||
@else
|
||||
<h2>{{ Str::headline($serviceApplication->name) }}</h2>
|
||||
@endif
|
||||
<x-forms.button canGate="update" :canResource="$serviceApplication" type="submit">Save</x-forms.button>
|
||||
@can('update', $serviceApplication)
|
||||
<x-modal-confirmation wire:click="convertToDatabase" title="Convert to Database"
|
||||
buttonTitle="Convert to Database" submitAction="convertToDatabase" :actions="['The selected resource will be converted to a service database.']"
|
||||
confirmationText="{{ Str::headline($serviceApplication->name) }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||
shortConfirmationLabel="Service Application Name" />
|
||||
@endcan
|
||||
@can('delete', $serviceApplication)
|
||||
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
|
||||
submitAction="deleteApplication" :actions="['The selected service application container will be stopped and permanently deleted.']"
|
||||
confirmationText="{{ Str::headline($serviceApplication->name) }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||
shortConfirmationLabel="Service Application Name" />
|
||||
@endcan
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@if ($requiredPort && !$serviceApplication->serviceType()?->contains(str($serviceApplication->image)->before(':')))
|
||||
<x-callout type="warning" title="Required Port: {{ $requiredPort }}" class="mb-2">
|
||||
This service requires port <strong>{{ $requiredPort }}</strong> to function correctly. All domains must include this port number (or any other port if you know what you're doing).
|
||||
<br><br>
|
||||
<strong>Example:</strong> http://app.coolify.io:{{ $requiredPort }}
|
||||
</x-callout>
|
||||
@endif
|
||||
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" label="Name" id="humanName"
|
||||
placeholder="Human readable name"></x-forms.input>
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" label="Description"
|
||||
id="description"></x-forms.input>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
@if (!$serviceApplication->serviceType()?->contains(str($serviceApplication->image)->before(':')))
|
||||
@if ($serviceApplication->required_fqdn)
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" required placeholder="https://app.coolify.io"
|
||||
label="Domains" id="fqdn"
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||
@else
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" placeholder="https://app.coolify.io"
|
||||
label="Domains" id="fqdn"
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||
@endif
|
||||
@endif
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication"
|
||||
helper="You can change the image you would like to deploy.<br><br><span class='dark:text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||
label="Image" id="image"></x-forms.input>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="py-2 pt-4">Advanced</h3>
|
||||
<div class="w-96 flex flex-col gap-1">
|
||||
@if ($currentRoute === 'project.service.index.advanced')
|
||||
<h2>Advanced</h2>
|
||||
<div class="w-full sm:w-96 flex flex-col gap-1 pt-4">
|
||||
@if (str($serviceApplication->image)->contains('pocketbase'))
|
||||
<x-forms.checkbox canGate="update" :canResource="$serviceApplication" instantSave="instantSaveApplicationSettings" id="isGzipEnabled"
|
||||
label="Enable Gzip Compression"
|
||||
|
|
@ -99,77 +48,134 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ
|
|||
helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||
instantSave="instantSaveApplicationAdvanced" id="isLogDrainEnabled" label="Drain Logs" />
|
||||
</div>
|
||||
</form>
|
||||
@else
|
||||
<form wire:submit='submitApplication'>
|
||||
<div class="flex items-center gap-2 pb-4">
|
||||
@if ($serviceApplication->human_name)
|
||||
<h2>{{ Str::headline($serviceApplication->human_name) }}</h2>
|
||||
@else
|
||||
<h2>{{ Str::headline($serviceApplication->name) }}</h2>
|
||||
@endif
|
||||
<x-forms.button canGate="update" :canResource="$serviceApplication" type="submit">Save</x-forms.button>
|
||||
@can('update', $serviceApplication)
|
||||
<x-modal-confirmation wire:click="convertToDatabase" title="Convert to Database"
|
||||
buttonTitle="Convert to Database" submitAction="convertToDatabase" :actions="['The selected resource will be converted to a service database.']"
|
||||
confirmationText="{{ Str::headline($serviceApplication->name) }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||
shortConfirmationLabel="Service Application Name" />
|
||||
@endcan
|
||||
@can('delete', $serviceApplication)
|
||||
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
|
||||
submitAction="deleteApplication" :actions="['The selected service application container will be stopped and permanently deleted.']"
|
||||
confirmationText="{{ Str::headline($serviceApplication->name) }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||
shortConfirmationLabel="Service Application Name" />
|
||||
@endcan
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@if ($requiredPort && !$serviceApplication->serviceType()?->contains(str($serviceApplication->image)->before(':')))
|
||||
<x-callout type="info" title="Required Port: {{ $requiredPort }}" class="mb-2">
|
||||
This service requires port <strong>{{ $requiredPort }}</strong> to function correctly. All domains must include this port number (or any other port if you know what you're doing).
|
||||
<br><br>
|
||||
<strong>Example:</strong> https://app.coolify.io:{{ $requiredPort }},https://www.app.coolify.io:{{ $requiredPort }}
|
||||
</x-callout>
|
||||
@endif
|
||||
|
||||
<x-domain-conflict-modal
|
||||
:conflicts="$domainConflicts"
|
||||
:showModal="$showDomainConflictModal"
|
||||
confirmAction="confirmDomainUsage">
|
||||
<x-slot:consequences>
|
||||
<ul class="mt-2 ml-4 list-disc">
|
||||
<li>Only one service will be accessible at this domain</li>
|
||||
<li>The routing behavior will be unpredictable</li>
|
||||
<li>You may experience service disruptions</li>
|
||||
<li>SSL certificates might not work correctly</li>
|
||||
</ul>
|
||||
</x-slot:consequences>
|
||||
</x-domain-conflict-modal>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" label="Name" id="humanName"
|
||||
placeholder="Human readable name"></x-forms.input>
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" label="Description"
|
||||
id="description"></x-forms.input>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
@if (!$serviceApplication->serviceType()?->contains(str($serviceApplication->image)->before(':')))
|
||||
@if ($serviceApplication->required_fqdn)
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" required placeholder="https://app.coolify.io"
|
||||
label="Domains" id="fqdn"
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- https://app.coolify.io,https://cloud.coolify.io/dashboard<br>- https://app.coolify.io/api/v3<br>- https://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container.<br>- https://app.coolify.io:8080/api -> app.coolify.io/api will point to port 8080 inside the container."></x-forms.input>
|
||||
@else
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication" placeholder="https://app.coolify.io"
|
||||
label="Domains" id="fqdn"
|
||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- https://app.coolify.io,https://cloud.coolify.io/dashboard<br>- https://app.coolify.io/api/v3<br>- https://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container.<br>- https://app.coolify.io:8080/api -> app.coolify.io/api will point to port 8080 inside the container."></x-forms.input>
|
||||
@endif
|
||||
@endif
|
||||
<x-forms.input canGate="update" :canResource="$serviceApplication"
|
||||
helper="You can change the image you would like to deploy.<br><br><span class='dark:text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||
label="Image" id="image"></x-forms.input>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if ($showPortWarningModal)
|
||||
<div x-data="{ modalOpen: true }" x-init="$nextTick(() => { modalOpen = true })"
|
||||
@keydown.escape.window="modalOpen = false; $wire.call('cancelRemovePort')"
|
||||
:class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
|
||||
<template x-teleport="body">
|
||||
<div x-show="modalOpen"
|
||||
class="fixed top-0 lg:pt-10 left-0 z-99 flex items-start justify-center w-screen h-screen" x-cloak>
|
||||
<div x-show="modalOpen" class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
|
||||
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
|
||||
class="relative w-full py-6 border rounded-sm min-w-full lg:min-w-[36rem] max-w-[48rem] bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
|
||||
<div class="flex justify-between items-center pb-3">
|
||||
<h2 class="pr-8 font-bold">Remove Required Port?</h2>
|
||||
<button @click="modalOpen = false; $wire.call('cancelRemovePort')"
|
||||
class="flex absolute top-2 right-2 justify-center items-center w-8 h-8 rounded-full dark:text-white hover:bg-coolgray-300">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative w-auto">
|
||||
<x-callout type="warning" title="Port Requirement Warning" class="mb-4">
|
||||
This service requires port <strong>{{ $requiredPort }}</strong> to function correctly.
|
||||
One or more of your domains are missing a port number.
|
||||
</x-callout>
|
||||
<x-domain-conflict-modal
|
||||
:conflicts="$domainConflicts"
|
||||
:showModal="$showDomainConflictModal"
|
||||
confirmAction="confirmDomainUsage">
|
||||
<x-slot:consequences>
|
||||
<ul class="mt-2 ml-4 list-disc">
|
||||
<li>Only one service will be accessible at this domain</li>
|
||||
<li>The routing behavior will be unpredictable</li>
|
||||
<li>You may experience service disruptions</li>
|
||||
<li>SSL certificates might not work correctly</li>
|
||||
</ul>
|
||||
</x-slot:consequences>
|
||||
</x-domain-conflict-modal>
|
||||
|
||||
<x-callout type="danger" title="What will happen if you continue?" class="mb-4">
|
||||
<ul class="mt-2 ml-4 list-disc">
|
||||
<li>The service may become unreachable</li>
|
||||
<li>The proxy may not be able to route traffic correctly</li>
|
||||
<li>Environment variables may not be generated properly</li>
|
||||
<li>The service may fail to start or function</li>
|
||||
</ul>
|
||||
</x-callout>
|
||||
@if ($showPortWarningModal)
|
||||
<div x-data="{ modalOpen: true }" x-init="$nextTick(() => { modalOpen = true })"
|
||||
@keydown.escape.window="modalOpen = false; $wire.call('cancelRemovePort')"
|
||||
:class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
|
||||
<template x-teleport="body">
|
||||
<div x-show="modalOpen"
|
||||
class="fixed top-0 lg:pt-10 left-0 z-99 flex items-start justify-center w-screen h-screen" x-cloak>
|
||||
<div x-show="modalOpen" class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"></div>
|
||||
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
|
||||
class="relative w-full py-6 border rounded-sm min-w-full lg:min-w-[36rem] max-w-[48rem] bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
|
||||
<div class="flex justify-between items-center pb-3">
|
||||
<h2 class="pr-8 font-bold">Remove Required Port?</h2>
|
||||
<button @click="modalOpen = false; $wire.call('cancelRemovePort')"
|
||||
class="flex absolute top-2 right-2 justify-center items-center w-8 h-8 rounded-full dark:text-white hover:bg-coolgray-300">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative w-auto">
|
||||
<x-callout type="warning" title="Port Requirement Warning" class="mb-4">
|
||||
This service requires port <strong>{{ $requiredPort }}</strong> to function correctly.
|
||||
One or more of your domains are missing a port number.
|
||||
</x-callout>
|
||||
|
||||
<div class="flex flex-wrap gap-2 justify-between mt-4">
|
||||
<x-forms.button @click="modalOpen = false; $wire.call('cancelRemovePort')"
|
||||
class="w-auto dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
|
||||
Cancel - Keep Port
|
||||
</x-forms.button>
|
||||
<x-forms.button wire:click="confirmRemovePort" @click="modalOpen = false" class="w-auto"
|
||||
isError>
|
||||
I understand, remove port anyway
|
||||
</x-forms.button>
|
||||
<x-callout type="danger" title="What will happen if you continue?" class="mb-4">
|
||||
<ul class="mt-2 ml-4 list-disc">
|
||||
<li>The service may become unreachable</li>
|
||||
<li>The proxy may not be able to route traffic correctly</li>
|
||||
<li>Environment variables may not be generated properly</li>
|
||||
<li>The service may fail to start or function</li>
|
||||
</ul>
|
||||
</x-callout>
|
||||
|
||||
<div class="flex flex-wrap gap-2 justify-between mt-4">
|
||||
<x-forms.button @click="modalOpen = false; $wire.call('cancelRemovePort')"
|
||||
class="w-auto dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
|
||||
Cancel - Keep Port
|
||||
</x-forms.button>
|
||||
<x-forms.button wire:click="confirmRemovePort" @click="modalOpen = false" class="w-auto"
|
||||
isError>
|
||||
I understand, remove port anyway
|
||||
</x-forms.button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@elseif ($resourceType === 'database')
|
||||
<x-slot:title>
|
||||
|
|
@ -178,6 +184,17 @@ class="w-auto dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
|
|||
</x-slot>
|
||||
@if ($currentRoute === 'project.service.database.import')
|
||||
<livewire:project.database.import :resource="$serviceDatabase" :key="'import-' . $serviceDatabase->uuid" />
|
||||
@elseif ($currentRoute === 'project.service.index.advanced')
|
||||
<h2>Advanced</h2>
|
||||
<div class="w-full sm:w-96 flex flex-col gap-1 pt-4">
|
||||
<x-forms.checkbox canGate="update" :canResource="$serviceDatabase" instantSave="instantSaveExclude"
|
||||
label="Exclude from service status"
|
||||
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
||||
id="excludeFromStatus"></x-forms.checkbox>
|
||||
<x-forms.checkbox canGate="update" :canResource="$serviceDatabase"
|
||||
helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||
instantSave="instantSaveLogDrain" id="isLogDrainEnabled" label="Drain Logs" />
|
||||
</div>
|
||||
@else
|
||||
<form wire:submit='submitDatabase'>
|
||||
<div class="flex items-center gap-2 pb-4">
|
||||
|
|
@ -242,16 +259,6 @@ class="w-auto dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
|
|||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="pt-2">Advanced</h3>
|
||||
<div class="w-full sm:w-96">
|
||||
<x-forms.checkbox canGate="update" :canResource="$serviceDatabase" instantSave="instantSaveExclude"
|
||||
label="Exclude from service status"
|
||||
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
|
||||
id="excludeFromStatus"></x-forms.checkbox>
|
||||
<x-forms.checkbox canGate="update" :canResource="$serviceDatabase"
|
||||
helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||
instantSave="instantSaveLogDrain" id="isLogDrainEnabled" label="Drain Logs" />
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
placeholder="My super WordPress site" />
|
||||
<x-forms.input canGate="update" :canResource="$service" id="description" label="Description" />
|
||||
</div>
|
||||
<div>
|
||||
<h3>Network</h3>
|
||||
</div>
|
||||
<div class="w-full sm:w-96">
|
||||
<x-forms.checkbox canGate="update" :canResource="$service" instantSave id="connectToDockerNetwork"
|
||||
label="Connect To Predefined Network"
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@
|
|||
@endif
|
||||
@else
|
||||
@if ($is_shared)
|
||||
<x-forms.checkbox instantSave id="is_buildtime"
|
||||
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
|
||||
label="Available at Buildtime" />
|
||||
<x-forms.checkbox instantSave id="is_runtime"
|
||||
helper="Make this variable available in the running container at runtime."
|
||||
label="Available at Runtime" />
|
||||
<x-forms.checkbox instantSave id="is_literal"
|
||||
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
|
||||
label="Is Literal?" />
|
||||
|
|
@ -89,6 +95,12 @@
|
|||
@endif
|
||||
@else
|
||||
@if ($is_shared)
|
||||
<x-forms.checkbox disabled id="is_buildtime"
|
||||
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
|
||||
label="Available at Buildtime" />
|
||||
<x-forms.checkbox disabled id="is_runtime"
|
||||
helper="Make this variable available in the running container at runtime."
|
||||
label="Available at Runtime" />
|
||||
<x-forms.checkbox disabled id="is_literal"
|
||||
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
|
||||
label="Is Literal?" />
|
||||
|
|
@ -209,6 +221,12 @@
|
|||
@endif
|
||||
@else
|
||||
@if ($is_shared)
|
||||
<x-forms.checkbox instantSave id="is_buildtime"
|
||||
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
|
||||
label="Available at Buildtime" />
|
||||
<x-forms.checkbox instantSave id="is_runtime"
|
||||
helper="Make this variable available in the running container at runtime."
|
||||
label="Available at Runtime" />
|
||||
<x-forms.checkbox instantSave id="is_literal"
|
||||
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
|
||||
label="Is Literal?" />
|
||||
|
|
@ -283,6 +301,12 @@
|
|||
@endif
|
||||
@else
|
||||
@if ($is_shared)
|
||||
<x-forms.checkbox disabled id="is_buildtime"
|
||||
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
|
||||
label="Available at Buildtime" />
|
||||
<x-forms.checkbox disabled id="is_runtime"
|
||||
helper="Make this variable available in the running container at runtime."
|
||||
label="Available at Runtime" />
|
||||
<x-forms.checkbox disabled id="is_literal"
|
||||
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
|
||||
label="Is Literal?" />
|
||||
|
|
|
|||
|
|
@ -18,24 +18,20 @@
|
|||
label="CPU Weight" id="limitsCpuShares" />
|
||||
</div>
|
||||
<h3 class="pt-4">Limit Memory</h3>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="Examples: 69b (byte) or 420k (kilobyte) or 1337m (megabyte) or 1g (gigabyte).<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_reservation'>here</a>."
|
||||
label="Soft Memory Limit" id="limitsMemoryReservation" />
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="0-100.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_swappiness'>here</a>."
|
||||
type="number" min="0" max="100" label="Swappiness"
|
||||
id="limitsMemorySwappiness" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="Examples: 69b (byte) or 420k (kilobyte) or 1337m (megabyte) or 1g (gigabyte).<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_limit'>here</a>."
|
||||
label="Maximum Memory Limit" id="limitsMemory" />
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="Examples:69b (byte) or 420k (kilobyte) or 1337m (megabyte) or 1g (gigabyte).<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#memswap_limit'>here</a>."
|
||||
label="Maximum Swap Limit" id="limitsMemorySwap" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="<span class='text-helper'>Examples</span><br>• 69b (byte)<br>• 420k (kilobyte)<br>• 1337m (megabyte)<br>• 1g (gigabyte)<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_reservation'>here</a>."
|
||||
label="Soft Memory Limit" id="limitsMemoryReservation" />
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="Value between 0-100.<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_swappiness'>here</a>."
|
||||
type="number" min="0" max="100" label="Swappiness"
|
||||
id="limitsMemorySwappiness" />
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="<span class='text-helper'>Examples</span><br>• 69b (byte)<br>• 420k (kilobyte)<br>• 1337m (megabyte)<br>• 1g (gigabyte)<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_limit'>here</a>."
|
||||
label="Maximum Memory Limit" id="limitsMemory" />
|
||||
<x-forms.input canGate="update" :canResource="$resource"
|
||||
helper="<span class='text-helper'>Examples</span><br>• 69b (byte)<br>• 420k (kilobyte)<br>• 1337m (megabyte)<br>• 1g (gigabyte)<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#memswap_limit'>here</a>."
|
||||
label="Maximum Swap Limit" id="limitsMemorySwap" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@
|
|||
}">
|
||||
<h3 class="pt-4">Clone Resource</h3>
|
||||
<div class="pb-2">Duplicate this resource to another server or network destination.</div>
|
||||
<x-callout type="info" title="Important" class="mb-4">
|
||||
Cloning only duplicates resource configuration (such as environment variables, build settings etc..). It does not include any resource data, such as databases or stored files.
|
||||
</x-callout>
|
||||
|
||||
@can('update', $resource)
|
||||
<div class="space-y-4 pb-8">
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@
|
|||
Route::get('/terminal', ExecuteContainerCommand::class)->name('project.service.command')->middleware('can.access.terminal');
|
||||
Route::get('/{stack_service_uuid}/backups', ServiceDatabaseBackups::class)->name('project.service.database.backups');
|
||||
Route::get('/{stack_service_uuid}/import', ServiceIndex::class)->name('project.service.database.import')->middleware('can.update.resource');
|
||||
Route::get('/{stack_service_uuid}/advanced', ServiceIndex::class)->name('project.service.index.advanced');
|
||||
Route::get('/{stack_service_uuid}', ServiceIndex::class)->name('project.service.index');
|
||||
Route::get('/tasks/{task_uuid}', ServiceConfiguration::class)->name('project.service.scheduled-tasks');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -539,6 +539,15 @@ install_docker_manually() {
|
|||
echo "Docker installed successfully."
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker_from_rhel_repo() {
|
||||
echo " - Installing Docker from the RHEL repository for Rocky Linux..."
|
||||
rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo
|
||||
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
|
||||
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl --now enable docker
|
||||
}
|
||||
|
||||
log_section "Step 3/9: Checking Docker installation"
|
||||
echo "3/9 Checking Docker installation..."
|
||||
if ! [ -x "$(command -v docker)" ]; then
|
||||
|
|
@ -579,6 +588,13 @@ if ! [ -x "$(command -v docker)" ]; then
|
|||
exit 1
|
||||
fi
|
||||
;;
|
||||
"rocky")
|
||||
install_docker_from_rhel_repo
|
||||
if ! [ -x "$(command -v docker)" ]; then
|
||||
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
"almalinux" | "tencentos")
|
||||
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo >/dev/null 2>&1
|
||||
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
# tags: calcom,calendso,scheduling,open,source
|
||||
# logo: svgs/calcom.svg
|
||||
# port: 3000
|
||||
# amd_only: true
|
||||
|
||||
services:
|
||||
calcom:
|
||||
|
|
|
|||
|
|
@ -408,7 +408,8 @@
|
|||
"category": "productivity",
|
||||
"logo": "svgs/calcom.svg",
|
||||
"minversion": "0.0.0",
|
||||
"port": "3000"
|
||||
"port": "3000",
|
||||
"amd_only": true
|
||||
},
|
||||
"calibre-web-automated-book-downloader": {
|
||||
"documentation": "https://github.com/calibrain/calibre-web-automated-book-downloader?utm_source=coolify.io",
|
||||
|
|
|
|||
|
|
@ -408,7 +408,8 @@
|
|||
"category": "productivity",
|
||||
"logo": "svgs/calcom.svg",
|
||||
"minversion": "0.0.0",
|
||||
"port": "3000"
|
||||
"port": "3000",
|
||||
"amd_only": true
|
||||
},
|
||||
"calibre-web-automated-book-downloader": {
|
||||
"documentation": "https://github.com/calibrain/calibre-web-automated-book-downloader?utm_source=coolify.io",
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
'updated_at' => now()->subDays(8),
|
||||
]);
|
||||
|
||||
$originalIp = $server->ip;
|
||||
$originalIp = (string) $server->ip;
|
||||
|
||||
$this->artisan('cleanup:unreachable-servers')->assertSuccessful();
|
||||
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
'updated_at' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
$originalIp = $server->ip;
|
||||
$originalIp = (string) $server->ip;
|
||||
|
||||
$this->artisan('cleanup:unreachable-servers')->assertSuccessful();
|
||||
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
'updated_at' => now()->subDays(8),
|
||||
]);
|
||||
|
||||
$originalIp = $server->ip;
|
||||
$originalIp = (string) $server->ip;
|
||||
|
||||
$this->artisan('cleanup:unreachable-servers')->assertSuccessful();
|
||||
|
||||
|
|
|
|||
78
tests/Feature/CleanupUnsubscribedServersTest.php
Normal file
78
tests/Feature/CleanupUnsubscribedServersTest.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('sets unreachable fields on servers when subscription ends', function () {
|
||||
$team = Team::factory()->create();
|
||||
Subscription::create([
|
||||
'team_id' => $team->id,
|
||||
'stripe_invoice_paid' => true,
|
||||
]);
|
||||
$server = Server::factory()->create([
|
||||
'team_id' => $team->id,
|
||||
'unreachable_count' => 0,
|
||||
'unreachable_notification_sent' => false,
|
||||
]);
|
||||
|
||||
$team->subscriptionEnded();
|
||||
|
||||
$server->refresh();
|
||||
expect($server->unreachable_count)->toBe(3);
|
||||
expect($server->unreachable_notification_sent)->toBeTrue();
|
||||
});
|
||||
|
||||
it('cleans up unsubscribed server IP after 7 days via cleanup command', function () {
|
||||
$team = Team::factory()->create();
|
||||
$server = Server::factory()->create([
|
||||
'team_id' => $team->id,
|
||||
'unreachable_count' => 3,
|
||||
'unreachable_notification_sent' => true,
|
||||
'updated_at' => now()->subDays(8),
|
||||
]);
|
||||
|
||||
$this->artisan('cleanup:unreachable-servers')->assertSuccessful();
|
||||
|
||||
$server->refresh();
|
||||
expect($server->ip)->toBe('1.2.3.4');
|
||||
});
|
||||
|
||||
it('does not clean up unsubscribed server IP within 7 day grace period', function () {
|
||||
$team = Team::factory()->create();
|
||||
$server = Server::factory()->create([
|
||||
'team_id' => $team->id,
|
||||
'unreachable_count' => 3,
|
||||
'unreachable_notification_sent' => true,
|
||||
'updated_at' => now()->subDays(3),
|
||||
]);
|
||||
|
||||
$originalIp = (string) $server->ip;
|
||||
|
||||
$this->artisan('cleanup:unreachable-servers')->assertSuccessful();
|
||||
|
||||
$server->refresh();
|
||||
expect((string) $server->ip)->toBe($originalIp);
|
||||
});
|
||||
|
||||
it('does not affect servers with active subscriptions', function () {
|
||||
$team = Team::factory()->create();
|
||||
Subscription::create([
|
||||
'team_id' => $team->id,
|
||||
'stripe_invoice_paid' => true,
|
||||
]);
|
||||
$server = Server::factory()->create([
|
||||
'team_id' => $team->id,
|
||||
'unreachable_count' => 0,
|
||||
'unreachable_notification_sent' => false,
|
||||
]);
|
||||
|
||||
$originalCount = $server->unreachable_count;
|
||||
$originalNotification = $server->unreachable_notification_sent;
|
||||
|
||||
expect($originalCount)->toBe(0);
|
||||
expect($originalNotification)->toBeFalse();
|
||||
});
|
||||
|
|
@ -60,3 +60,47 @@
|
|||
'port' => '766',
|
||||
]);
|
||||
});
|
||||
|
||||
test('convertGitUrlsForSourceAndSshUrlSchemeWithCustomPort', function () {
|
||||
$result = convertGitUrl('ssh://git@192.168.56.11:22222/User/Repo.git', 'source', null);
|
||||
expect($result)->toBe([
|
||||
'repository' => 'ssh://git@192.168.56.11:22222/User/Repo.git',
|
||||
'port' => '22222',
|
||||
]);
|
||||
});
|
||||
|
||||
test('convertGitUrlsForSourceAndSshUrlSchemeWithCustomPortAndIpv6Host', function () {
|
||||
$result = convertGitUrl('ssh://git@[2001:db8::10]:22222/group/project.git', 'source', null);
|
||||
expect($result)->toBe([
|
||||
'repository' => 'ssh://git@[2001:db8::10]:22222/group/project.git',
|
||||
'port' => '22222',
|
||||
]);
|
||||
});
|
||||
|
||||
test('convertGitUrlsForDeployKeyAndGithubAppWithCustomPort', function () {
|
||||
$githubApp = new GithubApp([
|
||||
'html_url' => 'https://github.example.com',
|
||||
'custom_user' => 'git',
|
||||
'custom_port' => 22222,
|
||||
]);
|
||||
|
||||
$result = convertGitUrl('andrasbacsai/coolify-examples.git', 'deploy_key', $githubApp);
|
||||
expect($result)->toBe([
|
||||
'repository' => 'ssh://git@github.example.com:22222/andrasbacsai/coolify-examples.git',
|
||||
'port' => '22222',
|
||||
]);
|
||||
});
|
||||
|
||||
test('convertGitUrlsForDeployKeyAndGithubAppWithCustomPortAndIpv6Host', function () {
|
||||
$githubApp = new GithubApp([
|
||||
'html_url' => 'https://[2001:db8::10]',
|
||||
'custom_user' => 'git',
|
||||
'custom_port' => 22222,
|
||||
]);
|
||||
|
||||
$result = convertGitUrl('andrasbacsai/coolify-examples.git', 'deploy_key', $githubApp);
|
||||
expect($result)->toBe([
|
||||
'repository' => 'ssh://git@[2001:db8::10]:22222/andrasbacsai/coolify-examples.git',
|
||||
'port' => '22222',
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -99,3 +99,23 @@
|
|||
// The malicious payload should be escaped (escapeshellarg wraps and escapes quotes)
|
||||
expect($command)->toContain("'https://github.com/user/repo.git'\\''");
|
||||
});
|
||||
|
||||
it('preserves ssh scheme URLs with custom ports in deploy_key commands', function () {
|
||||
$deploymentUuid = 'test-deployment-uuid';
|
||||
|
||||
$application = new Application;
|
||||
$application->git_branch = 'master';
|
||||
$application->git_repository = 'ssh://git@192.168.56.11:22222/User/Repo.git';
|
||||
$application->private_key_id = 1;
|
||||
|
||||
$privateKey = new PrivateKey;
|
||||
$privateKey->private_key = 'fake-private-key';
|
||||
$application->setRelation('private_key', $privateKey);
|
||||
|
||||
$result = $application->generateGitLsRemoteCommands($deploymentUuid, false);
|
||||
|
||||
expect($result['commands'])
|
||||
->toContain("'ssh://git@192.168.56.11:22222/User/Repo.git'")
|
||||
->toContain('-p 22222')
|
||||
->not->toContain('ssh:/git@192.168.56.11:22222/User/Repo.git');
|
||||
});
|
||||
|
|
|
|||
28
tests/Unit/InstallScriptRockyDockerRepoTest.php
Normal file
28
tests/Unit/InstallScriptRockyDockerRepoTest.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
function expectRockyInstallScriptToUseRhelRepo(string $path): void
|
||||
{
|
||||
$installScript = file_get_contents(base_path($path));
|
||||
|
||||
expect($installScript)
|
||||
->toContain('install_docker_from_rhel_repo() {')
|
||||
->toContain('echo " - Installing Docker from the RHEL repository for Rocky Linux..."')
|
||||
->toContain('rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo')
|
||||
->toContain('dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo')
|
||||
->toContain('dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin')
|
||||
->toContain('systemctl --now enable docker')
|
||||
->toContain('"rocky")')
|
||||
->toContain('install_docker_from_rhel_repo')
|
||||
->not->toContain('dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core')
|
||||
->not->toContain('dnf5 config-manager addrepo --overwrite --save-filename=docker-ce.repo --from-repofile=https://download.docker.com/linux/rhel/docker-ce.repo')
|
||||
->not->toContain('dnf makecache')
|
||||
->not->toContain('"ubuntu" | "debian" | "raspbian" | "centos" | "fedora" | "rhel" | "rocky" | "sles")');
|
||||
}
|
||||
|
||||
it('uses the rocky linux documented docker install flow in the stable install script', function () {
|
||||
expectRockyInstallScriptToUseRhelRepo('scripts/install.sh');
|
||||
});
|
||||
|
||||
it('uses the rocky linux documented docker install flow in the nightly install script', function () {
|
||||
expectRockyInstallScriptToUseRhelRepo('other/nightly/install.sh');
|
||||
});
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.473"
|
||||
"version": "4.0.0-beta.474"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0"
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
"version": "1.0.13"
|
||||
},
|
||||
"realtime": {
|
||||
"version": "1.0.12"
|
||||
"version": "1.0.13"
|
||||
},
|
||||
"sentinel": {
|
||||
"version": "0.0.21"
|
||||
|
|
|
|||
Loading…
Reference in a new issue