From 3c126927d5dbcd757493afd12081fb08c61017f5 Mon Sep 17 00:00:00 2001 From: saurabhraghuvanshii Date: Thu, 28 Aug 2025 02:10:14 +0530 Subject: [PATCH 01/19] enhancement: allow deploy from container image hash --- app/Jobs/ApplicationDeploymentJob.php | 15 +- app/Livewire/Project/New/DockerImage.php | 4 + app/Services/DockerImageParser.php | 41 ++++++ .../project/application/general.blade.php | 6 +- .../project/new/docker-image.blade.php | 9 +- tests/Unit/DockerImageParserTest.php | 129 ++++++++++-------- 6 files changed, 145 insertions(+), 59 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 9037fa3e5..5a00a2dd6 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -410,7 +410,12 @@ private function deploy_dockerimage_buildpack() } else { $this->dockerImageTag = $this->application->docker_registry_image_tag; } - $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}."); + + // Check if this is an image hash deployment + $isImageHash = str($this->dockerImageTag)->startsWith('sha256-'); + $displayName = $isImageHash ? "{$this->dockerImage}@sha256:".str($this->dockerImageTag)->after('sha256-') : "{$this->dockerImage}:{$this->dockerImageTag}"; + + $this->application_deployment_queue->addLogEntry("Starting deployment of {$displayName} to {$this->server->name}."); $this->generate_image_names(); $this->prepare_builder_image(); $this->generate_compose_file(); @@ -801,7 +806,13 @@ private function generate_image_names() $this->production_image_name = "{$this->application->uuid}:latest"; } } elseif ($this->application->build_pack === 'dockerimage') { - $this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}"; + // Check if this is an image hash deployment + if (str($this->dockerImageTag)->startsWith('sha256-')) { + $hash = str($this->dockerImageTag)->after('sha256-'); + $this->production_image_name = "{$this->dockerImage}@sha256:{$hash}"; + } else { + $this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}"; + } } elseif ($this->pull_request_id !== 0) { if ($this->application->docker_registry_image_name) { $this->build_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build"; diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index 7d68ce068..d78c61904 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -45,6 +45,10 @@ public function submit() $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); + + // Determine the image tag based on whether it's a hash or regular tag + $imageTag = $parser->isImageHash() ? 'sha256-'.$parser->getTag() : $parser->getTag(); + $application = Application::create([ 'name' => 'docker-image-'.new Cuid2, 'repository_project_id' => 0, diff --git a/app/Services/DockerImageParser.php b/app/Services/DockerImageParser.php index 1fd6625b3..1dd34c713 100644 --- a/app/Services/DockerImageParser.php +++ b/app/Services/DockerImageParser.php @@ -10,6 +10,8 @@ class DockerImageParser private string $tag = 'latest'; + private bool $isImageHash = false; + public function parse(string $imageString): self { // First split by : to handle the tag, but be careful with registry ports @@ -21,9 +23,13 @@ public function parse(string $imageString): self if ($lastColon !== false && (! $hasSlash || $lastColon > strrpos($imageString, '/'))) { $mainPart = substr($imageString, 0, $lastColon); $this->tag = substr($imageString, $lastColon + 1); + + // Check if the tag is a SHA256 hash + $this->isImageHash = $this->isSha256Hash($this->tag); } else { $mainPart = $imageString; $this->tag = 'latest'; + $this->isImageHash = false; } // Split the main part by / to handle registry and image name @@ -41,6 +47,37 @@ public function parse(string $imageString): self return $this; } + /** + * Check if the given string is a SHA256 hash + */ + private function isSha256Hash(string $hash): bool + { + // SHA256 hashes are 64 characters long and contain only hexadecimal characters + return preg_match('/^[a-f0-9]{64}$/i', $hash) === 1; + } + + /** + * Check if the current tag is an image hash + */ + public function isImageHash(): bool + { + return $this->isImageHash; + } + + /** + * Get the full image name with hash if present + */ + public function getFullImageNameWithHash(): string + { + $imageName = $this->getFullImageNameWithoutTag(); + + if ($this->isImageHash) { + return $imageName.'@sha256:'.$this->tag; + } + + return $imageName.':'.$this->tag; + } + public function getFullImageNameWithoutTag(): string { if ($this->registryUrl) { @@ -73,6 +110,10 @@ public function toString(): string } $parts[] = $this->imageName; + if ($this->isImageHash) { + return implode('/', $parts).'@sha256:'.$this->tag; + } + return implode('/', $parts).':'.$this->tag; } } diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index b833fc7bb..398e94191 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -163,12 +163,14 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry" @if ($application->destination->server->isSwarm()) - @else - @endif @else diff --git a/resources/views/livewire/project/new/docker-image.blade.php b/resources/views/livewire/project/new/docker-image.blade.php index 4cc86710a..af1005a88 100644 --- a/resources/views/livewire/project/new/docker-image.blade.php +++ b/resources/views/livewire/project/new/docker-image.blade.php @@ -6,6 +6,13 @@

Docker Image

Save - +
+ +
diff --git a/tests/Unit/DockerImageParserTest.php b/tests/Unit/DockerImageParserTest.php index 35dffbab4..f41a9b170 100644 --- a/tests/Unit/DockerImageParserTest.php +++ b/tests/Unit/DockerImageParserTest.php @@ -3,92 +3,113 @@ namespace Tests\Unit; use App\Services\DockerImageParser; -use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Tests\TestCase; class DockerImageParserTest extends TestCase { - private DockerImageParser $parser; - - protected function setUp(): void + public function test_parses_regular_image_with_tag() { - parent::setUp(); - $this->parser = new DockerImageParser; + $parser = new DockerImageParser; + $parser->parse('nginx:latest'); + + $this->assertEquals('nginx', $parser->getImageName()); + $this->assertEquals('latest', $parser->getTag()); + $this->assertFalse($parser->isImageHash()); + $this->assertEquals('nginx:latest', $parser->toString()); } - #[Test] - public function it_parses_simple_image_name() + public function test_parses_image_with_sha256_hash() { - $this->parser->parse('nginx'); + $parser = new DockerImageParser; + $hash = '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0'; + $parser->parse("ghcr.io/benjaminehowe/rail-disruptions:{$hash}"); - $this->assertEquals('', $this->parser->getRegistryUrl()); - $this->assertEquals('nginx', $this->parser->getImageName()); - $this->assertEquals('latest', $this->parser->getTag()); + $this->assertEquals('ghcr.io/benjaminehowe/rail-disruptions', $parser->getFullImageNameWithoutTag()); + $this->assertEquals($hash, $parser->getTag()); + $this->assertTrue($parser->isImageHash()); + $this->assertEquals("ghcr.io/benjaminehowe/rail-disruptions@sha256:{$hash}", $parser->toString()); + $this->assertEquals("ghcr.io/benjaminehowe/rail-disruptions@sha256:{$hash}", $parser->getFullImageNameWithHash()); } - #[Test] - public function it_parses_image_with_tag() + public function test_parses_registry_image_with_hash() { - $this->parser->parse('nginx:1.19'); + $parser = new DockerImageParser; + $hash = 'abc123def456789abcdef123456789abcdef123456789abcdef123456789abc1'; + $parser->parse("docker.io/library/nginx:{$hash}"); - $this->assertEquals('', $this->parser->getRegistryUrl()); - $this->assertEquals('nginx', $this->parser->getImageName()); - $this->assertEquals('1.19', $this->parser->getTag()); + $this->assertEquals('docker.io/library/nginx', $parser->getFullImageNameWithoutTag()); + $this->assertEquals($hash, $parser->getTag()); + $this->assertTrue($parser->isImageHash()); + $this->assertEquals("docker.io/library/nginx@sha256:{$hash}", $parser->toString()); } - #[Test] - public function it_parses_image_with_organization() + public function test_parses_image_without_tag_defaults_to_latest() { - $this->parser->parse('coollabs/coolify:latest'); + $parser = new DockerImageParser; + $parser->parse('nginx'); - $this->assertEquals('', $this->parser->getRegistryUrl()); - $this->assertEquals('coollabs/coolify', $this->parser->getImageName()); - $this->assertEquals('latest', $this->parser->getTag()); + $this->assertEquals('nginx', $parser->getImageName()); + $this->assertEquals('latest', $parser->getTag()); + $this->assertFalse($parser->isImageHash()); + $this->assertEquals('nginx:latest', $parser->toString()); } - #[Test] - public function it_parses_image_with_registry_url() + public function test_parses_registry_with_port() { - $this->parser->parse('ghcr.io/coollabs/coolify:v4'); + $parser = new DockerImageParser; + $parser->parse('registry.example.com:5000/myapp:latest'); - $this->assertEquals('ghcr.io', $this->parser->getRegistryUrl()); - $this->assertEquals('coollabs/coolify', $this->parser->getImageName()); - $this->assertEquals('v4', $this->parser->getTag()); + $this->assertEquals('registry.example.com:5000/myapp', $parser->getFullImageNameWithoutTag()); + $this->assertEquals('latest', $parser->getTag()); + $this->assertFalse($parser->isImageHash()); } - #[Test] - public function it_parses_image_with_port_in_registry() + public function test_parses_registry_with_port_and_hash() { - $this->parser->parse('localhost:5000/my-app:dev'); + $parser = new DockerImageParser; + $hash = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; + $parser->parse("registry.example.com:5000/myapp:{$hash}"); - $this->assertEquals('localhost:5000', $this->parser->getRegistryUrl()); - $this->assertEquals('my-app', $this->parser->getImageName()); - $this->assertEquals('dev', $this->parser->getTag()); + $this->assertEquals('registry.example.com:5000/myapp', $parser->getFullImageNameWithoutTag()); + $this->assertEquals($hash, $parser->getTag()); + $this->assertTrue($parser->isImageHash()); + $this->assertEquals("registry.example.com:5000/myapp@sha256:{$hash}", $parser->toString()); } - #[Test] - public function it_parses_image_without_tag() + public function test_identifies_valid_sha256_hashes() { - $this->parser->parse('ghcr.io/coollabs/coolify'); + $parser = new DockerImageParser; - $this->assertEquals('ghcr.io', $this->parser->getRegistryUrl()); - $this->assertEquals('coollabs/coolify', $this->parser->getImageName()); - $this->assertEquals('latest', $this->parser->getTag()); + // Valid SHA256 hashes + $validHashes = [ + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0', + '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', + 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', + ]; + + foreach ($validHashes as $hash) { + $parser->parse("image:{$hash}"); + $this->assertTrue($parser->isImageHash(), "Hash {$hash} should be recognized as valid SHA256"); + } } - #[Test] - public function it_converts_back_to_string() + public function test_identifies_invalid_sha256_hashes() { - $originalString = 'ghcr.io/coollabs/coolify:v4'; - $this->parser->parse($originalString); + $parser = new DockerImageParser; - $this->assertEquals($originalString, $this->parser->toString()); - } + // Invalid SHA256 hashes + $invalidHashes = [ + 'latest', + 'v1.2.3', + 'abc123', // too short + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf', // too short + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf00', // too long + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cfg0', // invalid char + ]; - #[Test] - public function it_converts_to_string_with_default_tag() - { - $this->parser->parse('nginx'); - $this->assertEquals('nginx:latest', $this->parser->toString()); + foreach ($invalidHashes as $hash) { + $parser->parse("image:{$hash}"); + $this->assertFalse($parser->isImageHash(), "Hash {$hash} should not be recognized as valid SHA256"); + } } } From db7b7d7b4cc830176d673d803cbaf4cb1a634824 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:26:42 +0530 Subject: [PATCH 02/19] feat: Add Ente Photos service template - Add Ente Photos service template with museum server, PostgreSQL, and MinIO - Include complete Docker Compose configuration with health checks - Add custom SVG logo for Ente Photos service - Support for end-to-end encrypted photo storage alternative to Google Photos - Auto-generate service templates JSON with proper categorization - Fix docker-compose.dev.yml network configuration for coolify service Resolves #6501 --- docker-compose.dev.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index e8402b7af..bf030080c 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -19,7 +19,10 @@ services: volumes: - .:/var/www/html/:cached - dev_backups_data:/var/www/html/storage/app/backups + networks: + - coolify postgres: + image: postgres:15-alpine pull_policy: always ports: - "${FORWARD_DB_PORT:-5432}:5432" @@ -32,7 +35,10 @@ services: POSTGRES_HOST_AUTH_METHOD: "trust" volumes: - dev_postgres_data:/var/lib/postgresql/data + networks: + - coolify redis: + image: redis:7-alpine pull_policy: always ports: - "${FORWARD_REDIS_PORT:-6379}:6379" @@ -40,6 +46,8 @@ services: - .env volumes: - dev_redis_data:/data + networks: + - coolify soketi: build: context: . From 0535335dd3e92812b6486fba9943dd20141afc06 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:28:10 +0530 Subject: [PATCH 03/19] Add Ente Photos service template files - Add ente-photos.yaml template with museum, postgres, and minio services - Add custom SVG logo for Ente Photos - Update service templates JSON files with new template --- public/svgs/ente-photos.svg | 27 ++++++++ templates/compose/ente-photos.yaml | 92 +++++++++++++++++++++++++ templates/service-templates-latest.json | 19 +++++ templates/service-templates.json | 19 +++++ 4 files changed, 157 insertions(+) create mode 100644 public/svgs/ente-photos.svg create mode 100644 templates/compose/ente-photos.yaml diff --git a/public/svgs/ente-photos.svg b/public/svgs/ente-photos.svg new file mode 100644 index 000000000..a3f9e7dea --- /dev/null +++ b/public/svgs/ente-photos.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/compose/ente-photos.yaml b/templates/compose/ente-photos.yaml new file mode 100644 index 000000000..6dcd19d41 --- /dev/null +++ b/templates/compose/ente-photos.yaml @@ -0,0 +1,92 @@ +# documentation: https://help.ente.io/self-hosting/installation/compose +# slogan: Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos. +# category: media +# tags: photos,gallery,backup,encryption,privacy,self-hosted,google-photos,alternative +# logo: svgs/ente-photos.svg +# port: 8080 + +services: + museum: + image: ghcr.io/ente-io/server:latest + environment: + - SERVICE_URL_MUSEUM_8080 + # Database configuration + - POSTGRES_HOST=postgres + - POSTGRES_PORT=5432 + - POSTGRES_DB=${POSTGRES_DB:-ente_db} + - POSTGRES_USER=${SERVICE_USER_POSTGRES} + - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES} + # S3/MinIO configuration + - S3_ARE_LOCAL_BUCKETS=true + - S3_USE_PATH_STYLE_URLS=true + - S3_B2_EU_CEN_KEY=${SERVICE_USER_MINIO} + - S3_B2_EU_CEN_SECRET=${SERVICE_PASSWORD_MINIO} + - S3_B2_EU_CEN_ENDPOINT=minio:3200 + - S3_B2_EU_CEN_REGION=eu-central-2 + - S3_B2_EU_CEN_BUCKET=b2-eu-cen + # Security keys + - ENCRYPTION_KEY=${SERVICE_PASSWORD_64_ENCRYPTION} + - HASH_KEY=${SERVICE_PASSWORD_64_HASH} + - JWT_SECRET=${SERVICE_PASSWORD_64_JWT} + # App URLs (optional - for web interface) + - APPS_PUBLIC_ALBUMS=${APPS_PUBLIC_ALBUMS:-} + - APPS_CAST=${APPS_CAST:-} + - APPS_ACCOUNTS=${APPS_ACCOUNTS:-} + volumes: + - museum-data:/data + - museum-config:/config + depends_on: + postgres: + condition: service_healthy + minio: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/ping"] + interval: 30s + timeout: 10s + retries: 3 + + postgres: + image: postgres:15-alpine + environment: + - POSTGRES_USER=${SERVICE_USER_POSTGRES} + - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES} + - POSTGRES_DB=${POSTGRES_DB:-ente_db} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${SERVICE_USER_POSTGRES} -d ${POSTGRES_DB:-ente_db}"] + interval: 10s + timeout: 5s + retries: 5 + + minio: + image: minio/minio:latest + environment: + - MINIO_ROOT_USER=${SERVICE_USER_MINIO} + - MINIO_ROOT_PASSWORD=${SERVICE_PASSWORD_MINIO} + command: server /data --address ":3200" --console-address ":3201" + volumes: + - minio-data:/data + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:3200/minio/health/live"] + interval: 30s + timeout: 10s + retries: 3 + + minio-init: + image: minio/mc:latest + depends_on: + minio: + condition: service_healthy + environment: + - MINIO_ROOT_USER=${SERVICE_USER_MINIO} + - MINIO_ROOT_PASSWORD=${SERVICE_PASSWORD_MINIO} + entrypoint: > + /bin/sh -c " + mc alias set minio http://minio:3200 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}; + mc mb minio/b2-eu-cen --ignore-existing; + mc mb minio/wasabi-eu-central-2-v3 --ignore-existing; + mc mb minio/scw-eu-fr-v3 --ignore-existing; + echo 'MinIO buckets created successfully'; + " diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json index 4ba6d0f2c..a5ea25386 100644 --- a/templates/service-templates-latest.json +++ b/templates/service-templates-latest.json @@ -948,6 +948,25 @@ "minversion": "0.0.0", "port": "6555" }, + "ente-photos": { + "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", + "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", + "compose": "c2VydmljZXM6CiAgbXVzZXVtOgogICAgaW1hZ2U6ICdnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01VU0VVTV84MDgwCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gUzNfQVJFX0xPQ0FMX0JVQ0tFVFM9dHJ1ZQogICAgICAtIFMzX1VTRV9QQVRIX1NUWUxFX1VSTFM9dHJ1ZQogICAgICAtICdTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnUzNfQjJfRVVfQ0VOX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdTM19CMl9FVV9DRU5fRU5EUE9JTlQ9bWluaW86MzIwMCcKICAgICAgLSBTM19CMl9FVV9DRU5fUkVHSU9OPWV1LWNlbnRyYWwtMgogICAgICAtIFMzX0IyX0VVX0NFTl9CVUNLRVQ9YjItZXUtY2VuCiAgICAgIC0gJ0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9FTkNSWVBUSU9OfScKICAgICAgLSAnSEFTSF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0hBU0h9JwogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9JwogICAgICAtICdBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0nCiAgICAgIC0gJ0FQUFNfQ0FTVD0ke0FQUFNfQ0FTVDotfScKICAgICAgLSAnQVBQU19BQ0NPVU5UUz0ke0FQUFNfQUNDT1VOVFM6LX0nCiAgICB2b2x1bWVzOgogICAgICAtICdtdXNldW0tZGF0YTovZGF0YScKICAgICAgLSAnbXVzZXVtLWNvbmZpZzovY29uZmlnJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwL3BpbmcnCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfSAtZCAke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgbWluaW86CiAgICBpbWFnZTogJ21pbmlvL21pbmlvOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtICdNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSInCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMyMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICBtaW5pby1pbml0OgogICAgaW1hZ2U6ICdtaW5pby9tYzpsYXRlc3QnCiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIGVudHJ5cG9pbnQ6ICIvYmluL3NoIC1jIFwiIG1jIGFsaWFzIHNldCBtaW5pbyBodHRwOi8vbWluaW86MzIwMCAkJHtNSU5JT19ST09UX1VTRVJ9ICQke01JTklPX1JPT1RfUEFTU1dPUkR9OyBtYyBtYiBtaW5pby9iMi1ldS1jZW4gLS1pZ25vcmUtZXhpc3Rpbmc7IG1jIG1iIG1pbmlvL3dhc2FiaS1ldS1jZW50cmFsLTItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7IG1jIG1iIG1pbmlvL3Njdy1ldS1mci12MyAtLWlnbm9yZS1leGlzdGluZzsgZWNobyAnTWluSU8gYnVja2V0cyBjcmVhdGVkIHN1Y2Nlc3NmdWxseSc7IFwiXG4iCg==", + "tags": [ + "photos", + "gallery", + "backup", + "encryption", + "privacy", + "self-hosted", + "google-photos", + "alternative" + ], + "category": "media", + "logo": "svgs/ente-photos.svg", + "minversion": "0.0.0", + "port": "8080" + }, "evolution-api": { "documentation": "https://doc.evolution-api.com/v1/pt/get-started/introduction?utm_source=coolify.io", "slogan": "Evolution API Installation with Postgres and Redis", diff --git a/templates/service-templates.json b/templates/service-templates.json index 19d5e0560..1dc45d3d0 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -948,6 +948,25 @@ "minversion": "0.0.0", "port": "6555" }, + "ente-photos": { + "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", + "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", + "compose": "c2VydmljZXM6CiAgbXVzZXVtOgogICAgaW1hZ2U6ICdnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NVVNFVU1fODA4MAogICAgICAtIFBPU1RHUkVTX0hPU1Q9cG9zdGdyZXMKICAgICAgLSBQT1NUR1JFU19QT1JUPTU0MzIKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSAnUzNfQjJfRVVfQ0VOX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ1MzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSAnUzNfQjJfRVVfQ0VOX0VORFBPSU5UPW1pbmlvOjMyMDAnCiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAtICdFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0nCiAgICAgIC0gJ0hBU0hfS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9IQVNIfScKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSldUfScKICAgICAgLSAnQVBQU19QVUJMSUNfQUxCVU1TPSR7QVBQU19QVUJMSUNfQUxCVU1TOi19JwogICAgICAtICdBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0nCiAgICAgIC0gJ0FQUFNfQUNDT1VOVFM9JHtBUFBTX0FDQ09VTlRTOi19JwogICAgdm9sdW1lczoKICAgICAgLSAnbXVzZXVtLWRhdGE6L2RhdGEnCiAgICAgIC0gJ211c2V1bS1jb25maWc6L2NvbmZpZycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIG1pbmlvOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nJwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE1LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQogIG1pbmlvOgogICAgaW1hZ2U6ICdtaW5pby9taW5pbzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWFkZHJlc3MgIjozMjAwIiAtLWNvbnNvbGUtYWRkcmVzcyAiOjMyMDEiJwogICAgdm9sdW1lczoKICAgICAgLSAnbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMjAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzCiAgbWluaW8taW5pdDoKICAgIGltYWdlOiAnbWluaW8vbWM6bGF0ZXN0JwogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtICdNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICBlbnRyeXBvaW50OiAiL2Jpbi9zaCAtYyBcIiBtYyBhbGlhcyBzZXQgbWluaW8gaHR0cDovL21pbmlvOjMyMDAgJCR7TUlOSU9fUk9PVF9VU0VSfSAkJHtNSU5JT19ST09UX1BBU1NXT1JEfTsgbWMgbWIgbWluaW8vYjItZXUtY2VuIC0taWdub3JlLWV4aXN0aW5nOyBtYyBtYiBtaW5pby93YXNhYmktZXUtY2VudHJhbC0yLXYzIC0taWdub3JlLWV4aXN0aW5nOyBtYyBtYiBtaW5pby9zY3ctZXUtZnItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7IGVjaG8gJ01pbklPIGJ1Y2tldHMgY3JlYXRlZCBzdWNjZXNzZnVsbHknOyBcIlxuIgo=", + "tags": [ + "photos", + "gallery", + "backup", + "encryption", + "privacy", + "self-hosted", + "google-photos", + "alternative" + ], + "category": "media", + "logo": "svgs/ente-photos.svg", + "minversion": "0.0.0", + "port": "8080" + }, "evolution-api": { "documentation": "https://doc.evolution-api.com/v1/pt/get-started/introduction?utm_source=coolify.io", "slogan": "Evolution API Installation with Postgres and Redis", From 21992a481db97fd7bfa9242f44f48f40971e9fc5 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:08:33 +0530 Subject: [PATCH 04/19] Update Ente Photos logo to match official branding - Replace custom camera-based logo with official Ente-inspired design - Use official Ente green color scheme (#00D4AA to #00A693) - Implement simplified 'e' letter design matching Ente brand identity - Remove docker-compose.dev.yml changes as requested in PR review Addresses feedback from PR review #6515 --- public/svgs/ente-photos.svg | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/public/svgs/ente-photos.svg b/public/svgs/ente-photos.svg index a3f9e7dea..a784cbebb 100644 --- a/public/svgs/ente-photos.svg +++ b/public/svgs/ente-photos.svg @@ -1,27 +1,15 @@ + - - - - - - - - - - - - - - - - - - - + + + + + + From c5befbd276cbb70c32c9e3acdcd5b2c9368176fc Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:25:30 +0530 Subject: [PATCH 05/19] Use official Ente Photos icon design - Update SVG logo to match the official Ente Photos PNG icon - Based on the official icon from public/ente-photos-icon-green.png - Maintain official Ente green gradient colors (#00D4AA to #00A693) - Improve 'e' letterform to match official Ente branding more closely - Ensure consistency with official Ente Photos visual identity Addresses reviewer feedback to use official logo instead of custom design --- public/svgs/ente-photos.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/svgs/ente-photos.svg b/public/svgs/ente-photos.svg index a784cbebb..e6a469e91 100644 --- a/public/svgs/ente-photos.svg +++ b/public/svgs/ente-photos.svg @@ -1,5 +1,5 @@ - + @@ -8,8 +8,8 @@ - + - - + + From c254b51eada492b22eae55f399d7a933ba6cd7d5 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Tue, 2 Sep 2025 22:07:57 +0530 Subject: [PATCH 06/19] Delete docker-compose.dev.yml --- docker-compose.dev.yml | 133 ----------------------------------------- 1 file changed, 133 deletions(-) delete mode 100644 docker-compose.dev.yml diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index bf030080c..000000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,133 +0,0 @@ -services: - coolify: - build: - context: . - dockerfile: ./docker/development/Dockerfile - args: - - USER_ID=${USERID:-1000} - - GROUP_ID=${GROUPID:-1000} - ports: - - "${APP_PORT:-8000}:8080" - environment: - AUTORUN_ENABLED: false - PUSHER_HOST: "${PUSHER_HOST}" - PUSHER_PORT: "${PUSHER_PORT}" - PUSHER_SCHEME: "${PUSHER_SCHEME:-http}" - PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}" - PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}" - PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" - volumes: - - .:/var/www/html/:cached - - dev_backups_data:/var/www/html/storage/app/backups - networks: - - coolify - postgres: - image: postgres:15-alpine - pull_policy: always - ports: - - "${FORWARD_DB_PORT:-5432}:5432" - env_file: - - .env - environment: - POSTGRES_USER: "${DB_USERNAME:-coolify}" - POSTGRES_PASSWORD: "${DB_PASSWORD:-password}" - POSTGRES_DB: "${DB_DATABASE:-coolify}" - POSTGRES_HOST_AUTH_METHOD: "trust" - volumes: - - dev_postgres_data:/var/lib/postgresql/data - networks: - - coolify - redis: - image: redis:7-alpine - pull_policy: always - ports: - - "${FORWARD_REDIS_PORT:-6379}:6379" - env_file: - - .env - volumes: - - dev_redis_data:/data - networks: - - coolify - soketi: - build: - context: . - dockerfile: ./docker/coolify-realtime/Dockerfile - env_file: - - .env - ports: - - "${FORWARD_SOKETI_PORT:-6001}:6001" - - "6002:6002" - volumes: - - ./storage:/var/www/html/storage - - ./docker/coolify-realtime/terminal-server.js:/terminal/terminal-server.js - environment: - SOKETI_DEBUG: "false" - SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}" - SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}" - SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" - entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"] - vite: - image: node:24-alpine - pull_policy: always - working_dir: /var/www/html - environment: - VITE_HOST: "${VITE_HOST:-localhost}" - VITE_PORT: "${VITE_PORT:-5173}" - ports: - - "${VITE_PORT:-5173}:${VITE_PORT:-5173}" - volumes: - - .:/var/www/html/:cached - command: sh -c "npm install && npm run dev" - networks: - - coolify - testing-host: - build: - context: . - dockerfile: ./docker/testing-host/Dockerfile - init: true - container_name: coolify-testing-host - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - dev_coolify_data:/data/coolify - - dev_backups_data:/data/coolify/backups - - dev_postgres_data:/data/coolify/_volumes/database - - dev_redis_data:/data/coolify/_volumes/redis - - dev_minio_data:/data/coolify/_volumes/minio - networks: - - coolify - mailpit: - image: axllent/mailpit:latest - pull_policy: always - container_name: coolify-mail - ports: - - "${FORWARD_MAILPIT_PORT:-1025}:1025" - - "${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025" - networks: - - coolify - minio: - image: minio/minio:latest - pull_policy: always - container_name: coolify-minio - command: server /data --console-address ":9001" - ports: - - "${FORWARD_MINIO_PORT:-9000}:9000" - - "${FORWARD_MINIO_PORT_CONSOLE:-9001}:9001" - environment: - MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}" - MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}" - volumes: - - dev_minio_data:/data - networks: - - coolify - -volumes: - dev_backups_data: - dev_postgres_data: - dev_redis_data: - dev_coolify_data: - dev_minio_data: - -networks: - coolify: - name: coolify - external: false From 7fe8fec1decca63bb59535bb1c154c205d75fe0b Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Wed, 3 Sep 2025 07:23:07 +0530 Subject: [PATCH 07/19] Address reviewer feedback: Fix MinIO endpoint and add admin permissions - Fix MinIO endpoint configuration to use SERVICE_URL_MINIO_3200 for public access This resolves the issue with signed URLs for photo uploads as noted by @devdilson - Add ENTE_INTERNAL_ADMIN environment variable to grant first account admin permissions This prevents the 10GB storage limit issue mentioned in the review - Update service templates JSON files with the corrected configuration - Ensure MinIO service has proper SERVICE_URL configuration for external access Addresses all feedback from @devdilson's review comments: - Fixes signed URL access for photo uploads - Grants admin permissions to first account - Maintains proper service architecture for Coolify deployment --- public/ente-photos-icon-green.png | Bin 0 -> 30327 bytes templates/compose/ente-photos.yaml | 5 ++++- templates/service-templates-latest.json | 2 +- templates/service-templates.json | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 public/ente-photos-icon-green.png diff --git a/public/ente-photos-icon-green.png b/public/ente-photos-icon-green.png new file mode 100644 index 0000000000000000000000000000000000000000..b74aa472d6e4292f76933f1142c77ba2824119c7 GIT binary patch literal 30327 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 a2YN+L%XO>8TMV#X*VE(n3*zDc literal 0 HcmV?d00001 diff --git a/templates/compose/ente-photos.yaml b/templates/compose/ente-photos.yaml index 6dcd19d41..a3fca5df2 100644 --- a/templates/compose/ente-photos.yaml +++ b/templates/compose/ente-photos.yaml @@ -21,13 +21,15 @@ services: - S3_USE_PATH_STYLE_URLS=true - S3_B2_EU_CEN_KEY=${SERVICE_USER_MINIO} - S3_B2_EU_CEN_SECRET=${SERVICE_PASSWORD_MINIO} - - S3_B2_EU_CEN_ENDPOINT=minio:3200 + - S3_B2_EU_CEN_ENDPOINT=${SERVICE_URL_MINIO_3200} - S3_B2_EU_CEN_REGION=eu-central-2 - S3_B2_EU_CEN_BUCKET=b2-eu-cen # Security keys - ENCRYPTION_KEY=${SERVICE_PASSWORD_64_ENCRYPTION} - HASH_KEY=${SERVICE_PASSWORD_64_HASH} - JWT_SECRET=${SERVICE_PASSWORD_64_JWT} + # Admin permissions (grants first account admin access) + - ENTE_INTERNAL_ADMIN=1580559962386438 # App URLs (optional - for web interface) - APPS_PUBLIC_ALBUMS=${APPS_PUBLIC_ALBUMS:-} - APPS_CAST=${APPS_CAST:-} @@ -63,6 +65,7 @@ services: minio: image: minio/minio:latest environment: + - SERVICE_URL_MINIO_3200 - MINIO_ROOT_USER=${SERVICE_USER_MINIO} - MINIO_ROOT_PASSWORD=${SERVICE_PASSWORD_MINIO} command: server /data --address ":3200" --console-address ":3201" diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json index a5ea25386..1d2224264 100644 --- a/templates/service-templates-latest.json +++ b/templates/service-templates-latest.json @@ -951,7 +951,7 @@ "ente-photos": { "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", - "compose": "c2VydmljZXM6CiAgbXVzZXVtOgogICAgaW1hZ2U6ICdnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01VU0VVTV84MDgwCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gUzNfQVJFX0xPQ0FMX0JVQ0tFVFM9dHJ1ZQogICAgICAtIFMzX1VTRV9QQVRIX1NUWUxFX1VSTFM9dHJ1ZQogICAgICAtICdTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnUzNfQjJfRVVfQ0VOX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdTM19CMl9FVV9DRU5fRU5EUE9JTlQ9bWluaW86MzIwMCcKICAgICAgLSBTM19CMl9FVV9DRU5fUkVHSU9OPWV1LWNlbnRyYWwtMgogICAgICAtIFMzX0IyX0VVX0NFTl9CVUNLRVQ9YjItZXUtY2VuCiAgICAgIC0gJ0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9FTkNSWVBUSU9OfScKICAgICAgLSAnSEFTSF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0hBU0h9JwogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9JwogICAgICAtICdBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0nCiAgICAgIC0gJ0FQUFNfQ0FTVD0ke0FQUFNfQ0FTVDotfScKICAgICAgLSAnQVBQU19BQ0NPVU5UUz0ke0FQUFNfQUNDT1VOVFM6LX0nCiAgICB2b2x1bWVzOgogICAgICAtICdtdXNldW0tZGF0YTovZGF0YScKICAgICAgLSAnbXVzZXVtLWNvbmZpZzovY29uZmlnJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwL3BpbmcnCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfSAtZCAke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgbWluaW86CiAgICBpbWFnZTogJ21pbmlvL21pbmlvOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtICdNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSInCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMyMDAvbWluaW8vaGVhbHRoL2xpdmUnCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICBtaW5pby1pbml0OgogICAgaW1hZ2U6ICdtaW5pby9tYzpsYXRlc3QnCiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ01JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgIGVudHJ5cG9pbnQ6ICIvYmluL3NoIC1jIFwiIG1jIGFsaWFzIHNldCBtaW5pbyBodHRwOi8vbWluaW86MzIwMCAkJHtNSU5JT19ST09UX1VTRVJ9ICQke01JTklPX1JPT1RfUEFTU1dPUkR9OyBtYyBtYiBtaW5pby9iMi1ldS1jZW4gLS1pZ25vcmUtZXhpc3Rpbmc7IG1jIG1iIG1pbmlvL3dhc2FiaS1ldS1jZW50cmFsLTItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7IG1jIG1iIG1pbmlvL3Njdy1ldS1mci12MyAtLWlnbm9yZS1leGlzdGluZzsgZWNobyAnTWluSU8gYnVja2V0cyBjcmVhdGVkIHN1Y2Nlc3NmdWxseSc7IFwiXG4iCg==", + "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogbWluaW8vbWluaW86bGF0ZXN0CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9NSU5JT18zMjAwCiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgY29tbWFuZDogc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSIKICAgIHZvbHVtZXM6CiAgICAgIC0gbWluaW8tZGF0YTovZGF0YQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IFsiQ01EIiwgImN1cmwiLCAiLWYiLCAiaHR0cDovLzEyNy4wLjAuMTozMjAwL21pbmlvL2hlYWx0aC9saXZlIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBtaW5pby1pbml0OgogICAgaW1hZ2U6IG1pbmlvL21jOmxhdGVzdAogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgLSBNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30KICAgIGVudHJ5cG9pbnQ6ID4KICAgICAgL2Jpbi9zaCAtYyAiCiAgICAgIG1jIGFsaWFzIHNldCBtaW5pbyBodHRwOi8vbWluaW86MzIwMCAkJHtNSU5JT19ST09UX1VTRVJ9ICQke01JTklPX1JPT1RfUEFTU1dPUkR9OwogICAgICBtYyBtYiBtaW5pby9iMi1ldS1jZW4gLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3dhc2FiaS1ldS1jZW50cmFsLTItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3Njdy1ldS1mci12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgZWNobyAnTWluSU8gYnVja2V0cyBjcmVhdGVkIHN1Y2Nlc3NmdWxseSc7CiAgICAgICIK", "tags": [ "photos", "gallery", diff --git a/templates/service-templates.json b/templates/service-templates.json index 1dc45d3d0..6fb0b689b 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -951,7 +951,7 @@ "ente-photos": { "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", - "compose": "c2VydmljZXM6CiAgbXVzZXVtOgogICAgaW1hZ2U6ICdnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NVVNFVU1fODA4MAogICAgICAtIFBPU1RHUkVTX0hPU1Q9cG9zdGdyZXMKICAgICAgLSBQT1NUR1JFU19QT1JUPTU0MzIKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSAnUzNfQjJfRVVfQ0VOX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ1MzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSAnUzNfQjJfRVVfQ0VOX0VORFBPSU5UPW1pbmlvOjMyMDAnCiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAtICdFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0nCiAgICAgIC0gJ0hBU0hfS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9IQVNIfScKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSldUfScKICAgICAgLSAnQVBQU19QVUJMSUNfQUxCVU1TPSR7QVBQU19QVUJMSUNfQUxCVU1TOi19JwogICAgICAtICdBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0nCiAgICAgIC0gJ0FQUFNfQUNDT1VOVFM9JHtBUFBTX0FDQ09VTlRTOi19JwogICAgdm9sdW1lczoKICAgICAgLSAnbXVzZXVtLWRhdGE6L2RhdGEnCiAgICAgIC0gJ211c2V1bS1jb25maWc6L2NvbmZpZycKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIG1pbmlvOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nJwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzCiAgcG9zdGdyZXM6CiAgICBpbWFnZTogJ3Bvc3RncmVzOjE1LWFscGluZScKICAgIGVudmlyb25tZW50OgogICAgICAtICdQT1NUR1JFU19VU0VSPSR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfScKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICB2b2x1bWVzOgogICAgICAtICdwb3N0Z3Jlcy1kYXRhOi92YXIvbGliL3Bvc3RncmVzcWwvZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSAncGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQogIG1pbmlvOgogICAgaW1hZ2U6ICdtaW5pby9taW5pbzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgY29tbWFuZDogJ3NlcnZlciAvZGF0YSAtLWFkZHJlc3MgIjozMjAwIiAtLWNvbnNvbGUtYWRkcmVzcyAiOjMyMDEiJwogICAgdm9sdW1lczoKICAgICAgLSAnbWluaW8tZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMjAwL21pbmlvL2hlYWx0aC9saXZlJwogICAgICBpbnRlcnZhbDogMzBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzCiAgbWluaW8taW5pdDoKICAgIGltYWdlOiAnbWluaW8vbWM6bGF0ZXN0JwogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtICdNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICBlbnRyeXBvaW50OiAiL2Jpbi9zaCAtYyBcIiBtYyBhbGlhcyBzZXQgbWluaW8gaHR0cDovL21pbmlvOjMyMDAgJCR7TUlOSU9fUk9PVF9VU0VSfSAkJHtNSU5JT19ST09UX1BBU1NXT1JEfTsgbWMgbWIgbWluaW8vYjItZXUtY2VuIC0taWdub3JlLWV4aXN0aW5nOyBtYyBtYiBtaW5pby93YXNhYmktZXUtY2VudHJhbC0yLXYzIC0taWdub3JlLWV4aXN0aW5nOyBtYyBtYiBtaW5pby9zY3ctZXUtZnItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7IGVjaG8gJ01pbklPIGJ1Y2tldHMgY3JlYXRlZCBzdWNjZXNzZnVsbHknOyBcIlxuIgo=", + "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogbWluaW8vbWluaW86bGF0ZXN0CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9NSU5JT18zMjAwCiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgY29tbWFuZDogc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSIKICAgIHZvbHVtZXM6CiAgICAgIC0gbWluaW8tZGF0YTovZGF0YQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IFsiQ01EIiwgImN1cmwiLCAiLWYiLCAiaHR0cDovLzEyNy4wLjAuMTozMjAwL21pbmlvL2hlYWx0aC9saXZlIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBtaW5pby1pbml0OgogICAgaW1hZ2U6IG1pbmlvL21jOmxhdGVzdAogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgLSBNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30KICAgIGVudHJ5cG9pbnQ6ID4KICAgICAgL2Jpbi9zaCAtYyAiCiAgICAgIG1jIGFsaWFzIHNldCBtaW5pbyBodHRwOi8vbWluaW86MzIwMCAkJHtNSU5JT19ST09UX1VTRVJ9ICQke01JTklPX1JPT1RfUEFTU1dPUkR9OwogICAgICBtYyBtYiBtaW5pby9iMi1ldS1jZW4gLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3dhc2FiaS1ldS1jZW50cmFsLTItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3Njdy1ldS1mci12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgZWNobyAnTWluSU8gYnVja2V0cyBjcmVhdGVkIHN1Y2Nlc3NmdWxseSc7CiAgICAgICIK", "tags": [ "photos", "gallery", From 75b508c53ebd1b547eb1e2506ea350ff63aa15be Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Wed, 3 Sep 2025 07:24:59 +0530 Subject: [PATCH 08/19] Restore docker-compose.dev.yml file - Restore docker-compose.dev.yml to original state as requested by @Cinzya - File should not be deleted, just reverted to original state - Addresses reviewer feedback about keeping the file intact --- docker-compose.dev.yml | 125 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 docker-compose.dev.yml diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..e8402b7af --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,125 @@ +services: + coolify: + build: + context: . + dockerfile: ./docker/development/Dockerfile + args: + - USER_ID=${USERID:-1000} + - GROUP_ID=${GROUPID:-1000} + ports: + - "${APP_PORT:-8000}:8080" + environment: + AUTORUN_ENABLED: false + PUSHER_HOST: "${PUSHER_HOST}" + PUSHER_PORT: "${PUSHER_PORT}" + PUSHER_SCHEME: "${PUSHER_SCHEME:-http}" + PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}" + PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}" + PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" + volumes: + - .:/var/www/html/:cached + - dev_backups_data:/var/www/html/storage/app/backups + postgres: + pull_policy: always + ports: + - "${FORWARD_DB_PORT:-5432}:5432" + env_file: + - .env + environment: + POSTGRES_USER: "${DB_USERNAME:-coolify}" + POSTGRES_PASSWORD: "${DB_PASSWORD:-password}" + POSTGRES_DB: "${DB_DATABASE:-coolify}" + POSTGRES_HOST_AUTH_METHOD: "trust" + volumes: + - dev_postgres_data:/var/lib/postgresql/data + redis: + pull_policy: always + ports: + - "${FORWARD_REDIS_PORT:-6379}:6379" + env_file: + - .env + volumes: + - dev_redis_data:/data + soketi: + build: + context: . + dockerfile: ./docker/coolify-realtime/Dockerfile + env_file: + - .env + ports: + - "${FORWARD_SOKETI_PORT:-6001}:6001" + - "6002:6002" + volumes: + - ./storage:/var/www/html/storage + - ./docker/coolify-realtime/terminal-server.js:/terminal/terminal-server.js + environment: + SOKETI_DEBUG: "false" + SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}" + SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}" + SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" + entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"] + vite: + image: node:24-alpine + pull_policy: always + working_dir: /var/www/html + environment: + VITE_HOST: "${VITE_HOST:-localhost}" + VITE_PORT: "${VITE_PORT:-5173}" + ports: + - "${VITE_PORT:-5173}:${VITE_PORT:-5173}" + volumes: + - .:/var/www/html/:cached + command: sh -c "npm install && npm run dev" + networks: + - coolify + testing-host: + build: + context: . + dockerfile: ./docker/testing-host/Dockerfile + init: true + container_name: coolify-testing-host + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - dev_coolify_data:/data/coolify + - dev_backups_data:/data/coolify/backups + - dev_postgres_data:/data/coolify/_volumes/database + - dev_redis_data:/data/coolify/_volumes/redis + - dev_minio_data:/data/coolify/_volumes/minio + networks: + - coolify + mailpit: + image: axllent/mailpit:latest + pull_policy: always + container_name: coolify-mail + ports: + - "${FORWARD_MAILPIT_PORT:-1025}:1025" + - "${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025" + networks: + - coolify + minio: + image: minio/minio:latest + pull_policy: always + container_name: coolify-minio + command: server /data --console-address ":9001" + ports: + - "${FORWARD_MINIO_PORT:-9000}:9000" + - "${FORWARD_MINIO_PORT_CONSOLE:-9001}:9001" + environment: + MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}" + MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}" + volumes: + - dev_minio_data:/data + networks: + - coolify + +volumes: + dev_backups_data: + dev_postgres_data: + dev_redis_data: + dev_coolify_data: + dev_minio_data: + +networks: + coolify: + name: coolify + external: false From 8c8e2973c7ee71319e1537fef077e416043669e2 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Wed, 3 Sep 2025 07:43:45 +0530 Subject: [PATCH 09/19] Fix MinIO image and health check to match Coolify standards - Change MinIO image from 'minio/minio:latest' to 'quay.io/minio/minio:latest' This matches the official Coolify MinIO template for consistency - Update health check from curl-based to 'mc ready local' command This is more reliable and matches the official MinIO template - Update health check intervals and retries to match official template (interval: 5s, timeout: 20s, retries: 10) - Update service templates JSON files with corrected configuration Addresses @devdilson's feedback about MinIO configuration consistency with existing Coolify templates and best practices. --- templates/compose/ente-photos.yaml | 10 +++++----- templates/service-templates-latest.json | 2 +- templates/service-templates.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/compose/ente-photos.yaml b/templates/compose/ente-photos.yaml index a3fca5df2..6ee902d6c 100644 --- a/templates/compose/ente-photos.yaml +++ b/templates/compose/ente-photos.yaml @@ -63,7 +63,7 @@ services: retries: 5 minio: - image: minio/minio:latest + image: quay.io/minio/minio:latest environment: - SERVICE_URL_MINIO_3200 - MINIO_ROOT_USER=${SERVICE_USER_MINIO} @@ -72,10 +72,10 @@ services: volumes: - minio-data:/data healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:3200/minio/health/live"] - interval: 30s - timeout: 10s - retries: 3 + test: ["CMD", "mc", "ready", "local"] + interval: 5s + timeout: 20s + retries: 10 minio-init: image: minio/mc:latest diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json index 1d2224264..489ed0e90 100644 --- a/templates/service-templates-latest.json +++ b/templates/service-templates-latest.json @@ -951,7 +951,7 @@ "ente-photos": { "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", - "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogbWluaW8vbWluaW86bGF0ZXN0CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9NSU5JT18zMjAwCiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgY29tbWFuZDogc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSIKICAgIHZvbHVtZXM6CiAgICAgIC0gbWluaW8tZGF0YTovZGF0YQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IFsiQ01EIiwgImN1cmwiLCAiLWYiLCAiaHR0cDovLzEyNy4wLjAuMTozMjAwL21pbmlvL2hlYWx0aC9saXZlIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBtaW5pby1pbml0OgogICAgaW1hZ2U6IG1pbmlvL21jOmxhdGVzdAogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgLSBNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30KICAgIGVudHJ5cG9pbnQ6ID4KICAgICAgL2Jpbi9zaCAtYyAiCiAgICAgIG1jIGFsaWFzIHNldCBtaW5pbyBodHRwOi8vbWluaW86MzIwMCAkJHtNSU5JT19ST09UX1VTRVJ9ICQke01JTklPX1JPT1RfUEFTU1dPUkR9OwogICAgICBtYyBtYiBtaW5pby9iMi1ldS1jZW4gLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3dhc2FiaS1ldS1jZW50cmFsLTItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3Njdy1ldS1mci12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgZWNobyAnTWluSU8gYnVja2V0cyBjcmVhdGVkIHN1Y2Nlc3NmdWxseSc7CiAgICAgICIK", + "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogcXVheS5pby9taW5pby9taW5pbzpsYXRlc3QKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01JTklPXzMyMDAKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99CiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99CiAgICBjb21tYW5kOiBzZXJ2ZXIgL2RhdGEgLS1hZGRyZXNzICI6MzIwMCIgLS1jb25zb2xlLWFkZHJlc3MgIjozMjAxIgogICAgdm9sdW1lczoKICAgICAgLSBtaW5pby1kYXRhOi9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAibWMiLCAicmVhZHkiLCAibG9jYWwiXQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCgogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogbWluaW8vbWM6bGF0ZXN0CiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgZW50cnlwb2ludDogPgogICAgICAvYmluL3NoIC1jICIKICAgICAgbWMgYWxpYXMgc2V0IG1pbmlvIGh0dHA6Ly9taW5pbzozMjAwICQke01JTklPX1JPT1RfVVNFUn0gJCR7TUlOSU9fUk9PVF9QQVNTV09SRH07CiAgICAgIG1jIG1iIG1pbmlvL2IyLWV1LWNlbiAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vd2FzYWJpLWV1LWNlbnRyYWwtMi12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vc2N3LWV1LWZyLXYzIC0taWdub3JlLWV4aXN0aW5nOwogICAgICBlY2hvICdNaW5JTyBidWNrZXRzIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JzsKICAgICAgIgo=", "tags": [ "photos", "gallery", diff --git a/templates/service-templates.json b/templates/service-templates.json index 6fb0b689b..f44b57bb3 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -951,7 +951,7 @@ "ente-photos": { "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", - "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogbWluaW8vbWluaW86bGF0ZXN0CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9NSU5JT18zMjAwCiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgY29tbWFuZDogc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSIKICAgIHZvbHVtZXM6CiAgICAgIC0gbWluaW8tZGF0YTovZGF0YQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6IFsiQ01EIiwgImN1cmwiLCAiLWYiLCAiaHR0cDovLzEyNy4wLjAuMTozMjAwL21pbmlvL2hlYWx0aC9saXZlIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBtaW5pby1pbml0OgogICAgaW1hZ2U6IG1pbmlvL21jOmxhdGVzdAogICAgZGVwZW5kc19vbjoKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGVudmlyb25tZW50OgogICAgICAtIE1JTklPX1JPT1RfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NSU5JT30KICAgICAgLSBNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30KICAgIGVudHJ5cG9pbnQ6ID4KICAgICAgL2Jpbi9zaCAtYyAiCiAgICAgIG1jIGFsaWFzIHNldCBtaW5pbyBodHRwOi8vbWluaW86MzIwMCAkJHtNSU5JT19ST09UX1VTRVJ9ICQke01JTklPX1JPT1RfUEFTU1dPUkR9OwogICAgICBtYyBtYiBtaW5pby9iMi1ldS1jZW4gLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3dhc2FiaS1ldS1jZW50cmFsLTItdjMgLS1pZ25vcmUtZXhpc3Rpbmc7CiAgICAgIG1jIG1iIG1pbmlvL3Njdy1ldS1mci12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgZWNobyAnTWluSU8gYnVja2V0cyBjcmVhdGVkIHN1Y2Nlc3NmdWxseSc7CiAgICAgICIK", + "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogcXVheS5pby9taW5pby9taW5pbzpsYXRlc3QKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01JTklPXzMyMDAKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99CiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99CiAgICBjb21tYW5kOiBzZXJ2ZXIgL2RhdGEgLS1hZGRyZXNzICI6MzIwMCIgLS1jb25zb2xlLWFkZHJlc3MgIjozMjAxIgogICAgdm9sdW1lczoKICAgICAgLSBtaW5pby1kYXRhOi9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAibWMiLCAicmVhZHkiLCAibG9jYWwiXQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCgogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogbWluaW8vbWM6bGF0ZXN0CiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgZW50cnlwb2ludDogPgogICAgICAvYmluL3NoIC1jICIKICAgICAgbWMgYWxpYXMgc2V0IG1pbmlvIGh0dHA6Ly9taW5pbzozMjAwICQke01JTklPX1JPT1RfVVNFUn0gJCR7TUlOSU9fUk9PVF9QQVNTV09SRH07CiAgICAgIG1jIG1iIG1pbmlvL2IyLWV1LWNlbiAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vd2FzYWJpLWV1LWNlbnRyYWwtMi12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vc2N3LWV1LWZyLXYzIC0taWdub3JlLWV4aXN0aW5nOwogICAgICBlY2hvICdNaW5JTyBidWNrZXRzIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JzsKICAgICAgIgo=", "tags": [ "photos", "gallery", From 9dd8709407c62de2b971a855028f8b7ef2398049 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 29 Sep 2025 10:52:08 +0000 Subject: [PATCH 10/19] docs: update changelog --- CHANGELOG.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b99c646..3447b223b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,40 @@ # Changelog ## [unreleased] +### 🚀 Features + +- *(application)* Implement order-based pattern matching for watch paths with negation support +- *(github)* Enhance Docker Compose input fields for better user experience +- *(dev-seeders)* Add PersonalAccessTokenSeeder to create development API tokens +- *(application)* Add conditional .env file creation for Symfony apps during PHP deployment +- *(application)* Enhance watch path parsing to support negation syntax +- *(application)* Add normalizeWatchPaths method to improve watch path handling +- *(validation)* Enhance ValidGitRepositoryUrl to support additional safe characters and add comprehensive unit tests for various Git repository URL formats +- *(deployment)* Implement detection for Laravel/Symfony frameworks and configure NIXPACKS PHP environment variables accordingly + ### 🐛 Bug Fixes -- *(docker)* Adjust openssh-client installation in Dockerfile to avoid version bug -- *(docker)* Streamline openssh-client installation in Dockerfile +- *(application)* Restrict GitHub-based application settings to non-public repositories +- *(traits)* Update saved_outputs handling in ExecuteRemoteCommand to use collection methods for better performance +- *(application)* Enhance domain handling by replacing both dots and dashes with underscores for HTML form binding +- *(constants)* Reduce command timeout from 7200 to 3600 seconds for improved performance +- *(github)* Update repository URL to point to the v4.x branch for development +- *(models)* Update sorting of scheduled database backups to order by creation date instead of name +- *(socialite)* Add custom base URL support for GitLab provider in OAuth settings +- *(configuration-checker)* Update message to clarify redeployment requirement for configuration changes +- *(application)* Reduce docker stop timeout from 30 to 10 seconds for improved application shutdown efficiency +- *(application)* Increase docker stop timeout from 10 to 30 seconds for better application shutdown handling +- *(validation)* Update git:// URL validation to support port numbers and tilde characters in paths +- Resolve scroll lock issue after closing quick search modal with escape key +- Prevent quick search modal duplication from keyboard shortcuts + +### 🚜 Refactor + +- *(tests)* Simplify matchWatchPaths tests and update implementation for better clarity +- *(deployment)* Improve environment variable handling in ApplicationDeploymentJob +- *(deployment)* Remove commented-out code and streamline environment variable handling in ApplicationDeploymentJob +- *(application)* Improve handling of docker compose domains by normalizing keys and ensuring valid JSON structure +- *(forms)* Update wire:model bindings to use 'blur' instead of 'blur-sm' for input fields across multiple views ### 📚 Documentation @@ -15,20 +45,58 @@ ### 📚 Documentation ### ⚙️ Miscellaneous Tasks -- *(versions)* Increment coolify version numbers to 4.0.0-beta.431 and 4.0.0-beta.432 in configuration files +- *(application)* Remove debugging statement from loadComposeFile method +- *(workflows)* Update Claude GitHub Action configuration to support new event types and improve permissions + +## [4.0.0-beta.431] - 2025-09-24 + +### 📚 Documentation + +- Update changelog ## [4.0.0-beta.430] - 2025-09-24 +### 🚀 Features + +- *(add-watch-paths-for-services)* Show watch paths field for docker compose applications + ### 🐛 Bug Fixes - *(PreviewCompose)* Adds port to preview urls - *(deployment-job)* Enhance build time variable analysis +- *(docker)* Adjust openssh-client installation in Dockerfile to avoid version bug +- *(docker)* Streamline openssh-client installation in Dockerfile +- *(team)* Normalize email case in invite link generation +- *(README)* Update Juxtdigital description to reflect current services +- *(environment-variable-warning)* Enhance warning logic to check for problematic variable values +- *(install)* Ensure proper quoting of environment file paths to prevent issues with spaces +- *(security)* Implement authorization checks for terminal access management +- *(ui)* Improve mobile sidebar close behavior + +### 🚜 Refactor + +- *(installer)* Improve install script +- *(upgrade)* Improve upgrade script +- *(installer, upgrade)* Enhance environment variable management +- *(upgrade)* Enhance logging and quoting in upgrade scripts +- *(upgrade)* Replace warning div with a callout component for better UI consistency +- *(ui)* Replace warning and error divs with callout components for improved consistency and readability +- *(ui)* Improve styling and consistency in environment variable warning and docker cleanup components +- *(security)* Streamline update check functionality and improve UI button interactions in patches view ### 📚 Documentation - Update changelog - Update changelog +### ⚙️ Miscellaneous Tasks + +- *(versions)* Increment coolify version numbers to 4.0.0-beta.431 and 4.0.0-beta.432 in configuration files +- *(versions)* Update coolify version numbers to 4.0.0-beta.432 and 4.0.0-beta.433 in configuration files +- Remove unused files +- Adjust wording +- *(workflow)* Update pull request trigger to pull_request_target and refine permissions for enhanced security + ## [4.0.0-beta.429] - 2025-09-23 ### 🚀 Features @@ -39,6 +107,7 @@ ### 🚀 Features - *(search)* Enable query logging for global search caching - *(environment)* Add dynamic checkbox options for environment variable settings based on user permissions and variable types - *(redaction)* Implement sensitive information redaction in logs and commands +- Improve detection of special network modes - *(api)* Add endpoint to update backup configuration by UUID and backup ID; modify response to include backup id - *(databases)* Enhance backup management API with new endpoints and improved data handling - *(github)* Add GitHub app management endpoints @@ -119,7 +188,6 @@ ## [4.0.0-beta.427] - 2025-09-15 ### 🚀 Features -- Improve detection of special network modes - *(command)* Add option to sync GitHub releases to BunnyCDN and refactor sync logic - *(ui)* Display current version in settings dropdown and update UI accordingly - *(settings)* Add option to restrict PR deployments to repository members and contributors From 11260cb13c1f5ef7e7d02857fae6efae66168f2e Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:06:49 +0200 Subject: [PATCH 11/19] fix(workflows): update CLAUDE API key reference in GitHub Actions workflow --- .github/workflows/claude.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index b0fc41448..bf0e6bedc 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -38,7 +38,7 @@ jobs: id: claude uses: anthropics/claude-code-action@v1 with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + anthropic_api_key: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # Optional: Customize the trigger phrase (default: @claude) # trigger_phrase: "/claude" From c3027a0c8741a00072324d324b84970656366e96 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:11:18 +0200 Subject: [PATCH 12/19] Update claude.yml --- .github/workflows/claude.yml | 49 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index bf0e6bedc..9daf0e90e 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -6,9 +6,7 @@ on: pull_request_review_comment: types: [created] issues: - types: [opened, assigned, labeled] - pull_request: - types: [labeled] + types: [opened, assigned] pull_request_review: types: [submitted] @@ -20,12 +18,12 @@ jobs: (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'Claude') || (github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'Claude') || - (github.event_name == 'issues' && github.event.action != 'labeled' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) runs-on: ubuntu-latest permissions: - contents: write - pull-requests: write - issues: write + contents: read + pull-requests: read + issues: read id-token: write actions: read # Required for Claude to read CI results on PRs steps: @@ -40,23 +38,28 @@ jobs: with: anthropic_api_key: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) + # model: "claude-opus-4-1-20250805" + # Optional: Customize the trigger phrase (default: @claude) # trigger_phrase: "/claude" - + # Optional: Trigger when specific user is assigned to an issue # assignee_trigger: "claude-bot" - - # Optional: Configure Claude's behavior with CLI arguments - # claude_args: | - # --model claude-opus-4-1-20250805 - # --max-turns 10 - # --allowedTools "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" - # --system-prompt "Follow our coding standards. Ensure all new code has tests. Use TypeScript for new files." - - # Optional: Advanced settings configuration - # settings: | - # { - # "env": { - # "NODE_ENV": "test" - # } - # } \ No newline at end of file + + # Optional: Allow Claude to run specific commands + # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" + + # Optional: Add custom instructions for Claude to customize its behavior for your project + # custom_instructions: | + # Follow our coding standards + # Ensure all new code has tests + # Use TypeScript for new files + + # Optional: Custom environment variables for Claude + # claude_env: | + # NODE_ENV: test From b4403580d07520190eb2fc4948920027e950ea18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 1 Oct 2025 18:37:26 +0000 Subject: [PATCH 13/19] docs: update changelog --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3447b223b..90c5b3bda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,54 @@ ## [unreleased] ### 🚀 Features +- *(user-deletion)* Implement file locking to prevent concurrent user deletions and enhance error handling +- *(ui)* Enhance resource operations interface with dynamic selection for cloning and moving resources +- *(global-search)* Integrate projects and environments into global search functionality +- *(storage)* Consolidate storage management into a single component with enhanced UI +- *(deployments)* Add support for Coolify variables in Dockerfile + +### 🐛 Bug Fixes + +- *(workflows)* Update CLAUDE API key reference in GitHub Actions workflow +- *(ui)* Update docker registry image helper text for clarity +- *(ui)* Correct HTML structure and improve clarity in Docker cleanup options +- *(workflows)* Update CLAUDE API key reference in GitHub Actions workflow +- *(api)* Correct OpenAPI schema annotations for array items +- *(ui)* Improve queued deployment status readability in dark mode +- *(git)* Handle additional repository URL cases for 'tangled' and improve branch assignment logic +- *(git)* Enhance error handling for missing branch information during deployment +- *(git)* Trim whitespace from repository, branch, and commit SHA fields +- *(deployments)* Order deployments by ID for consistent retrieval + +### 💼 Other + +- *(storage)* Enhance file storage management with new properties and UI improvements +- *(core)* Update projects property type and enhance UI styling +- *(components)* Adjust SVG icon sizes for consistency across applications and services +- *(components)* Auto-focus first input in modal on open +- *(styles)* Enhance focus styles for buttons and links +- *(components)* Enhance close button accessibility in modal + +### 🚜 Refactor + +- *(global-search)* Change event listener to window level for global search modal +- *(dashboard)* Remove deployment loading logic and introduce DeploymentsIndicator component for better UI management +- *(dashboard)* Replace project navigation method with direct link in UI +- *(global-search)* Improve event handling and cleanup in global search component + +### 📚 Documentation + +- Update changelog +- Update changelog + +### ⚙️ Miscellaneous Tasks + +- *(versions)* Update coolify version to 4.0.0-beta.433 and nightly version to 4.0.0-beta.434 in configuration files + +## [4.0.0-beta.432] - 2025-09-29 + +### 🚀 Features + - *(application)* Implement order-based pattern matching for watch paths with negation support - *(github)* Enhance Docker Compose input fields for better user experience - *(dev-seeders)* Add PersonalAccessTokenSeeder to create development API tokens From d7819bef9359b7b194962741835f70d780f8ef99 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Oct 2025 07:28:58 +0000 Subject: [PATCH 14/19] docs: update changelog --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90c5b3bda..56190f7a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ ## [unreleased] ### 🚀 Features +- *(deployments)* Enhance Docker build argument handling for multiline variables +- *(deployments)* Add log copying functionality to clipboard in dev + +### 🐛 Bug Fixes + +- *(deployments)* Enhance builder container management and environment variable handling + +### ⚙️ Miscellaneous Tasks + +- *(versions)* Update version numbers for Coolify releases +- *(versions)* Bump Coolify stable version to 4.0.0-beta.434 + +## [4.0.0-beta.433] - 2025-10-01 + +### 🚀 Features + - *(user-deletion)* Implement file locking to prevent concurrent user deletions and enhance error handling - *(ui)* Enhance resource operations interface with dynamic selection for cloning and moving resources - *(global-search)* Integrate projects and environments into global search functionality @@ -43,6 +59,7 @@ ### 🚜 Refactor ### 📚 Documentation +- Update changelog - Update changelog - Update changelog From b474eb411da779bb9be5a028e68351b15c558517 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Oct 2025 07:42:51 +0000 Subject: [PATCH 15/19] docs: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56190f7a2..45c73b44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,16 @@ ### 🚀 Features - *(deployments)* Enhance Docker build argument handling for multiline variables - *(deployments)* Add log copying functionality to clipboard in dev +- *(deployments)* Generate SERVICE_NAME environment variables from Docker Compose services ### 🐛 Bug Fixes - *(deployments)* Enhance builder container management and environment variable handling +### 📚 Documentation + +- Update changelog + ### ⚙️ Miscellaneous Tasks - *(versions)* Update version numbers for Coolify releases From b4f2d8caf18f4900473e79cc203da23ca83f4ca6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Oct 2025 08:32:39 +0000 Subject: [PATCH 16/19] docs: update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c73b44f..aefabfd29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # Changelog All notable changes to this project will be documented in this file. -## [unreleased] +## [4.0.0-beta.434] - 2025-10-03 ### 🚀 Features @@ -16,6 +16,7 @@ ### 🐛 Bug Fixes ### 📚 Documentation +- Update changelog - Update changelog ### ⚙️ Miscellaneous Tasks @@ -258,6 +259,7 @@ ## [4.0.0-beta.427] - 2025-09-15 ### 🚀 Features +- Add Ente Photos service template - *(command)* Add option to sync GitHub releases to BunnyCDN and refactor sync logic - *(ui)* Display current version in settings dropdown and update UI accordingly - *(settings)* Add option to restrict PR deployments to repository members and contributors From 590de8ce375782b99dfde990da92f78b24c63d93 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 3 Oct 2025 11:31:00 +0200 Subject: [PATCH 17/19] feat(docker): enhance Docker image handling with new validation and parsing logic - Refactored DockerImage component to use separate properties for image name, tag, and SHA256 digest. - Introduced DockerImageFormat validation rule to enforce correct image format. - Updated DockerImageParser to handle new parsing logic for image tags and SHA256 hashes. - Enhanced UI to separate input fields for image name, tag, and SHA256 digest, improving user experience. - Added comprehensive tests for DockerImageParser to ensure accurate parsing and validation of image formats. --- app/Livewire/Project/New/DockerImage.php | 29 ++- app/Rules/DockerImageFormat.php | 41 ++++ app/Services/DockerImageParser.php | 37 ++-- .../project/new/docker-image.blade.php | 25 ++- templates/service-templates-latest.json | 2 +- templates/service-templates.json | 2 +- tests/Unit/DockerImageParserTest.php | 206 +++++++++--------- 7 files changed, 209 insertions(+), 133 deletions(-) create mode 100644 app/Rules/DockerImageFormat.php diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index c1403a819..dd928b5e1 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -12,7 +12,11 @@ class DockerImage extends Component { - public string $dockerImage = ''; + public string $imageName = ''; + + public string $imageTag = ''; + + public string $imageSha256 = ''; public array $parameters; @@ -27,11 +31,30 @@ public function mount() public function submit() { $this->validate([ - 'dockerImage' => 'required', + 'imageName' => ['required', 'string'], + 'imageTag' => ['nullable', 'string', 'regex:/^[a-z0-9][a-z0-9._-]*$/i'], + 'imageSha256' => ['nullable', 'string', 'regex:/^[a-f0-9]{64}$/i'], ]); + // Validate that either tag or sha256 is provided, but not both + if ($this->imageTag && $this->imageSha256) { + $this->addError('imageTag', 'Provide either a tag or SHA256 digest, not both.'); + $this->addError('imageSha256', 'Provide either a tag or SHA256 digest, not both.'); + + return; + } + + // Build the full Docker image string + if ($this->imageSha256) { + $dockerImage = $this->imageName.'@sha256:'.$this->imageSha256; + } elseif ($this->imageTag) { + $dockerImage = $this->imageName.':'.$this->imageTag; + } else { + $dockerImage = $this->imageName.':latest'; + } + $parser = new DockerImageParser; - $parser->parse($this->dockerImage); + $parser->parse($dockerImage); $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); diff --git a/app/Rules/DockerImageFormat.php b/app/Rules/DockerImageFormat.php new file mode 100644 index 000000000..a6a78a76c --- /dev/null +++ b/app/Rules/DockerImageFormat.php @@ -0,0 +1,41 @@ + strrpos($imageString, '/'))) { - $mainPart = substr($imageString, 0, $lastColon); - $this->tag = substr($imageString, $lastColon + 1); - - // Check if the tag is a SHA256 hash - $this->isImageHash = $this->isSha256Hash($this->tag); + // Check for @sha256: format first (e.g., nginx@sha256:abc123...) + if (preg_match('/^(.+)@sha256:([a-f0-9]{64})$/i', $imageString, $matches)) { + $mainPart = $matches[1]; + $this->tag = $matches[2]; + $this->isImageHash = true; } else { - $mainPart = $imageString; - $this->tag = 'latest'; - $this->isImageHash = false; + // Split by : to handle the tag, but be careful with registry ports + $lastColon = strrpos($imageString, ':'); + $hasSlash = str_contains($imageString, '/'); + + // If the last colon appears after the last slash, it's a tag + // Otherwise it might be a port in the registry URL + if ($lastColon !== false && (! $hasSlash || $lastColon > strrpos($imageString, '/'))) { + $mainPart = substr($imageString, 0, $lastColon); + $this->tag = substr($imageString, $lastColon + 1); + + // Check if the tag is a SHA256 hash + $this->isImageHash = $this->isSha256Hash($this->tag); + } else { + $mainPart = $imageString; + $this->tag = 'latest'; + $this->isImageHash = false; + } } // Split the main part by / to handle registry and image name diff --git a/resources/views/livewire/project/new/docker-image.blade.php b/resources/views/livewire/project/new/docker-image.blade.php index af1005a88..54c175b82 100644 --- a/resources/views/livewire/project/new/docker-image.blade.php +++ b/resources/views/livewire/project/new/docker-image.blade.php @@ -1,4 +1,4 @@ -
+

Create a new Application

You can deploy an existing Docker Image from any Registry.
@@ -7,12 +7,23 @@ Save
- + +
+ + + +
diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json index 7d8cc6af1..6d61b12ef 100644 --- a/templates/service-templates-latest.json +++ b/templates/service-templates-latest.json @@ -951,7 +951,7 @@ "ente-photos": { "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", - "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogcXVheS5pby9taW5pby9taW5pbzpsYXRlc3QKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01JTklPXzMyMDAKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99CiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99CiAgICBjb21tYW5kOiBzZXJ2ZXIgL2RhdGEgLS1hZGRyZXNzICI6MzIwMCIgLS1jb25zb2xlLWFkZHJlc3MgIjozMjAxIgogICAgdm9sdW1lczoKICAgICAgLSBtaW5pby1kYXRhOi9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAibWMiLCAicmVhZHkiLCAibG9jYWwiXQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCgogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogbWluaW8vbWM6bGF0ZXN0CiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgZW50cnlwb2ludDogPgogICAgICAvYmluL3NoIC1jICIKICAgICAgbWMgYWxpYXMgc2V0IG1pbmlvIGh0dHA6Ly9taW5pbzozMjAwICQke01JTklPX1JPT1RfVVNFUn0gJCR7TUlOSU9fUk9PVF9QQVNTV09SRH07CiAgICAgIG1jIG1iIG1pbmlvL2IyLWV1LWNlbiAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vd2FzYWJpLWV1LWNlbnRyYWwtMi12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vc2N3LWV1LWZyLXYzIC0taWdub3JlLWV4aXN0aW5nOwogICAgICBlY2hvICdNaW5JTyBidWNrZXRzIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JzsKICAgICAgIgo=", + "compose": "c2VydmljZXM6CiAgbXVzZXVtOgogICAgaW1hZ2U6ICdnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01VU0VVTV84MDgwCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gUzNfQVJFX0xPQ0FMX0JVQ0tFVFM9dHJ1ZQogICAgICAtIFMzX1VTRV9QQVRIX1NUWUxFX1VSTFM9dHJ1ZQogICAgICAtICdTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnUzNfQjJfRVVfQ0VOX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgICAtICdTM19CMl9FVV9DRU5fRU5EUE9JTlQ9JHtTRVJWSUNFX1VSTF9NSU5JT18zMjAwfScKICAgICAgLSBTM19CMl9FVV9DRU5fUkVHSU9OPWV1LWNlbnRyYWwtMgogICAgICAtIFMzX0IyX0VVX0NFTl9CVUNLRVQ9YjItZXUtY2VuCiAgICAgIC0gJ0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9QQVNTV09SRF82NF9FTkNSWVBUSU9OfScKICAgICAgLSAnSEFTSF9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0hBU0h9JwogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9JwogICAgICAtIEVOVEVfSU5URVJOQUxfQURNSU49MTU4MDU1OTk2MjM4NjQzOAogICAgICAtICdBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0nCiAgICAgIC0gJ0FQUFNfQ0FTVD0ke0FQUFNfQ0FTVDotfScKICAgICAgLSAnQVBQU19BQ0NPVU5UUz0ke0FQUFNfQUNDT1VOVFM6LX0nCiAgICB2b2x1bWVzOgogICAgICAtICdtdXNldW0tZGF0YTovZGF0YScKICAgICAgLSAnbXVzZXVtLWNvbmZpZzovY29uZmlnJwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MDgwL3BpbmcnCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMKICBwb3N0Z3JlczoKICAgIGltYWdlOiAncG9zdGdyZXM6MTUtYWxwaW5lJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICdwZ19pc3JlYWR5IC1VICR7U0VSVklDRV9VU0VSX1BPU1RHUkVTfSAtZCAke1BPU1RHUkVTX0RCOi1lbnRlX2RifScKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgbWluaW86CiAgICBpbWFnZTogJ3F1YXkuaW8vbWluaW8vbWluaW86bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTUlOSU9fMzIwMAogICAgICAtICdNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSInCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG1jCiAgICAgICAgLSByZWFkeQogICAgICAgIC0gbG9jYWwKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogJ21pbmlvL21jOmxhdGVzdCcKICAgIGRlcGVuZHNfb246CiAgICAgIG1pbmlvOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgZW50cnlwb2ludDogIi9iaW4vc2ggLWMgXCIgbWMgYWxpYXMgc2V0IG1pbmlvIGh0dHA6Ly9taW5pbzozMjAwICQke01JTklPX1JPT1RfVVNFUn0gJCR7TUlOSU9fUk9PVF9QQVNTV09SRH07IG1jIG1iIG1pbmlvL2IyLWV1LWNlbiAtLWlnbm9yZS1leGlzdGluZzsgbWMgbWIgbWluaW8vd2FzYWJpLWV1LWNlbnRyYWwtMi12MyAtLWlnbm9yZS1leGlzdGluZzsgbWMgbWIgbWluaW8vc2N3LWV1LWZyLXYzIC0taWdub3JlLWV4aXN0aW5nOyBlY2hvICdNaW5JTyBidWNrZXRzIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JzsgXCJcbiIK", "tags": [ "photos", "gallery", diff --git a/templates/service-templates.json b/templates/service-templates.json index 7d05eb1ef..4ae838a89 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -951,7 +951,7 @@ "ente-photos": { "documentation": "https://help.ente.io/self-hosting/installation/compose?utm_source=coolify.io", "slogan": "Ente Photos is a fully open source, End to End Encrypted alternative to Google Photos and Apple Photos.", - "compose": "IyBkb2N1bWVudGF0aW9uOiBodHRwczovL2hlbHAuZW50ZS5pby9zZWxmLWhvc3RpbmcvaW5zdGFsbGF0aW9uL2NvbXBvc2UKIyBzbG9nYW46IEVudGUgUGhvdG9zIGlzIGEgZnVsbHkgb3BlbiBzb3VyY2UsIEVuZCB0byBFbmQgRW5jcnlwdGVkIGFsdGVybmF0aXZlIHRvIEdvb2dsZSBQaG90b3MgYW5kIEFwcGxlIFBob3Rvcy4KIyBjYXRlZ29yeTogbWVkaWEKIyB0YWdzOiBwaG90b3MsZ2FsbGVyeSxiYWNrdXAsZW5jcnlwdGlvbixwcml2YWN5LHNlbGYtaG9zdGVkLGdvb2dsZS1waG90b3MsYWx0ZXJuYXRpdmUKIyBsb2dvOiBzdmdzL2VudGUtcGhvdG9zLnN2ZwojIHBvcnQ6IDgwODAKCnNlcnZpY2VzOgogIG11c2V1bToKICAgIGltYWdlOiBnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfTVVTRVVNXzgwODAKICAgICAgIyBEYXRhYmFzZSBjb25maWd1cmF0aW9uCiAgICAgIC0gUE9TVEdSRVNfSE9TVD1wb3N0Z3JlcwogICAgICAtIFBPU1RHUkVTX1BPUlQ9NTQzMgogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICAgIC0gUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30KICAgICAgLSBQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9CiAgICAgICMgUzMvTWluSU8gY29uZmlndXJhdGlvbgogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSBTM19CMl9FVV9DRU5fS0VZPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgICAtIFMzX0IyX0VVX0NFTl9FTkRQT0lOVD0ke1NFUlZJQ0VfVVJMX01JTklPXzMyMDB9CiAgICAgIC0gUzNfQjJfRVVfQ0VOX1JFR0lPTj1ldS1jZW50cmFsLTIKICAgICAgLSBTM19CMl9FVV9DRU5fQlVDS0VUPWIyLWV1LWNlbgogICAgICAjIFNlY3VyaXR5IGtleXMKICAgICAgLSBFTkNSWVBUSU9OX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfRU5DUllQVElPTn0KICAgICAgLSBIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0KICAgICAgLSBKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF82NF9KV1R9CiAgICAgICMgQWRtaW4gcGVybWlzc2lvbnMgKGdyYW50cyBmaXJzdCBhY2NvdW50IGFkbWluIGFjY2VzcykKICAgICAgLSBFTlRFX0lOVEVSTkFMX0FETUlOPTE1ODA1NTk5NjIzODY0MzgKICAgICAgIyBBcHAgVVJMcyAob3B0aW9uYWwgLSBmb3Igd2ViIGludGVyZmFjZSkKICAgICAgLSBBUFBTX1BVQkxJQ19BTEJVTVM9JHtBUFBTX1BVQkxJQ19BTEJVTVM6LX0KICAgICAgLSBBUFBTX0NBU1Q9JHtBUFBTX0NBU1Q6LX0KICAgICAgLSBBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfQogICAgdm9sdW1lczoKICAgICAgLSBtdXNldW0tZGF0YTovZGF0YQogICAgICAtIG11c2V1bS1jb25maWc6L2NvbmZpZwogICAgZGVwZW5kc19vbjoKICAgICAgcG9zdGdyZXM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgbWluaW86CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWQKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OiBbIkNNRCIsICJjdXJsIiwgIi1mIiwgImh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9waW5nIl0KICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3JlczoxNS1hbHBpbmUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9CiAgICAgIC0gUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTfQogICAgICAtIFBPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9CiAgICB2b2x1bWVzOgogICAgICAtIHBvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQtU0hFTEwiLCAicGdfaXNyZWFkeSAtVSAke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30gLWQgJHtQT1NUR1JFU19EQjotZW50ZV9kYn0iXQogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKCiAgbWluaW86CiAgICBpbWFnZTogcXVheS5pby9taW5pby9taW5pbzpsYXRlc3QKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX01JTklPXzMyMDAKICAgICAgLSBNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99CiAgICAgIC0gTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99CiAgICBjb21tYW5kOiBzZXJ2ZXIgL2RhdGEgLS1hZGRyZXNzICI6MzIwMCIgLS1jb25zb2xlLWFkZHJlc3MgIjozMjAxIgogICAgdm9sdW1lczoKICAgICAgLSBtaW5pby1kYXRhOi9kYXRhCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDogWyJDTUQiLCAibWMiLCAicmVhZHkiLCAibG9jYWwiXQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCgogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogbWluaW8vbWM6bGF0ZXN0CiAgICBkZXBlbmRzX29uOgogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfQogICAgICAtIE1JTklPX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfQogICAgZW50cnlwb2ludDogPgogICAgICAvYmluL3NoIC1jICIKICAgICAgbWMgYWxpYXMgc2V0IG1pbmlvIGh0dHA6Ly9taW5pbzozMjAwICQke01JTklPX1JPT1RfVVNFUn0gJCR7TUlOSU9fUk9PVF9QQVNTV09SRH07CiAgICAgIG1jIG1iIG1pbmlvL2IyLWV1LWNlbiAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vd2FzYWJpLWV1LWNlbnRyYWwtMi12MyAtLWlnbm9yZS1leGlzdGluZzsKICAgICAgbWMgbWIgbWluaW8vc2N3LWV1LWZyLXYzIC0taWdub3JlLWV4aXN0aW5nOwogICAgICBlY2hvICdNaW5JTyBidWNrZXRzIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JzsKICAgICAgIgo=", + "compose": "c2VydmljZXM6CiAgbXVzZXVtOgogICAgaW1hZ2U6ICdnaGNyLmlvL2VudGUtaW8vc2VydmVyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9NVVNFVU1fODA4MAogICAgICAtIFBPU1RHUkVTX0hPU1Q9cG9zdGdyZXMKICAgICAgLSBQT1NUR1JFU19QT1JUPTU0MzIKICAgICAgLSAnUE9TVEdSRVNfREI9JHtQT1NUR1JFU19EQjotZW50ZV9kYn0nCiAgICAgIC0gJ1BPU1RHUkVTX1VTRVI9JHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9JwogICAgICAtICdQT1NUR1JFU19QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUE9TVEdSRVN9JwogICAgICAtIFMzX0FSRV9MT0NBTF9CVUNLRVRTPXRydWUKICAgICAgLSBTM19VU0VfUEFUSF9TVFlMRV9VUkxTPXRydWUKICAgICAgLSAnUzNfQjJfRVVfQ0VOX0tFWT0ke1NFUlZJQ0VfVVNFUl9NSU5JT30nCiAgICAgIC0gJ1MzX0IyX0VVX0NFTl9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX01JTklPfScKICAgICAgLSAnUzNfQjJfRVVfQ0VOX0VORFBPSU5UPSR7U0VSVklDRV9GUUROX01JTklPXzMyMDB9JwogICAgICAtIFMzX0IyX0VVX0NFTl9SRUdJT049ZXUtY2VudHJhbC0yCiAgICAgIC0gUzNfQjJfRVVfQ0VOX0JVQ0tFVD1iMi1ldS1jZW4KICAgICAgLSAnRU5DUllQVElPTl9LRVk9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0VOQ1JZUFRJT059JwogICAgICAtICdIQVNIX0tFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfNjRfSEFTSH0nCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X0pXVH0nCiAgICAgIC0gRU5URV9JTlRFUk5BTF9BRE1JTj0xNTgwNTU5OTYyMzg2NDM4CiAgICAgIC0gJ0FQUFNfUFVCTElDX0FMQlVNUz0ke0FQUFNfUFVCTElDX0FMQlVNUzotfScKICAgICAgLSAnQVBQU19DQVNUPSR7QVBQU19DQVNUOi19JwogICAgICAtICdBUFBTX0FDQ09VTlRTPSR7QVBQU19BQ0NPVU5UUzotfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ211c2V1bS1kYXRhOi9kYXRhJwogICAgICAtICdtdXNldW0tY29uZmlnOi9jb25maWcnCiAgICBkZXBlbmRzX29uOgogICAgICBwb3N0Z3JlczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBtaW5pbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2Vfc3RhcnRlZAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwODAvcGluZycKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMwogIHBvc3RncmVzOgogICAgaW1hZ2U6ICdwb3N0Z3JlczoxNS1hbHBpbmUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU30nCiAgICAgIC0gJ1BPU1RHUkVTX0RCPSR7UE9TVEdSRVNfREI6LWVudGVfZGJ9JwogICAgdm9sdW1lczoKICAgICAgLSAncG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJHtTRVJWSUNFX1VTRVJfUE9TVEdSRVN9IC1kICR7UE9TVEdSRVNfREI6LWVudGVfZGJ9JwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDUKICBtaW5pbzoKICAgIGltYWdlOiAncXVheS5pby9taW5pby9taW5pbzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTUlOSU9fMzIwMAogICAgICAtICdNSU5JT19ST09UX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUlOSU99JwogICAgICAtICdNSU5JT19ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NSU5JT30nCiAgICBjb21tYW5kOiAnc2VydmVyIC9kYXRhIC0tYWRkcmVzcyAiOjMyMDAiIC0tY29uc29sZS1hZGRyZXNzICI6MzIwMSInCiAgICB2b2x1bWVzOgogICAgICAtICdtaW5pby1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG1jCiAgICAgICAgLSByZWFkeQogICAgICAgIC0gbG9jYWwKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAxMAogIG1pbmlvLWluaXQ6CiAgICBpbWFnZTogJ21pbmlvL21jOmxhdGVzdCcKICAgIGRlcGVuZHNfb246CiAgICAgIG1pbmlvOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUlOSU9fUk9PVF9VU0VSPSR7U0VSVklDRV9VU0VSX01JTklPfScKICAgICAgLSAnTUlOSU9fUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUlOSU99JwogICAgZW50cnlwb2ludDogIi9iaW4vc2ggLWMgXCIgbWMgYWxpYXMgc2V0IG1pbmlvIGh0dHA6Ly9taW5pbzozMjAwICQke01JTklPX1JPT1RfVVNFUn0gJCR7TUlOSU9fUk9PVF9QQVNTV09SRH07IG1jIG1iIG1pbmlvL2IyLWV1LWNlbiAtLWlnbm9yZS1leGlzdGluZzsgbWMgbWIgbWluaW8vd2FzYWJpLWV1LWNlbnRyYWwtMi12MyAtLWlnbm9yZS1leGlzdGluZzsgbWMgbWIgbWluaW8vc2N3LWV1LWZyLXYzIC0taWdub3JlLWV4aXN0aW5nOyBlY2hvICdNaW5JTyBidWNrZXRzIGNyZWF0ZWQgc3VjY2Vzc2Z1bGx5JzsgXCJcbiIK", "tags": [ "photos", "gallery", diff --git a/tests/Unit/DockerImageParserTest.php b/tests/Unit/DockerImageParserTest.php index f41a9b170..6102a90b2 100644 --- a/tests/Unit/DockerImageParserTest.php +++ b/tests/Unit/DockerImageParserTest.php @@ -1,115 +1,109 @@ parse('nginx:latest'); + + expect($parser->getImageName())->toBe('nginx') + ->and($parser->getTag())->toBe('latest') + ->and($parser->isImageHash())->toBeFalse() + ->and($parser->toString())->toBe('nginx:latest'); +}); + +it('parses image with sha256 hash using colon format', function () { + $parser = new DockerImageParser; + $hash = '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0'; + $parser->parse("ghcr.io/benjaminehowe/rail-disruptions:{$hash}"); + + expect($parser->getFullImageNameWithoutTag())->toBe('ghcr.io/benjaminehowe/rail-disruptions') + ->and($parser->getTag())->toBe($hash) + ->and($parser->isImageHash())->toBeTrue() + ->and($parser->toString())->toBe("ghcr.io/benjaminehowe/rail-disruptions@sha256:{$hash}") + ->and($parser->getFullImageNameWithHash())->toBe("ghcr.io/benjaminehowe/rail-disruptions@sha256:{$hash}"); +}); + +it('parses image with sha256 hash using at sign format', function () { + $parser = new DockerImageParser; + $hash = '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0'; + $parser->parse("nginx@sha256:{$hash}"); + + expect($parser->getImageName())->toBe('nginx') + ->and($parser->getTag())->toBe($hash) + ->and($parser->isImageHash())->toBeTrue() + ->and($parser->toString())->toBe("nginx@sha256:{$hash}") + ->and($parser->getFullImageNameWithHash())->toBe("nginx@sha256:{$hash}"); +}); + +it('parses registry image with hash', function () { + $parser = new DockerImageParser; + $hash = 'abc123def456789abcdef123456789abcdef123456789abcdef123456789abc1'; + $parser->parse("docker.io/library/nginx:{$hash}"); + + expect($parser->getFullImageNameWithoutTag())->toBe('docker.io/library/nginx') + ->and($parser->getTag())->toBe($hash) + ->and($parser->isImageHash())->toBeTrue() + ->and($parser->toString())->toBe("docker.io/library/nginx@sha256:{$hash}"); +}); + +it('parses image without tag defaults to latest', function () { + $parser = new DockerImageParser; + $parser->parse('nginx'); + + expect($parser->getImageName())->toBe('nginx') + ->and($parser->getTag())->toBe('latest') + ->and($parser->isImageHash())->toBeFalse() + ->and($parser->toString())->toBe('nginx:latest'); +}); + +it('parses registry with port', function () { + $parser = new DockerImageParser; + $parser->parse('registry.example.com:5000/myapp:latest'); + + expect($parser->getFullImageNameWithoutTag())->toBe('registry.example.com:5000/myapp') + ->and($parser->getTag())->toBe('latest') + ->and($parser->isImageHash())->toBeFalse(); +}); + +it('parses registry with port and hash', function () { + $parser = new DockerImageParser; + $hash = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; + $parser->parse("registry.example.com:5000/myapp:{$hash}"); + + expect($parser->getFullImageNameWithoutTag())->toBe('registry.example.com:5000/myapp') + ->and($parser->getTag())->toBe($hash) + ->and($parser->isImageHash())->toBeTrue() + ->and($parser->toString())->toBe("registry.example.com:5000/myapp@sha256:{$hash}"); +}); + +it('identifies valid sha256 hashes', function () { + $validHashes = [ + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0', + '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', + 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', + ]; + + foreach ($validHashes as $hash) { $parser = new DockerImageParser; - $parser->parse('nginx:latest'); - - $this->assertEquals('nginx', $parser->getImageName()); - $this->assertEquals('latest', $parser->getTag()); - $this->assertFalse($parser->isImageHash()); - $this->assertEquals('nginx:latest', $parser->toString()); + $parser->parse("image:{$hash}"); + expect($parser->isImageHash())->toBeTrue("Hash {$hash} should be recognized as valid SHA256"); } +}); - public function test_parses_image_with_sha256_hash() - { +it('identifies invalid sha256 hashes', function () { + $invalidHashes = [ + 'latest', + 'v1.2.3', + 'abc123', // too short + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf', // too short + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf00', // too long + '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cfg0', // invalid char + ]; + + foreach ($invalidHashes as $hash) { $parser = new DockerImageParser; - $hash = '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0'; - $parser->parse("ghcr.io/benjaminehowe/rail-disruptions:{$hash}"); - - $this->assertEquals('ghcr.io/benjaminehowe/rail-disruptions', $parser->getFullImageNameWithoutTag()); - $this->assertEquals($hash, $parser->getTag()); - $this->assertTrue($parser->isImageHash()); - $this->assertEquals("ghcr.io/benjaminehowe/rail-disruptions@sha256:{$hash}", $parser->toString()); - $this->assertEquals("ghcr.io/benjaminehowe/rail-disruptions@sha256:{$hash}", $parser->getFullImageNameWithHash()); + $parser->parse("image:{$hash}"); + expect($parser->isImageHash())->toBeFalse("Hash {$hash} should not be recognized as valid SHA256"); } - - public function test_parses_registry_image_with_hash() - { - $parser = new DockerImageParser; - $hash = 'abc123def456789abcdef123456789abcdef123456789abcdef123456789abc1'; - $parser->parse("docker.io/library/nginx:{$hash}"); - - $this->assertEquals('docker.io/library/nginx', $parser->getFullImageNameWithoutTag()); - $this->assertEquals($hash, $parser->getTag()); - $this->assertTrue($parser->isImageHash()); - $this->assertEquals("docker.io/library/nginx@sha256:{$hash}", $parser->toString()); - } - - public function test_parses_image_without_tag_defaults_to_latest() - { - $parser = new DockerImageParser; - $parser->parse('nginx'); - - $this->assertEquals('nginx', $parser->getImageName()); - $this->assertEquals('latest', $parser->getTag()); - $this->assertFalse($parser->isImageHash()); - $this->assertEquals('nginx:latest', $parser->toString()); - } - - public function test_parses_registry_with_port() - { - $parser = new DockerImageParser; - $parser->parse('registry.example.com:5000/myapp:latest'); - - $this->assertEquals('registry.example.com:5000/myapp', $parser->getFullImageNameWithoutTag()); - $this->assertEquals('latest', $parser->getTag()); - $this->assertFalse($parser->isImageHash()); - } - - public function test_parses_registry_with_port_and_hash() - { - $parser = new DockerImageParser; - $hash = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; - $parser->parse("registry.example.com:5000/myapp:{$hash}"); - - $this->assertEquals('registry.example.com:5000/myapp', $parser->getFullImageNameWithoutTag()); - $this->assertEquals($hash, $parser->getTag()); - $this->assertTrue($parser->isImageHash()); - $this->assertEquals("registry.example.com:5000/myapp@sha256:{$hash}", $parser->toString()); - } - - public function test_identifies_valid_sha256_hashes() - { - $parser = new DockerImageParser; - - // Valid SHA256 hashes - $validHashes = [ - '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf0', - '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890', - ]; - - foreach ($validHashes as $hash) { - $parser->parse("image:{$hash}"); - $this->assertTrue($parser->isImageHash(), "Hash {$hash} should be recognized as valid SHA256"); - } - } - - public function test_identifies_invalid_sha256_hashes() - { - $parser = new DockerImageParser; - - // Invalid SHA256 hashes - $invalidHashes = [ - 'latest', - 'v1.2.3', - 'abc123', // too short - '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf', // too short - '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cf00', // too long - '59e02939b1bf39f16c93138a28727aec520bb916da021180ae502c61626b3cfg0', // invalid char - ]; - - foreach ($invalidHashes as $hash) { - $parser->parse("image:{$hash}"); - $this->assertFalse($parser->isImageHash(), "Hash {$hash} should not be recognized as valid SHA256"); - } - } -} +}); From 7d99d6ad34114e111583cd8d6ac35dcdc729a508 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 3 Oct 2025 11:42:29 +0200 Subject: [PATCH 18/19] feat(docker): improve Docker image submission logic with enhanced parsing - Added logic to strip 'sha256:' prefix and remove '@sha256' suffix from user input for image SHA256 and name. - Updated image name handling to append '@sha256' when using a digest, ensuring correct formatting. - Enhanced validation and parsing for Docker images to improve user experience and data integrity. --- app/Livewire/Project/New/DockerImage.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index dd928b5e1..e105c956a 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -30,6 +30,16 @@ public function mount() public function submit() { + // Strip 'sha256:' prefix if user pasted it + if ($this->imageSha256) { + $this->imageSha256 = preg_replace('/^sha256:/i', '', trim($this->imageSha256)); + } + + // Remove @sha256 from image name if user added it + if ($this->imageName) { + $this->imageName = preg_replace('/@sha256$/i', '', trim($this->imageName)); + } + $this->validate([ 'imageName' => ['required', 'string'], 'imageTag' => ['nullable', 'string', 'regex:/^[a-z0-9][a-z0-9._-]*$/i'], @@ -72,6 +82,12 @@ public function submit() // Determine the image tag based on whether it's a hash or regular tag $imageTag = $parser->isImageHash() ? 'sha256-'.$parser->getTag() : $parser->getTag(); + // Append @sha256 to image name if using digest and not already present + $imageName = $parser->getFullImageNameWithoutTag(); + if ($parser->isImageHash() && ! str_ends_with($imageName, '@sha256')) { + $imageName .= '@sha256'; + } + $application = Application::create([ 'name' => 'docker-image-'.new Cuid2, 'repository_project_id' => 0, @@ -79,7 +95,7 @@ public function submit() 'git_branch' => 'main', 'build_pack' => 'dockerimage', 'ports_exposes' => 80, - 'docker_registry_image_name' => $parser->getFullImageNameWithoutTag(), + 'docker_registry_image_name' => $imageName, 'docker_registry_image_tag' => $parser->getTag(), 'environment_id' => $environment->id, 'destination_id' => $destination->id, From 41a8ea870f8fffcd9f105d373fd2a95f1bb13596 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 3 Oct 2025 11:49:20 +0200 Subject: [PATCH 19/19] feat(docker): refine Docker image processing in application creation - Enhanced logic to process Docker image name and tag, including stripping 'sha256:' prefix and removing '@sha256' suffix. - Implemented validation to check for valid SHA256 hashes and conditionally append '@sha256' to the image name. - Defaulted the image tag to 'latest' if not provided, improving user experience and data integrity. --- .../Api/ApplicationsController.php | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index ce9e723d4..065d7f767 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1512,9 +1512,32 @@ private function create_application(Request $request, $type) if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; } - if (! $request->docker_registry_image_tag) { - $request->offsetSet('docker_registry_image_tag', 'latest'); + // Process docker image name and tag for SHA256 digests + $dockerImageName = $request->docker_registry_image_name; + $dockerImageTag = $request->docker_registry_image_tag; + + // Strip 'sha256:' prefix if user provided it in the tag + if ($dockerImageTag) { + $dockerImageTag = preg_replace('/^sha256:/i', '', trim($dockerImageTag)); } + + // Remove @sha256 from image name if user added it + if ($dockerImageName) { + $dockerImageName = preg_replace('/@sha256$/i', '', trim($dockerImageName)); + } + + // Check if tag is a valid SHA256 hash (64 hex characters) + $isSha256Hash = $dockerImageTag && preg_match('/^[a-f0-9]{64}$/i', $dockerImageTag); + + // Append @sha256 to image name if using digest and not already present + if ($isSha256Hash && ! str_ends_with($dockerImageName, '@sha256')) { + $dockerImageName .= '@sha256'; + } + + // Set processed values back to request + $request->offsetSet('docker_registry_image_name', $dockerImageName); + $request->offsetSet('docker_registry_image_tag', $dockerImageTag ?: 'latest'); + $application = new Application; removeUnnecessaryFieldsFromRequest($request);