diff --git a/.dockerignore b/.dockerignore index 6f3c903f0f..aba2137d0e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -23,7 +23,7 @@ yarn-error.log /.ssh .ignition.json .env.dusk.local -docker/coolify-realtime/node_modules +docker/coolify-terminal/node_modules /storage/*.key /storage/app/backups diff --git a/.env.production b/.env.production index fe3c8370e2..9e5d689f20 100644 --- a/.env.production +++ b/.env.production @@ -10,6 +10,8 @@ REDIS_PASSWORD= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= +PUSHER_PORT=6001 +PUSHER_BACKEND_PORT=6001 ROOT_USERNAME= ROOT_USER_EMAIL= diff --git a/.env.windows-docker-desktop.example b/.env.windows-docker-desktop.example index b067b4c5c0..08c5dfda7f 100644 --- a/.env.windows-docker-desktop.example +++ b/.env.windows-docker-desktop.example @@ -11,3 +11,5 @@ REDIS_PASSWORD=coolify PUSHER_APP_ID=coolify PUSHER_APP_KEY=coolify PUSHER_APP_SECRET=coolify +PUSHER_PORT=6001 +PUSHER_BACKEND_PORT=6001 diff --git a/.github/workflows/cleanup-ghcr-untagged.yml b/.github/workflows/cleanup-ghcr-untagged.yml index a86cedcb03..b6dc7618d2 100644 --- a/.github/workflows/cleanup-ghcr-untagged.yml +++ b/.github/workflows/cleanup-ghcr-untagged.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - package: ['coolify', 'coolify-helper', 'coolify-realtime', 'coolify-testing-host'] + package: ['coolify', 'coolify-helper', 'coolify-testing-host'] steps: - name: Delete untagged ${{ matrix.package }} images uses: actions/delete-package-versions@v5 diff --git a/.github/workflows/coolify-production-build.yml b/.github/workflows/coolify-production-build.yml index 5ccb43a8e0..f6973ca0bf 100644 --- a/.github/workflows/coolify-production-build.yml +++ b/.github/workflows/coolify-production-build.yml @@ -6,11 +6,8 @@ on: paths-ignore: - .github/workflows/coolify-helper.yml - .github/workflows/coolify-helper-next.yml - - .github/workflows/coolify-realtime.yml - - .github/workflows/coolify-realtime-next.yml - .github/workflows/pr-quality.yaml - docker/coolify-helper/Dockerfile - - docker/coolify-realtime/Dockerfile - docker/testing-host/Dockerfile - templates/** - CHANGELOG.md diff --git a/.github/workflows/coolify-realtime-next.yml b/.github/workflows/coolify-realtime-next.yml deleted file mode 100644 index 8937ea27dd..0000000000 --- a/.github/workflows/coolify-realtime-next.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Coolify Realtime Development - -on: - push: - branches: [ "next" ] - paths: - - .github/workflows/coolify-realtime-next.yml - - docker/coolify-realtime/Dockerfile - - docker/coolify-realtime/terminal-server.js - - docker/coolify-realtime/package.json - - docker/coolify-realtime/package-lock.json - - docker/coolify-realtime/soketi-entrypoint.sh - -permissions: - contents: read - packages: write - -env: - GITHUB_REGISTRY: ghcr.io - DOCKER_REGISTRY: docker.io - IMAGE_NAME: "coollabsio/coolify-realtime" - -jobs: - build-push: - strategy: - matrix: - include: - - arch: amd64 - platform: linux/amd64 - runner: ubuntu-24.04 - - arch: aarch64 - platform: linux/aarch64 - runner: ubuntu-24.04-arm - runs-on: ${{ matrix.runner }} - steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - - name: Login to ${{ env.GITHUB_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.GITHUB_REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to ${{ env.DOCKER_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Get Version - id: version - run: | - echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT - - - name: Build and Push Image (${{ matrix.arch }}) - uses: docker/build-push-action@v6 - with: - context: . - file: docker/coolify-realtime/Dockerfile - platforms: ${{ matrix.platform }} - push: true - tags: | - ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-${{ matrix.arch }} - ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-${{ matrix.arch }} - labels: | - coolify.managed=true - - merge-manifest: - runs-on: ubuntu-24.04 - needs: build-push - steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - - uses: docker/setup-buildx-action@v3 - - - name: Login to ${{ env.GITHUB_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.GITHUB_REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to ${{ env.DOCKER_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Get Version - id: version - run: | - echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT - - - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }} - run: | - docker buildx imagetools create \ - ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-amd64 \ - ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 \ - --tag ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next \ - --tag ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:next - - - name: Create & publish manifest on ${{ env.DOCKER_REGISTRY }} - run: | - docker buildx imagetools create \ - ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-amd64 \ - ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 \ - --tag ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next \ - --tag ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:next - - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} diff --git a/.github/workflows/coolify-realtime.yml b/.github/workflows/coolify-realtime.yml deleted file mode 100644 index d8784dd500..0000000000 --- a/.github/workflows/coolify-realtime.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Coolify Realtime - -on: - push: - branches: [ "v4.x" ] - paths: - - .github/workflows/coolify-realtime.yml - - docker/coolify-realtime/Dockerfile - - docker/coolify-realtime/terminal-server.js - - docker/coolify-realtime/package.json - - docker/coolify-realtime/package-lock.json - - docker/coolify-realtime/soketi-entrypoint.sh - -permissions: - contents: read - packages: write - -env: - GITHUB_REGISTRY: ghcr.io - DOCKER_REGISTRY: docker.io - IMAGE_NAME: "coollabsio/coolify-realtime" - -jobs: - build-push: - strategy: - matrix: - include: - - arch: amd64 - platform: linux/amd64 - runner: ubuntu-24.04 - - arch: aarch64 - platform: linux/aarch64 - runner: ubuntu-24.04-arm - runs-on: ${{ matrix.runner }} - steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - - name: Login to ${{ env.GITHUB_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.GITHUB_REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to ${{ env.DOCKER_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Get Version - id: version - run: | - echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT - - - name: Build and Push Image (${{ matrix.arch }}) - uses: docker/build-push-action@v6 - with: - context: . - file: docker/coolify-realtime/Dockerfile - platforms: ${{ matrix.platform }} - push: true - tags: | - ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-${{ matrix.arch }} - ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-${{ matrix.arch }} - labels: | - coolify.managed=true - - merge-manifest: - runs-on: ubuntu-24.04 - needs: build-push - steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - - uses: docker/setup-buildx-action@v3 - - - name: Login to ${{ env.GITHUB_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.GITHUB_REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to ${{ env.DOCKER_REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Get Version - id: version - run: | - echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php)"|xargs >> $GITHUB_OUTPUT - - - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }} - run: | - docker buildx imagetools create \ - ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-amd64 \ - ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 \ - --tag ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} \ - --tag ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest - - - name: Create & publish manifest on ${{ env.DOCKER_REGISTRY }} - run: | - docker buildx imagetools create \ - ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-amd64 \ - ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 \ - --tag ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} \ - --tag ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest - - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }} diff --git a/.github/workflows/coolify-staging-build.yml b/.github/workflows/coolify-staging-build.yml index c5b70ca92c..cdbc9c5b44 100644 --- a/.github/workflows/coolify-staging-build.yml +++ b/.github/workflows/coolify-staging-build.yml @@ -9,11 +9,8 @@ on: paths-ignore: - .github/workflows/coolify-helper.yml - .github/workflows/coolify-helper-next.yml - - .github/workflows/coolify-realtime.yml - - .github/workflows/coolify-realtime-next.yml - .github/workflows/pr-quality.yaml - docker/coolify-helper/Dockerfile - - docker/coolify-realtime/Dockerfile - docker/testing-host/Dockerfile - templates/** - CHANGELOG.md diff --git a/.github/workflows/generate-changelog.yml b/.github/workflows/generate-changelog.yml index c02c138482..f3e44be9d6 100644 --- a/.github/workflows/generate-changelog.yml +++ b/.github/workflows/generate-changelog.yml @@ -6,8 +6,6 @@ on: paths-ignore: - .github/workflows/coolify-helper.yml - .github/workflows/coolify-helper-next.yml - - .github/workflows/coolify-realtime.yml - - .github/workflows/coolify-realtime-next.yml - .github/workflows/pr-quality.yaml workflow_dispatch: diff --git a/.gitignore b/.gitignore index 403028761f..ba69eee331 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,7 @@ _ide_helper_models.php scripts/load-test/* .ignition.json .env.dusk.local -docker/coolify-realtime/node_modules +docker/coolify-terminal/node_modules .DS_Store CHANGELOG.md /.workspaces diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85fceb28f6..2556e37f5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -248,7 +248,7 @@ If you encounter issues or break your database or something else, follow these s 2. Remove all Coolify containers: ```bash - docker rm coolify coolify-db coolify-redis coolify-realtime coolify-testing-host coolify-minio coolify-vite-1 coolify-mail + docker rm coolify coolify-db coolify-redis coolify-testing-host coolify-minio coolify-vite-1 coolify-mail ``` 3. Remove Coolify volumes (it is possible that the volumes have no `coolify` prefix on your machine, in that case remove the prefix from the command): diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 06abeb3a65..2669873661 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -13,12 +13,6 @@ class CleanupDocker public function handle(Server $server, bool $deleteUnusedVolumes = false, bool $deleteUnusedNetworks = false) { - $realtimeImage = config('constants.coolify.realtime_image'); - $realtimeImageVersion = config('constants.coolify.realtime_version'); - $realtimeImageWithVersion = "$realtimeImage:$realtimeImageVersion"; - $realtimeImageWithoutPrefix = 'coollabsio/coolify-realtime'; - $realtimeImageWithoutPrefixVersion = "coollabsio/coolify-realtime:$realtimeImageVersion"; - $helperImageVersion = getHelperVersion(); $helperImage = config('constants.coolify.helper_image'); $helperImageWithVersion = "$helperImage:$helperImageVersion"; @@ -38,13 +32,12 @@ class CleanupDocker $cleanupLog = array_merge($cleanupLog, $applicationCleanupLog); // Build image prune command that excludes application images and current Coolify infrastructure images - // This ensures we clean up non-Coolify images while preserving rollback images and current helper/realtime images + // This ensures we clean up non-Coolify images while preserving rollback images and current helper image // Note: Only the current version is protected; old versions will be cleaned up by explicit commands below // We pass the version strings so all registry variants are protected (ghcr.io, docker.io, no prefix) $imagePruneCmd = $this->buildImagePruneCommand( $applicationImageRepos, - $helperImageVersion, - $realtimeImageVersion + $helperImageVersion ); $commands = [ @@ -53,9 +46,7 @@ class CleanupDocker 'docker builder prune -af', "docker run --rm -v \$HOME/.docker/buildx:/root/.docker/buildx -v /var/run/docker.sock:/var/run/docker.sock {$helperImageWithVersion} docker buildx prune --builder coolify-railpack -af 2>/dev/null || true", "docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi -f", - "docker images --filter before=$realtimeImageWithVersion --filter reference=$realtimeImage | grep $realtimeImage | awk '{print $3}' | xargs -r docker rmi -f", "docker images --filter before=$helperImageWithoutPrefixVersion --filter reference=$helperImageWithoutPrefix | grep $helperImageWithoutPrefix | awk '{print $3}' | xargs -r docker rmi -f", - "docker images --filter before=$realtimeImageWithoutPrefixVersion --filter reference=$realtimeImageWithoutPrefix | grep $realtimeImageWithoutPrefix | awk '{print $3}' | xargs -r docker rmi -f", ]; if ($deleteUnusedVolumes) { @@ -87,8 +78,7 @@ class CleanupDocker */ private function buildImagePruneCommand( $applicationImageRepos, - string $helperImageVersion, - string $realtimeImageVersion + string $helperImageVersion ): string { // Step 1: Always prune dangling images (untagged) $commands = ['docker image prune -f']; @@ -105,14 +95,13 @@ class CleanupDocker // - ghcr.io/coollabsio/coolify-helper:1.0.12 // - docker.io/coollabsio/coolify-helper:1.0.12 // - coollabsio/coolify-helper:1.0.12 - // Pattern: (^|/)coollabsio/coolify-(helper|realtime):VERSION$ + // Pattern: (^|/)coollabsio/coolify-helper:VERSION$ $escapedHelperVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $helperImageVersion); - $escapedRealtimeVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $realtimeImageVersion); - $infraExcludePattern = "(^|/)coollabsio/coolify-helper:{$escapedHelperVersion}$|(^|/)coollabsio/coolify-realtime:{$escapedRealtimeVersion}$"; + $infraExcludePattern = "(^|/)coollabsio/coolify-helper:{$escapedHelperVersion}$"; // Delete unused images that: // - Are not application images (don't match app repos) - // - Are not current Coolify infrastructure images (any registry) + // - Are not current Coolify helper image (any registry) // - Don't have coolify.managed=true label // Images in use by containers will fail silently with docker rmi // Pattern matches both uuid:tag and uuid_servicename:tag (Docker Compose with build) diff --git a/app/Models/Server.php b/app/Models/Server.php index 2b7bbac553..1ba8362b17 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -481,13 +481,20 @@ class Server extends BaseModel 'service' => 'coolify', 'rule' => "Host(`{$host}`)", ], - 'coolify-realtime-ws' => [ + 'coolify-reverb-ws' => [ 'entryPoints' => [ 0 => 'http', ], - 'service' => 'coolify-realtime', + 'service' => 'coolify-reverb', 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", ], + 'coolify-reverb-api' => [ + 'entryPoints' => [ + 0 => 'http', + ], + 'service' => 'coolify-reverb', + 'rule' => "Host(`{$host}`) && PathPrefix(`/apps`)", + ], 'coolify-terminal-ws' => [ 'entryPoints' => [ 0 => 'http', @@ -506,11 +513,11 @@ class Server extends BaseModel ], ], ], - 'coolify-realtime' => [ + 'coolify-reverb' => [ 'loadBalancer' => [ 'servers' => [ 0 => [ - 'url' => 'http://coolify-realtime:6001', + 'url' => 'http://coolify:6001', ], ], ], @@ -519,7 +526,7 @@ class Server extends BaseModel 'loadBalancer' => [ 'servers' => [ 0 => [ - 'url' => 'http://coolify-realtime:6002', + 'url' => 'http://coolify:6002', ], ], ], @@ -543,16 +550,26 @@ class Server extends BaseModel 'certresolver' => 'letsencrypt', ], ]; - $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [ + $traefik_dynamic_conf['http']['routers']['coolify-reverb-wss'] = [ 'entryPoints' => [ 0 => 'https', ], - 'service' => 'coolify-realtime', + 'service' => 'coolify-reverb', 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", 'tls' => [ 'certresolver' => 'letsencrypt', ], ]; + $traefik_dynamic_conf['http']['routers']['coolify-reverb-api-https'] = [ + 'entryPoints' => [ + 0 => 'https', + ], + 'service' => 'coolify-reverb', + 'rule' => "Host(`{$host}`) && PathPrefix(`/apps`)", + 'tls' => [ + 'certresolver' => 'letsencrypt', + ], + ]; $traefik_dynamic_conf['http']['routers']['coolify-terminal-wss'] = [ 'entryPoints' => [ 0 => 'https', @@ -590,10 +607,13 @@ class Server extends BaseModel $caddy_file = " $schema://$host { handle /app/* { - reverse_proxy coolify-realtime:6001 + reverse_proxy coolify:6001 + } + handle /apps* { + reverse_proxy coolify:6001 } handle /terminal/ws { - reverse_proxy coolify-realtime:6002 + reverse_proxy coolify:6002 } reverse_proxy coolify:8080 }"; diff --git a/bootstrap/getRealtimeVersion.php b/bootstrap/getRealtimeVersion.php deleted file mode 100644 index 1af56f09e5..0000000000 --- a/bootstrap/getRealtimeVersion.php +++ /dev/null @@ -1,10 +0,0 @@ -=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\Redis\\Protocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A streaming Redis protocol (RESP) parser and serializer written in pure PHP.", + "homepage": "https://github.com/clue/redis-protocol", + "keywords": [ + "parser", + "protocol", + "redis", + "resp", + "serializer", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/redis-protocol/issues", + "source": "https://github.com/clue/redis-protocol/tree/v0.3.2" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2024-08-07T11:06:28+00:00" + }, + { + "name": "clue/redis-react", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-redis.git", + "reference": "84569198dfd5564977d2ae6a32de4beb5a24bdca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-redis/zipball/84569198dfd5564977d2ae6a32de4beb5a24bdca", + "reference": "84569198dfd5564977d2ae6a32de4beb5a24bdca", + "shasum": "" + }, + "require": { + "clue/redis-protocol": "^0.3.2", + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.0 || ^1.1", + "react/promise-timer": "^1.11", + "react/socket": "^1.16" + }, + "require-dev": { + "clue/block-react": "^1.5", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Redis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Async Redis client implementation, built on top of ReactPHP.", + "homepage": "https://github.com/clue/reactphp-redis", + "keywords": [ + "async", + "client", + "database", + "reactphp", + "redis" + ], + "support": { + "issues": "https://github.com/clue/reactphp-redis/issues", + "source": "https://github.com/clue/reactphp-redis/tree/v2.8.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2025-01-03T16:18:33+00:00" + }, { "name": "danharrin/livewire-rate-limiting", "version": "v2.2.0", @@ -972,6 +1102,53 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, { "name": "ezyang/htmlpurifier", "version": "v4.19.0", @@ -2443,6 +2620,85 @@ }, "time": "2026-05-19T00:47:18+00:00" }, + { + "name": "laravel/reverb", + "version": "v1.10.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/reverb.git", + "reference": "43a5c0a99b1aaba33dc32f97fcf51f182dd8c8ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/reverb/zipball/43a5c0a99b1aaba33dc32f97fcf51f182dd8c8ac", + "reference": "43a5c0a99b1aaba33dc32f97fcf51f182dd8c8ac", + "shasum": "" + }, + "require": { + "clue/redis-react": "^2.6", + "guzzlehttp/psr7": "^2.6", + "illuminate/console": "^10.47|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.47|^11.0|^12.0|^13.0", + "illuminate/http": "^10.47|^11.0|^12.0|^13.0", + "illuminate/support": "^10.47|^11.0|^12.0|^13.0", + "laravel/prompts": "^0.1.15|^0.2.0|^0.3.0", + "php": "^8.2", + "pusher/pusher-php-server": "^7.2", + "ratchet/rfc6455": "^0.4", + "react/promise-timer": "^1.10", + "react/socket": "^1.14", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/http-foundation": "^6.3|^7.0|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^8.36|^9.15|^10.8|^11.0", + "pestphp/pest": "^2.0|^3.0|^4.0", + "phpstan/phpstan": "^1.10", + "ratchet/pawl": "^0.4.1", + "react/async": "^4.2", + "react/http": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Reverb\\ApplicationManagerServiceProvider", + "Laravel\\Reverb\\ReverbServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Reverb\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Joe Dixon", + "email": "joe@laravel.com" + } + ], + "description": "Laravel Reverb provides a real-time WebSocket communication backend for Laravel applications.", + "keywords": [ + "WebSockets", + "laravel", + "real-time", + "websocket" + ], + "support": { + "issues": "https://github.com/laravel/reverb/issues", + "source": "https://github.com/laravel/reverb/tree/v1.10.2" + }, + "time": "2026-05-10T15:47:52+00:00" + }, { "name": "laravel/sanctum", "version": "v4.3.2", @@ -6293,6 +6549,595 @@ }, "time": "2025-12-14T04:43:48+00:00" }, + { + "name": "ratchet/rfc6455", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "859d95f85dda0912c6d5b936d036d044e3af47ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/859d95f85dda0912c6d5b936d036d044e3af47ef", + "reference": "859d95f85dda0912c6d5b936d036d044e3af47ef", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "psr/http-factory-implementation": "^1.0", + "symfony/polyfill-php80": "^1.15" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.7", + "phpunit/phpunit": "^9.5", + "react/socket": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", + "keywords": [ + "WebSockets", + "rfc6455", + "websocket" + ], + "support": { + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/RFC6455/issues", + "source": "https://github.com/ratchetphp/RFC6455/tree/v0.4.0" + }, + "time": "2025-02-24T01:18:22+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/dns", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.14.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-18T19:34:28+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-17T20:46:25+00:00" + }, + { + "name": "react/promise", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "4f70306ed66b8b44768941ca7f142092600fafc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/4f70306ed66b8b44768941ca7f142092600fafc1", + "reference": "4f70306ed66b8b44768941ca7f142092600fafc1", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\Timer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "support": { + "issues": "https://github.com/reactphp/promise-timer/issues", + "source": "https://github.com/reactphp/promise-timer/tree/v1.11.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-04T14:27:45+00:00" + }, + { + "name": "react/socket", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.17.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-19T20:47:34+00:00" + }, + { + "name": "react/stream", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" + }, { "name": "resend/resend-laravel", "version": "v0.20.0", diff --git a/config/broadcasting.php b/config/broadcasting.php index 5509b00730..8348c1a052 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -11,11 +11,11 @@ return [ | framework when an event needs to be broadcast. You may set this to | any of the connections defined in the "connections" array below. | - | Supported: "pusher", "ably", "redis", "log", "null" + | Supported: "reverb", "pusher", "ably", "redis", "log", "null" | */ - 'default' => env('BROADCAST_DRIVER', 'pusher'), + 'default' => env('BROADCAST_CONNECTION', env('BROADCAST_DRIVER', 'reverb')), /* |-------------------------------------------------------------------------- @@ -30,15 +30,33 @@ return [ 'connections' => [ + 'reverb' => [ + 'driver' => 'reverb', + 'key' => env('PUSHER_APP_KEY', 'coolify'), + 'secret' => env('PUSHER_APP_SECRET', 'coolify'), + 'app_id' => env('PUSHER_APP_ID', 'coolify'), + 'options' => [ + 'host' => env('PUSHER_HOST', 'coolify'), + 'port' => env('PUSHER_BACKEND_PORT', 6001), + 'scheme' => env('PUSHER_SCHEME', 'http'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'http') === 'https', + 'path' => '', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY', 'coolify'), 'secret' => env('PUSHER_APP_SECRET', 'coolify'), 'app_id' => env('PUSHER_APP_ID', 'coolify'), 'options' => [ - 'host' => env('PUSHER_BACKEND_HOST', 'coolify-realtime'), - 'port' => env('PUSHER_BACKEND_PORT', 6001), - 'scheme' => env('PUSHER_SCHEME', 'http'), + 'host' => env('PUSHER_HOST'), + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), 'encrypted' => true, 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', ], diff --git a/config/constants.php b/config/constants.php index a016696734..ce5252c916 100644 --- a/config/constants.php +++ b/config/constants.php @@ -4,14 +4,12 @@ return [ 'coolify' => [ 'version' => '4.1.2', 'helper_version' => '1.0.14', - 'realtime_version' => '1.0.16', 'railpack_version' => '0.23.0', 'self_hosted' => env('SELF_HOSTED', true), 'autoupdate' => env('AUTOUPDATE'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), 'registry_url' => env('REGISTRY_URL', 'ghcr.io'), 'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'), - 'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-realtime'), 'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false), 'cdn_url' => env('CDN_URL', 'https://cdn.coollabs.io'), 'versions_url' => env('VERSIONS_URL', env('CDN_URL', 'https://cdn.coollabs.io').'/coolify/versions.json'), diff --git a/config/reverb.php b/config/reverb.php new file mode 100644 index 0000000000..5551e9be3a --- /dev/null +++ b/config/reverb.php @@ -0,0 +1,102 @@ + 'reverb', + + /* + |-------------------------------------------------------------------------- + | Reverb Servers + |-------------------------------------------------------------------------- + | + | Here you may define details for each of the supported Reverb servers. + | Each server has its own configuration options that are defined in + | the array below. You should ensure all the options are present. + | + */ + + 'servers' => [ + + 'reverb' => [ + 'host' => '0.0.0.0', + 'port' => env('PUSHER_BACKEND_PORT', 6001), + 'path' => '', + 'hostname' => env('PUSHER_HOST'), + 'options' => [ + 'tls' => [], + ], + 'max_request_size' => 10_000, + 'scaling' => [ + 'enabled' => false, + 'channel' => 'reverb', + 'server' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'port' => env('REDIS_PORT', '6379'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'database' => env('REDIS_DB', '0'), + 'timeout' => env('REDIS_TIMEOUT', 60), + ], + ], + 'pulse_ingest_interval' => 15, + 'telescope_ingest_interval' => 15, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Reverb Applications + |-------------------------------------------------------------------------- + | + | Here you may define how Reverb applications are managed. If you choose + | to use the "config" provider, you may define an array of apps which + | your server will support, including their connection credentials. + | + */ + + 'apps' => [ + + 'provider' => 'config', + + 'apps' => [ + [ + 'key' => env('PUSHER_APP_KEY', 'coolify'), + 'secret' => env('PUSHER_APP_SECRET', 'coolify'), + 'app_id' => env('PUSHER_APP_ID', 'coolify'), + 'options' => [ + 'host' => env('PUSHER_HOST', 'coolify'), + 'port' => env('PUSHER_PORT', 6001), + 'scheme' => env('PUSHER_SCHEME', 'http'), + 'useTLS' => env('PUSHER_SCHEME', 'http') === 'https', + ], + 'allowed_origins' => ['*'], + 'ping_interval' => 60, + 'activity_timeout' => 30, + 'max_connections' => null, + 'max_message_size' => 10_000, + 'accept_client_events_from' => 'members', + 'rate_limiting' => [ + 'enabled' => false, + 'max_attempts' => 60, + 'decay_seconds' => 60, + 'terminate_on_limit' => false, + ], + ], + ], + + ], + +]; diff --git a/docker-compose-maxio.dev.yml b/docker-compose-maxio.dev.yml index bbb483d7a8..bb049ff400 100644 --- a/docker-compose-maxio.dev.yml +++ b/docker-compose-maxio.dev.yml @@ -10,10 +10,13 @@ services: - GROUP_ID=${GROUPID:-1000} ports: - "${APP_PORT:-8000}:8080" + - "${FORWARD_PUSHER_PORT:-6001}:6001" + - "6002:6002" environment: AUTORUN_ENABLED: false - PUSHER_HOST: "${PUSHER_HOST}" - PUSHER_PORT: "${PUSHER_PORT}" + PUSHER_HOST: "${PUSHER_HOST:-coolify}" + PUSHER_PORT: "${PUSHER_PORT:-6001}" + PUSHER_BACKEND_PORT: "${PUSHER_BACKEND_PORT:-6001}" PUSHER_SCHEME: "${PUSHER_SCHEME:-http}" PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}" PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}" @@ -26,6 +29,8 @@ services: volumes: - .:/var/www/html/:cached - dev_backups_data:/var/www/html/storage/app/backups + - ./docker/coolify-terminal/terminal-server.js:/terminal/terminal-server.js + - ./docker/coolify-terminal/terminal-utils.js:/terminal/terminal-utils.js networks: - coolify postgres: @@ -59,33 +64,7 @@ services: timeout: 2s volumes: - dev_redis_data:/data - soketi: - image: coolify-realtime:dev - pull_policy: never - 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 - - ./docker/coolify-realtime/terminal-utils.js:/terminal/terminal-utils.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}" - SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}" - healthcheck: - test: [ "CMD-SHELL", "curl -fsS http://127.0.0.1:6001/ready && curl -fsS http://127.0.0.1:6002/ready || exit 1" ] - interval: 5s - retries: 10 - timeout: 2s - entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"] + vite: image: node:24-alpine pull_policy: always diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 9c93678afe..cc520c992c 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -10,10 +10,13 @@ services: - GROUP_ID=${GROUPID:-1000} ports: - "${APP_PORT:-8000}:8080" + - "${FORWARD_PUSHER_PORT:-6001}:6001" + - "6002:6002" environment: AUTORUN_ENABLED: false - PUSHER_HOST: "${PUSHER_HOST}" - PUSHER_PORT: "${PUSHER_PORT}" + PUSHER_HOST: "${PUSHER_HOST:-coolify}" + PUSHER_PORT: "${PUSHER_PORT:-6001}" + PUSHER_BACKEND_PORT: "${PUSHER_BACKEND_PORT:-6001}" PUSHER_SCHEME: "${PUSHER_SCHEME:-http}" PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}" PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}" @@ -26,6 +29,8 @@ services: volumes: - .:/var/www/html/:cached - dev_backups_data:/var/www/html/storage/app/backups + - ./docker/coolify-terminal/terminal-server.js:/terminal/terminal-server.js + - ./docker/coolify-terminal/terminal-utils.js:/terminal/terminal-utils.js networks: - coolify postgres: @@ -59,33 +64,7 @@ services: timeout: 2s volumes: - dev_redis_data:/data - soketi: - image: coolify-realtime:dev - pull_policy: never - 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 - - ./docker/coolify-realtime/terminal-utils.js:/terminal/terminal-utils.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}" - SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}" - healthcheck: - test: [ "CMD-SHELL", "curl -fsS http://127.0.0.1:6001/ready && curl -fsS http://127.0.0.1:6002/ready || exit 1" ] - interval: 5s - retries: 10 - timeout: 2s - entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"] + vite: image: node:24-alpine pull_policy: always diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 8907a30b91..cc8febcb35 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -18,12 +18,18 @@ services: - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1} - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1} - PHP_FPM_PM_MAX_SPARE_SERVERS=${PHP_FPM_PM_MAX_SPARE_SERVERS:-10} + - PUSHER_PORT=${PUSHER_PORT:-6001} + - PUSHER_BACKEND_PORT=${PUSHER_BACKEND_PORT:-6001} env_file: - /data/coolify/source/.env ports: - "${APP_PORT:-8000}:8080" + - "${PUSHER_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "${PUSHER_PORT:-6001}" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://127.0.0.1:8080/api/health || exit 1 interval: 5s @@ -34,8 +40,6 @@ services: condition: service_healthy redis: condition: service_healthy - soketi: - condition: service_healthy postgres: volumes: - coolify-db:/var/lib/postgresql/data @@ -59,26 +63,6 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.16' - ports: - - "${SOKETI_PORT:-6001}:6001" - - "6002:6002" - volumes: - - /data/coolify/ssh:/var/www/html/storage/app/ssh - environment: - APP_NAME: "${APP_NAME:-Coolify}" - SOKETI_DEBUG: "${SOKETI_DEBUG:-false}" - SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID}" - SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}" - SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}" - SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}" - healthcheck: - test: [ "CMD-SHELL", "wget -qO- http://127.0.0.1:6001/ready && wget -qO- http://127.0.0.1:6002/ready || exit 1" ] - interval: 5s - retries: 10 - timeout: 2s - volumes: coolify-db: name: coolify-db diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml index da045fe032..cf0ef27111 100644 --- a/docker-compose.windows.yml +++ b/docker-compose.windows.yml @@ -42,14 +42,20 @@ services: - PUSHER_APP_ID - PUSHER_APP_KEY - PUSHER_APP_SECRET + - PUSHER_PORT + - PUSHER_BACKEND_PORT - AUTOUPDATE=true - SELF_HOSTED=true - SSH_MUX_ENABLED=false - IS_WINDOWS_DOCKER_DESKTOP=true ports: - "${APP_PORT:-8000}:8080" + - "${PUSHER_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "${PUSHER_PORT:-6001}" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://localhost:8080/api/health || exit 1 interval: 5s @@ -95,30 +101,7 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: 'ghcr.io/coollabsio/coolify-realtime:1.0.16' - pull_policy: always - container_name: coolify-realtime - restart: always - env_file: - - .env - ports: - - "${SOKETI_PORT:-6001}:6001" - - "6002:6002" - volumes: - - ./ssh:/var/www/html/storage/app/ssh - environment: - APP_NAME: "${APP_NAME:-Coolify}" - SOKETI_DEBUG: "${SOKETI_DEBUG:-false}" - SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID}" - SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}" - SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}" - SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}" - healthcheck: - test: [ "CMD-SHELL", "wget -qO- http://127.0.0.1:6001/ready && wget -qO- http://127.0.0.1:6002/ready || exit 1" ] - interval: 5s - retries: 10 - timeout: 2s + volumes: coolify-db: diff --git a/docker-compose.yml b/docker-compose.yml index 0fd3dda070..c4fc1233a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,12 +5,14 @@ services: working_dir: /var/www/html extra_hosts: - host.docker.internal:host-gateway + expose: + - "6001" + - "6002" networks: - coolify depends_on: - postgres - redis - - soketi postgres: image: postgres:15-alpine container_name: coolify-db @@ -23,13 +25,6 @@ services: restart: always networks: - coolify - soketi: - container_name: coolify-realtime - extra_hosts: - - host.docker.internal:host-gateway - restart: always - networks: - - coolify networks: coolify: name: coolify diff --git a/docker/coolify-realtime/Dockerfile b/docker/coolify-realtime/Dockerfile deleted file mode 100644 index 8395d6f874..0000000000 --- a/docker/coolify-realtime/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -# Versions -# https://github.com/soketi/soketi/releases -ARG SOKETI_VERSION=1.6-16-alpine -# https://github.com/cloudflare/cloudflared/releases -ARG CLOUDFLARED_VERSION=2025.7.0 - -FROM quay.io/soketi/soketi:${SOKETI_VERSION} - -ARG TARGETPLATFORM -ARG CLOUDFLARED_VERSION - -WORKDIR /terminal -RUN apk upgrade --no-cache && \ - apk add --no-cache openssh-client make g++ python3 curl -COPY docker/coolify-realtime/package*.json ./ -RUN npm ci -RUN npm rebuild node-pty --update-binary -COPY docker/coolify-realtime/soketi-entrypoint.sh /soketi-entrypoint.sh -COPY docker/coolify-realtime/terminal-server.js /terminal/terminal-server.js -COPY docker/coolify-realtime/terminal-utils.js /terminal/terminal-utils.js - -# Install Cloudflared based on architecture -RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \ - curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64" -o /usr/local/bin/cloudflared; \ - elif [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \ - curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64" -o /usr/local/bin/cloudflared; \ - fi && \ - chmod +x /usr/local/bin/cloudflared - -ENTRYPOINT ["/bin/sh", "/soketi-entrypoint.sh"] diff --git a/docker/coolify-realtime/soketi-entrypoint.sh b/docker/coolify-realtime/soketi-entrypoint.sh deleted file mode 100644 index 7197e4a0c6..0000000000 --- a/docker/coolify-realtime/soketi-entrypoint.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh - -if [ "$1" = "watch" ]; then - WATCH_MODE="--watch" -else - WATCH_MODE="" -fi - -log() { - echo "$(date '+%Y-%m-%d %H:%M:%S') [ENTRYPOINT] $*" -} - -start_logger() { - prefix="$1" - fifo_path="$2" - - while read -r line; do - echo "$(date '+%Y-%m-%d %H:%M:%S') [$prefix] $line" - done < "$fifo_path" & -} - -cleanup() { - rm -f "$TERMINAL_LOG_FIFO" "$SOKETI_LOG_FIFO" -} - -TERMINAL_LOG_FIFO="/tmp/coolify-terminal-log.$$" -SOKETI_LOG_FIFO="/tmp/coolify-soketi-log.$$" - -rm -f "$TERMINAL_LOG_FIFO" "$SOKETI_LOG_FIFO" -mkfifo "$TERMINAL_LOG_FIFO" "$SOKETI_LOG_FIFO" - -trap cleanup EXIT - -log "Starting realtime container" -log "WATCH_MODE=${WATCH_MODE:-off}" -log "SOKETI_DEBUG=${SOKETI_DEBUG:-unset}" -log "NODE_OPTIONS=${NODE_OPTIONS:-unset}" - -start_logger "TERMINAL" "$TERMINAL_LOG_FIFO" -TERMINAL_LOGGER_PID=$! - -start_logger "SOKETI" "$SOKETI_LOG_FIFO" -SOKETI_LOGGER_PID=$! - -node $WATCH_MODE /terminal/terminal-server.js > "$TERMINAL_LOG_FIFO" 2>&1 & -TERMINAL_PID=$! - -log "Terminal server started pid=$TERMINAL_PID logger_pid=$TERMINAL_LOGGER_PID" - -node /app/bin/server.js start > "$SOKETI_LOG_FIFO" 2>&1 & -SOKETI_PID=$! - -log "Soketi started pid=$SOKETI_PID logger_pid=$SOKETI_LOGGER_PID" - -forward_signal() { - log "Forwarding signal $1 to terminal=$TERMINAL_PID soketi=$SOKETI_PID" - - kill -"$1" "$TERMINAL_PID" 2>/dev/null || true - kill -"$1" "$SOKETI_PID" 2>/dev/null || true -} - -trap 'forward_signal TERM' TERM -trap 'forward_signal INT' INT - -while true; do - if ! kill -0 "$TERMINAL_PID" 2>/dev/null; then - wait "$TERMINAL_PID" - EXIT_CODE=$? - - log "Terminal server exited code=$EXIT_CODE; stopping soketi pid=$SOKETI_PID" - - kill "$SOKETI_PID" 2>/dev/null || true - wait "$SOKETI_PID" 2>/dev/null || true - - exit "$EXIT_CODE" - fi - - if ! kill -0 "$SOKETI_PID" 2>/dev/null; then - wait "$SOKETI_PID" - EXIT_CODE=$? - - log "Soketi exited code=$EXIT_CODE; stopping terminal pid=$TERMINAL_PID" - - kill "$TERMINAL_PID" 2>/dev/null || true - wait "$TERMINAL_PID" 2>/dev/null || true - - exit "$EXIT_CODE" - fi - - sleep 1 -done diff --git a/docker/coolify-realtime/package-lock.json b/docker/coolify-terminal/package-lock.json similarity index 99% rename from docker/coolify-realtime/package-lock.json rename to docker/coolify-terminal/package-lock.json index cdb29bffa0..a3fbe513f4 100644 --- a/docker/coolify-realtime/package-lock.json +++ b/docker/coolify-terminal/package-lock.json @@ -1,5 +1,5 @@ { - "name": "coolify-realtime", + "name": "coolify-terminal", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/docker/coolify-realtime/package.json b/docker/coolify-terminal/package.json similarity index 100% rename from docker/coolify-realtime/package.json rename to docker/coolify-terminal/package.json diff --git a/docker/coolify-realtime/terminal-server.js b/docker/coolify-terminal/terminal-server.js similarity index 99% rename from docker/coolify-realtime/terminal-server.js rename to docker/coolify-terminal/terminal-server.js index 5197927168..62d9074be0 100755 --- a/docker/coolify-realtime/terminal-server.js +++ b/docker/coolify-terminal/terminal-server.js @@ -15,7 +15,7 @@ import { async function postToCoolify(path, headers) { return new Promise((resolve, reject) => { const request = http.request({ - hostname: 'coolify', + hostname: process.env.TERMINAL_AUTH_HOST || '127.0.0.1', port: 8080, path, method: 'POST', diff --git a/docker/coolify-realtime/terminal-utils.js b/docker/coolify-terminal/terminal-utils.js similarity index 100% rename from docker/coolify-realtime/terminal-utils.js rename to docker/coolify-terminal/terminal-utils.js diff --git a/docker/coolify-realtime/terminal-utils.test.js b/docker/coolify-terminal/terminal-utils.test.js similarity index 100% rename from docker/coolify-realtime/terminal-utils.test.js rename to docker/coolify-terminal/terminal-utils.test.js diff --git a/docker/development/Dockerfile b/docker/development/Dockerfile index 8fc46e32d5..71d9cb3444 100644 --- a/docker/development/Dockerfile +++ b/docker/development/Dockerfile @@ -57,6 +57,11 @@ RUN apk upgrade --no-cache && \ # Install system dependencies RUN apk add --no-cache \ postgresql${POSTGRES_VERSION}-client \ + nodejs \ + npm \ + make \ + g++ \ + python3 \ openssh-client \ git \ git-lfs \ @@ -85,6 +90,14 @@ RUN mkdir -p /usr/local/bin && \ COPY docker/development/etc/php/conf.d/zzz-custom-php.ini /usr/local/etc/php/conf.d/zzz-custom-php.ini ENV PHP_OPCACHE_ENABLE=0 +# Install terminal WebSocket server dependencies +COPY docker/coolify-terminal/package*.json /terminal/ +RUN npm ci --prefix /terminal && \ + npm rebuild node-pty --prefix /terminal --update-binary +COPY docker/coolify-terminal/terminal-server.js /terminal/terminal-server.js +COPY docker/coolify-terminal/terminal-utils.js /terminal/terminal-utils.js +RUN chown -R www-data:www-data /terminal + # Configure Nginx and S6 overlay COPY docker/development/etc/nginx/conf.d/custom.conf /etc/nginx/conf.d/custom.conf COPY docker/development/etc/nginx/site-opts.d/http.conf /etc/nginx/site-opts.d/http.conf diff --git a/docker/development/etc/s6-overlay/s6-rc.d/reverb/dependencies.d/init-setup b/docker/development/etc/s6-overlay/s6-rc.d/reverb/dependencies.d/init-setup new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/development/etc/s6-overlay/s6-rc.d/reverb/run b/docker/development/etc/s6-overlay/s6-rc.d/reverb/run new file mode 100755 index 0000000000..c189a24057 --- /dev/null +++ b/docker/development/etc/s6-overlay/s6-rc.d/reverb/run @@ -0,0 +1,11 @@ +#!/bin/sh + +cd /var/www/html + +if grep -qE '^PUSHER_ENABLED=false' .env 2>/dev/null; then + echo " INFO Reverb is disabled, sleeping." + exec sleep infinity +fi + +echo " INFO Reverb is enabled, starting..." +exec php artisan reverb:start --host=0.0.0.0 --port=${PUSHER_BACKEND_PORT:-6001} diff --git a/docker/development/etc/s6-overlay/s6-rc.d/reverb/type b/docker/development/etc/s6-overlay/s6-rc.d/reverb/type new file mode 100644 index 0000000000..5883cff0cd --- /dev/null +++ b/docker/development/etc/s6-overlay/s6-rc.d/reverb/type @@ -0,0 +1 @@ +longrun diff --git a/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/dependencies.d/init-setup b/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/dependencies.d/init-setup new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/run b/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/run new file mode 100755 index 0000000000..b099b5dbcc --- /dev/null +++ b/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/run @@ -0,0 +1,6 @@ +#!/bin/sh + +cd /var/www/html + +echo " INFO Terminal WebSocket server is starting..." +exec node /terminal/terminal-server.js diff --git a/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/type b/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/type new file mode 100644 index 0000000000..5883cff0cd --- /dev/null +++ b/docker/development/etc/s6-overlay/s6-rc.d/terminal-server/type @@ -0,0 +1 @@ +longrun diff --git a/docker/development/etc/s6-overlay/s6-rc.d/user/contents.d/reverb b/docker/development/etc/s6-overlay/s6-rc.d/user/contents.d/reverb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/development/etc/s6-overlay/s6-rc.d/user/contents.d/terminal-server b/docker/development/etc/s6-overlay/s6-rc.d/user/contents.d/terminal-server new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index 0f849785e2..02197e7940 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -107,6 +107,11 @@ RUN --mount=type=cache,target=/var/cache/apk \ apk upgrade && \ apk add --no-cache \ postgresql${POSTGRES_VERSION}-client \ + nodejs \ + npm \ + make \ + g++ \ + python3 \ openssh-client \ git \ git-lfs \ @@ -157,6 +162,14 @@ COPY --chown=www-data:www-data changelogs/ ./changelogs/ RUN composer dump-autoload +# Install terminal WebSocket server dependencies +COPY docker/coolify-terminal/package*.json /terminal/ +RUN npm ci --prefix /terminal && \ + npm rebuild node-pty --prefix /terminal --update-binary +COPY docker/coolify-terminal/terminal-server.js /terminal/terminal-server.js +COPY docker/coolify-terminal/terminal-utils.js /terminal/terminal-utils.js +RUN chown -R www-data:www-data /terminal + # Configure Nginx and S6 overlay COPY docker/production/etc/nginx/conf.d/custom.conf /etc/nginx/conf.d/custom.conf COPY docker/production/etc/nginx/site-opts.d/http.conf /etc/nginx/site-opts.d/http.conf diff --git a/docker/production/etc/s6-overlay/s6-rc.d/reverb/dependencies.d/init-script b/docker/production/etc/s6-overlay/s6-rc.d/reverb/dependencies.d/init-script new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/production/etc/s6-overlay/s6-rc.d/reverb/run b/docker/production/etc/s6-overlay/s6-rc.d/reverb/run new file mode 100755 index 0000000000..c189a24057 --- /dev/null +++ b/docker/production/etc/s6-overlay/s6-rc.d/reverb/run @@ -0,0 +1,11 @@ +#!/bin/sh + +cd /var/www/html + +if grep -qE '^PUSHER_ENABLED=false' .env 2>/dev/null; then + echo " INFO Reverb is disabled, sleeping." + exec sleep infinity +fi + +echo " INFO Reverb is enabled, starting..." +exec php artisan reverb:start --host=0.0.0.0 --port=${PUSHER_BACKEND_PORT:-6001} diff --git a/docker/production/etc/s6-overlay/s6-rc.d/reverb/type b/docker/production/etc/s6-overlay/s6-rc.d/reverb/type new file mode 100644 index 0000000000..5883cff0cd --- /dev/null +++ b/docker/production/etc/s6-overlay/s6-rc.d/reverb/type @@ -0,0 +1 @@ +longrun diff --git a/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/dependencies.d/init-script b/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/dependencies.d/init-script new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/run b/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/run new file mode 100755 index 0000000000..b099b5dbcc --- /dev/null +++ b/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/run @@ -0,0 +1,6 @@ +#!/bin/sh + +cd /var/www/html + +echo " INFO Terminal WebSocket server is starting..." +exec node /terminal/terminal-server.js diff --git a/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/type b/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/type new file mode 100644 index 0000000000..5883cff0cd --- /dev/null +++ b/docker/production/etc/s6-overlay/s6-rc.d/terminal-server/type @@ -0,0 +1 @@ +longrun diff --git a/docker/production/etc/s6-overlay/s6-rc.d/user/contents.d/reverb b/docker/production/etc/s6-overlay/s6-rc.d/user/contents.d/reverb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/production/etc/s6-overlay/s6-rc.d/user/contents.d/terminal-server b/docker/production/etc/s6-overlay/s6-rc.d/user/contents.d/terminal-server new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jean.json b/jean.json index 5cd8362d9d..f638feab80 100644 --- a/jean.json +++ b/jean.json @@ -2,7 +2,7 @@ "scripts": { "setup": "cp $JEAN_ROOT_PATH/.env . && mkdir -p .claude && cp $JEAN_ROOT_PATH/.claude/settings.local.json .claude/settings.local.json", "teardown": null, - "run": "docker rm -f coolify coolify-minio-init coolify-realtime coolify-minio coolify-testing-host coolify-redis coolify-db coolify-mail coolify-vite; spin up; spin down" + "run": "docker rm -f coolify coolify-minio-init coolify-minio coolify-testing-host coolify-redis coolify-db coolify-mail coolify-vite; spin up; spin down" }, "ports": [ { diff --git a/other/nightly/.env.production b/other/nightly/.env.production index fe3c8370e2..9e5d689f20 100644 --- a/other/nightly/.env.production +++ b/other/nightly/.env.production @@ -10,6 +10,8 @@ REDIS_PASSWORD= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= +PUSHER_PORT=6001 +PUSHER_BACKEND_PORT=6001 ROOT_USERNAME= ROOT_USER_EMAIL= diff --git a/other/nightly/docker-compose.prod.yml b/other/nightly/docker-compose.prod.yml index 8907a30b91..cc8febcb35 100644 --- a/other/nightly/docker-compose.prod.yml +++ b/other/nightly/docker-compose.prod.yml @@ -18,12 +18,18 @@ services: - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1} - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1} - PHP_FPM_PM_MAX_SPARE_SERVERS=${PHP_FPM_PM_MAX_SPARE_SERVERS:-10} + - PUSHER_PORT=${PUSHER_PORT:-6001} + - PUSHER_BACKEND_PORT=${PUSHER_BACKEND_PORT:-6001} env_file: - /data/coolify/source/.env ports: - "${APP_PORT:-8000}:8080" + - "${PUSHER_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "${PUSHER_PORT:-6001}" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://127.0.0.1:8080/api/health || exit 1 interval: 5s @@ -34,8 +40,6 @@ services: condition: service_healthy redis: condition: service_healthy - soketi: - condition: service_healthy postgres: volumes: - coolify-db:/var/lib/postgresql/data @@ -59,26 +63,6 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.16' - ports: - - "${SOKETI_PORT:-6001}:6001" - - "6002:6002" - volumes: - - /data/coolify/ssh:/var/www/html/storage/app/ssh - environment: - APP_NAME: "${APP_NAME:-Coolify}" - SOKETI_DEBUG: "${SOKETI_DEBUG:-false}" - SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID}" - SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}" - SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}" - SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}" - healthcheck: - test: [ "CMD-SHELL", "wget -qO- http://127.0.0.1:6001/ready && wget -qO- http://127.0.0.1:6002/ready || exit 1" ] - interval: 5s - retries: 10 - timeout: 2s - volumes: coolify-db: name: coolify-db diff --git a/other/nightly/docker-compose.windows.yml b/other/nightly/docker-compose.windows.yml index da045fe032..cf0ef27111 100644 --- a/other/nightly/docker-compose.windows.yml +++ b/other/nightly/docker-compose.windows.yml @@ -42,14 +42,20 @@ services: - PUSHER_APP_ID - PUSHER_APP_KEY - PUSHER_APP_SECRET + - PUSHER_PORT + - PUSHER_BACKEND_PORT - AUTOUPDATE=true - SELF_HOSTED=true - SSH_MUX_ENABLED=false - IS_WINDOWS_DOCKER_DESKTOP=true ports: - "${APP_PORT:-8000}:8080" + - "${PUSHER_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "${PUSHER_PORT:-6001}" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://localhost:8080/api/health || exit 1 interval: 5s @@ -95,30 +101,7 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: 'ghcr.io/coollabsio/coolify-realtime:1.0.16' - pull_policy: always - container_name: coolify-realtime - restart: always - env_file: - - .env - ports: - - "${SOKETI_PORT:-6001}:6001" - - "6002:6002" - volumes: - - ./ssh:/var/www/html/storage/app/ssh - environment: - APP_NAME: "${APP_NAME:-Coolify}" - SOKETI_DEBUG: "${SOKETI_DEBUG:-false}" - SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID}" - SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}" - SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}" - SOKETI_HOST: "${SOKETI_HOST:-0.0.0.0}" - healthcheck: - test: [ "CMD-SHELL", "wget -qO- http://127.0.0.1:6001/ready && wget -qO- http://127.0.0.1:6002/ready || exit 1" ] - interval: 5s - retries: 10 - timeout: 2s + volumes: coolify-db: diff --git a/other/nightly/docker-compose.yml b/other/nightly/docker-compose.yml index 0fd3dda070..c4fc1233a6 100644 --- a/other/nightly/docker-compose.yml +++ b/other/nightly/docker-compose.yml @@ -5,12 +5,14 @@ services: working_dir: /var/www/html extra_hosts: - host.docker.internal:host-gateway + expose: + - "6001" + - "6002" networks: - coolify depends_on: - postgres - redis - - soketi postgres: image: postgres:15-alpine container_name: coolify-db @@ -23,13 +25,6 @@ services: restart: always networks: - coolify - soketi: - container_name: coolify-realtime - extra_hosts: - - host.docker.internal:host-gateway - restart: always - networks: - - coolify networks: coolify: name: coolify diff --git a/other/nightly/install.sh b/other/nightly/install.sh index 028652d802..5079d37583 100755 --- a/other/nightly/install.sh +++ b/other/nightly/install.sh @@ -838,6 +838,13 @@ update_env_var() { fi } +normalize_pusher_port() { + if grep -q "^PUSHER_PORT=8080$" "$ENV_FILE"; then + sed -i "s|^PUSHER_PORT=8080$|PUSHER_PORT=6001|" "$ENV_FILE" + echo " - Updated PUSHER_PORT from app HTTP port 8080 to Reverb port 6001" + fi +} + update_env_var "APP_ID" "$(openssl rand -hex 16)" update_env_var "APP_KEY" "base64:$(openssl rand -base64 32)" # update_env_var "DB_USERNAME" "$(openssl rand -hex 16)" # Causes issues: database "random-user" does not exist @@ -846,6 +853,9 @@ update_env_var "REDIS_PASSWORD" "$(openssl rand -base64 32)" update_env_var "PUSHER_APP_ID" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_KEY" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_SECRET" "$(openssl rand -hex 32)" +update_env_var "PUSHER_PORT" "6001" +normalize_pusher_port +update_env_var "PUSHER_BACKEND_PORT" "6001" # Add default root user credentials from environment variables if [ -n "$ROOT_USERNAME" ] && [ -n "$ROOT_USER_EMAIL" ] && [ -n "$ROOT_USER_PASSWORD" ]; then diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh index c8fb9a98fc..8fb366e798 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -127,10 +127,20 @@ update_env_var() { fi } +normalize_pusher_port() { + if grep -q "^PUSHER_PORT=8080$" "$ENV_FILE"; then + sed -i "s|^PUSHER_PORT=8080$|PUSHER_PORT=6001|" "$ENV_FILE" + log "Updated PUSHER_PORT from app HTTP port 8080 to Reverb port 6001" + fi +} + log "Checking environment variables..." update_env_var "PUSHER_APP_ID" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_KEY" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_SECRET" "$(openssl rand -hex 32)" +update_env_var "PUSHER_PORT" "6001" +normalize_pusher_port +update_env_var "PUSHER_BACKEND_PORT" "6001" log "Environment variables check complete" echo " Done." @@ -225,7 +235,7 @@ nohup bash -c " } # Stop and remove containers - for container in coolify coolify-db coolify-redis coolify-realtime; do + for container in coolify coolify-db coolify-redis; do if docker ps -a --format '{{.Names}}' | grep -q \"^\${container}\$\"; then log \"Stopping container: \${container}\" docker stop \"\$container\" >>\"\$LOGFILE\" 2>&1 || true diff --git a/other/nightly/versions.json b/other/nightly/versions.json index 9c9a405aaa..b483549167 100644 --- a/other/nightly/versions.json +++ b/other/nightly/versions.json @@ -9,9 +9,6 @@ "helper": { "version": "1.0.14" }, - "realtime": { - "version": "1.0.16" - }, "sentinel": { "version": "0.0.21" } diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index be7b928aba..0789bfe103 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -174,8 +174,7 @@ window.Pusher = Pusher; const EchoConstructor = typeof Echo === 'function' ? Echo : Echo.default; window.Echo = new EchoConstructor({ - broadcaster: 'pusher', - cluster: "{{ config('constants.pusher.host') }}" || window.location.hostname, + broadcaster: 'reverb', key: "{{ config('constants.pusher.app_key') }}" || 'coolify', wsHost: "{{ config('constants.pusher.host') }}" || window.location.hostname, wsPort: "{{ getRealtime() }}", diff --git a/scripts/install.sh b/scripts/install.sh index 430dd9d832..17417b9fc1 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -838,6 +838,13 @@ update_env_var() { fi } +normalize_pusher_port() { + if grep -q "^PUSHER_PORT=8080$" "$ENV_FILE"; then + sed -i "s|^PUSHER_PORT=8080$|PUSHER_PORT=6001|" "$ENV_FILE" + echo " - Updated PUSHER_PORT from app HTTP port 8080 to Reverb port 6001" + fi +} + update_env_var "APP_ID" "$(openssl rand -hex 16)" update_env_var "APP_KEY" "base64:$(openssl rand -base64 32)" # update_env_var "DB_USERNAME" "$(openssl rand -hex 16)" # Causes issues: database "random-user" does not exist @@ -846,6 +853,9 @@ update_env_var "REDIS_PASSWORD" "$(openssl rand -base64 32)" update_env_var "PUSHER_APP_ID" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_KEY" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_SECRET" "$(openssl rand -hex 32)" +update_env_var "PUSHER_PORT" "6001" +normalize_pusher_port +update_env_var "PUSHER_BACKEND_PORT" "6001" # Add default root user credentials from environment variables if [ -n "$ROOT_USERNAME" ] && [ -n "$ROOT_USER_EMAIL" ] && [ -n "$ROOT_USER_PASSWORD" ]; then diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 199b531fd6..d4b0485e24 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -127,10 +127,20 @@ update_env_var() { fi } +normalize_pusher_port() { + if grep -q "^PUSHER_PORT=8080$" "$ENV_FILE"; then + sed -i "s|^PUSHER_PORT=8080$|PUSHER_PORT=6001|" "$ENV_FILE" + log "Updated PUSHER_PORT from app HTTP port 8080 to Reverb port 6001" + fi +} + log "Checking environment variables..." update_env_var "PUSHER_APP_ID" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_KEY" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_SECRET" "$(openssl rand -hex 32)" +update_env_var "PUSHER_PORT" "6001" +normalize_pusher_port +update_env_var "PUSHER_BACKEND_PORT" "6001" log "Environment variables check complete" echo " Done." @@ -234,7 +244,7 @@ nohup bash -c " } # Stop and remove containers - for container in coolify coolify-db coolify-redis coolify-realtime; do + for container in coolify coolify-db coolify-redis; do if docker ps -a --format '{{.Names}}' | grep -q \"^\${container}\$\"; then log \"Stopping container: \${container}\" docker stop \"\$container\" >>\"\$LOGFILE\" 2>&1 || true diff --git a/tests/Feature/RealtimeTerminalPackagingTest.php b/tests/Feature/RealtimeTerminalPackagingTest.php index 48072065d9..4fccd53b7e 100644 --- a/tests/Feature/RealtimeTerminalPackagingTest.php +++ b/tests/Feature/RealtimeTerminalPackagingTest.php @@ -1,15 +1,18 @@ toContain('COPY docker/coolify-realtime/terminal-utils.js /terminal/terminal-utils.js'); -}); + expect($dockerfile)->toContain('COPY docker/coolify-terminal/terminal-utils.js /terminal/terminal-utils.js'); +})->with([ + 'production image' => 'docker/production/Dockerfile', + 'development image' => 'docker/development/Dockerfile', +]); it('mounts the realtime terminal utilities in local development compose files', function (string $composeFile) { $composeContents = file_get_contents(base_path($composeFile)); - expect($composeContents)->toContain('./docker/coolify-realtime/terminal-utils.js:/terminal/terminal-utils.js'); + expect($composeContents)->toContain('./docker/coolify-terminal/terminal-utils.js:/terminal/terminal-utils.js'); })->with([ 'default dev compose' => 'docker-compose.dev.yml', 'maxio dev compose' => 'docker-compose-maxio.dev.yml', @@ -25,7 +28,7 @@ it('keeps terminal browser logging restricted to Vite development mode', functio }); it('keeps realtime terminal server logging behind the explicit debug flag', function () { - $terminalServer = file_get_contents(base_path('docker/coolify-realtime/terminal-server.js')); + $terminalServer = file_get_contents(base_path('docker/coolify-terminal/terminal-server.js')); expect($terminalServer) ->toContain('const debugOverride = String(process.env.TERMINAL_DEBUG') @@ -35,7 +38,7 @@ it('keeps realtime terminal server logging behind the explicit debug flag', func }); it('configures a server-initiated WebSocket heartbeat to survive proxy idle timeouts', function () { - $terminalServer = file_get_contents(base_path('docker/coolify-realtime/terminal-server.js')); + $terminalServer = file_get_contents(base_path('docker/coolify-terminal/terminal-server.js')); expect($terminalServer) ->toContain('ws.isAlive = true;') @@ -60,7 +63,7 @@ it('uses a fast probe timeout when the tab regains visibility', function () { }); it('does not hard close terminal sessions after 30 minutes on the server', function () { - $terminalServer = file_get_contents(base_path('docker/coolify-realtime/terminal-server.js')); + $terminalServer = file_get_contents(base_path('docker/coolify-terminal/terminal-server.js')); expect($terminalServer) ->not->toContain('IDLE_TIMEOUT_MS = 30 * 60 * 1000') @@ -110,7 +113,7 @@ it('replays the last command on reconnect so the PTY respawns automatically', fu }); it('buffers messages received before the realtime server finishes auth so the replay is not lost', function () { - $terminalServer = file_get_contents(base_path('docker/coolify-realtime/terminal-server.js')); + $terminalServer = file_get_contents(base_path('docker/coolify-terminal/terminal-server.js')); expect($terminalServer) ->toContain('authReady: false') diff --git a/tests/Feature/ReverbAndTerminalPackagingTest.php b/tests/Feature/ReverbAndTerminalPackagingTest.php new file mode 100644 index 0000000000..6677fa0468 --- /dev/null +++ b/tests/Feature/ReverbAndTerminalPackagingTest.php @@ -0,0 +1,141 @@ +toContain('"laravel/reverb"') + ->and(file_get_contents(config_path('broadcasting.php'))) + ->toContain("'default' => env('BROADCAST_CONNECTION', env('BROADCAST_DRIVER', 'reverb'))") + ->toContain("'reverb' => [") + ->toContain("'key' => env('PUSHER_APP_KEY', 'coolify')") + ->toContain("'secret' => env('PUSHER_APP_SECRET', 'coolify')") + ->toContain("'app_id' => env('PUSHER_APP_ID', 'coolify')") + ->toContain("'host' => env('PUSHER_HOST', 'coolify')") + ->toContain("'port' => env('PUSHER_BACKEND_PORT', 6001)") + ->and(file_exists(config_path('reverb.php')))->toBeTrue(); +}); + +it('runs Reverb and terminal websocket services inside the Coolify containers', function (string $dockerfile, string $dependencyService) { + $dockerfileContents = file_get_contents(base_path($dockerfile)); + + expect($dockerfileContents) + ->toContain('nodejs') + ->toContain('npm') + ->toContain('COPY docker/coolify-terminal/package*.json /terminal/') + ->toContain('COPY docker/coolify-terminal/terminal-server.js /terminal/terminal-server.js') + ->toContain('COPY docker/coolify-terminal/terminal-utils.js /terminal/terminal-utils.js') + ->toContain('npm ci --prefix /terminal') + ->and(file_get_contents(base_path(dirname($dockerfile).'/etc/s6-overlay/s6-rc.d/reverb/run'))) + ->toContain('exec php artisan reverb:start --host=0.0.0.0 --port=${PUSHER_BACKEND_PORT:-6001}') + ->and(file_get_contents(base_path(dirname($dockerfile).'/etc/s6-overlay/s6-rc.d/terminal-server/run'))) + ->toContain('exec node /terminal/terminal-server.js') + ->and(file_exists(base_path(dirname($dockerfile)."/etc/s6-overlay/s6-rc.d/reverb/dependencies.d/{$dependencyService}")))->toBeTrue() + ->and(file_exists(base_path(dirname($dockerfile)."/etc/s6-overlay/s6-rc.d/terminal-server/dependencies.d/{$dependencyService}")))->toBeTrue() + ->and(file_exists(base_path(dirname($dockerfile).'/etc/s6-overlay/s6-rc.d/user/contents.d/reverb')))->toBeTrue() + ->and(file_exists(base_path(dirname($dockerfile).'/etc/s6-overlay/s6-rc.d/user/contents.d/terminal-server')))->toBeTrue(); +})->with([ + 'production image' => ['docker/production/Dockerfile', 'init-script'], + 'development image' => ['docker/development/Dockerfile', 'init-setup'], +]); + +it('removes the dedicated realtime service from bundled compose files', function (string $composeFile, bool $hasRuntimeEnvironment) { + $composeContents = file_get_contents(base_path($composeFile)); + + expect($composeContents) + ->not->toContain('coolify-realtime') + ->not->toContain('soketi:') + ->not->toContain('SOKETI_DEFAULT_APP_ID') + ->toContain('6001') + ->toContain('6002') + ->not->toContain('REVERB_'); + + if ($hasRuntimeEnvironment) { + expect($composeContents)->toContain('PUSHER_BACKEND_PORT'); + } +})->with([ + 'base compose' => ['docker-compose.yml', false], + 'production compose' => ['docker-compose.prod.yml', true], + 'development compose' => ['docker-compose.dev.yml', true], + 'maxio development compose' => ['docker-compose-maxio.dev.yml', true], + 'windows compose' => ['docker-compose.windows.yml', true], +]); + +it('keeps the internal Reverb listen port separate from the public Pusher port', function () { + expect(file_get_contents(config_path('reverb.php'))) + ->toContain("'port' => env('PUSHER_BACKEND_PORT', 6001)") + ->toContain("'port' => env('PUSHER_PORT', 6001)") + ->and(file_get_contents(base_path('docker/production/etc/s6-overlay/s6-rc.d/reverb/run'))) + ->toContain('exec php artisan reverb:start --host=0.0.0.0 --port=${PUSHER_BACKEND_PORT:-6001}') + ->and(file_get_contents(base_path('docker/development/etc/s6-overlay/s6-rc.d/reverb/run'))) + ->toContain('exec php artisan reverb:start --host=0.0.0.0 --port=${PUSHER_BACKEND_PORT:-6001}'); +}); + +it('proxies Reverb and terminal websocket traffic to the Coolify app container', function () { + $serverModel = file_get_contents(app_path('Models/Server.php')); + + expect($serverModel) + ->toContain("'rule' => \"Host(`{\$host}`) && PathPrefix(`/app`)\"") + ->toContain("'rule' => \"Host(`{\$host}`) && PathPrefix(`/apps`)\"") + ->toContain("'rule' => \"Host(`{\$host}`) && PathPrefix(`/terminal/ws`)\"") + ->toContain("'url' => 'http://coolify:6001'") + ->toContain("'url' => 'http://coolify:6002'") + ->toContain('reverse_proxy coolify:6001') + ->toContain('reverse_proxy coolify:6002') + ->not->toContain('http://coolify-realtime:6001') + ->not->toContain('http://coolify-realtime:6002') + ->not->toContain('reverse_proxy coolify-realtime'); +}); + +it('uses Pusher environment keys for self-hosted Reverb compatibility', function () { + $files = [ + '.env.production', + '.env.windows-docker-desktop.example', + 'docker-compose.prod.yml', + 'docker-compose.dev.yml', + 'docker-compose-maxio.dev.yml', + 'docker-compose.windows.yml', + 'scripts/install.sh', + 'scripts/upgrade.sh', + 'other/nightly/.env.production', + 'other/nightly/docker-compose.prod.yml', + 'other/nightly/docker-compose.windows.yml', + 'other/nightly/install.sh', + 'other/nightly/upgrade.sh', + ]; + + foreach ($files as $file) { + expect(file_get_contents(base_path($file))) + ->toContain('PUSHER_') + ->not->toContain('REVERB_'); + } +}); + +it('defaults the public Pusher websocket port to Reverb instead of the HTTP app port', function () { + expect(file_get_contents(base_path('.env.production'))) + ->toContain('PUSHER_PORT=6001') + ->toContain('PUSHER_BACKEND_PORT=6001') + ->and(file_get_contents(base_path('.env.windows-docker-desktop.example'))) + ->toContain('PUSHER_PORT=6001') + ->toContain('PUSHER_BACKEND_PORT=6001') + ->and(file_get_contents(base_path('scripts/install.sh'))) + ->toContain('update_env_var "PUSHER_PORT" "6001"') + ->toContain('update_env_var "PUSHER_BACKEND_PORT" "6001"') + ->toContain('normalize_pusher_port') + ->and(file_get_contents(base_path('scripts/upgrade.sh'))) + ->toContain('update_env_var "PUSHER_PORT" "6001"') + ->toContain('update_env_var "PUSHER_BACKEND_PORT" "6001"') + ->toContain('normalize_pusher_port'); +}); + +it('stops publishing or preserving the obsolete realtime image', function () { + expect(file_get_contents(config_path('constants.php'))) + ->not->toContain('realtime_version') + ->not->toContain('realtime_image') + ->and(file_get_contents(app_path('Actions/Server/CleanupDocker.php'))) + ->not->toContain('coolify-realtime') + ->not->toContain('realtimeImage') + ->and(file_get_contents(base_path('versions.json'))) + ->not->toContain('"realtime"') + ->and(is_dir(base_path('docker/coolify-realtime')))->toBeFalse() + ->and(file_exists(base_path('.github/workflows/coolify-realtime.yml')))->toBeFalse() + ->and(file_exists(base_path('.github/workflows/coolify-realtime-next.yml')))->toBeFalse(); +}); diff --git a/tests/Unit/Actions/Server/CleanupDockerTest.php b/tests/Unit/Actions/Server/CleanupDockerTest.php index a70f07d15d..a74684e6e3 100644 --- a/tests/Unit/Actions/Server/CleanupDockerTest.php +++ b/tests/Unit/Actions/Server/CleanupDockerTest.php @@ -255,54 +255,34 @@ it('correctly excludes Docker Compose images from general prune', function () { } }); -it('excludes current version of Coolify infrastructure images from any registry', function () { - // Test the regex pattern used to protect the current version of infrastructure images - // regardless of which registry they come from (ghcr.io, docker.io, or no prefix) +it('excludes current version of the Coolify helper image from any registry', function () { $helperVersion = '1.0.12'; - $realtimeVersion = '1.0.10'; - // Build the exclusion pattern the same way CleanupDocker does - // Pattern: (^|/)coollabsio/coolify-helper:VERSION$|(^|/)coollabsio/coolify-realtime:VERSION$ - $escapedHelperVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $helperVersion); - $escapedRealtimeVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $realtimeVersion); + $escapedHelperVersion = preg_quote($helperVersion, '/'); + $infraPattern = "(^|/)coollabsio/coolify-helper:{$escapedHelperVersion}$"; + $pattern = "#{$infraPattern}#"; - // For PHP preg_match, escape forward slashes - $infraPattern = "(^|\\/)coollabsio\\/coolify-helper:{$escapedHelperVersion}$|(^|\\/)coollabsio\\/coolify-realtime:{$escapedRealtimeVersion}$"; - $pattern = "/{$infraPattern}/"; - - // Current versioned infrastructure images from ANY registry should be PROTECTED $protectedImages = [ - // ghcr.io registry "ghcr.io/coollabsio/coolify-helper:{$helperVersion}", - "ghcr.io/coollabsio/coolify-realtime:{$realtimeVersion}", - // docker.io registry (explicit) "docker.io/coollabsio/coolify-helper:{$helperVersion}", - "docker.io/coollabsio/coolify-realtime:{$realtimeVersion}", - // No registry prefix (Docker Hub implicit) "coollabsio/coolify-helper:{$helperVersion}", - "coollabsio/coolify-realtime:{$realtimeVersion}", ]; - // Verify current infrastructure images ARE protected from any registry foreach ($protectedImages as $image) { expect(preg_match($pattern, $image))->toBe(1, "Current infrastructure image {$image} should be protected"); } - // Verify OLD versions of infrastructure images are NOT protected (can be deleted) $oldVersionImages = [ 'ghcr.io/coollabsio/coolify-helper:1.0.11', 'docker.io/coollabsio/coolify-helper:1.0.10', 'coollabsio/coolify-helper:1.0.9', - 'ghcr.io/coollabsio/coolify-realtime:1.0.9', 'ghcr.io/coollabsio/coolify-helper:latest', - 'coollabsio/coolify-realtime:latest', ]; foreach ($oldVersionImages as $image) { expect(preg_match($pattern, $image))->toBe(0, "Old infrastructure image {$image} should NOT be protected"); } - // Verify other images are NOT protected (can be deleted) $deletableImages = [ 'nginx:alpine', 'postgres:15', @@ -316,31 +296,19 @@ it('excludes current version of Coolify infrastructure images from any registry' } }); -it('protects current infrastructure images from any registry even when no applications exist', function () { - // When there are no applications, current versioned infrastructure images should still be protected - // regardless of which registry they come from +it('protects current helper image from any registry even when no applications exist', function () { $helperVersion = '1.0.12'; - $realtimeVersion = '1.0.10'; - // Build the pattern the same way CleanupDocker does - $escapedHelperVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $helperVersion); - $escapedRealtimeVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $realtimeVersion); + $escapedHelperVersion = preg_quote($helperVersion, '/'); + $infraPattern = "(^|/)coollabsio/coolify-helper:{$escapedHelperVersion}$"; + $pattern = "#{$infraPattern}#"; - // For PHP preg_match, escape forward slashes - $infraPattern = "(^|\\/)coollabsio\\/coolify-helper:{$escapedHelperVersion}$|(^|\\/)coollabsio\\/coolify-realtime:{$escapedRealtimeVersion}$"; - $pattern = "/{$infraPattern}/"; - - // Verify current infrastructure images from any registry are protected expect(preg_match($pattern, "ghcr.io/coollabsio/coolify-helper:{$helperVersion}"))->toBe(1); expect(preg_match($pattern, "docker.io/coollabsio/coolify-helper:{$helperVersion}"))->toBe(1); expect(preg_match($pattern, "coollabsio/coolify-helper:{$helperVersion}"))->toBe(1); - expect(preg_match($pattern, "ghcr.io/coollabsio/coolify-realtime:{$realtimeVersion}"))->toBe(1); - // Old versions should NOT be protected expect(preg_match($pattern, 'ghcr.io/coollabsio/coolify-helper:1.0.11'))->toBe(0); expect(preg_match($pattern, 'docker.io/coollabsio/coolify-helper:1.0.11'))->toBe(0); - - // Other images should not be protected expect(preg_match($pattern, 'nginx:alpine'))->toBe(0); }); diff --git a/versions.json b/versions.json index 9c9a405aaa..b483549167 100644 --- a/versions.json +++ b/versions.json @@ -9,9 +9,6 @@ "helper": { "version": "1.0.14" }, - "realtime": { - "version": "1.0.16" - }, "sentinel": { "version": "0.0.21" }