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 cda3214d2d..c6990a51d4 100644 --- a/.env.production +++ b/.env.production @@ -10,7 +10,6 @@ REDIS_PASSWORD= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= - ROOT_USERNAME= ROOT_USER_EMAIL= ROOT_USER_PASSWORD= diff --git a/.env.windows-docker-desktop.example b/.env.windows-docker-desktop.example index b067b4c5c0..e97249505d 100644 --- a/.env.windows-docker-desktop.example +++ b/.env.windows-docker-desktop.example @@ -11,3 +11,4 @@ REDIS_PASSWORD=coolify PUSHER_APP_ID=coolify PUSHER_APP_KEY=coolify PUSHER_APP_SECRET=coolify +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-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 495e6a4637..0000000000 --- a/.github/workflows/coolify-realtime.yml +++ /dev/null @@ -1,163 +0,0 @@ -name: Coolify Realtime - -on: - workflow_dispatch: - push: - branches: [ "main" ] - paths: - - .github/workflows/coolify-realtime.yml - - config/constants.php - - docker/coolify-realtime/** - -permissions: - contents: read - packages: write - -env: - GITHUB_REGISTRY: ghcr.io - DOCKER_REGISTRY: docker.io - IMAGE_NAME: "coollabsio/coolify-realtime" - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false - -jobs: - check-version: - runs-on: ubuntu-24.04 - 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: Ensure version is not published - run: | - BASE_VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getRealtimeVersion.php) - VERSION="${BASE_VERSION}" - for registry in "${DOCKER_REGISTRY}" "${GITHUB_REGISTRY}"; do - IMAGE="${registry}/${IMAGE_NAME}:${VERSION}" - if output=$(docker buildx imagetools inspect "$IMAGE" 2>&1); then - echo "::error::Version $VERSION already exists in $registry" - exit 1 - fi - if ! grep -Eqi 'manifest unknown|not found|no such manifest' <<< "$output"; then - echo "::error::Could not verify $IMAGE: $output" - exit 1 - fi - done - echo "Version $VERSION is available in both registries" - - build-push: - needs: check-version - 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/generate-changelog.yml b/.github/workflows/generate-changelog.yml index 6e88c0d600..a9878e7c41 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 efba1ec76d..3b583795bf 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/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 04fe00ad48..9f58fd4f88 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 = coolifyHelperImage(); $helperImageWithVersion = "$helperImage:$helperImageVersion"; @@ -41,13 +35,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 = [ @@ -56,9 +49,7 @@ class CleanupDocker 'docker builder prune -af', "docker run --rm -v {$buildxMetadataVolume}:/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) { @@ -90,8 +81,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']; @@ -108,14 +98,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 7a9269d4c7..20952d5598 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -692,13 +692,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', @@ -717,11 +724,11 @@ class Server extends BaseModel ], ], ], - 'coolify-realtime' => [ + 'coolify-reverb' => [ 'loadBalancer' => [ 'servers' => [ 0 => [ - 'url' => 'http://coolify-realtime:6001', + 'url' => 'http://coolify:6001', ], ], ], @@ -730,7 +737,7 @@ class Server extends BaseModel 'loadBalancer' => [ 'servers' => [ 0 => [ - 'url' => 'http://coolify-realtime:6002', + 'url' => 'http://coolify:6002', ], ], ], @@ -755,16 +762,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', @@ -804,10 +821,13 @@ class Server extends BaseModel $siteAddress { encode zstd gzip 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.1", @@ -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", @@ -2449,6 +2626,85 @@ }, "time": "2026-08-04T14:50:50+00:00" }, + { + "name": "laravel/reverb", + "version": "v1.11.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/reverb.git", + "reference": "52ce5fd88cd1d7eacfdcf6b91cea4704cc546f27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/reverb/zipball/52ce5fd88cd1d7eacfdcf6b91cea4704cc546f27", + "reference": "52ce5fd88cd1d7eacfdcf6b91cea4704cc546f27", + "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.11.1" + }, + "time": "2026-08-06T14:48:58+00:00" + }, { "name": "laravel/sanctum", "version": "v4.3.3", @@ -6176,6 +6432,595 @@ }, "time": "2026-06-18T03:57:49+00:00" }, + { + "name": "ratchet/rfc6455", + "version": "v0.4.1", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "9b05f371219cbaf9748b505f139617dd0715592b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/9b05f371219cbaf9748b505f139617dd0715592b", + "reference": "9b05f371219cbaf9748b505f139617dd0715592b", + "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.1" + }, + "time": "2026-06-06T14:34:23+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 f48b686826..9ca003f3b0 100644 --- a/config/constants.php +++ b/config/constants.php @@ -4,14 +4,12 @@ return [ 'coolify' => [ 'version' => env('COOLIFY_VERSION') ?: '4.4', 'helper_version' => '1.0.17', - 'realtime_version' => '1.0.20', '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', 'docker.io'), - 'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'docker.io').'/coollabsio/coolify-helper'), - 'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'docker.io').'/coollabsio/coolify-realtime'), + 'registry_url' => env('REGISTRY_URL', 'ghcr.io'), + 'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'), '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 885d17f3c8..3d4525510e 100644 --- a/docker-compose-maxio.dev.yml +++ b/docker-compose-maxio.dev.yml @@ -10,12 +10,15 @@ services: - GROUP_ID=${GROUPID:-1000} ports: - "${APP_PORT:-8000}:8080" + - "${FORWARD_PUSHER_PORT:-6001}:6001" + - "6002:6002" extra_hosts: - "host.docker.internal:host-gateway" 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}" @@ -28,6 +31,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: @@ -61,35 +66,6 @@ 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" - extra_hosts: - - "host.docker.internal:host-gateway" - 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-multi.yml b/docker-compose.dev-multi.yml index c8f2833117..26ba2236b6 100644 --- a/docker-compose.dev-multi.yml +++ b/docker-compose.dev-multi.yml @@ -10,7 +10,7 @@ # Optional profiles: vite, mailpit, minio, testing-host # ./scripts/dev-instances up a --with vite mailpit # -# Ports (a / b): app 8000/8001, db 5432/5433, redis 6379/6380, soketi 6001/6011 +# Ports (a / b): app 8000/8001, db 5432/5433, redis 6379/6380, Reverb 6001/6011 services: coolify: @@ -24,6 +24,8 @@ services: - GROUP_ID=${GROUPID:-1000} ports: - "${APP_PORT:-8000}:8080" + - "${FORWARD_PUSHER_PORT:-6001}:6001" + - "${FORWARD_TERMINAL_PORT:-6002}:6002" extra_hosts: - "host.docker.internal:host-gateway" environment: @@ -43,13 +45,14 @@ services: REDIS_PORT: 6379 REDIS_PASSWORD: "${REDIS_PASSWORD:-null}" COOLIFY_CONTAINER_ROLE: all - PUSHER_HOST: "" - PUSHER_PORT: "" + PUSHER_HOST: coolify + PUSHER_PORT: 6001 + PUSHER_BACKEND_PORT: 6001 PUSHER_SCHEME: http PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}" PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}" PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" - BROADCAST_CONNECTION: pusher + BROADCAST_CONNECTION: reverb QUEUE_CONNECTION: redis CACHE_STORE: redis SESSION_DRIVER: redis @@ -61,6 +64,8 @@ services: volumes: - .:/var/www/html/:cached - 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 depends_on: postgres: condition: service_healthy @@ -104,36 +109,6 @@ services: networks: - coolify - soketi: - image: coolify-realtime:dev - pull_policy: never - build: - context: . - dockerfile: ./docker/coolify-realtime/Dockerfile - ports: - - "${FORWARD_SOKETI_PORT:-6001}:6001" - - "${FORWARD_SOKETI_PORT_ALT:-6002}:6002" - extra_hosts: - - "host.docker.internal:host-gateway" - 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: "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"] - networks: - - coolify - vite: profiles: ["vite"] image: node:24-alpine diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index d33d9aab5c..6210d70bd4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -10,13 +10,16 @@ services: - GROUP_ID=${GROUPID:-1000} ports: - "${DEV_BIND_ADDRESS:-0.0.0.0}:${APP_PORT:-8000}:8080" + - "${DEV_BIND_ADDRESS:-0.0.0.0}:${FORWARD_PUSHER_PORT:-6001}:6001" + - "${DEV_BIND_ADDRESS:-0.0.0.0}:6002:6002" extra_hosts: - "host.docker.internal:host-gateway" environment: AUTORUN_ENABLED: false COOLIFY_CONTAINER_ROLE: "${COOLIFY_CONTAINER_ROLE:-all}" - 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}" @@ -29,6 +32,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: @@ -62,35 +67,6 @@ 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: - - "${DEV_BIND_ADDRESS:-0.0.0.0}:${FORWARD_SOKETI_PORT:-6001}:6001" - - "${DEV_BIND_ADDRESS:-0.0.0.0}:6002:6002" - extra_hosts: - - "host.docker.internal:host-gateway" - 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 2615ba63f9..169a65393a 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -19,12 +19,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" + - "${SOKETI_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "6001" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://127.0.0.1:8080/api/health || exit 1 interval: 5s @@ -36,8 +42,6 @@ services: condition: service_healthy redis: condition: service_healthy - soketi: - condition: service_healthy postgres: volumes: - coolify-db:/var/lib/postgresql/data @@ -61,26 +65,6 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: '${REGISTRY_URL:-docker.io}/coollabsio/coolify-realtime:1.0.20' - 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 106b3e1dc3..499584c14d 100644 --- a/docker-compose.windows.yml +++ b/docker-compose.windows.yml @@ -43,14 +43,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" + - "${SOKETI_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "6001" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://localhost:8080/api/health || exit 1 interval: 5s @@ -96,30 +102,6 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: 'ghcr.io/coollabsio/coolify-realtime:1.0.20' - 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 da8e57be54..e59be50baf 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 09f5bd5e12..00a9dd9590 100755 --- a/docker/coolify-realtime/terminal-server.js +++ b/docker/coolify-terminal/terminal-server.js @@ -18,7 +18,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 62bb0ee241..6297da2cfe 100644 --- a/docker/development/Dockerfile +++ b/docker/development/Dockerfile @@ -56,6 +56,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 \ openssl \ git \ @@ -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..e35e88a6cb --- /dev/null +++ b/docker/development/etc/s6-overlay/s6-rc.d/reverb/run @@ -0,0 +1,6 @@ +#!/bin/sh + +cd /var/www/html + +echo " INFO Reverb is 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 540602eceb..03d1145df7 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -108,6 +108,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 \ @@ -158,6 +163,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..e35e88a6cb --- /dev/null +++ b/docker/production/etc/s6-overlay/s6-rc.d/reverb/run @@ -0,0 +1,6 @@ +#!/bin/sh + +cd /var/www/html + +echo " INFO Reverb is 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 7e4bcc00bc..77cf921f7a 100644 --- a/jean.json +++ b/jean.json @@ -2,7 +2,7 @@ "scripts": { "setup": "cp $JEAN_ROOT_PATH/.env .", "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; docker compose -f docker-compose.yml -f docker-compose.dev.yml up; docker compose -f docker-compose.yml -f docker-compose.dev.yml down" + "run": "docker rm -f coolify coolify-minio-init coolify-minio coolify-testing-host coolify-redis coolify-db coolify-mail coolify-vite; docker compose -f docker-compose.yml -f docker-compose.dev.yml up; docker compose -f docker-compose.yml -f docker-compose.dev.yml down" }, "ports": [ { diff --git a/other/nightly/.env.production b/other/nightly/.env.production index cda3214d2d..f872a214f9 100644 --- a/other/nightly/.env.production +++ b/other/nightly/.env.production @@ -10,6 +10,7 @@ REDIS_PASSWORD= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= +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 ebf12379d5..169a65393a 100644 --- a/other/nightly/docker-compose.prod.yml +++ b/other/nightly/docker-compose.prod.yml @@ -19,12 +19,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" + - "${SOKETI_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "6001" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://127.0.0.1:8080/api/health || exit 1 interval: 5s @@ -36,8 +42,6 @@ services: condition: service_healthy redis: condition: service_healthy - soketi: - condition: service_healthy postgres: volumes: - coolify-db:/var/lib/postgresql/data @@ -61,26 +65,6 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: '${REGISTRY_URL:-docker.io}/coollabsio/coolify-realtime:1.0.18' - 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 32524c4f43..302c89c27f 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" + - "${SOKETI_PORT:-6001}:6001" + - "${TERMINAL_PORT:-6002}:6002" expose: - "${APP_PORT:-8000}" + - "6001" + - "${TERMINAL_PORT:-6002}" healthcheck: test: curl --fail http://localhost:8080/api/health || exit 1 interval: 5s @@ -95,30 +101,6 @@ services: interval: 5s retries: 10 timeout: 2s - soketi: - image: 'ghcr.io/coollabsio/coolify-realtime:1.0.18' - 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 595dac2a51..7d5f868dc8 100755 --- a/other/nightly/install.sh +++ b/other/nightly/install.sh @@ -320,16 +320,11 @@ fi VERSIONS_JSON=$(curl -L --silent $CDN/versions.json) LATEST_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $2}' | tr -d ',') LATEST_HELPER_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $6}' | tr -d ',') -LATEST_REALTIME_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $8}' | tr -d ',') if [ -z "$LATEST_HELPER_VERSION" ]; then LATEST_HELPER_VERSION=latest fi -if [ -z "$LATEST_REALTIME_VERSION" ]; then - LATEST_REALTIME_VERSION=latest -fi - case "$OS_TYPE" in arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine | postmarketos | tencentos) ;; *) @@ -350,7 +345,6 @@ echo "| Operating System | $OS_TYPE $OS_VERSION" echo "| Docker | $DOCKER_VERSION" echo "| Coolify | $LATEST_VERSION" echo "| Helper | $LATEST_HELPER_VERSION" -echo "| Realtime | $LATEST_REALTIME_VERSION" echo "| Docker Pool | $DOCKER_ADDRESS_POOL_BASE (size $DOCKER_ADDRESS_POOL_SIZE)" echo "| Registry URL | $REGISTRY_URL" echo "---------------------------------------------" @@ -846,6 +840,7 @@ 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_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 c5f38df6e0..0e441ad380 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -151,6 +151,7 @@ set_env_var "REGISTRY_URL" "$REGISTRY_URL" 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_BACKEND_PORT" "6001" log "Environment variables check complete" echo " Done." @@ -248,7 +249,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 6eb14d45e8..7262de886a 100644 --- a/other/nightly/versions.json +++ b/other/nightly/versions.json @@ -9,9 +9,6 @@ "helper": { "version": "1.0.17" }, - "realtime": { - "version": "1.0.20" - }, "sentinel": { "version": "1.0.1" } diff --git a/phpunit.xml b/phpunit.xml index 6ddcaed7dc..b1091f2abd 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -22,7 +22,7 @@ - + diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 66201da936..61df6d22f1 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -284,8 +284,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/dev-instances b/scripts/dev-instances index eac6b84383..36437807e6 100755 --- a/scripts/dev-instances +++ b/scripts/dev-instances @@ -12,8 +12,8 @@ # ./scripts/dev-instances exec a php artisan migrate --force # # Ports (fixed): -# a app 8000 db 5432 redis 6379 soketi 6001/6002 flux 6443 -# b app 8001 db 5433 redis 6380 soketi 6011/6012 flux 6444 +# a app 8000 db 5432 redis 6379 Reverb 6001/6002 flux 6443 +# b app 8001 db 5433 redis 6380 Reverb 6011/6012 flux 6444 # # Frontend: multi-instance uses npm run build (shared public/build). Do not use # two Vite HMR servers — public/hot is a single shared file. @@ -94,8 +94,8 @@ APP_KEY=${existing_key} APP_PORT=${app_port} FORWARD_DB_PORT=$((5432 + offset)) FORWARD_REDIS_PORT=$((6379 + offset)) -FORWARD_SOKETI_PORT=$((6001 + offset * 10)) -FORWARD_SOKETI_PORT_ALT=$((6002 + offset * 10)) +FORWARD_PUSHER_PORT=$((6001 + offset * 10)) +FORWARD_TERMINAL_PORT=$((6002 + offset * 10)) FORWARD_FLUX_PORT=$((6443 + offset)) VITE_PORT=$((5173 + offset)) FORWARD_MAILPIT_PORT=$((1025 + offset * 100)) @@ -276,7 +276,7 @@ cmd_urls() { "$(grep -E '^APP_URL=' "$envf" | cut -d= -f2-)" \ "$(grep -E '^FORWARD_DB_PORT=' "$envf" | cut -d= -f2-)" \ "$(grep -E '^FORWARD_REDIS_PORT=' "$envf" | cut -d= -f2-)" \ - "$(grep -E '^FORWARD_SOKETI_PORT=' "$envf" | cut -d= -f2-)" + "$(grep -E '^FORWARD_PUSHER_PORT=' "$envf" | cut -d= -f2-)" done } diff --git a/scripts/install.sh b/scripts/install.sh index 226b1cc5e0..e2060b34d9 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -321,16 +321,11 @@ fi VERSIONS_JSON=$(curl -L --silent $CDN/versions.json) LATEST_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $2}' | tr -d ',') LATEST_HELPER_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $6}' | tr -d ',') -LATEST_REALTIME_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $8}' | tr -d ',') if [ -z "$LATEST_HELPER_VERSION" ]; then LATEST_HELPER_VERSION=latest fi -if [ -z "$LATEST_REALTIME_VERSION" ]; then - LATEST_REALTIME_VERSION=latest -fi - case "$OS_TYPE" in arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine | postmarketos | tencentos) ;; *) @@ -351,7 +346,6 @@ echo "| Operating System | $OS_TYPE $OS_VERSION" echo "| Docker | $DOCKER_VERSION" echo "| Coolify | $LATEST_VERSION" echo "| Helper | $LATEST_HELPER_VERSION" -echo "| Realtime | $LATEST_REALTIME_VERSION" echo "| Docker Pool | $DOCKER_ADDRESS_POOL_BASE (size $DOCKER_ADDRESS_POOL_SIZE)" echo "| Registry URL | $REGISTRY_URL" echo "---------------------------------------------" @@ -847,6 +841,7 @@ 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_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 5baac23acb..c3cdf00593 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -151,6 +151,7 @@ set_env_var "REGISTRY_URL" "$REGISTRY_URL" 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_BACKEND_PORT" "6001" log "Environment variables check complete" echo " Done." @@ -261,7 +262,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/PullChangelogTest.php b/tests/Feature/PullChangelogTest.php index 7793b0b775..faadfb2cee 100644 --- a/tests/Feature/PullChangelogTest.php +++ b/tests/Feature/PullChangelogTest.php @@ -32,9 +32,9 @@ afterEach(function () { File::delete(base_path('changelogs/1999-01.json')); }); -test('releases_url config defaults to the GitHub raw source', function () { +test('releases_url config defaults to the Coollabs CDN', function () { expect(config('constants.coolify.releases_url')) - ->toBe('https://cdn.coollabs.io/coolify/service-templates-latest.json'); + ->toBe('https://cdn.coollabs.io/coolify/releases.json'); }); test('PullChangelog fetches from the configured releases_url and writes the changelog', function () { diff --git a/tests/Feature/RealtimeTerminalPackagingTest.php b/tests/Feature/RealtimeTerminalPackagingTest.php index 4b400b49a6..d144ebd696 100644 --- a/tests/Feature/RealtimeTerminalPackagingTest.php +++ b/tests/Feature/RealtimeTerminalPackagingTest.php @@ -193,16 +193,19 @@ it('does not overlay the session expiry label on the application terminal', func ->not->toContain('terminal-session-expiry'); }); -it('copies the realtime terminal utilities into the container image', function () { - $dockerfile = file_get_contents(base_path('docker/coolify-realtime/Dockerfile')); +it('copies the terminal utilities into the Coolify container images', function (string $dockerfile) { + $dockerfile = file_get_contents(base_path($dockerfile)); - expect($dockerfile)->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', @@ -219,7 +222,7 @@ it('keeps terminal browser logging restricted to development or explicit diagnos }); 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') @@ -229,7 +232,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;') @@ -254,7 +257,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') @@ -304,7 +307,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..1461ec9707 --- /dev/null +++ b/tests/Feature/ReverbAndTerminalPackagingTest.php @@ -0,0 +1,173 @@ +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}') + ->not->toContain('PUSHER_ENABLED') + ->not->toContain('Reverb is disabled') + ->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], + 'multi-instance development compose' => ['docker-compose.dev-multi.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('keeps the public websocket port adaptive and configures only the internal Reverb port', function () { + expect(file_get_contents(base_path('.env.production'))) + ->not->toContain('PUSHER_PORT=') + ->not->toContain('PUSHER_BACKEND_PORT=') + ->and(file_get_contents(base_path('.env.windows-docker-desktop.example'))) + ->not->toContain('PUSHER_PORT=') + ->toContain('PUSHER_BACKEND_PORT=6001') + ->and(file_get_contents(base_path('scripts/install.sh'))) + ->not->toContain('update_env_var "PUSHER_PORT"') + ->toContain('update_env_var "PUSHER_BACKEND_PORT" "6001"') + ->and(file_get_contents(base_path('scripts/upgrade.sh'))) + ->not->toContain('update_env_var "PUSHER_PORT"') + ->toContain('update_env_var "PUSHER_BACKEND_PORT" "6001"'); +}); + +it('does not use the browser websocket port as the Docker host port', function (string $composeFile) { + expect(file_get_contents(base_path($composeFile))) + ->toContain('"${SOKETI_PORT:-6001}:6001"') + ->not->toContain('"${PUSHER_PORT:-6001}:6001"'); +})->with([ + 'production compose' => ['docker-compose.prod.yml'], + 'nightly production compose' => ['other/nightly/docker-compose.prod.yml'], + 'windows compose' => ['docker-compose.windows.yml'], + 'nightly windows compose' => ['other/nightly/docker-compose.windows.yml'], +]); + +it('preserves existing public websocket port overrides during install and upgrade', function (string $script) { + $contents = file_get_contents(base_path($script)); + + expect($contents) + ->not->toContain('normalize_pusher_port') + ->not->toMatch('/(?:set|update)_env_var "PUSHER_PORT"/'); +})->with([ + 'install script' => ['scripts/install.sh'], + 'upgrade script' => ['scripts/upgrade.sh'], + 'nightly install script' => ['other/nightly/install.sh'], + 'nightly upgrade script' => ['other/nightly/upgrade.sh'], +]); + +it('stops publishing or preserving the obsolete realtime image', function () { + $productionInstallScript = file_get_contents(base_path('scripts/install.sh')); + $nightlyInstallScript = file_get_contents(base_path('other/nightly/install.sh')); + + 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() + ->and($productionInstallScript)->not->toContain('LATEST_REALTIME_VERSION') + ->not->toContain('| Realtime') + ->and($nightlyInstallScript)->not->toContain('LATEST_REALTIME_VERSION') + ->not->toContain('| Realtime'); +}); diff --git a/tests/Pest.php b/tests/Pest.php index d143e9c125..acc38329e7 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -79,8 +79,7 @@ beforeEach(function () { // Flush the Server identity map cache to ensure tests get fresh data Server::flushIdentityMap(); - // Browser Livewire actions often dispatch events; the Soketi host is not - // resolvable from host-side Pest runs (docker DNS name coolify-realtime). + // Browser Livewire actions often dispatch events; disable broadcasting for host-side runs. config(['broadcasting.default' => 'null']); }); 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/tests/Unit/ProductionImageWorkflowTest.php b/tests/Unit/ProductionImageWorkflowTest.php index 2c29fe55be..9c6e210651 100644 --- a/tests/Unit/ProductionImageWorkflowTest.php +++ b/tests/Unit/ProductionImageWorkflowTest.php @@ -72,7 +72,6 @@ it('runs support image workflows from main', function (string $workflowFile) { ->not->toContain('v4.x'); })->with([ 'helper' => 'coolify-helper.yml', - 'realtime' => 'coolify-realtime.yml', ]); it('prevents the stable helper workflow from publishing an existing version', function () { @@ -90,23 +89,6 @@ it('prevents the stable helper workflow from publishing an existing version', fu ->toContain('cancel-in-progress: false'); }); -it('prevents the stable realtime workflow from publishing an existing version', function () { - $workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-realtime.yml'); - - expect($workflow) - ->toContain('workflow_dispatch:') - ->toContain('- config/constants.php') - ->toContain('check-version:') - ->toContain('needs: check-version') - ->toContain('php bootstrap/getRealtimeVersion.php') - ->toContain('VERSION="${BASE_VERSION}"') - ->toContain('docker buildx imagetools inspect "$IMAGE"') - ->toContain('Version $VERSION already exists in $registry') - ->toContain('Version $VERSION is available in both registries') - ->toContain('Could not verify $IMAGE') - ->toContain('cancel-in-progress: false'); -}); - it('generates the production changelog from main', function () { $workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/generate-changelog.yml'); diff --git a/versions.json b/versions.json index 6eb14d45e8..7262de886a 100644 --- a/versions.json +++ b/versions.json @@ -9,9 +9,6 @@ "helper": { "version": "1.0.17" }, - "realtime": { - "version": "1.0.20" - }, "sentinel": { "version": "1.0.1" }