Merge branch 'next' into main
This commit is contained in:
		@@ -23,3 +23,4 @@ yarn-error.log
 | 
			
		||||
.rnd
 | 
			
		||||
/.ssh
 | 
			
		||||
.ignition.json
 | 
			
		||||
.env.dusk.local
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								.env.dusk.ci
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.env.dusk.ci
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
APP_ENV=production
 | 
			
		||||
APP_NAME="Coolify Staging"
 | 
			
		||||
APP_ID=development
 | 
			
		||||
APP_KEY=
 | 
			
		||||
APP_URL=http://localhost
 | 
			
		||||
APP_PORT=8000
 | 
			
		||||
SSH_MUX_ENABLED=true
 | 
			
		||||
 | 
			
		||||
# PostgreSQL Database Configuration
 | 
			
		||||
DB_DATABASE=coolify
 | 
			
		||||
DB_USERNAME=coolify
 | 
			
		||||
DB_PASSWORD=password
 | 
			
		||||
DB_HOST=localhost
 | 
			
		||||
DB_PORT=5432
 | 
			
		||||
 | 
			
		||||
@@ -4,6 +4,7 @@ APP_ID=coolify-windows-docker-desktop
 | 
			
		||||
APP_NAME=Coolify
 | 
			
		||||
APP_KEY=base64:ssTlCmrIE/q7whnKMvT6DwURikg69COzGsAwFVROm80=
 | 
			
		||||
 | 
			
		||||
DB_USERNAME=coolify
 | 
			
		||||
DB_PASSWORD=coolify
 | 
			
		||||
REDIS_PASSWORD=coolify
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										65
									
								
								.github/workflows/browser-tests.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								.github/workflows/browser-tests.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
name: Dusk
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ "not-existing" ]
 | 
			
		||||
jobs:
 | 
			
		||||
  dusk:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
 | 
			
		||||
    services:
 | 
			
		||||
      redis:
 | 
			
		||||
        image: redis
 | 
			
		||||
        env:
 | 
			
		||||
          REDIS_HOST: localhost
 | 
			
		||||
          REDIS_PORT: 6379
 | 
			
		||||
        ports:
 | 
			
		||||
          - 6379:6379
 | 
			
		||||
        options: >-
 | 
			
		||||
          --health-cmd "redis-cli ping"
 | 
			
		||||
          --health-interval 10s
 | 
			
		||||
          --health-timeout 5s
 | 
			
		||||
          --health-retries 5
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up PostgreSQL
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo systemctl start postgresql
 | 
			
		||||
          sudo -u postgres psql -c "CREATE DATABASE coolify;"
 | 
			
		||||
          sudo -u postgres psql -c "CREATE USER coolify WITH PASSWORD 'password';"
 | 
			
		||||
          sudo -u postgres psql -c "ALTER ROLE coolify SET client_encoding TO 'utf8';"
 | 
			
		||||
          sudo -u postgres psql -c "ALTER ROLE coolify SET default_transaction_isolation TO 'read committed';"
 | 
			
		||||
          sudo -u postgres psql -c "ALTER ROLE coolify SET timezone TO 'UTC';"
 | 
			
		||||
          sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE coolify TO coolify;"
 | 
			
		||||
      - name: Setup PHP
 | 
			
		||||
        uses: shivammathur/setup-php@v2
 | 
			
		||||
        with:
 | 
			
		||||
          php-version: '8.2'
 | 
			
		||||
      - name: Copy .env
 | 
			
		||||
        run: cp .env.dusk.ci .env
 | 
			
		||||
      - name: Install Dependencies
 | 
			
		||||
        run: composer install --no-progress --prefer-dist --optimize-autoloader
 | 
			
		||||
      - name: Generate key
 | 
			
		||||
        run: php artisan key:generate
 | 
			
		||||
      - name: Install Chrome binaries
 | 
			
		||||
        run: php artisan dusk:chrome-driver --detect
 | 
			
		||||
      - name: Start Chrome Driver
 | 
			
		||||
        run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=4444 &
 | 
			
		||||
      - name: Build assets
 | 
			
		||||
        run: npm install && npm run build
 | 
			
		||||
      - name: Run Laravel Server
 | 
			
		||||
        run: php artisan serve --no-reload &
 | 
			
		||||
      - name: Execute tests
 | 
			
		||||
        run: php artisan dusk
 | 
			
		||||
      - name: Upload Screenshots
 | 
			
		||||
        if: failure()
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: screenshots
 | 
			
		||||
          path: tests/Browser/screenshots
 | 
			
		||||
      - name: Upload Console Logs
 | 
			
		||||
        if: failure()
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: console
 | 
			
		||||
          path: tests/Browser/console
 | 
			
		||||
							
								
								
									
										89
									
								
								.github/workflows/coolify-helper-next.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										89
									
								
								.github/workflows/coolify-helper-next.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
name: Coolify Helper Image Development (v4)
 | 
			
		||||
name: Coolify Helper Image Development
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
@@ -8,7 +8,8 @@ on:
 | 
			
		||||
      - docker/coolify-helper/Dockerfile
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify-helper"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -19,25 +20,36 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-helper/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
  aarch64:
 | 
			
		||||
@@ -47,27 +59,39 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-helper/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -75,25 +99,42 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [ amd64, aarch64 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ 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 \
 | 
			
		||||
          --append ${{ 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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								.github/workflows/coolify-helper.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										93
									
								
								.github/workflows/coolify-helper.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
name: Coolify Helper Image (v4)
 | 
			
		||||
name: Coolify Helper Image
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
@@ -8,7 +8,8 @@ on:
 | 
			
		||||
      - docker/coolify-helper/Dockerfile
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify-helper"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -19,25 +20,36 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-helper/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
  aarch64:
 | 
			
		||||
@@ -47,25 +59,36 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-helper/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
@@ -75,25 +98,43 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [ amd64, aarch64 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run  --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ 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 \
 | 
			
		||||
          --append ${{ 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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										139
									
								
								.github/workflows/coolify-production-build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								.github/workflows/coolify-production-build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
name: Production Build (v4)
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: ["main"]
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
      - .github/workflows/coolify-helper.yml
 | 
			
		||||
      - .github/workflows/coolify-helper-next.yml
 | 
			
		||||
      - .github/workflows/coolify-realtime.yml
 | 
			
		||||
      - .github/workflows/coolify-realtime-next.yml
 | 
			
		||||
      - docker/coolify-helper/Dockerfile
 | 
			
		||||
      - docker/coolify-realtime/Dockerfile
 | 
			
		||||
      - docker/testing-host/Dockerfile
 | 
			
		||||
      - templates/service-templates.json
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  amd64:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - 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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT]
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [self-hosted, arm64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - 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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT]
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [amd64, aarch64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - 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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ 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 \
 | 
			
		||||
          --append ${{ 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  }}
 | 
			
		||||
							
								
								
									
										95
									
								
								.github/workflows/coolify-realtime-next.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										95
									
								
								.github/workflows/coolify-realtime-next.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,17 +1,18 @@
 | 
			
		||||
name: Coolify Realtime Development (v4)
 | 
			
		||||
name: Coolify Realtime Development
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ "next" ]
 | 
			
		||||
    paths:
 | 
			
		||||
      - .github/workflows/coolify-realtime.yml
 | 
			
		||||
      - .github/workflows/coolify-realtime-next.yml
 | 
			
		||||
      - docker/coolify-realtime/Dockerfile
 | 
			
		||||
      - docker/coolify-realtime/terminal-server.js
 | 
			
		||||
      - docker/coolify-realtime/package.json
 | 
			
		||||
      - docker/coolify-realtime/soketi-entrypoint.sh
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify-realtime"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -22,27 +23,39 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-realtime/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [ self-hosted, arm64 ]
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -50,27 +63,39 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-realtime/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -78,26 +103,44 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [ amd64, aarch64 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ 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 \
 | 
			
		||||
          --append ${{ 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_PROD_RELEASE_CHANNEL  }}
 | 
			
		||||
          webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL  }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								.github/workflows/coolify-realtime.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										91
									
								
								.github/workflows/coolify-realtime.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
name: Coolify Realtime (v4)
 | 
			
		||||
name: Coolify Realtime
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
@@ -11,7 +11,8 @@ on:
 | 
			
		||||
      - docker/coolify-realtime/soketi-entrypoint.sh
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify-realtime"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -22,27 +23,39 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-realtime/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [ self-hosted, arm64 ]
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -50,27 +63,39 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/coolify-realtime/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -78,25 +103,43 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [ amd64, aarch64 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
           echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ 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 \
 | 
			
		||||
          --append ${{ 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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										125
									
								
								.github/workflows/coolify-staging-build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								.github/workflows/coolify-staging-build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
name: Staging Build
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches-ignore: ["main", "v3"]
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
        - .github/workflows/coolify-helper.yml
 | 
			
		||||
        - .github/workflows/coolify-helper-next.yml
 | 
			
		||||
        - .github/workflows/coolify-realtime.yml
 | 
			
		||||
        - .github/workflows/coolify-realtime-next.yml
 | 
			
		||||
        - docker/coolify-helper/Dockerfile
 | 
			
		||||
        - docker/coolify-realtime/Dockerfile
 | 
			
		||||
        - docker/testing-host/Dockerfile
 | 
			
		||||
        - templates/service-templates.json
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  amd64:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - 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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
 | 
			
		||||
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [self-hosted, arm64]
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - 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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
 | 
			
		||||
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [amd64, aarch64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - 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.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
         docker buildx imagetools create \
 | 
			
		||||
         --append ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64 \
 | 
			
		||||
         --tag ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64 \
 | 
			
		||||
          --tag ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
 | 
			
		||||
 | 
			
		||||
      - uses: sarisia/actions-status-discord@v1
 | 
			
		||||
        if: always()
 | 
			
		||||
        with:
 | 
			
		||||
          webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL  }}
 | 
			
		||||
							
								
								
									
										92
									
								
								.github/workflows/coolify-testing-host.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										92
									
								
								.github/workflows/coolify-testing-host.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,14 +1,15 @@
 | 
			
		||||
name: Coolify Testing Host (v4-non-prod)
 | 
			
		||||
name: Coolify Testing Host
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ "main", "next" ]
 | 
			
		||||
    branches: [ "next" ]
 | 
			
		||||
    paths:
 | 
			
		||||
      - .github/workflows/coolify-testing-host.yml
 | 
			
		||||
      - docker/testing-host/Dockerfile
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  GITHUB_REGISTRY: ghcr.io
 | 
			
		||||
  DOCKER_REGISTRY: docker.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify-testing-host"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -19,21 +20,34 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          registry: ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
          username: ${{ secrets.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/testing-host/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [ self-hosted, arm64 ]
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -41,21 +55,34 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          registry: ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
          username: ${{ secrets.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          no-cache: true
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/testing-host/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64
 | 
			
		||||
          tags: |
 | 
			
		||||
            ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64
 | 
			
		||||
            ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64
 | 
			
		||||
          labels: |
 | 
			
		||||
            coolify.managed=true
 | 
			
		||||
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
@@ -63,21 +90,36 @@ jobs:
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [ amd64, aarch64 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          registry: ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
 | 
			
		||||
      - name: Login to ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
          username: ${{ secrets.DOCKER_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 \
 | 
			
		||||
          --tag ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
 | 
			
		||||
      - name: Create & publish manifest on ${{ env.DOCKER_REGISTRY }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create \
 | 
			
		||||
          --append ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 \
 | 
			
		||||
          --tag ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
 | 
			
		||||
      - uses: sarisia/actions-status-discord@v1
 | 
			
		||||
        if: always()
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								.github/workflows/development-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								.github/workflows/development-build.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,79 +0,0 @@
 | 
			
		||||
name: Development Build (v4)
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches-ignore: ["main", "v3"]
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
      - .github/workflows/coolify-helper.yml
 | 
			
		||||
      - docker/coolify-helper/Dockerfile
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  amd64:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [self-hosted, arm64]
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [amd64, aarch64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
 | 
			
		||||
      - uses: sarisia/actions-status-discord@v1
 | 
			
		||||
        if: always()
 | 
			
		||||
        with:
 | 
			
		||||
          webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL  }}
 | 
			
		||||
							
								
								
									
										89
									
								
								.github/workflows/production-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										89
									
								
								.github/workflows/production-build.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,89 +0,0 @@
 | 
			
		||||
name: Production Build (v4)
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: ["main"]
 | 
			
		||||
    paths-ignore:
 | 
			
		||||
      - .github/workflows/coolify-helper.yml
 | 
			
		||||
      - docker/coolify-helper/Dockerfile
 | 
			
		||||
      - templates/service-templates.json
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  REGISTRY: ghcr.io
 | 
			
		||||
  IMAGE_NAME: "coollabsio/coolify"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  amd64:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
 | 
			
		||||
  aarch64:
 | 
			
		||||
    runs-on: [self-hosted, arm64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Build image and push to registry
 | 
			
		||||
        uses: docker/build-push-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: docker/prod/Dockerfile
 | 
			
		||||
          platforms: linux/aarch64
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
 | 
			
		||||
  merge-manifest:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: read
 | 
			
		||||
      packages: write
 | 
			
		||||
    needs: [amd64, aarch64]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
      - name: Login to ghcr.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ${{ env.REGISTRY }}
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN  }}
 | 
			
		||||
      - name: Get Version
 | 
			
		||||
        id: version
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
 | 
			
		||||
      - name: Create & publish manifest
 | 
			
		||||
        run: |
 | 
			
		||||
          docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 | 
			
		||||
      - uses: sarisia/actions-status-discord@v1
 | 
			
		||||
        if: always()
 | 
			
		||||
        with:
 | 
			
		||||
          webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL  }}
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -33,3 +33,4 @@ _ide_helper_models.php
 | 
			
		||||
/.ssh
 | 
			
		||||
scripts/load-test/*
 | 
			
		||||
.ignition.json
 | 
			
		||||
.env.dusk.local
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ class GenerateConfig
 | 
			
		||||
 | 
			
		||||
    public function handle(Application $application, bool $is_json = false)
 | 
			
		||||
    {
 | 
			
		||||
        ray()->clearAll();
 | 
			
		||||
        return $application->generateConfig(is_json: $is_json);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ class StopApplication
 | 
			
		||||
            if (! $server->isFunctional()) {
 | 
			
		||||
                return 'Server is not functional';
 | 
			
		||||
            }
 | 
			
		||||
            ray('Stopping application: '.$application->name);
 | 
			
		||||
 | 
			
		||||
            if ($server->isSwarm()) {
 | 
			
		||||
                instant_remote_process(["docker stack rm {$application->uuid}"], $server);
 | 
			
		||||
@@ -36,8 +35,6 @@ class StopApplication
 | 
			
		||||
                CleanupDocker::dispatch($server, true);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return $e->getMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,6 @@ class StopApplicationOneServer
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return $e->getMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,6 @@ class PrepareCoolifyTask
 | 
			
		||||
            call_event_data: $this->remoteProcessArgs->call_event_data,
 | 
			
		||||
        );
 | 
			
		||||
        if ($this->remoteProcessArgs->type === ActivityTypes::COMMAND->value) {
 | 
			
		||||
            ray('Dispatching a high priority job');
 | 
			
		||||
            dispatch($job)->onQueue('high');
 | 
			
		||||
        } else {
 | 
			
		||||
            dispatch($job);
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,6 @@ class RunRemoteProcess
 | 
			
		||||
                    ]));
 | 
			
		||||
                }
 | 
			
		||||
            } catch (\Throwable $e) {
 | 
			
		||||
                ray($e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,28 +23,28 @@ class StartDatabase
 | 
			
		||||
            return 'Server is not functional';
 | 
			
		||||
        }
 | 
			
		||||
        switch ($database->getMorphClass()) {
 | 
			
		||||
            case 'App\Models\StandalonePostgresql':
 | 
			
		||||
            case \App\Models\StandalonePostgresql::class:
 | 
			
		||||
                $activity = StartPostgresql::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneRedis':
 | 
			
		||||
            case \App\Models\StandaloneRedis::class:
 | 
			
		||||
                $activity = StartRedis::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneMongodb':
 | 
			
		||||
            case \App\Models\StandaloneMongodb::class:
 | 
			
		||||
                $activity = StartMongodb::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneMysql':
 | 
			
		||||
            case \App\Models\StandaloneMysql::class:
 | 
			
		||||
                $activity = StartMysql::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneMariadb':
 | 
			
		||||
            case \App\Models\StandaloneMariadb::class:
 | 
			
		||||
                $activity = StartMariadb::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneKeydb':
 | 
			
		||||
            case \App\Models\StandaloneKeydb::class:
 | 
			
		||||
                $activity = StartKeydb::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneDragonfly':
 | 
			
		||||
            case \App\Models\StandaloneDragonfly::class:
 | 
			
		||||
                $activity = StartDragonfly::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\StandaloneClickhouse':
 | 
			
		||||
            case \App\Models\StandaloneClickhouse::class:
 | 
			
		||||
                $activity = StartClickhouse::run($database);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ class StartDatabaseProxy
 | 
			
		||||
        $server = data_get($database, 'destination.server');
 | 
			
		||||
        $containerName = data_get($database, 'uuid');
 | 
			
		||||
        $proxyContainerName = "{$database->uuid}-proxy";
 | 
			
		||||
        if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
 | 
			
		||||
        if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) {
 | 
			
		||||
            $databaseType = $database->databaseType();
 | 
			
		||||
            // $connectPredefined = data_get($database, 'service.connect_to_docker_network');
 | 
			
		||||
            $network = $database->service->uuid;
 | 
			
		||||
@@ -34,54 +34,54 @@ class StartDatabaseProxy
 | 
			
		||||
            $proxyContainerName = "{$database->service->uuid}-proxy";
 | 
			
		||||
            switch ($databaseType) {
 | 
			
		||||
                case 'standalone-mariadb':
 | 
			
		||||
                    $type = 'App\Models\StandaloneMariadb';
 | 
			
		||||
                    $type = \App\Models\StandaloneMariadb::class;
 | 
			
		||||
                    $containerName = "mariadb-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-mongodb':
 | 
			
		||||
                    $type = 'App\Models\StandaloneMongodb';
 | 
			
		||||
                    $type = \App\Models\StandaloneMongodb::class;
 | 
			
		||||
                    $containerName = "mongodb-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-mysql':
 | 
			
		||||
                    $type = 'App\Models\StandaloneMysql';
 | 
			
		||||
                    $type = \App\Models\StandaloneMysql::class;
 | 
			
		||||
                    $containerName = "mysql-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-postgresql':
 | 
			
		||||
                    $type = 'App\Models\StandalonePostgresql';
 | 
			
		||||
                    $type = \App\Models\StandalonePostgresql::class;
 | 
			
		||||
                    $containerName = "postgresql-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-redis':
 | 
			
		||||
                    $type = 'App\Models\StandaloneRedis';
 | 
			
		||||
                    $type = \App\Models\StandaloneRedis::class;
 | 
			
		||||
                    $containerName = "redis-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-keydb':
 | 
			
		||||
                    $type = 'App\Models\StandaloneKeydb';
 | 
			
		||||
                    $type = \App\Models\StandaloneKeydb::class;
 | 
			
		||||
                    $containerName = "keydb-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-dragonfly':
 | 
			
		||||
                    $type = 'App\Models\StandaloneDragonfly';
 | 
			
		||||
                    $type = \App\Models\StandaloneDragonfly::class;
 | 
			
		||||
                    $containerName = "dragonfly-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'standalone-clickhouse':
 | 
			
		||||
                    $type = 'App\Models\StandaloneClickhouse';
 | 
			
		||||
                    $type = \App\Models\StandaloneClickhouse::class;
 | 
			
		||||
                    $containerName = "clickhouse-{$database->service->uuid}";
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ($type === 'App\Models\StandaloneRedis') {
 | 
			
		||||
        if ($type === \App\Models\StandaloneRedis::class) {
 | 
			
		||||
            $internalPort = 6379;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandalonePostgresql') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandalonePostgresql::class) {
 | 
			
		||||
            $internalPort = 5432;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandaloneMongodb') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandaloneMongodb::class) {
 | 
			
		||||
            $internalPort = 27017;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandaloneMysql') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandaloneMysql::class) {
 | 
			
		||||
            $internalPort = 3306;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandaloneMariadb') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandaloneMariadb::class) {
 | 
			
		||||
            $internalPort = 3306;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandaloneKeydb') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandaloneKeydb::class) {
 | 
			
		||||
            $internalPort = 6379;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandaloneDragonfly') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandaloneDragonfly::class) {
 | 
			
		||||
            $internalPort = 6379;
 | 
			
		||||
        } elseif ($type === 'App\Models\StandaloneClickhouse') {
 | 
			
		||||
        } elseif ($type === \App\Models\StandaloneClickhouse::class) {
 | 
			
		||||
            $internalPort = 9000;
 | 
			
		||||
        }
 | 
			
		||||
        $configuration_dir = database_proxy_dir($database->uuid);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,6 @@ class StartRedis
 | 
			
		||||
    {
 | 
			
		||||
        $this->database = $database;
 | 
			
		||||
 | 
			
		||||
        $startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
 | 
			
		||||
 | 
			
		||||
        $container_name = $this->database->uuid;
 | 
			
		||||
        $this->configuration_dir = database_configuration_dir().'/'.$container_name;
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +35,8 @@ class StartRedis
 | 
			
		||||
        $environment_variables = $this->generate_environment_variables();
 | 
			
		||||
        $this->add_custom_redis();
 | 
			
		||||
 | 
			
		||||
        $startCommand = $this->buildStartCommand();
 | 
			
		||||
 | 
			
		||||
        $docker_compose = [
 | 
			
		||||
            'services' => [
 | 
			
		||||
                $container_name => [
 | 
			
		||||
@@ -105,7 +105,6 @@ class StartRedis
 | 
			
		||||
                'target' => '/usr/local/etc/redis/redis.conf',
 | 
			
		||||
                'read_only' => true,
 | 
			
		||||
            ];
 | 
			
		||||
            $docker_compose['services'][$container_name]['command'] = "redis-server /usr/local/etc/redis/redis.conf --requirepass {$this->database->redis_password} --appendonly yes";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add custom docker run options
 | 
			
		||||
@@ -160,12 +159,26 @@ class StartRedis
 | 
			
		||||
    private function generate_environment_variables()
 | 
			
		||||
    {
 | 
			
		||||
        $environment_variables = collect();
 | 
			
		||||
        foreach ($this->database->runtime_environment_variables as $env) {
 | 
			
		||||
            $environment_variables->push("$env->key=$env->real_value");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
 | 
			
		||||
            $environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
 | 
			
		||||
        foreach ($this->database->runtime_environment_variables as $env) {
 | 
			
		||||
            if ($env->is_shared) {
 | 
			
		||||
                $environment_variables->push("$env->key=$env->real_value");
 | 
			
		||||
 | 
			
		||||
                if ($env->key === 'REDIS_PASSWORD') {
 | 
			
		||||
                    $this->database->update(['redis_password' => $env->real_value]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ($env->key === 'REDIS_USERNAME') {
 | 
			
		||||
                    $this->database->update(['redis_username' => $env->real_value]);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if ($env->key === 'REDIS_PASSWORD') {
 | 
			
		||||
                    $env->update(['value' => $this->database->redis_password]);
 | 
			
		||||
                } elseif ($env->key === 'REDIS_USERNAME') {
 | 
			
		||||
                    $env->update(['value' => $this->database->redis_username]);
 | 
			
		||||
                }
 | 
			
		||||
                $environment_variables->push("$env->key=$env->real_value");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        add_coolify_default_environment_variables($this->database, $environment_variables, $environment_variables);
 | 
			
		||||
@@ -173,6 +186,27 @@ class StartRedis
 | 
			
		||||
        return $environment_variables->all();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function buildStartCommand(): string
 | 
			
		||||
    {
 | 
			
		||||
        $hasRedisConf = ! is_null($this->database->redis_conf) && ! empty($this->database->redis_conf);
 | 
			
		||||
        $redisConfPath = '/usr/local/etc/redis/redis.conf';
 | 
			
		||||
 | 
			
		||||
        if ($hasRedisConf) {
 | 
			
		||||
            $confContent = $this->database->redis_conf;
 | 
			
		||||
            $hasRequirePass = str_contains($confContent, 'requirepass');
 | 
			
		||||
 | 
			
		||||
            if ($hasRequirePass) {
 | 
			
		||||
                $command = "redis-server $redisConfPath";
 | 
			
		||||
            } else {
 | 
			
		||||
                $command = "redis-server $redisConfPath --requirepass {$this->database->redis_password}";
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $command = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $command;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function add_custom_redis()
 | 
			
		||||
    {
 | 
			
		||||
        if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ class StopDatabaseProxy
 | 
			
		||||
    {
 | 
			
		||||
        $server = data_get($database, 'destination.server');
 | 
			
		||||
        $uuid = $database->uuid;
 | 
			
		||||
        if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
 | 
			
		||||
        if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) {
 | 
			
		||||
            $uuid = $database->service->uuid;
 | 
			
		||||
            $server = data_get($database, 'service.server');
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
namespace App\Actions\Docker;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Database\StartDatabaseProxy;
 | 
			
		||||
use App\Actions\Proxy\CheckProxy;
 | 
			
		||||
use App\Actions\Proxy\StartProxy;
 | 
			
		||||
use App\Actions\Shared\ComplexStatusCheck;
 | 
			
		||||
use App\Models\ApplicationPreview;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
@@ -49,323 +47,8 @@ class GetContainersStatus
 | 
			
		||||
        $this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) {
 | 
			
		||||
            return ! $skip_these_applications->pluck('id')->contains($value->id);
 | 
			
		||||
        });
 | 
			
		||||
        $this->old_way();
 | 
			
		||||
        // if ($this->server->isSwarm()) {
 | 
			
		||||
        //     $this->old_way();
 | 
			
		||||
        // } else {
 | 
			
		||||
        //     if (!$this->server->is_metrics_enabled) {
 | 
			
		||||
        //         $this->old_way();
 | 
			
		||||
        //         return;
 | 
			
		||||
        //     }
 | 
			
		||||
        //     $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false);
 | 
			
		||||
        //     $sentinel_found = json_decode($sentinel_found, true);
 | 
			
		||||
        //     $status = data_get($sentinel_found, '0.State.Status', 'exited');
 | 
			
		||||
        //     if ($status === 'running') {
 | 
			
		||||
        //         ray('Checking with Sentinel');
 | 
			
		||||
        //         $this->sentinel();
 | 
			
		||||
        //     } else {
 | 
			
		||||
        //         ray('Checking the Old way');
 | 
			
		||||
        //         $this->old_way();
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // private function sentinel()
 | 
			
		||||
    // {
 | 
			
		||||
    //     try {
 | 
			
		||||
    //         $this->containers = $this->server->getContainersWithSentinel();
 | 
			
		||||
    //         if ($this->containers->count() === 0) {
 | 
			
		||||
    //             return;
 | 
			
		||||
    //         }
 | 
			
		||||
    //         $databases = $this->server->databases();
 | 
			
		||||
    //         $services = $this->server->services()->get();
 | 
			
		||||
    //         $previews = $this->server->previews();
 | 
			
		||||
    //         $foundApplications = [];
 | 
			
		||||
    //         $foundApplicationPreviews = [];
 | 
			
		||||
    //         $foundDatabases = [];
 | 
			
		||||
    //         $foundServices = [];
 | 
			
		||||
 | 
			
		||||
    //         foreach ($this->containers as $container) {
 | 
			
		||||
    //             $labels = Arr::undot(data_get($container, 'labels'));
 | 
			
		||||
    //             $containerStatus = data_get($container, 'state');
 | 
			
		||||
    //             $containerHealth = data_get($container, 'health_status', 'unhealthy');
 | 
			
		||||
    //             $containerStatus = "$containerStatus ($containerHealth)";
 | 
			
		||||
    //             $applicationId = data_get($labels, 'coolify.applicationId');
 | 
			
		||||
    //             if ($applicationId) {
 | 
			
		||||
    //                 $pullRequestId = data_get($labels, 'coolify.pullRequestId');
 | 
			
		||||
    //                 if ($pullRequestId) {
 | 
			
		||||
    //                     if (str($applicationId)->contains('-')) {
 | 
			
		||||
    //                         $applicationId = str($applicationId)->before('-');
 | 
			
		||||
    //                     }
 | 
			
		||||
    //                     $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
 | 
			
		||||
    //                     if ($preview) {
 | 
			
		||||
    //                         $foundApplicationPreviews[] = $preview->id;
 | 
			
		||||
    //                         $statusFromDb = $preview->status;
 | 
			
		||||
    //                         if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
    //                             $preview->update(['status' => $containerStatus]);
 | 
			
		||||
    //                         }
 | 
			
		||||
    //                     } else {
 | 
			
		||||
    //                         //Notify user that this container should not be there.
 | 
			
		||||
    //                     }
 | 
			
		||||
    //                 } else {
 | 
			
		||||
    //                     $application = $this->applications->where('id', $applicationId)->first();
 | 
			
		||||
    //                     if ($application) {
 | 
			
		||||
    //                         $foundApplications[] = $application->id;
 | 
			
		||||
    //                         $statusFromDb = $application->status;
 | 
			
		||||
    //                         if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
    //                             $application->update(['status' => $containerStatus]);
 | 
			
		||||
    //                         }
 | 
			
		||||
    //                     } else {
 | 
			
		||||
    //                         //Notify user that this container should not be there.
 | 
			
		||||
    //                     }
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 $uuid = data_get($labels, 'com.docker.compose.service');
 | 
			
		||||
    //                 $type = data_get($labels, 'coolify.type');
 | 
			
		||||
    //                 if ($uuid) {
 | 
			
		||||
    //                     if ($type === 'service') {
 | 
			
		||||
    //                         $database_id = data_get($labels, 'coolify.service.subId');
 | 
			
		||||
    //                         if ($database_id) {
 | 
			
		||||
    //                             $service_db = ServiceDatabase::where('id', $database_id)->first();
 | 
			
		||||
    //                             if ($service_db) {
 | 
			
		||||
    //                                 $uuid = $service_db->service->uuid;
 | 
			
		||||
    //                                 $isPublic = data_get($service_db, 'is_public');
 | 
			
		||||
    //                                 if ($isPublic) {
 | 
			
		||||
    //                                     $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
 | 
			
		||||
    //                                         if ($this->server->isSwarm()) {
 | 
			
		||||
    //                                             // TODO: fix this with sentinel
 | 
			
		||||
    //                                             return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
			
		||||
    //                                         } else {
 | 
			
		||||
    //                                             return data_get($value, 'name') === "$uuid-proxy";
 | 
			
		||||
    //                                         }
 | 
			
		||||
    //                                     })->first();
 | 
			
		||||
    //                                     if (! $foundTcpProxy) {
 | 
			
		||||
    //                                         StartDatabaseProxy::run($service_db);
 | 
			
		||||
    //                                         // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
 | 
			
		||||
    //                                     }
 | 
			
		||||
    //                                 }
 | 
			
		||||
    //                             }
 | 
			
		||||
    //                         }
 | 
			
		||||
    //                     } else {
 | 
			
		||||
    //                         $database = $databases->where('uuid', $uuid)->first();
 | 
			
		||||
    //                         if ($database) {
 | 
			
		||||
    //                             $isPublic = data_get($database, 'is_public');
 | 
			
		||||
    //                             $foundDatabases[] = $database->id;
 | 
			
		||||
    //                             $statusFromDb = $database->status;
 | 
			
		||||
    //                             if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
    //                                 $database->update(['status' => $containerStatus]);
 | 
			
		||||
    //                             }
 | 
			
		||||
    //                             if ($isPublic) {
 | 
			
		||||
    //                                 $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
 | 
			
		||||
    //                                     if ($this->server->isSwarm()) {
 | 
			
		||||
    //                                         // TODO: fix this with sentinel
 | 
			
		||||
    //                                         return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
			
		||||
    //                                     } else {
 | 
			
		||||
    //                                         return data_get($value, 'name') === "$uuid-proxy";
 | 
			
		||||
    //                                     }
 | 
			
		||||
    //                                 })->first();
 | 
			
		||||
    //                                 if (! $foundTcpProxy) {
 | 
			
		||||
    //                                     StartDatabaseProxy::run($database);
 | 
			
		||||
    //                                     $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
 | 
			
		||||
    //                                 }
 | 
			
		||||
    //                             }
 | 
			
		||||
    //                         } else {
 | 
			
		||||
    //                             // Notify user that this container should not be there.
 | 
			
		||||
    //                         }
 | 
			
		||||
    //                     }
 | 
			
		||||
    //                 }
 | 
			
		||||
    //                 if (data_get($container, 'name') === 'coolify-db') {
 | 
			
		||||
    //                     $foundDatabases[] = 0;
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             }
 | 
			
		||||
    //             $serviceLabelId = data_get($labels, 'coolify.serviceId');
 | 
			
		||||
    //             if ($serviceLabelId) {
 | 
			
		||||
    //                 $subType = data_get($labels, 'coolify.service.subType');
 | 
			
		||||
    //                 $subId = data_get($labels, 'coolify.service.subId');
 | 
			
		||||
    //                 $service = $services->where('id', $serviceLabelId)->first();
 | 
			
		||||
    //                 if (! $service) {
 | 
			
		||||
    //                     continue;
 | 
			
		||||
    //                 }
 | 
			
		||||
    //                 if ($subType === 'application') {
 | 
			
		||||
    //                     $service = $service->applications()->where('id', $subId)->first();
 | 
			
		||||
    //                 } else {
 | 
			
		||||
    //                     $service = $service->databases()->where('id', $subId)->first();
 | 
			
		||||
    //                 }
 | 
			
		||||
    //                 if ($service) {
 | 
			
		||||
    //                     $foundServices[] = "$service->id-$service->name";
 | 
			
		||||
    //                     $statusFromDb = $service->status;
 | 
			
		||||
    //                     if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
    //                         // ray('Updating status: ' . $containerStatus);
 | 
			
		||||
    //                         $service->update(['status' => $containerStatus]);
 | 
			
		||||
    //                     }
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             }
 | 
			
		||||
    //         }
 | 
			
		||||
    //         $exitedServices = collect([]);
 | 
			
		||||
    //         foreach ($services as $service) {
 | 
			
		||||
    //             $apps = $service->applications()->get();
 | 
			
		||||
    //             $dbs = $service->databases()->get();
 | 
			
		||||
    //             foreach ($apps as $app) {
 | 
			
		||||
    //                 if (in_array("$app->id-$app->name", $foundServices)) {
 | 
			
		||||
    //                     continue;
 | 
			
		||||
    //                 } else {
 | 
			
		||||
    //                     $exitedServices->push($app);
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             }
 | 
			
		||||
    //             foreach ($dbs as $db) {
 | 
			
		||||
    //                 if (in_array("$db->id-$db->name", $foundServices)) {
 | 
			
		||||
    //                     continue;
 | 
			
		||||
    //                 } else {
 | 
			
		||||
    //                     $exitedServices->push($db);
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             }
 | 
			
		||||
    //         }
 | 
			
		||||
    //         $exitedServices = $exitedServices->unique('id');
 | 
			
		||||
    //         foreach ($exitedServices as $exitedService) {
 | 
			
		||||
    //             if (str($exitedService->status)->startsWith('exited')) {
 | 
			
		||||
    //                 continue;
 | 
			
		||||
    //             }
 | 
			
		||||
    //             $name = data_get($exitedService, 'name');
 | 
			
		||||
    //             $fqdn = data_get($exitedService, 'fqdn');
 | 
			
		||||
    //             if ($name) {
 | 
			
		||||
    //                 if ($fqdn) {
 | 
			
		||||
    //                     $containerName = "$name, available at $fqdn";
 | 
			
		||||
    //                 } else {
 | 
			
		||||
    //                     $containerName = $name;
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 if ($fqdn) {
 | 
			
		||||
    //                     $containerName = $fqdn;
 | 
			
		||||
    //                 } else {
 | 
			
		||||
    //                     $containerName = null;
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             }
 | 
			
		||||
    //             $projectUuid = data_get($service, 'environment.project.uuid');
 | 
			
		||||
    //             $serviceUuid = data_get($service, 'uuid');
 | 
			
		||||
    //             $environmentName = data_get($service, 'environment.name');
 | 
			
		||||
 | 
			
		||||
    //             if ($projectUuid && $serviceUuid && $environmentName) {
 | 
			
		||||
    //                 $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 $url = null;
 | 
			
		||||
    //             }
 | 
			
		||||
    //             // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
    //             $exitedService->update(['status' => 'exited']);
 | 
			
		||||
    //         }
 | 
			
		||||
 | 
			
		||||
    //         $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
 | 
			
		||||
    //         foreach ($notRunningApplications as $applicationId) {
 | 
			
		||||
    //             $application = $this->applications->where('id', $applicationId)->first();
 | 
			
		||||
    //             if (str($application->status)->startsWith('exited')) {
 | 
			
		||||
    //                 continue;
 | 
			
		||||
    //             }
 | 
			
		||||
    //             $application->update(['status' => 'exited']);
 | 
			
		||||
 | 
			
		||||
    //             $name = data_get($application, 'name');
 | 
			
		||||
    //             $fqdn = data_get($application, 'fqdn');
 | 
			
		||||
 | 
			
		||||
    //             $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
			
		||||
 | 
			
		||||
    //             $projectUuid = data_get($application, 'environment.project.uuid');
 | 
			
		||||
    //             $applicationUuid = data_get($application, 'uuid');
 | 
			
		||||
    //             $environment = data_get($application, 'environment.name');
 | 
			
		||||
 | 
			
		||||
    //             if ($projectUuid && $applicationUuid && $environment) {
 | 
			
		||||
    //                 $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 $url = null;
 | 
			
		||||
    //             }
 | 
			
		||||
 | 
			
		||||
    //             // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
    //         }
 | 
			
		||||
    //         $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
 | 
			
		||||
    //         foreach ($notRunningApplicationPreviews as $previewId) {
 | 
			
		||||
    //             $preview = $previews->where('id', $previewId)->first();
 | 
			
		||||
    //             if (str($preview->status)->startsWith('exited')) {
 | 
			
		||||
    //                 continue;
 | 
			
		||||
    //             }
 | 
			
		||||
    //             $preview->update(['status' => 'exited']);
 | 
			
		||||
 | 
			
		||||
    //             $name = data_get($preview, 'name');
 | 
			
		||||
    //             $fqdn = data_get($preview, 'fqdn');
 | 
			
		||||
 | 
			
		||||
    //             $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
			
		||||
 | 
			
		||||
    //             $projectUuid = data_get($preview, 'application.environment.project.uuid');
 | 
			
		||||
    //             $environmentName = data_get($preview, 'application.environment.name');
 | 
			
		||||
    //             $applicationUuid = data_get($preview, 'application.uuid');
 | 
			
		||||
 | 
			
		||||
    //             if ($projectUuid && $applicationUuid && $environmentName) {
 | 
			
		||||
    //                 $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 $url = null;
 | 
			
		||||
    //             }
 | 
			
		||||
 | 
			
		||||
    //             // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
    //         }
 | 
			
		||||
    //         $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
 | 
			
		||||
    //         foreach ($notRunningDatabases as $database) {
 | 
			
		||||
    //             $database = $databases->where('id', $database)->first();
 | 
			
		||||
    //             if (str($database->status)->startsWith('exited')) {
 | 
			
		||||
    //                 continue;
 | 
			
		||||
    //             }
 | 
			
		||||
    //             $database->update(['status' => 'exited']);
 | 
			
		||||
 | 
			
		||||
    //             $name = data_get($database, 'name');
 | 
			
		||||
    //             $fqdn = data_get($database, 'fqdn');
 | 
			
		||||
 | 
			
		||||
    //             $containerName = $name;
 | 
			
		||||
 | 
			
		||||
    //             $projectUuid = data_get($database, 'environment.project.uuid');
 | 
			
		||||
    //             $environmentName = data_get($database, 'environment.name');
 | 
			
		||||
    //             $databaseUuid = data_get($database, 'uuid');
 | 
			
		||||
 | 
			
		||||
    //             if ($projectUuid && $databaseUuid && $environmentName) {
 | 
			
		||||
    //                 $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 $url = null;
 | 
			
		||||
    //             }
 | 
			
		||||
    //             // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
    //         }
 | 
			
		||||
 | 
			
		||||
    //         // Check if proxy is running
 | 
			
		||||
    //         $this->server->proxyType();
 | 
			
		||||
    //         $foundProxyContainer = $this->containers->filter(function ($value, $key) {
 | 
			
		||||
    //             if ($this->server->isSwarm()) {
 | 
			
		||||
    //                 // TODO: fix this with sentinel
 | 
			
		||||
    //                 return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 return data_get($value, 'name') === 'coolify-proxy';
 | 
			
		||||
    //             }
 | 
			
		||||
    //         })->first();
 | 
			
		||||
    //         if (! $foundProxyContainer) {
 | 
			
		||||
    //             try {
 | 
			
		||||
    //                 $shouldStart = CheckProxy::run($this->server);
 | 
			
		||||
    //                 if ($shouldStart) {
 | 
			
		||||
    //                     StartProxy::run($this->server, false);
 | 
			
		||||
    //                     $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
			
		||||
    //                 }
 | 
			
		||||
    //             } catch (\Throwable $e) {
 | 
			
		||||
    //                 ray($e);
 | 
			
		||||
    //             }
 | 
			
		||||
    //         } else {
 | 
			
		||||
    //             $this->server->proxy->status = data_get($foundProxyContainer, 'state');
 | 
			
		||||
    //             $this->server->save();
 | 
			
		||||
    //             $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
			
		||||
    //             instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
			
		||||
    //         }
 | 
			
		||||
    //     } catch (\Exception $e) {
 | 
			
		||||
    //         // send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
 | 
			
		||||
    //         ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
    //         return handleError($e);
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    private function old_way()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->containers === null) {
 | 
			
		||||
            ['containers' => $this->containers,'containerReplicates' => $this->containerReplicates] = $this->server->getContainers();
 | 
			
		||||
            ['containers' => $this->containers, 'containerReplicates' => $this->containerReplicates] = $this->server->getContainers();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (is_null($this->containers)) {
 | 
			
		||||
@@ -650,32 +333,5 @@ class GetContainersStatus
 | 
			
		||||
            }
 | 
			
		||||
            // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! $this->server->proxySet() || $this->server->proxy->force_stop) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $foundProxyContainer = $this->containers->filter(function ($value, $key) {
 | 
			
		||||
            if ($this->server->isSwarm()) {
 | 
			
		||||
                return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
			
		||||
            } else {
 | 
			
		||||
                return data_get($value, 'Name') === '/coolify-proxy';
 | 
			
		||||
            }
 | 
			
		||||
        })->first();
 | 
			
		||||
        if (! $foundProxyContainer) {
 | 
			
		||||
            try {
 | 
			
		||||
                $shouldStart = CheckProxy::run($this->server);
 | 
			
		||||
                if ($shouldStart) {
 | 
			
		||||
                    StartProxy::run($this->server, false);
 | 
			
		||||
                    $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
			
		||||
                }
 | 
			
		||||
            } catch (\Throwable $e) {
 | 
			
		||||
                ray($e);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
 | 
			
		||||
            $this->server->save();
 | 
			
		||||
            $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
			
		||||
            instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,11 @@ use App\Models\User;
 | 
			
		||||
use Illuminate\Support\Facades\Hash;
 | 
			
		||||
use Illuminate\Support\Facades\Validator;
 | 
			
		||||
use Illuminate\Validation\Rule;
 | 
			
		||||
use Illuminate\Validation\Rules\Password;
 | 
			
		||||
use Laravel\Fortify\Contracts\CreatesNewUsers;
 | 
			
		||||
 | 
			
		||||
class CreateNewUser implements CreatesNewUsers
 | 
			
		||||
{
 | 
			
		||||
    use PasswordValidationRules;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate and create a newly registered user.
 | 
			
		||||
     *
 | 
			
		||||
@@ -32,7 +31,7 @@ class CreateNewUser implements CreatesNewUsers
 | 
			
		||||
                'max:255',
 | 
			
		||||
                Rule::unique(User::class),
 | 
			
		||||
            ],
 | 
			
		||||
            'password' => $this->passwordRules(),
 | 
			
		||||
            'password' => ['required', Password::defaults(), 'confirmed'],
 | 
			
		||||
        ])->validate();
 | 
			
		||||
 | 
			
		||||
        if (User::count() == 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Actions\Fortify;
 | 
			
		||||
 | 
			
		||||
use Laravel\Fortify\Rules\Password;
 | 
			
		||||
 | 
			
		||||
trait PasswordValidationRules
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the validation rules used to validate passwords.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array<int, \Illuminate\Contracts\Validation\Rule|array|string>
 | 
			
		||||
     */
 | 
			
		||||
    protected function passwordRules(): array
 | 
			
		||||
    {
 | 
			
		||||
        return ['required', 'string', new Password, 'confirmed'];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,12 +5,11 @@ namespace App\Actions\Fortify;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use Illuminate\Support\Facades\Hash;
 | 
			
		||||
use Illuminate\Support\Facades\Validator;
 | 
			
		||||
use Illuminate\Validation\Rules\Password;
 | 
			
		||||
use Laravel\Fortify\Contracts\ResetsUserPasswords;
 | 
			
		||||
 | 
			
		||||
class ResetUserPassword implements ResetsUserPasswords
 | 
			
		||||
{
 | 
			
		||||
    use PasswordValidationRules;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate and reset the user's forgotten password.
 | 
			
		||||
     *
 | 
			
		||||
@@ -19,7 +18,7 @@ class ResetUserPassword implements ResetsUserPasswords
 | 
			
		||||
    public function reset(User $user, array $input): void
 | 
			
		||||
    {
 | 
			
		||||
        Validator::make($input, [
 | 
			
		||||
            'password' => $this->passwordRules(),
 | 
			
		||||
            'password' => ['required', Password::defaults(), 'confirmed'],
 | 
			
		||||
        ])->validate();
 | 
			
		||||
 | 
			
		||||
        $user->forceFill([
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,11 @@ namespace App\Actions\Fortify;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use Illuminate\Support\Facades\Hash;
 | 
			
		||||
use Illuminate\Support\Facades\Validator;
 | 
			
		||||
use Illuminate\Validation\Rules\Password;
 | 
			
		||||
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
 | 
			
		||||
 | 
			
		||||
class UpdateUserPassword implements UpdatesUserPasswords
 | 
			
		||||
{
 | 
			
		||||
    use PasswordValidationRules;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate and update the user's password.
 | 
			
		||||
     *
 | 
			
		||||
@@ -20,7 +19,7 @@ class UpdateUserPassword implements UpdatesUserPasswords
 | 
			
		||||
    {
 | 
			
		||||
        Validator::make($input, [
 | 
			
		||||
            'current_password' => ['required', 'string', 'current_password:web'],
 | 
			
		||||
            'password' => $this->passwordRules(),
 | 
			
		||||
            'password' => ['required', Password::defaults(), 'confirmed'],
 | 
			
		||||
        ], [
 | 
			
		||||
            'current_password.current_password' => __('The provided password does not match your current password.'),
 | 
			
		||||
        ])->validateWithBag('updatePassword');
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,6 @@ class CheckResaleLicense
 | 
			
		||||
            // }
 | 
			
		||||
            $base_url = config('coolify.license_url');
 | 
			
		||||
            $instance_id = config('app.id');
 | 
			
		||||
 | 
			
		||||
            ray("Checking license key against $base_url/lemon/validate");
 | 
			
		||||
            $data = Http::withHeaders([
 | 
			
		||||
                'Accept' => 'application/json',
 | 
			
		||||
            ])->get("$base_url/lemon/validate", [
 | 
			
		||||
@@ -34,7 +32,6 @@ class CheckResaleLicense
 | 
			
		||||
                'instance_id' => $instance_id,
 | 
			
		||||
            ])->json();
 | 
			
		||||
            if (data_get($data, 'valid') === true && data_get($data, 'license_key.status') === 'active') {
 | 
			
		||||
                ray('Valid & active license key');
 | 
			
		||||
                $settings->update([
 | 
			
		||||
                    'is_resale_license_active' => true,
 | 
			
		||||
                ]);
 | 
			
		||||
@@ -48,7 +45,6 @@ class CheckResaleLicense
 | 
			
		||||
                'instance_id' => $instance_id,
 | 
			
		||||
            ])->json();
 | 
			
		||||
            if (data_get($data, 'activated') === true) {
 | 
			
		||||
                ray('Activated license key');
 | 
			
		||||
                $settings->update([
 | 
			
		||||
                    'is_resale_license_active' => true,
 | 
			
		||||
                ]);
 | 
			
		||||
@@ -60,7 +56,6 @@ class CheckResaleLicense
 | 
			
		||||
            }
 | 
			
		||||
            throw new \Exception('Cannot activate license key.');
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e);
 | 
			
		||||
            $settings->update([
 | 
			
		||||
                'resale_license' => null,
 | 
			
		||||
                'is_resale_license_active' => false,
 | 
			
		||||
 
 | 
			
		||||
@@ -88,7 +88,6 @@ class CheckProxy
 | 
			
		||||
                    $portsToCheck = [];
 | 
			
		||||
                }
 | 
			
		||||
            } catch (\Exception $e) {
 | 
			
		||||
                ray($e->getMessage());
 | 
			
		||||
            }
 | 
			
		||||
            if (count($portsToCheck) === 0) {
 | 
			
		||||
                return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -13,67 +13,63 @@ class StartProxy
 | 
			
		||||
 | 
			
		||||
    public function handle(Server $server, bool $async = true, bool $force = false): string|Activity
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $proxyType = $server->proxyType();
 | 
			
		||||
            if ((is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop || $server->isBuildServer()) && $force === false) {
 | 
			
		||||
                return 'OK';
 | 
			
		||||
            }
 | 
			
		||||
            $commands = collect([]);
 | 
			
		||||
            $proxy_path = $server->proxyPath();
 | 
			
		||||
            $configuration = CheckConfiguration::run($server);
 | 
			
		||||
            if (! $configuration) {
 | 
			
		||||
                throw new \Exception('Configuration is not synced');
 | 
			
		||||
            }
 | 
			
		||||
            SaveConfiguration::run($server, $configuration);
 | 
			
		||||
            $docker_compose_yml_base64 = base64_encode($configuration);
 | 
			
		||||
            $server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value();
 | 
			
		||||
            $server->save();
 | 
			
		||||
            if ($server->isSwarm()) {
 | 
			
		||||
                $commands = $commands->merge([
 | 
			
		||||
                    "mkdir -p $proxy_path/dynamic",
 | 
			
		||||
                    "cd $proxy_path",
 | 
			
		||||
                    "echo 'Creating required Docker Compose file.'",
 | 
			
		||||
                    "echo 'Starting coolify-proxy.'",
 | 
			
		||||
                    'docker stack deploy -c docker-compose.yml coolify-proxy',
 | 
			
		||||
                    "echo 'Successfully started coolify-proxy.'",
 | 
			
		||||
                ]);
 | 
			
		||||
            } else {
 | 
			
		||||
                $caddfile = 'import /dynamic/*.caddy';
 | 
			
		||||
                $commands = $commands->merge([
 | 
			
		||||
                    "mkdir -p $proxy_path/dynamic",
 | 
			
		||||
                    "cd $proxy_path",
 | 
			
		||||
                    "echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
 | 
			
		||||
                    "echo 'Creating required Docker Compose file.'",
 | 
			
		||||
                    "echo 'Pulling docker image.'",
 | 
			
		||||
                    'docker compose pull',
 | 
			
		||||
                    'if docker ps -a --format "{{.Names}}" | grep -q "^coolify-proxy$"; then',
 | 
			
		||||
                    "    echo 'Stopping and removing existing coolify-proxy.'",
 | 
			
		||||
                    '    docker rm -f coolify-proxy || true',
 | 
			
		||||
                    "    echo 'Successfully stopped and removed existing coolify-proxy.'",
 | 
			
		||||
                    'fi',
 | 
			
		||||
                    "echo 'Starting coolify-proxy.'",
 | 
			
		||||
                    'docker compose up -d --remove-orphans',
 | 
			
		||||
                    "echo 'Successfully started coolify-proxy.'",
 | 
			
		||||
                ]);
 | 
			
		||||
                $commands = $commands->merge(connectProxyToNetworks($server));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($async) {
 | 
			
		||||
                $activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
 | 
			
		||||
 | 
			
		||||
                return $activity;
 | 
			
		||||
            } else {
 | 
			
		||||
                instant_remote_process($commands, $server);
 | 
			
		||||
                $server->proxy->set('status', 'running');
 | 
			
		||||
                $server->proxy->set('type', $proxyType);
 | 
			
		||||
                $server->save();
 | 
			
		||||
                ProxyStarted::dispatch($server);
 | 
			
		||||
 | 
			
		||||
                return 'OK';
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e);
 | 
			
		||||
            throw $e;
 | 
			
		||||
        $proxyType = $server->proxyType();
 | 
			
		||||
        if ((is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop || $server->isBuildServer()) && $force === false) {
 | 
			
		||||
            return 'OK';
 | 
			
		||||
        }
 | 
			
		||||
        $commands = collect([]);
 | 
			
		||||
        $proxy_path = $server->proxyPath();
 | 
			
		||||
        $configuration = CheckConfiguration::run($server);
 | 
			
		||||
        if (! $configuration) {
 | 
			
		||||
            throw new \Exception('Configuration is not synced');
 | 
			
		||||
        }
 | 
			
		||||
        SaveConfiguration::run($server, $configuration);
 | 
			
		||||
        $docker_compose_yml_base64 = base64_encode($configuration);
 | 
			
		||||
        $server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value();
 | 
			
		||||
        $server->save();
 | 
			
		||||
        if ($server->isSwarm()) {
 | 
			
		||||
            $commands = $commands->merge([
 | 
			
		||||
                "mkdir -p $proxy_path/dynamic",
 | 
			
		||||
                "cd $proxy_path",
 | 
			
		||||
                "echo 'Creating required Docker Compose file.'",
 | 
			
		||||
                "echo 'Starting coolify-proxy.'",
 | 
			
		||||
                'docker stack deploy -c docker-compose.yml coolify-proxy',
 | 
			
		||||
                "echo 'Successfully started coolify-proxy.'",
 | 
			
		||||
            ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            $caddfile = 'import /dynamic/*.caddy';
 | 
			
		||||
            $commands = $commands->merge([
 | 
			
		||||
                "mkdir -p $proxy_path/dynamic",
 | 
			
		||||
                "cd $proxy_path",
 | 
			
		||||
                "echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
 | 
			
		||||
                "echo 'Creating required Docker Compose file.'",
 | 
			
		||||
                "echo 'Pulling docker image.'",
 | 
			
		||||
                'docker compose pull',
 | 
			
		||||
                'if docker ps -a --format "{{.Names}}" | grep -q "^coolify-proxy$"; then',
 | 
			
		||||
                "    echo 'Stopping and removing existing coolify-proxy.'",
 | 
			
		||||
                '    docker rm -f coolify-proxy || true',
 | 
			
		||||
                "    echo 'Successfully stopped and removed existing coolify-proxy.'",
 | 
			
		||||
                'fi',
 | 
			
		||||
                "echo 'Starting coolify-proxy.'",
 | 
			
		||||
                'docker compose up -d --remove-orphans',
 | 
			
		||||
                "echo 'Successfully started coolify-proxy.'",
 | 
			
		||||
            ]);
 | 
			
		||||
            $commands = $commands->merge(connectProxyToNetworks($server));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($async) {
 | 
			
		||||
            $activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
 | 
			
		||||
 | 
			
		||||
            return $activity;
 | 
			
		||||
        } else {
 | 
			
		||||
            instant_remote_process($commands, $server);
 | 
			
		||||
            $server->proxy->set('status', 'running');
 | 
			
		||||
            $server->proxy->set('type', $proxyType);
 | 
			
		||||
            $server->save();
 | 
			
		||||
            ProxyStarted::dispatch($server);
 | 
			
		||||
 | 
			
		||||
            return 'OK';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ class ConfigureCloudflared
 | 
			
		||||
            ]);
 | 
			
		||||
            instant_remote_process($commands, $server);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e);
 | 
			
		||||
            $server->settings->is_cloudflare_tunnel = false;
 | 
			
		||||
            $server->settings->save();
 | 
			
		||||
            throw $e;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								app/Actions/Server/DeleteServer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/Actions/Server/DeleteServer.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Actions\Server;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Lorisleiva\Actions\Concerns\AsAction;
 | 
			
		||||
 | 
			
		||||
class DeleteServer
 | 
			
		||||
{
 | 
			
		||||
    use AsAction;
 | 
			
		||||
 | 
			
		||||
    public function handle(Server $server)
 | 
			
		||||
    {
 | 
			
		||||
        StopSentinel::run($server);
 | 
			
		||||
        $server->forceDelete();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,8 +16,7 @@ class InstallDocker
 | 
			
		||||
        if (! $supported_os_type) {
 | 
			
		||||
            throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
 | 
			
		||||
        }
 | 
			
		||||
        ray('Installing Docker on server: '.$server->name.' ('.$server->ip.')'.' with OS type: '.$supported_os_type);
 | 
			
		||||
        $dockerVersion = '24.0';
 | 
			
		||||
        $dockerVersion = '26.0';
 | 
			
		||||
        $config = base64_encode('{
 | 
			
		||||
            "log-driver": "json-file",
 | 
			
		||||
            "log-opts": {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								app/Actions/Server/RestartContainer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Actions/Server/RestartContainer.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Actions\Server;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Lorisleiva\Actions\Concerns\AsAction;
 | 
			
		||||
 | 
			
		||||
class RestartContainer
 | 
			
		||||
{
 | 
			
		||||
    use AsAction;
 | 
			
		||||
 | 
			
		||||
    public function handle(Server $server, string $containerName)
 | 
			
		||||
    {
 | 
			
		||||
        $server->restartContainer($containerName);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace App\Actions\Server;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Lorisleiva\Actions\Concerns\AsAction;
 | 
			
		||||
 | 
			
		||||
class InstallLogDrain
 | 
			
		||||
class StartLogDrain
 | 
			
		||||
{
 | 
			
		||||
    use AsAction;
 | 
			
		||||
 | 
			
		||||
@@ -13,12 +13,16 @@ class InstallLogDrain
 | 
			
		||||
    {
 | 
			
		||||
        if ($server->settings->is_logdrain_newrelic_enabled) {
 | 
			
		||||
            $type = 'newrelic';
 | 
			
		||||
            StopLogDrain::run($server);
 | 
			
		||||
        } elseif ($server->settings->is_logdrain_highlight_enabled) {
 | 
			
		||||
            $type = 'highlight';
 | 
			
		||||
            StopLogDrain::run($server);
 | 
			
		||||
        } elseif ($server->settings->is_logdrain_axiom_enabled) {
 | 
			
		||||
            $type = 'axiom';
 | 
			
		||||
            StopLogDrain::run($server);
 | 
			
		||||
        } elseif ($server->settings->is_logdrain_custom_enabled) {
 | 
			
		||||
            $type = 'custom';
 | 
			
		||||
            StopLogDrain::run($server);
 | 
			
		||||
        } else {
 | 
			
		||||
            $type = 'none';
 | 
			
		||||
        }
 | 
			
		||||
@@ -151,6 +155,8 @@ services:
 | 
			
		||||
      - ./parsers.conf:/parsers.conf
 | 
			
		||||
    ports:
 | 
			
		||||
      - 127.0.0.1:24224:24224
 | 
			
		||||
    labels:
 | 
			
		||||
      - coolify.managed=true
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
');
 | 
			
		||||
            $readme = base64_encode('# New Relic Log Drain
 | 
			
		||||
@@ -202,10 +208,8 @@ Files:
 | 
			
		||||
                throw new \Exception('Unknown log drain type.');
 | 
			
		||||
            }
 | 
			
		||||
            $restart_command = [
 | 
			
		||||
                "echo 'Stopping old Fluent Bit'",
 | 
			
		||||
                "cd $config_path && docker compose down --remove-orphans || true",
 | 
			
		||||
                "echo 'Starting Fluent Bit'",
 | 
			
		||||
                "cd $config_path && docker compose up -d --remove-orphans",
 | 
			
		||||
                "cd $config_path && docker compose up -d",
 | 
			
		||||
            ];
 | 
			
		||||
            $command = array_merge($command, $add_envs_command, $restart_command);
 | 
			
		||||
 | 
			
		||||
@@ -9,18 +9,57 @@ class StartSentinel
 | 
			
		||||
{
 | 
			
		||||
    use AsAction;
 | 
			
		||||
 | 
			
		||||
    public function handle(Server $server, $version = 'latest', bool $restart = false)
 | 
			
		||||
    public function handle(Server $server, bool $restart = false, ?string $latestVersion = null)
 | 
			
		||||
    {
 | 
			
		||||
        if ($server->isSwarm() || $server->isBuildServer()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if ($restart) {
 | 
			
		||||
            StopSentinel::run($server);
 | 
			
		||||
        }
 | 
			
		||||
        $metrics_history = $server->settings->metrics_history_days;
 | 
			
		||||
        $refresh_rate = $server->settings->metrics_refresh_rate_seconds;
 | 
			
		||||
        $token = $server->settings->metrics_token;
 | 
			
		||||
        $version = $latestVersion ?? get_latest_sentinel_version();
 | 
			
		||||
        $metricsHistory = data_get($server, 'settings.sentinel_metrics_history_days');
 | 
			
		||||
        $refreshRate = data_get($server, 'settings.sentinel_metrics_refresh_rate_seconds');
 | 
			
		||||
        $pushInterval = data_get($server, 'settings.sentinel_push_interval_seconds');
 | 
			
		||||
        $token = data_get($server, 'settings.sentinel_token');
 | 
			
		||||
        $endpoint = data_get($server, 'settings.sentinel_custom_url');
 | 
			
		||||
        $debug = data_get($server, 'settings.is_sentinel_debug_enabled');
 | 
			
		||||
        $mountDir = '/data/coolify/sentinel';
 | 
			
		||||
        $image = "ghcr.io/coollabsio/sentinel:$version";
 | 
			
		||||
        if (! $endpoint) {
 | 
			
		||||
            throw new \Exception('You should set FQDN in Instance Settings.');
 | 
			
		||||
        }
 | 
			
		||||
        $environments = [
 | 
			
		||||
            'TOKEN' => $token,
 | 
			
		||||
            'DEBUG' => $debug ? 'true' : 'false',
 | 
			
		||||
            'PUSH_ENDPOINT' => $endpoint,
 | 
			
		||||
            'PUSH_INTERVAL_SECONDS' => $pushInterval,
 | 
			
		||||
            'COLLECTOR_ENABLED' => $server->isMetricsEnabled() ? 'true' : 'false',
 | 
			
		||||
            'COLLECTOR_REFRESH_RATE_SECONDS' => $refreshRate,
 | 
			
		||||
            'COLLECTOR_RETENTION_PERIOD_DAYS' => $metricsHistory,
 | 
			
		||||
        ];
 | 
			
		||||
        $labels = [
 | 
			
		||||
            'coolify.managed' => 'true',
 | 
			
		||||
        ];
 | 
			
		||||
        if (isDev()) {
 | 
			
		||||
            // data_set($environments, 'DEBUG', 'true');
 | 
			
		||||
            // $image = 'sentinel';
 | 
			
		||||
            $mountDir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/sentinel';
 | 
			
		||||
        }
 | 
			
		||||
        $dockerEnvironments = '-e "'.implode('" -e "', array_map(fn ($key, $value) => "$key=$value", array_keys($environments), $environments)).'"';
 | 
			
		||||
        $dockerLabels = implode(' ', array_map(fn ($key, $value) => "$key=$value", array_keys($labels), $labels));
 | 
			
		||||
        $dockerCommand = "docker run -d $dockerEnvironments --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v $mountDir:/app/db --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 --add-host=host.docker.internal:host-gateway --label $dockerLabels $image";
 | 
			
		||||
 | 
			
		||||
        instant_remote_process([
 | 
			
		||||
            "docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
 | 
			
		||||
            'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
 | 
			
		||||
            'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
 | 
			
		||||
        ], $server, true);
 | 
			
		||||
            'docker rm -f coolify-sentinel || true',
 | 
			
		||||
            "mkdir -p $mountDir",
 | 
			
		||||
            $dockerCommand,
 | 
			
		||||
            "chown -R 9999:root $mountDir",
 | 
			
		||||
            "chmod -R 700 $mountDir",
 | 
			
		||||
        ], $server);
 | 
			
		||||
 | 
			
		||||
        $server->settings->is_sentinel_enabled = true;
 | 
			
		||||
        $server->settings->save();
 | 
			
		||||
        $server->sentinelHeartbeat();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ class StopLogDrain
 | 
			
		||||
    public function handle(Server $server)
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            return instant_remote_process(['docker rm -f coolify-log-drain || true'], $server);
 | 
			
		||||
            return instant_remote_process(['docker rm -f coolify-log-drain'], $server, false);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,5 +12,6 @@ class StopSentinel
 | 
			
		||||
    public function handle(Server $server)
 | 
			
		||||
    {
 | 
			
		||||
        instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
 | 
			
		||||
        $server->sentinelHeartbeat(isReset: true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,32 +18,28 @@ class UpdateCoolify
 | 
			
		||||
 | 
			
		||||
    public function handle($manual_update = false)
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $settings = instanceSettings();
 | 
			
		||||
            $this->server = Server::find(0);
 | 
			
		||||
            if (! $this->server) {
 | 
			
		||||
        $settings = instanceSettings();
 | 
			
		||||
        $this->server = Server::find(0);
 | 
			
		||||
        if (! $this->server) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        CleanupDocker::dispatch($this->server)->onQueue('high');
 | 
			
		||||
        $this->latestVersion = get_latest_version_of_coolify();
 | 
			
		||||
        $this->currentVersion = config('version');
 | 
			
		||||
        if (! $manual_update) {
 | 
			
		||||
            if (! $settings->is_auto_update_enabled) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            CleanupDocker::dispatch($this->server)->onQueue('high');
 | 
			
		||||
            $this->latestVersion = get_latest_version_of_coolify();
 | 
			
		||||
            $this->currentVersion = config('version');
 | 
			
		||||
            if (! $manual_update) {
 | 
			
		||||
                if (! $settings->is_auto_update_enabled) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if ($this->latestVersion === $this->currentVersion) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            if ($this->latestVersion === $this->currentVersion) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            $this->update();
 | 
			
		||||
            $settings->new_version_available = false;
 | 
			
		||||
            $settings->save();
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
        $this->update();
 | 
			
		||||
        $settings->new_version_available = false;
 | 
			
		||||
        $settings->save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function update()
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ class DeleteService
 | 
			
		||||
                    foreach ($commands as $command) {
 | 
			
		||||
                        $result = instant_remote_process([$command], $server, false);
 | 
			
		||||
                        if ($result !== 0) {
 | 
			
		||||
                            ray("Failed to execute: $command");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ class StartService
 | 
			
		||||
 | 
			
		||||
    public function handle(Service $service)
 | 
			
		||||
    {
 | 
			
		||||
        ray('Starting service: '.$service->name);
 | 
			
		||||
        $service->saveComposeConfigs();
 | 
			
		||||
        $commands[] = 'cd '.$service->workdir();
 | 
			
		||||
        $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,6 @@ class StopService
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return $e->getMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ class CleanupStuckedResources extends Command
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        ray('Running cleanup stucked resources.');
 | 
			
		||||
        echo "Running cleanup stucked resources.\n";
 | 
			
		||||
        $this->cleanup_stucked_resources();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@ class CloudCleanupSubscriptions extends Command
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            ray()->clearAll();
 | 
			
		||||
            $this->info('Cleaning up subcriptions teams');
 | 
			
		||||
            $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ use App\Notifications\Application\DeploymentSuccess;
 | 
			
		||||
use App\Notifications\Application\StatusChanged;
 | 
			
		||||
use App\Notifications\Database\BackupFailed;
 | 
			
		||||
use App\Notifications\Database\BackupSuccess;
 | 
			
		||||
use App\Notifications\Database\DailyBackup;
 | 
			
		||||
use App\Notifications\Test;
 | 
			
		||||
use Exception;
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
@@ -121,28 +120,10 @@ class Emails extends Command
 | 
			
		||||
                $this->mail = (new Test)->toMail();
 | 
			
		||||
                $this->sendEmail();
 | 
			
		||||
                break;
 | 
			
		||||
            case 'database-backup-statuses-daily':
 | 
			
		||||
                $scheduled_backups = ScheduledDatabaseBackup::all();
 | 
			
		||||
                $databases = collect();
 | 
			
		||||
                foreach ($scheduled_backups as $scheduled_backup) {
 | 
			
		||||
                    $last_days_backups = $scheduled_backup->get_last_days_backup_status();
 | 
			
		||||
                    if ($last_days_backups->isEmpty()) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    $failed = $last_days_backups->where('status', 'failed');
 | 
			
		||||
                    $database = $scheduled_backup->database;
 | 
			
		||||
                    $databases->put($database->name, [
 | 
			
		||||
                        'failed_count' => $failed->count(),
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
                $this->mail = (new DailyBackup($databases))->toMail();
 | 
			
		||||
                $this->sendEmail();
 | 
			
		||||
                break;
 | 
			
		||||
            case 'application-deployment-success-daily':
 | 
			
		||||
                $applications = Application::all();
 | 
			
		||||
                foreach ($applications as $application) {
 | 
			
		||||
                    $deployments = $application->get_last_days_deployments();
 | 
			
		||||
                    ray($deployments);
 | 
			
		||||
                    if ($deployments->isEmpty()) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -180,7 +180,7 @@ class Init extends Command
 | 
			
		||||
                            'save_s3' => false,
 | 
			
		||||
                            'frequency' => '0 0 * * *',
 | 
			
		||||
                            'database_id' => $database->id,
 | 
			
		||||
                            'database_type' => 'App\Models\StandalonePostgresql',
 | 
			
		||||
                            'database_type' => \App\Models\StandalonePostgresql::class,
 | 
			
		||||
                            'team_id' => 0,
 | 
			
		||||
                        ]);
 | 
			
		||||
                    }
 | 
			
		||||
@@ -219,7 +219,6 @@ class Init extends Command
 | 
			
		||||
            }
 | 
			
		||||
            $queued_inprogress_deployments = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get();
 | 
			
		||||
            foreach ($queued_inprogress_deployments as $deployment) {
 | 
			
		||||
                ray($deployment->id, $deployment->status);
 | 
			
		||||
                echo "Cleaning up deployment: {$deployment->id}\n";
 | 
			
		||||
                $deployment->status = ApplicationDeploymentStatus::FAILED->value;
 | 
			
		||||
                $deployment->save();
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,6 @@ class NotifyDemo extends Command
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ray($channel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function showHelp()
 | 
			
		||||
 
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Console;
 | 
			
		||||
 | 
			
		||||
use App\Jobs\CheckAndStartSentinelJob;
 | 
			
		||||
use App\Jobs\CheckForUpdatesJob;
 | 
			
		||||
use App\Jobs\CheckHelperImageJob;
 | 
			
		||||
use App\Jobs\CleanupInstanceStuffsJob;
 | 
			
		||||
use App\Jobs\CleanupStaleMultiplexedConnections;
 | 
			
		||||
use App\Jobs\DatabaseBackupJob;
 | 
			
		||||
use App\Jobs\DockerCleanupJob;
 | 
			
		||||
use App\Jobs\PullHelperImageJob;
 | 
			
		||||
use App\Jobs\PullSentinelImageJob;
 | 
			
		||||
use App\Jobs\PullTemplatesFromCDN;
 | 
			
		||||
use App\Jobs\ScheduledTaskJob;
 | 
			
		||||
use App\Jobs\ServerCheckJob;
 | 
			
		||||
use App\Jobs\ServerStorageCheckJob;
 | 
			
		||||
use App\Jobs\ServerCleanupMux;
 | 
			
		||||
use App\Jobs\UpdateCoolifyJob;
 | 
			
		||||
use App\Models\ScheduledDatabaseBackup;
 | 
			
		||||
use App\Models\ScheduledTask;
 | 
			
		||||
@@ -20,14 +20,15 @@ use App\Models\Server;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use Illuminate\Console\Scheduling\Schedule;
 | 
			
		||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
			
		||||
use Illuminate\Support\Carbon;
 | 
			
		||||
 | 
			
		||||
class Kernel extends ConsoleKernel
 | 
			
		||||
{
 | 
			
		||||
    private $all_servers;
 | 
			
		||||
    private $allServers;
 | 
			
		||||
 | 
			
		||||
    protected function schedule(Schedule $schedule): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->all_servers = Server::all();
 | 
			
		||||
        $this->allServers = Server::all();
 | 
			
		||||
        $settings = instanceSettings();
 | 
			
		||||
 | 
			
		||||
        $schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
 | 
			
		||||
@@ -37,56 +38,51 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
            $schedule->command('horizon:snapshot')->everyMinute();
 | 
			
		||||
            $schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
 | 
			
		||||
            // Server Jobs
 | 
			
		||||
            $this->check_scheduled_backups($schedule);
 | 
			
		||||
            $this->check_resources($schedule);
 | 
			
		||||
            $this->check_scheduled_tasks($schedule);
 | 
			
		||||
            $this->checkScheduledBackups($schedule);
 | 
			
		||||
            $this->checkResources($schedule);
 | 
			
		||||
            $this->checkScheduledTasks($schedule);
 | 
			
		||||
            $schedule->command('uploads:clear')->everyTwoMinutes();
 | 
			
		||||
 | 
			
		||||
            $schedule->command('telescope:prune')->daily();
 | 
			
		||||
 | 
			
		||||
            $schedule->job(new PullHelperImageJob)->everyFiveMinutes()->onOneServer();
 | 
			
		||||
            $schedule->job(new CheckHelperImageJob)->everyFiveMinutes()->onOneServer();
 | 
			
		||||
        } else {
 | 
			
		||||
            // Instance Jobs
 | 
			
		||||
            $schedule->command('horizon:snapshot')->everyFiveMinutes();
 | 
			
		||||
            $schedule->command('cleanup:unreachable-servers')->daily()->onOneServer();
 | 
			
		||||
            $schedule->job(new PullTemplatesFromCDN)->cron($settings->update_check_frequency)->timezone($settings->instance_timezone)->onOneServer();
 | 
			
		||||
            $schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
 | 
			
		||||
            $this->schedule_updates($schedule);
 | 
			
		||||
            $this->scheduleUpdates($schedule);
 | 
			
		||||
 | 
			
		||||
            // Server Jobs
 | 
			
		||||
            $this->check_scheduled_backups($schedule);
 | 
			
		||||
            $this->check_resources($schedule);
 | 
			
		||||
            $this->pull_images($schedule);
 | 
			
		||||
            $this->check_scheduled_tasks($schedule);
 | 
			
		||||
            $this->checkScheduledBackups($schedule);
 | 
			
		||||
            $this->checkResources($schedule);
 | 
			
		||||
            $this->pullImages($schedule);
 | 
			
		||||
            $this->checkScheduledTasks($schedule);
 | 
			
		||||
 | 
			
		||||
            $schedule->command('cleanup:database --yes')->daily();
 | 
			
		||||
            $schedule->command('uploads:clear')->everyTwoMinutes();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function pull_images($schedule)
 | 
			
		||||
    private function pullImages($schedule): void
 | 
			
		||||
    {
 | 
			
		||||
        $settings = instanceSettings();
 | 
			
		||||
        $servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
 | 
			
		||||
        $servers = $this->allServers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
 | 
			
		||||
        foreach ($servers as $server) {
 | 
			
		||||
            if ($server->isSentinelEnabled()) {
 | 
			
		||||
                $schedule->job(function () use ($server) {
 | 
			
		||||
                    $sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $server, false);
 | 
			
		||||
                    $sentinel_found = json_decode($sentinel_found, true);
 | 
			
		||||
                    $status = data_get($sentinel_found, '0.State.Status', 'exited');
 | 
			
		||||
                    if ($status !== 'running') {
 | 
			
		||||
                        PullSentinelImageJob::dispatch($server);
 | 
			
		||||
                    }
 | 
			
		||||
                    CheckAndStartSentinelJob::dispatch($server);
 | 
			
		||||
                })->cron($settings->update_check_frequency)->timezone($settings->instance_timezone)->onOneServer();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $schedule->job(new PullHelperImageJob)
 | 
			
		||||
        $schedule->job(new CheckHelperImageJob)
 | 
			
		||||
            ->cron($settings->update_check_frequency)
 | 
			
		||||
            ->timezone($settings->instance_timezone)
 | 
			
		||||
            ->onOneServer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function schedule_updates($schedule)
 | 
			
		||||
    private function scheduleUpdates($schedule): void
 | 
			
		||||
    {
 | 
			
		||||
        $settings = instanceSettings();
 | 
			
		||||
 | 
			
		||||
@@ -105,28 +101,39 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function check_resources($schedule)
 | 
			
		||||
    private function checkResources($schedule): void
 | 
			
		||||
    {
 | 
			
		||||
        if (isCloud()) {
 | 
			
		||||
            $servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
 | 
			
		||||
            $servers = $this->allServers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
 | 
			
		||||
            $own = Team::find(0)->servers;
 | 
			
		||||
            $servers = $servers->merge($own);
 | 
			
		||||
        } else {
 | 
			
		||||
            $servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
 | 
			
		||||
            $servers = $this->allServers->where('ip', '!=', '1.2.3.4');
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($servers as $server) {
 | 
			
		||||
            $schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
 | 
			
		||||
            // $schedule->job(new ServerStorageCheckJob($server))->everyMinute()->onOneServer();
 | 
			
		||||
            $lastSentinelUpdate = $server->sentinel_updated_at;
 | 
			
		||||
            $serverTimezone = $server->settings->server_timezone;
 | 
			
		||||
            if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) {
 | 
			
		||||
                $schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
 | 
			
		||||
            }
 | 
			
		||||
            if ($server->settings->force_docker_cleanup) {
 | 
			
		||||
                $schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
 | 
			
		||||
            } else {
 | 
			
		||||
                $schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->timezone($serverTimezone)->onOneServer();
 | 
			
		||||
            }
 | 
			
		||||
            // Cleanup multiplexed connections every hour
 | 
			
		||||
            $schedule->job(new ServerCleanupMux($server))->hourly()->onOneServer();
 | 
			
		||||
 | 
			
		||||
            // Temporary solution until we have better memory management for Sentinel
 | 
			
		||||
            if ($server->isSentinelEnabled()) {
 | 
			
		||||
                $schedule->job(function () use ($server) {
 | 
			
		||||
                    $server->restartContainer('coolify-sentinel');
 | 
			
		||||
                })->daily()->onOneServer();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function check_scheduled_backups($schedule)
 | 
			
		||||
    private function checkScheduledBackups($schedule): void
 | 
			
		||||
    {
 | 
			
		||||
        $scheduled_backups = ScheduledDatabaseBackup::all();
 | 
			
		||||
        if ($scheduled_backups->isEmpty()) {
 | 
			
		||||
@@ -137,7 +144,6 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (is_null(data_get($scheduled_backup, 'database'))) {
 | 
			
		||||
                ray('database not found');
 | 
			
		||||
                $scheduled_backup->delete();
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
@@ -159,7 +165,7 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function check_scheduled_tasks($schedule)
 | 
			
		||||
    private function checkScheduledTasks($schedule): void
 | 
			
		||||
    {
 | 
			
		||||
        $scheduled_tasks = ScheduledTask::all();
 | 
			
		||||
        if ($scheduled_tasks->isEmpty()) {
 | 
			
		||||
@@ -173,7 +179,6 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
            $application = $scheduled_task->application;
 | 
			
		||||
 | 
			
		||||
            if (! $application && ! $service) {
 | 
			
		||||
                ray('application/service attached to scheduled task does not exist');
 | 
			
		||||
                $scheduled_task->delete();
 | 
			
		||||
 | 
			
		||||
                continue;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								app/Enums/Role.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/Enums/Role.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Enums;
 | 
			
		||||
 | 
			
		||||
enum Role: string
 | 
			
		||||
{
 | 
			
		||||
    case MEMBER = 'member';
 | 
			
		||||
    case ADMIN = 'admin';
 | 
			
		||||
    case OWNER = 'owner';
 | 
			
		||||
 | 
			
		||||
    public function rank(): int
 | 
			
		||||
    {
 | 
			
		||||
        return match ($this) {
 | 
			
		||||
            self::MEMBER => 1,
 | 
			
		||||
            self::ADMIN => 2,
 | 
			
		||||
            self::OWNER => 3,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function lt(Role|string $role): bool
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($role)) {
 | 
			
		||||
            $role = Role::from($role);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->rank() < $role->rank();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function gt(Role|string $role): bool
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($role)) {
 | 
			
		||||
            $role = Role::from($role);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->rank() > $role->rank();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,7 +16,6 @@ class FileStorageChanged implements ShouldBroadcast
 | 
			
		||||
 | 
			
		||||
    public function __construct($teamId = null)
 | 
			
		||||
    {
 | 
			
		||||
        ray($teamId);
 | 
			
		||||
        if (is_null($teamId)) {
 | 
			
		||||
            throw new \Exception('Team id is null');
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,6 @@ class Handler extends ExceptionHandler
 | 
			
		||||
            if (str($e->getMessage())->contains('No space left on device')) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            ray('reporting to sentry');
 | 
			
		||||
            Integration::captureUnhandledException($e);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1579,11 +1579,16 @@ class ApplicationsController extends Controller
 | 
			
		||||
            $request->offsetUnset('docker_compose_domains');
 | 
			
		||||
        }
 | 
			
		||||
        $instantDeploy = $request->instant_deploy;
 | 
			
		||||
        $isStatic = $request->is_static;
 | 
			
		||||
        $useBuildServer = $request->use_build_server;
 | 
			
		||||
 | 
			
		||||
        $use_build_server = $request->use_build_server;
 | 
			
		||||
        if (isset($useBuildServer)) {
 | 
			
		||||
            $application->settings->is_build_server_enabled = $useBuildServer;
 | 
			
		||||
            $application->settings->save();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($use_build_server)) {
 | 
			
		||||
            $application->settings->is_build_server_enabled = $use_build_server;
 | 
			
		||||
        if (isset($isStatic)) {
 | 
			
		||||
            $application->settings->is_static = $isStatic;
 | 
			
		||||
            $application->settings->save();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -292,7 +292,7 @@ class DeployController extends Controller
 | 
			
		||||
            return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
 | 
			
		||||
        }
 | 
			
		||||
        switch ($resource?->getMorphClass()) {
 | 
			
		||||
            case 'App\Models\Application':
 | 
			
		||||
            case \App\Models\Application::class:
 | 
			
		||||
                $deployment_uuid = new Cuid2;
 | 
			
		||||
                queue_application_deployment(
 | 
			
		||||
                    application: $resource,
 | 
			
		||||
@@ -301,7 +301,7 @@ class DeployController extends Controller
 | 
			
		||||
                );
 | 
			
		||||
                $message = "Application {$resource->name} deployment queued.";
 | 
			
		||||
                break;
 | 
			
		||||
            case 'App\Models\Service':
 | 
			
		||||
            case \App\Models\Service::class:
 | 
			
		||||
                StartService::run($resource);
 | 
			
		||||
                $message = "Service {$resource->name} started. It could take a while, be patient.";
 | 
			
		||||
                break;
 | 
			
		||||
 
 | 
			
		||||
@@ -160,7 +160,7 @@ class OtherController extends Controller
 | 
			
		||||
    #[OA\Get(
 | 
			
		||||
        summary: 'Healthcheck',
 | 
			
		||||
        description: 'Healthcheck endpoint.',
 | 
			
		||||
        path: '/healthcheck',
 | 
			
		||||
        path: '/health',
 | 
			
		||||
        operationId: 'healthcheck',
 | 
			
		||||
        responses: [
 | 
			
		||||
            new OA\Response(
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ class ResourcesController extends Controller
 | 
			
		||||
        $resources = $resources->flatten();
 | 
			
		||||
        $resources = $resources->map(function ($resource) {
 | 
			
		||||
            $payload = $resource->toArray();
 | 
			
		||||
            if ($resource->getMorphClass() === 'App\Models\Service') {
 | 
			
		||||
            if ($resource->getMorphClass() === \App\Models\Service::class) {
 | 
			
		||||
                $payload['status'] = $resource->status();
 | 
			
		||||
            } else {
 | 
			
		||||
                $payload['status'] = $resource->status;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers\Api;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Server\DeleteServer;
 | 
			
		||||
use App\Actions\Server\ValidateServer;
 | 
			
		||||
use App\Enums\ProxyStatus;
 | 
			
		||||
use App\Enums\ProxyTypes;
 | 
			
		||||
@@ -23,7 +24,7 @@ class ServersController extends Controller
 | 
			
		||||
            return serializeApiResponse($settings);
 | 
			
		||||
        }
 | 
			
		||||
        $settings = $settings->makeHidden([
 | 
			
		||||
            'metrics_token',
 | 
			
		||||
            'sentinel_token',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        return serializeApiResponse($settings);
 | 
			
		||||
@@ -248,7 +249,6 @@ class ServersController extends Controller
 | 
			
		||||
            return $payload;
 | 
			
		||||
        });
 | 
			
		||||
        $server = $this->removeSensitiveData($server);
 | 
			
		||||
        ray($server);
 | 
			
		||||
 | 
			
		||||
        return response()->json(serializeApiResponse(data_get($server, 'resources')));
 | 
			
		||||
    }
 | 
			
		||||
@@ -726,6 +726,7 @@ class ServersController extends Controller
 | 
			
		||||
            return response()->json(['message' => 'Server has resources, so you need to delete them before.'], 400);
 | 
			
		||||
        }
 | 
			
		||||
        $server->delete();
 | 
			
		||||
        DeleteServer::dispatch($server);
 | 
			
		||||
 | 
			
		||||
        return response()->json(['message' => 'Server deleted.']);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -112,57 +112,48 @@ class Controller extends BaseController
 | 
			
		||||
 | 
			
		||||
    public function accept_invitation()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $resetPassword = request()->query('reset-password');
 | 
			
		||||
            $invitationUuid = request()->route('uuid');
 | 
			
		||||
            $invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
 | 
			
		||||
            $user = User::whereEmail($invitation->email)->firstOrFail();
 | 
			
		||||
            $invitationValid = $invitation->isValid();
 | 
			
		||||
            if ($invitationValid) {
 | 
			
		||||
                if ($resetPassword) {
 | 
			
		||||
                    $user->update([
 | 
			
		||||
                        'password' => Hash::make($invitationUuid),
 | 
			
		||||
                        'force_password_reset' => true,
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
                if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
 | 
			
		||||
                    $invitation->delete();
 | 
			
		||||
 | 
			
		||||
                    return redirect()->route('team.index');
 | 
			
		||||
                }
 | 
			
		||||
                $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
 | 
			
		||||
        $resetPassword = request()->query('reset-password');
 | 
			
		||||
        $invitationUuid = request()->route('uuid');
 | 
			
		||||
        $invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
 | 
			
		||||
        $user = User::whereEmail($invitation->email)->firstOrFail();
 | 
			
		||||
        $invitationValid = $invitation->isValid();
 | 
			
		||||
        if ($invitationValid) {
 | 
			
		||||
            if ($resetPassword) {
 | 
			
		||||
                $user->update([
 | 
			
		||||
                    'password' => Hash::make($invitationUuid),
 | 
			
		||||
                    'force_password_reset' => true,
 | 
			
		||||
                ]);
 | 
			
		||||
            }
 | 
			
		||||
            if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
 | 
			
		||||
                $invitation->delete();
 | 
			
		||||
                if (auth()->user()?->id !== $user->id) {
 | 
			
		||||
                    return redirect()->route('login');
 | 
			
		||||
                }
 | 
			
		||||
                refreshSession($invitation->team);
 | 
			
		||||
 | 
			
		||||
                return redirect()->route('team.index');
 | 
			
		||||
            } else {
 | 
			
		||||
                abort(401);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
            $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
 | 
			
		||||
            $invitation->delete();
 | 
			
		||||
            if (auth()->user()?->id !== $user->id) {
 | 
			
		||||
                return redirect()->route('login');
 | 
			
		||||
            }
 | 
			
		||||
            refreshSession($invitation->team);
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('team.index');
 | 
			
		||||
        } else {
 | 
			
		||||
            abort(401);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function revoke_invitation()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
 | 
			
		||||
            $user = User::whereEmail($invitation->email)->firstOrFail();
 | 
			
		||||
            if (is_null(auth()->user())) {
 | 
			
		||||
                return redirect()->route('login');
 | 
			
		||||
            }
 | 
			
		||||
            if (auth()->user()->id !== $user->id) {
 | 
			
		||||
                abort(401);
 | 
			
		||||
            }
 | 
			
		||||
            $invitation->delete();
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('team.index');
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            throw $e;
 | 
			
		||||
        $invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
 | 
			
		||||
        $user = User::whereEmail($invitation->email)->firstOrFail();
 | 
			
		||||
        if (is_null(auth()->user())) {
 | 
			
		||||
            return redirect()->route('login');
 | 
			
		||||
        }
 | 
			
		||||
        if (auth()->user()->id !== $user->id) {
 | 
			
		||||
            abort(401);
 | 
			
		||||
        }
 | 
			
		||||
        $invitation->delete();
 | 
			
		||||
 | 
			
		||||
        return redirect()->route('team.index');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,6 @@ class OauthController extends Controller
 | 
			
		||||
 | 
			
		||||
            return redirect('/');
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            $errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback';
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('login')->withErrors([__($errorCode)]);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@ class Bitbucket extends Controller
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $data = [
 | 
			
		||||
                    'attributes' => $request->attributes->all(),
 | 
			
		||||
@@ -55,7 +54,6 @@ class Bitbucket extends Controller
 | 
			
		||||
                        'message' => 'Nothing to do. No branch found in the request.',
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
                ray('Manual webhook bitbucket push event with branch: '.$branch);
 | 
			
		||||
            }
 | 
			
		||||
            if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
 | 
			
		||||
                $branch = data_get($payload, 'pullrequest.destination.branch.name');
 | 
			
		||||
@@ -85,7 +83,6 @@ class Bitbucket extends Controller
 | 
			
		||||
                        'status' => 'failed',
 | 
			
		||||
                        'message' => 'Invalid signature.',
 | 
			
		||||
                    ]);
 | 
			
		||||
                    ray('Invalid signature');
 | 
			
		||||
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
@@ -96,13 +93,11 @@ class Bitbucket extends Controller
 | 
			
		||||
                        'status' => 'failed',
 | 
			
		||||
                        'message' => 'Server is not functional.',
 | 
			
		||||
                    ]);
 | 
			
		||||
                    ray('Server is not functional: '.$application->destination->server->name);
 | 
			
		||||
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                if ($x_bitbucket_event === 'repo:push') {
 | 
			
		||||
                    if ($application->isDeployable()) {
 | 
			
		||||
                        ray('Deploying '.$application->name.' with branch '.$branch);
 | 
			
		||||
                        $deployment_uuid = new Cuid2;
 | 
			
		||||
                        queue_application_deployment(
 | 
			
		||||
                            application: $application,
 | 
			
		||||
@@ -126,7 +121,6 @@ class Bitbucket extends Controller
 | 
			
		||||
                }
 | 
			
		||||
                if ($x_bitbucket_event === 'pullrequest:created') {
 | 
			
		||||
                    if ($application->isPRDeployable()) {
 | 
			
		||||
                        ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id);
 | 
			
		||||
                        $deployment_uuid = new Cuid2;
 | 
			
		||||
                        $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
 | 
			
		||||
                        if (! $found) {
 | 
			
		||||
@@ -171,7 +165,6 @@ class Bitbucket extends Controller
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if ($x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
 | 
			
		||||
                    ray('Pull request rejected');
 | 
			
		||||
                    $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
 | 
			
		||||
                    if ($found) {
 | 
			
		||||
                        $found->delete();
 | 
			
		||||
@@ -191,12 +184,9 @@ class Bitbucket extends Controller
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ray($return_payloads);
 | 
			
		||||
 | 
			
		||||
            return response($return_payloads);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            ray($e);
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -19,15 +19,12 @@ class Gitea extends Controller
 | 
			
		||||
            $return_payloads = collect([]);
 | 
			
		||||
            $x_gitea_delivery = request()->header('X-Gitea-Delivery');
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $files = Storage::disk('webhooks-during-maintenance')->files();
 | 
			
		||||
                $gitea_delivery_found = collect($files)->filter(function ($file) use ($x_gitea_delivery) {
 | 
			
		||||
                    return Str::contains($file, $x_gitea_delivery);
 | 
			
		||||
                })->first();
 | 
			
		||||
                if ($gitea_delivery_found) {
 | 
			
		||||
                    ray('Webhook already found');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $data = [
 | 
			
		||||
@@ -67,8 +64,6 @@ class Gitea extends Controller
 | 
			
		||||
                $removed_files = data_get($payload, 'commits.*.removed');
 | 
			
		||||
                $modified_files = data_get($payload, 'commits.*.modified');
 | 
			
		||||
                $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
 | 
			
		||||
                ray($changed_files);
 | 
			
		||||
                ray('Manual Webhook Gitea Push Event with branch: '.$branch);
 | 
			
		||||
            }
 | 
			
		||||
            if ($x_gitea_event === 'pull_request') {
 | 
			
		||||
                $action = data_get($payload, 'action');
 | 
			
		||||
@@ -77,7 +72,6 @@ class Gitea extends Controller
 | 
			
		||||
                $pull_request_html_url = data_get($payload, 'pull_request.html_url');
 | 
			
		||||
                $branch = data_get($payload, 'pull_request.head.ref');
 | 
			
		||||
                $base_branch = data_get($payload, 'pull_request.base.ref');
 | 
			
		||||
                ray('Webhook Gitea Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
 | 
			
		||||
            }
 | 
			
		||||
            if (! $branch) {
 | 
			
		||||
                return response('Nothing to do. No branch found in the request.');
 | 
			
		||||
@@ -99,7 +93,6 @@ class Gitea extends Controller
 | 
			
		||||
                $webhook_secret = data_get($application, 'manual_webhook_secret_gitea');
 | 
			
		||||
                $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
 | 
			
		||||
                if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) {
 | 
			
		||||
                    ray('Invalid signature');
 | 
			
		||||
                    $return_payloads->push([
 | 
			
		||||
                        'application' => $application->name,
 | 
			
		||||
                        'status' => 'failed',
 | 
			
		||||
@@ -122,7 +115,6 @@ class Gitea extends Controller
 | 
			
		||||
                    if ($application->isDeployable()) {
 | 
			
		||||
                        $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
 | 
			
		||||
                        if ($is_watch_path_triggered || is_null($application->watch_paths)) {
 | 
			
		||||
                            ray('Deploying '.$application->name.' with branch '.$branch);
 | 
			
		||||
                            $deployment_uuid = new Cuid2;
 | 
			
		||||
                            queue_application_deployment(
 | 
			
		||||
                                application: $application,
 | 
			
		||||
@@ -228,12 +220,9 @@ class Gitea extends Controller
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ray($return_payloads);
 | 
			
		||||
 | 
			
		||||
            return response($return_payloads);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,15 +25,12 @@ class Github extends Controller
 | 
			
		||||
            $return_payloads = collect([]);
 | 
			
		||||
            $x_github_delivery = request()->header('X-GitHub-Delivery');
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $files = Storage::disk('webhooks-during-maintenance')->files();
 | 
			
		||||
                $github_delivery_found = collect($files)->filter(function ($file) use ($x_github_delivery) {
 | 
			
		||||
                    return Str::contains($file, $x_github_delivery);
 | 
			
		||||
                })->first();
 | 
			
		||||
                if ($github_delivery_found) {
 | 
			
		||||
                    ray('Webhook already found');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $data = [
 | 
			
		||||
@@ -73,7 +70,6 @@ class Github extends Controller
 | 
			
		||||
                $removed_files = data_get($payload, 'commits.*.removed');
 | 
			
		||||
                $modified_files = data_get($payload, 'commits.*.modified');
 | 
			
		||||
                $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
 | 
			
		||||
                ray('Manual Webhook GitHub Push Event with branch: '.$branch);
 | 
			
		||||
            }
 | 
			
		||||
            if ($x_github_event === 'pull_request') {
 | 
			
		||||
                $action = data_get($payload, 'action');
 | 
			
		||||
@@ -82,7 +78,6 @@ class Github extends Controller
 | 
			
		||||
                $pull_request_html_url = data_get($payload, 'pull_request.html_url');
 | 
			
		||||
                $branch = data_get($payload, 'pull_request.head.ref');
 | 
			
		||||
                $base_branch = data_get($payload, 'pull_request.base.ref');
 | 
			
		||||
                ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
 | 
			
		||||
            }
 | 
			
		||||
            if (! $branch) {
 | 
			
		||||
                return response('Nothing to do. No branch found in the request.');
 | 
			
		||||
@@ -104,7 +99,6 @@ class Github extends Controller
 | 
			
		||||
                $webhook_secret = data_get($application, 'manual_webhook_secret_github');
 | 
			
		||||
                $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
 | 
			
		||||
                if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) {
 | 
			
		||||
                    ray('Invalid signature');
 | 
			
		||||
                    $return_payloads->push([
 | 
			
		||||
                        'application' => $application->name,
 | 
			
		||||
                        'status' => 'failed',
 | 
			
		||||
@@ -127,7 +121,6 @@ class Github extends Controller
 | 
			
		||||
                    if ($application->isDeployable()) {
 | 
			
		||||
                        $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
 | 
			
		||||
                        if ($is_watch_path_triggered || is_null($application->watch_paths)) {
 | 
			
		||||
                            ray('Deploying '.$application->name.' with branch '.$branch);
 | 
			
		||||
                            $deployment_uuid = new Cuid2;
 | 
			
		||||
                            queue_application_deployment(
 | 
			
		||||
                                application: $application,
 | 
			
		||||
@@ -232,12 +225,9 @@ class Github extends Controller
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ray($return_payloads);
 | 
			
		||||
 | 
			
		||||
            return response($return_payloads);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -249,15 +239,12 @@ class Github extends Controller
 | 
			
		||||
            $id = null;
 | 
			
		||||
            $x_github_delivery = $request->header('X-GitHub-Delivery');
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $files = Storage::disk('webhooks-during-maintenance')->files();
 | 
			
		||||
                $github_delivery_found = collect($files)->filter(function ($file) use ($x_github_delivery) {
 | 
			
		||||
                    return Str::contains($file, $x_github_delivery);
 | 
			
		||||
                })->first();
 | 
			
		||||
                if ($github_delivery_found) {
 | 
			
		||||
                    ray('Webhook already found');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $data = [
 | 
			
		||||
@@ -313,7 +300,6 @@ class Github extends Controller
 | 
			
		||||
                $removed_files = data_get($payload, 'commits.*.removed');
 | 
			
		||||
                $modified_files = data_get($payload, 'commits.*.modified');
 | 
			
		||||
                $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
 | 
			
		||||
                ray('Webhook GitHub Push Event: '.$id.' with branch: '.$branch);
 | 
			
		||||
            }
 | 
			
		||||
            if ($x_github_event === 'pull_request') {
 | 
			
		||||
                $action = data_get($payload, 'action');
 | 
			
		||||
@@ -322,7 +308,6 @@ class Github extends Controller
 | 
			
		||||
                $pull_request_html_url = data_get($payload, 'pull_request.html_url');
 | 
			
		||||
                $branch = data_get($payload, 'pull_request.head.ref');
 | 
			
		||||
                $base_branch = data_get($payload, 'pull_request.base.ref');
 | 
			
		||||
                ray('Webhook GitHub Pull Request Event: '.$id.' with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
 | 
			
		||||
            }
 | 
			
		||||
            if (! $id || ! $branch) {
 | 
			
		||||
                return response('Nothing to do. No id or branch found.');
 | 
			
		||||
@@ -356,7 +341,6 @@ class Github extends Controller
 | 
			
		||||
                    if ($application->isDeployable()) {
 | 
			
		||||
                        $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
 | 
			
		||||
                        if ($is_watch_path_triggered || is_null($application->watch_paths)) {
 | 
			
		||||
                            ray('Deploying '.$application->name.' with branch '.$branch);
 | 
			
		||||
                            $deployment_uuid = new Cuid2;
 | 
			
		||||
                            queue_application_deployment(
 | 
			
		||||
                                application: $application,
 | 
			
		||||
@@ -460,8 +444,6 @@ class Github extends Controller
 | 
			
		||||
 | 
			
		||||
            return response($return_payloads);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -505,7 +487,6 @@ class Github extends Controller
 | 
			
		||||
        try {
 | 
			
		||||
            $installation_id = $request->get('installation_id');
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $data = [
 | 
			
		||||
                    'attributes' => $request->attributes->all(),
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ class Gitlab extends Controller
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $data = [
 | 
			
		||||
                    'attributes' => $request->attributes->all(),
 | 
			
		||||
@@ -67,7 +66,6 @@ class Gitlab extends Controller
 | 
			
		||||
                $removed_files = data_get($payload, 'commits.*.removed');
 | 
			
		||||
                $modified_files = data_get($payload, 'commits.*.modified');
 | 
			
		||||
                $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
 | 
			
		||||
                ray('Manual Webhook GitLab Push Event with branch: '.$branch);
 | 
			
		||||
            }
 | 
			
		||||
            if ($x_gitlab_event === 'merge_request') {
 | 
			
		||||
                $action = data_get($payload, 'object_attributes.action');
 | 
			
		||||
@@ -84,7 +82,6 @@ class Gitlab extends Controller
 | 
			
		||||
 | 
			
		||||
                    return response($return_payloads);
 | 
			
		||||
                }
 | 
			
		||||
                ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
 | 
			
		||||
            }
 | 
			
		||||
            $applications = Application::where('git_repository', 'like', "%$full_name%");
 | 
			
		||||
            if ($x_gitlab_event === 'push') {
 | 
			
		||||
@@ -117,7 +114,6 @@ class Gitlab extends Controller
 | 
			
		||||
                        'status' => 'failed',
 | 
			
		||||
                        'message' => 'Invalid signature.',
 | 
			
		||||
                    ]);
 | 
			
		||||
                    ray('Invalid signature');
 | 
			
		||||
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
@@ -128,7 +124,6 @@ class Gitlab extends Controller
 | 
			
		||||
                        'status' => 'failed',
 | 
			
		||||
                        'message' => 'Server is not functional',
 | 
			
		||||
                    ]);
 | 
			
		||||
                    ray('Server is not functional: '.$application->destination->server->name);
 | 
			
		||||
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
@@ -136,7 +131,6 @@ class Gitlab extends Controller
 | 
			
		||||
                    if ($application->isDeployable()) {
 | 
			
		||||
                        $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
 | 
			
		||||
                        if ($is_watch_path_triggered || is_null($application->watch_paths)) {
 | 
			
		||||
                            ray('Deploying '.$application->name.' with branch '.$branch);
 | 
			
		||||
                            $deployment_uuid = new Cuid2;
 | 
			
		||||
                            queue_application_deployment(
 | 
			
		||||
                                application: $application,
 | 
			
		||||
@@ -171,7 +165,6 @@ class Gitlab extends Controller
 | 
			
		||||
                            'application_uuid' => $application->uuid,
 | 
			
		||||
                            'application_name' => $application->name,
 | 
			
		||||
                        ]);
 | 
			
		||||
                        ray('Deployments disabled for '.$application->name);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if ($x_gitlab_event === 'merge_request') {
 | 
			
		||||
@@ -207,7 +200,6 @@ class Gitlab extends Controller
 | 
			
		||||
                                is_webhook: true,
 | 
			
		||||
                                git_type: 'gitlab'
 | 
			
		||||
                            );
 | 
			
		||||
                            ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id);
 | 
			
		||||
                            $return_payloads->push([
 | 
			
		||||
                                'application' => $application->name,
 | 
			
		||||
                                'status' => 'success',
 | 
			
		||||
@@ -219,7 +211,6 @@ class Gitlab extends Controller
 | 
			
		||||
                                'status' => 'failed',
 | 
			
		||||
                                'message' => 'Preview deployments disabled',
 | 
			
		||||
                            ]);
 | 
			
		||||
                            ray('Preview deployments disabled for '.$application->name);
 | 
			
		||||
                        }
 | 
			
		||||
                    } elseif ($action === 'closed' || $action === 'close' || $action === 'merge') {
 | 
			
		||||
                        $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
 | 
			
		||||
@@ -253,8 +244,6 @@ class Gitlab extends Controller
 | 
			
		||||
 | 
			
		||||
            return response($return_payloads);
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ class Stripe extends Controller
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (app()->isDownForMaintenance()) {
 | 
			
		||||
                ray('Maintenance mode is on');
 | 
			
		||||
                $epoch = now()->valueOf();
 | 
			
		||||
                $data = [
 | 
			
		||||
                    'attributes' => $request->attributes->all(),
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ class Waitlist extends Controller
 | 
			
		||||
    {
 | 
			
		||||
        $email = request()->get('email');
 | 
			
		||||
        $confirmation_code = request()->get('confirmation_code');
 | 
			
		||||
        ray($email, $confirmation_code);
 | 
			
		||||
        try {
 | 
			
		||||
            $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
 | 
			
		||||
            if ($found) {
 | 
			
		||||
@@ -36,7 +35,6 @@ class Waitlist extends Controller
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            send_internal_notification('Waitlist confirmation failed: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
@@ -58,7 +56,6 @@ class Waitlist extends Controller
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        } catch (Exception $e) {
 | 
			
		||||
            send_internal_notification('Waitlist cancellation failed: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ class ApiAllowed
 | 
			
		||||
{
 | 
			
		||||
    public function handle(Request $request, Closure $next): Response
 | 
			
		||||
    {
 | 
			
		||||
        ray()->clearAll();
 | 
			
		||||
        if (isCloud()) {
 | 
			
		||||
            return $next($request);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -208,7 +208,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
                $this->container_name = "{$this->application->settings->custom_internal_name}-pr-{$this->pull_request_id}";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ray('New container name: ', $this->container_name)->green();
 | 
			
		||||
 | 
			
		||||
        $this->saved_outputs = collect();
 | 
			
		||||
 | 
			
		||||
@@ -298,7 +297,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
 | 
			
		||||
                ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
 | 
			
		||||
            }
 | 
			
		||||
            ray($e);
 | 
			
		||||
            $this->fail($e);
 | 
			
		||||
            throw $e;
 | 
			
		||||
        } finally {
 | 
			
		||||
@@ -389,7 +387,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->dockerImageTag = $this->application->docker_registry_image_tag;
 | 
			
		||||
        }
 | 
			
		||||
        ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.'");
 | 
			
		||||
        $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
 | 
			
		||||
        $this->generate_image_names();
 | 
			
		||||
        $this->prepare_builder_image();
 | 
			
		||||
@@ -712,38 +709,26 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
    {
 | 
			
		||||
        $forceFail = true;
 | 
			
		||||
        if (str($this->application->docker_registry_image_name)->isEmpty()) {
 | 
			
		||||
            ray('empty docker_registry_image_name');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->restart_only) {
 | 
			
		||||
            ray('restart_only');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->application->build_pack === 'dockerimage') {
 | 
			
		||||
            ray('dockerimage');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->use_build_server) {
 | 
			
		||||
            ray('use_build_server');
 | 
			
		||||
            $forceFail = true;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->server->isSwarm() && $this->build_pack !== 'dockerimage') {
 | 
			
		||||
            ray('isSwarm');
 | 
			
		||||
            $forceFail = true;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->application->additional_servers->count() > 0) {
 | 
			
		||||
            ray('additional_servers');
 | 
			
		||||
            $forceFail = true;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->is_this_additional_server) {
 | 
			
		||||
            ray('this is an additional_servers, no pushy pushy');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        ray('push_to_docker_registry noww: '.$this->production_image_name);
 | 
			
		||||
        try {
 | 
			
		||||
            instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server);
 | 
			
		||||
            $this->application_deployment_queue->addLogEntry('----------------------------------------');
 | 
			
		||||
@@ -775,7 +760,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            if ($forceFail) {
 | 
			
		||||
                throw new RuntimeException($e->getMessage(), 69420);
 | 
			
		||||
            }
 | 
			
		||||
            ray($e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1386,8 +1370,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if ($destination_ids->contains($this->destination->id)) {
 | 
			
		||||
            ray('Same destination found in additional destinations. Skipping.');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        foreach ($destination_ids as $destination_id) {
 | 
			
		||||
@@ -2449,7 +2431,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
 | 
			
		||||
 | 
			
		||||
        if ($this->application->build_pack !== 'dockercompose') {
 | 
			
		||||
            $code = $exception->getCode();
 | 
			
		||||
            ray($code);
 | 
			
		||||
            if ($code !== 69420) {
 | 
			
		||||
                // 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
 | 
			
		||||
                if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,8 +31,6 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if ($this->application->is_public_repository()) {
 | 
			
		||||
                ray('Public repository. Skipping comment update.');
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if ($this->status === ProcessStatus::CLOSED) {
 | 
			
		||||
@@ -53,16 +51,12 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
 | 
			
		||||
            $this->body .= '[Open Build Logs]('.$this->build_logs_url.")\n\n\n";
 | 
			
		||||
            $this->body .= 'Last updated at: '.now()->toDateTimeString().' CET';
 | 
			
		||||
 | 
			
		||||
            ray('Updating comment', $this->body);
 | 
			
		||||
            if ($this->preview->pull_request_issue_comment_id) {
 | 
			
		||||
                $this->update_comment();
 | 
			
		||||
            } else {
 | 
			
		||||
                $this->create_comment();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e);
 | 
			
		||||
 | 
			
		||||
            return $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -73,7 +67,6 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            'body' => $this->body,
 | 
			
		||||
        ], throwError: false);
 | 
			
		||||
        if (data_get($data, 'message') === 'Not Found') {
 | 
			
		||||
            ray('Comment not found. Creating new one.');
 | 
			
		||||
            $this->create_comment();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								app/Jobs/CheckAndStartSentinelJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								app/Jobs/CheckAndStartSentinelJob.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Server\StartSentinel;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
class CheckAndStartSentinelJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public $timeout = 120;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public Server $server) {}
 | 
			
		||||
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        $latestVersion = get_latest_sentinel_version();
 | 
			
		||||
 | 
			
		||||
        // Check if sentinel is running
 | 
			
		||||
        $sentinelFound = instant_remote_process(['docker inspect coolify-sentinel'], $this->server, false);
 | 
			
		||||
        $sentinelFoundJson = json_decode($sentinelFound, true);
 | 
			
		||||
        $sentinelStatus = data_get($sentinelFoundJson, '0.State.Status', 'exited');
 | 
			
		||||
        if ($sentinelStatus !== 'running') {
 | 
			
		||||
            StartSentinel::run(server: $this->server, restart: true, latestVersion: $latestVersion);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // If sentinel is running, check if it needs an update
 | 
			
		||||
        $runningVersion = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false);
 | 
			
		||||
        if (empty($runningVersion)) {
 | 
			
		||||
            $runningVersion = '0.0.0';
 | 
			
		||||
        }
 | 
			
		||||
        if ($latestVersion === '0.0.0' && $runningVersion === '0.0.0') {
 | 
			
		||||
            StartSentinel::run(server: $this->server, restart: true, latestVersion: 'latest');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (version_compare($runningVersion, $latestVersion, '<')) {
 | 
			
		||||
                StartSentinel::run(server: $this->server, restart: true, latestVersion: $latestVersion);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								app/Jobs/CheckHelperImageJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/Jobs/CheckHelperImageJob.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Facades\Http;
 | 
			
		||||
 | 
			
		||||
class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public $timeout = 1000;
 | 
			
		||||
 | 
			
		||||
    public function __construct() {}
 | 
			
		||||
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
 | 
			
		||||
            if ($response->successful()) {
 | 
			
		||||
                $versions = $response->json();
 | 
			
		||||
                $settings = instanceSettings();
 | 
			
		||||
                $latest_version = data_get($versions, 'coolify.helper.version');
 | 
			
		||||
                $current_version = $settings->helper_version;
 | 
			
		||||
                if (version_compare($latest_version, $current_version, '>')) {
 | 
			
		||||
                    $settings->update(['helper_version' => $latest_version]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('CheckHelperImageJob failed with: '.$e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +22,6 @@ class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            CheckResaleLicense::run();
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('CheckResaleLicenseJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e);
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,18 +20,15 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            ray('Cleaning up helper containers on '.$this->server->name);
 | 
			
		||||
            $containers = instant_remote_process(['docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image | contains("ghcr.io/coollabsio/coolify-helper")))\''], $this->server, false);
 | 
			
		||||
            $containerIds = collect(json_decode($containers))->pluck('ID');
 | 
			
		||||
            if ($containerIds->count() > 0) {
 | 
			
		||||
                foreach ($containerIds as $containerId) {
 | 
			
		||||
                    ray('Removing container '.$containerId);
 | 
			
		||||
                    instant_remote_process(['docker container rm -f '.$containerId], $this->server, false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('CleanupHelperContainersJob failed with error: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,13 +29,11 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
 | 
			
		||||
            // $this->cleanup_waitlist();
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            $this->cleanup_invitation_link();
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -72,7 +72,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
 | 
			
		||||
            if (data_get($this->backup, 'database_type') === \App\Models\ServiceDatabase::class) {
 | 
			
		||||
                $this->database = data_get($this->backup, 'database');
 | 
			
		||||
                $this->server = $this->database->service->server;
 | 
			
		||||
                $this->s3 = $this->backup->s3;
 | 
			
		||||
@@ -92,11 +92,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
 | 
			
		||||
            $status = str(data_get($this->database, 'status'));
 | 
			
		||||
            if (! $status->startsWith('running') && $this->database->id !== 0) {
 | 
			
		||||
                ray('database not running');
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
 | 
			
		||||
            if (data_get($this->backup, 'database_type') === \App\Models\ServiceDatabase::class) {
 | 
			
		||||
                $databaseType = $this->database->databaseType();
 | 
			
		||||
                $serviceUuid = $this->database->service->uuid;
 | 
			
		||||
                $serviceName = str($this->database->service->name)->slug();
 | 
			
		||||
@@ -222,7 +220,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
                    // Format: db1:collection1,collection2|db2:collection3,collection4
 | 
			
		||||
                    $databasesToBackup = explode('|', $databasesToBackup);
 | 
			
		||||
                    $databasesToBackup = array_map('trim', $databasesToBackup);
 | 
			
		||||
                    ray($databasesToBackup);
 | 
			
		||||
                } elseif (str($databaseType)->contains('mysql')) {
 | 
			
		||||
                    // Format: db1,db2,db3
 | 
			
		||||
                    $databasesToBackup = explode(',', $databasesToBackup);
 | 
			
		||||
@@ -244,7 +241,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($databasesToBackup as $database) {
 | 
			
		||||
                $size = 0;
 | 
			
		||||
                ray('Backing up '.$database);
 | 
			
		||||
                try {
 | 
			
		||||
                    if (str($databaseType)->contains('postgres')) {
 | 
			
		||||
                        $this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
 | 
			
		||||
@@ -377,10 +373,8 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            if ($this->backup_output === '') {
 | 
			
		||||
                $this->backup_output = null;
 | 
			
		||||
            }
 | 
			
		||||
            ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            $this->add_to_backup_output($e->getMessage());
 | 
			
		||||
            ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -400,16 +394,13 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $commands[] = $backupCommand;
 | 
			
		||||
            ray($commands);
 | 
			
		||||
            $this->backup_output = instant_remote_process($commands, $this->server);
 | 
			
		||||
            $this->backup_output = trim($this->backup_output);
 | 
			
		||||
            if ($this->backup_output === '') {
 | 
			
		||||
                $this->backup_output = null;
 | 
			
		||||
            }
 | 
			
		||||
            ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            $this->add_to_backup_output($e->getMessage());
 | 
			
		||||
            ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -428,10 +419,8 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            if ($this->backup_output === '') {
 | 
			
		||||
                $this->backup_output = null;
 | 
			
		||||
            }
 | 
			
		||||
            ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            $this->add_to_backup_output($e->getMessage());
 | 
			
		||||
            ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -445,16 +434,13 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            } else {
 | 
			
		||||
                $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
 | 
			
		||||
            }
 | 
			
		||||
            ray($commands);
 | 
			
		||||
            $this->backup_output = instant_remote_process($commands, $this->server);
 | 
			
		||||
            $this->backup_output = trim($this->backup_output);
 | 
			
		||||
            if ($this->backup_output === '') {
 | 
			
		||||
                $this->backup_output = null;
 | 
			
		||||
            }
 | 
			
		||||
            ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            $this->add_to_backup_output($e->getMessage());
 | 
			
		||||
            ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -498,14 +484,12 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            $bucket = $this->s3->bucket;
 | 
			
		||||
            $endpoint = $this->s3->endpoint;
 | 
			
		||||
            $this->s3->testConnection(shouldSave: true);
 | 
			
		||||
            if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
 | 
			
		||||
            if (data_get($this->backup, 'database_type') === \App\Models\ServiceDatabase::class) {
 | 
			
		||||
                $network = $this->database->service->destination->network;
 | 
			
		||||
            } else {
 | 
			
		||||
                $network = $this->database->destination->network;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->ensureHelperImageAvailable();
 | 
			
		||||
 | 
			
		||||
            $fullImageName = $this->getFullImageName();
 | 
			
		||||
 | 
			
		||||
            if (isDev()) {
 | 
			
		||||
@@ -538,35 +522,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function ensureHelperImageAvailable(): void
 | 
			
		||||
    {
 | 
			
		||||
        $fullImageName = $this->getFullImageName();
 | 
			
		||||
 | 
			
		||||
        $imageExists = $this->checkImageExists($fullImageName);
 | 
			
		||||
 | 
			
		||||
        if (! $imageExists) {
 | 
			
		||||
            $this->pullHelperImage($fullImageName);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkImageExists(string $fullImageName): bool
 | 
			
		||||
    {
 | 
			
		||||
        $result = instant_remote_process(["docker image inspect {$fullImageName} >/dev/null 2>&1 && echo 'exists' || echo 'not exists'"], $this->server, false);
 | 
			
		||||
 | 
			
		||||
        return trim($result) === 'exists';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function pullHelperImage(string $fullImageName): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            instant_remote_process(["docker pull {$fullImageName}"], $this->server);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            $errorMessage = 'Failed to pull helper image: '.$e->getMessage();
 | 
			
		||||
            $this->add_to_backup_output($errorMessage);
 | 
			
		||||
            throw new \RuntimeException($errorMessage);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getFullImageName(): string
 | 
			
		||||
    {
 | 
			
		||||
        $settings = instanceSettings();
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,6 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Facades\Http;
 | 
			
		||||
 | 
			
		||||
class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
@@ -17,29 +16,12 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
 | 
			
		||||
    public $timeout = 1000;
 | 
			
		||||
 | 
			
		||||
    public function __construct() {}
 | 
			
		||||
    public function __construct(public Server $server) {}
 | 
			
		||||
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
 | 
			
		||||
            if ($response->successful()) {
 | 
			
		||||
                $versions = $response->json();
 | 
			
		||||
                $settings = instanceSettings();
 | 
			
		||||
                $latest_version = data_get($versions, 'coolify.helper.version');
 | 
			
		||||
                $current_version = $settings->helper_version;
 | 
			
		||||
                if (version_compare($latest_version, $current_version, '>')) {
 | 
			
		||||
                    // New version available
 | 
			
		||||
                    // $helperImage = config('coolify.helper_image');
 | 
			
		||||
                    // instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server);
 | 
			
		||||
                    $settings->update(['helper_version' => $latest_version]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('PullHelperImageJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
        $helperImage = config('coolify.helper_image');
 | 
			
		||||
        $latest_version = instanceSettings()->helper_version;
 | 
			
		||||
        instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server, false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Server\StartSentinel;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public $timeout = 1000;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public Server $server) {}
 | 
			
		||||
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $version = get_latest_sentinel_version();
 | 
			
		||||
            if (! $version) {
 | 
			
		||||
                ray('Failed to get latest Sentinel version');
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            $local_version = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false);
 | 
			
		||||
            if (empty($local_version)) {
 | 
			
		||||
                $local_version = '0.0.0';
 | 
			
		||||
            }
 | 
			
		||||
            if (version_compare($local_version, $version, '<')) {
 | 
			
		||||
                StartSentinel::run($this->server, $version, true);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            ray('Sentinel image is up to date');
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            // send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +25,6 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            if (isDev() || isCloud()) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            ray('PullTemplatesAndVersions service-templates');
 | 
			
		||||
            $response = Http::retry(3, 1000)->get(config('constants.services.official'));
 | 
			
		||||
            if ($response->successful()) {
 | 
			
		||||
                $services = $response->json();
 | 
			
		||||
@@ -35,7 +34,6 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('PullTemplatesAndVersions failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Facades\File;
 | 
			
		||||
use Illuminate\Support\Facades\Http;
 | 
			
		||||
 | 
			
		||||
class PullVersionsFromCDN implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public $timeout = 10;
 | 
			
		||||
 | 
			
		||||
    public function __construct() {}
 | 
			
		||||
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (! isDev() && ! isCloud()) {
 | 
			
		||||
                ray('PullTemplatesAndVersions versions.json');
 | 
			
		||||
                $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
 | 
			
		||||
                if ($response->successful()) {
 | 
			
		||||
                    $versions = $response->json();
 | 
			
		||||
                    File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
 | 
			
		||||
                } else {
 | 
			
		||||
                    send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										368
									
								
								app/Jobs/PushServerUpdateJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								app/Jobs/PushServerUpdateJob.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,368 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Database\StartDatabaseProxy;
 | 
			
		||||
use App\Actions\Database\StopDatabaseProxy;
 | 
			
		||||
use App\Actions\Proxy\CheckProxy;
 | 
			
		||||
use App\Actions\Proxy\StartProxy;
 | 
			
		||||
use App\Actions\Server\StartLogDrain;
 | 
			
		||||
use App\Actions\Shared\ComplexStatusCheck;
 | 
			
		||||
use App\Models\Application;
 | 
			
		||||
use App\Models\ApplicationPreview;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\ServiceApplication;
 | 
			
		||||
use App\Models\ServiceDatabase;
 | 
			
		||||
use App\Notifications\Container\ContainerRestarted;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
 | 
			
		||||
class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public $tries = 1;
 | 
			
		||||
 | 
			
		||||
    public $timeout = 30;
 | 
			
		||||
 | 
			
		||||
    public Collection $containers;
 | 
			
		||||
 | 
			
		||||
    public Collection $applications;
 | 
			
		||||
 | 
			
		||||
    public Collection $previews;
 | 
			
		||||
 | 
			
		||||
    public Collection $databases;
 | 
			
		||||
 | 
			
		||||
    public Collection $services;
 | 
			
		||||
 | 
			
		||||
    public Collection $allApplicationIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $allDatabaseUuids;
 | 
			
		||||
 | 
			
		||||
    public Collection $allTcpProxyUuids;
 | 
			
		||||
 | 
			
		||||
    public Collection $allServiceApplicationIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $allApplicationPreviewsIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $allServiceDatabaseIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $allApplicationsWithAdditionalServers;
 | 
			
		||||
 | 
			
		||||
    public Collection $foundApplicationIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $foundDatabaseUuids;
 | 
			
		||||
 | 
			
		||||
    public Collection $foundServiceApplicationIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $foundServiceDatabaseIds;
 | 
			
		||||
 | 
			
		||||
    public Collection $foundApplicationPreviewsIds;
 | 
			
		||||
 | 
			
		||||
    public bool $foundProxy = false;
 | 
			
		||||
 | 
			
		||||
    public bool $foundLogDrainContainer = false;
 | 
			
		||||
 | 
			
		||||
    public function backoff(): int
 | 
			
		||||
    {
 | 
			
		||||
        return isDev() ? 1 : 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(public Server $server, public $data)
 | 
			
		||||
    {
 | 
			
		||||
        $this->containers = collect();
 | 
			
		||||
        $this->foundApplicationIds = collect();
 | 
			
		||||
        $this->foundDatabaseUuids = collect();
 | 
			
		||||
        $this->foundServiceApplicationIds = collect();
 | 
			
		||||
        $this->foundApplicationPreviewsIds = collect();
 | 
			
		||||
        $this->foundServiceDatabaseIds = collect();
 | 
			
		||||
        $this->allApplicationIds = collect();
 | 
			
		||||
        $this->allDatabaseUuids = collect();
 | 
			
		||||
        $this->allTcpProxyUuids = collect();
 | 
			
		||||
        $this->allServiceApplicationIds = collect();
 | 
			
		||||
        $this->allServiceDatabaseIds = collect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: Swarm is not supported yet
 | 
			
		||||
        if (! $this->data) {
 | 
			
		||||
            throw new \Exception('No data provided');
 | 
			
		||||
        }
 | 
			
		||||
        $data = collect($this->data);
 | 
			
		||||
 | 
			
		||||
        $this->server->sentinelHeartbeat();
 | 
			
		||||
 | 
			
		||||
        $this->containers = collect(data_get($data, 'containers'));
 | 
			
		||||
 | 
			
		||||
        $filesystemUsageRoot = data_get($data, 'filesystem_usage_root.used_percentage');
 | 
			
		||||
        ServerStorageCheckJob::dispatch($this->server, $filesystemUsageRoot);
 | 
			
		||||
 | 
			
		||||
        if ($this->containers->isEmpty()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $this->applications = $this->server->applications();
 | 
			
		||||
        $this->databases = $this->server->databases();
 | 
			
		||||
        $this->previews = $this->server->previews();
 | 
			
		||||
        $this->services = $this->server->services()->get();
 | 
			
		||||
        $this->allApplicationIds = $this->applications->filter(function ($application) {
 | 
			
		||||
            return $application->additional_servers->count() === 0;
 | 
			
		||||
        })->pluck('id');
 | 
			
		||||
        $this->allApplicationsWithAdditionalServers = $this->applications->filter(function ($application) {
 | 
			
		||||
            return $application->additional_servers->count() > 0;
 | 
			
		||||
        });
 | 
			
		||||
        $this->allApplicationPreviewsIds = $this->previews->pluck('id');
 | 
			
		||||
        $this->allDatabaseUuids = $this->databases->pluck('uuid');
 | 
			
		||||
        $this->allTcpProxyUuids = $this->databases->where('is_public', true)->pluck('uuid');
 | 
			
		||||
        $this->services->each(function ($service) {
 | 
			
		||||
            $service->applications()->pluck('id')->each(function ($applicationId) {
 | 
			
		||||
                $this->allServiceApplicationIds->push($applicationId);
 | 
			
		||||
            });
 | 
			
		||||
            $service->databases()->pluck('id')->each(function ($databaseId) {
 | 
			
		||||
                $this->allServiceDatabaseIds->push($databaseId);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        foreach ($this->containers as $container) {
 | 
			
		||||
            $containerStatus = data_get($container, 'state', 'exited');
 | 
			
		||||
            $containerHealth = data_get($container, 'health_status', 'unhealthy');
 | 
			
		||||
            $containerStatus = "$containerStatus ($containerHealth)";
 | 
			
		||||
            $labels = collect(data_get($container, 'labels'));
 | 
			
		||||
            $coolify_managed = $labels->has('coolify.managed');
 | 
			
		||||
            if ($coolify_managed) {
 | 
			
		||||
                $name = data_get($container, 'name');
 | 
			
		||||
                if ($name === 'coolify-log-drain' && $this->isRunning($containerStatus)) {
 | 
			
		||||
                    $this->foundLogDrainContainer = true;
 | 
			
		||||
                }
 | 
			
		||||
                if ($labels->has('coolify.applicationId')) {
 | 
			
		||||
                    $applicationId = $labels->get('coolify.applicationId');
 | 
			
		||||
                    $pullRequestId = data_get($labels, 'coolify.pullRequestId', '0');
 | 
			
		||||
                    try {
 | 
			
		||||
                        if ($pullRequestId === '0') {
 | 
			
		||||
                            if ($this->allApplicationIds->contains($applicationId) && $this->isRunning($containerStatus)) {
 | 
			
		||||
                                $this->foundApplicationIds->push($applicationId);
 | 
			
		||||
                            }
 | 
			
		||||
                            $this->updateApplicationStatus($applicationId, $containerStatus);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if ($this->allApplicationPreviewsIds->contains($applicationId) && $this->isRunning($containerStatus)) {
 | 
			
		||||
                                $this->foundApplicationPreviewsIds->push($applicationId);
 | 
			
		||||
                            }
 | 
			
		||||
                            $this->updateApplicationPreviewStatus($applicationId, $containerStatus);
 | 
			
		||||
                        }
 | 
			
		||||
                    } catch (\Exception $e) {
 | 
			
		||||
                    }
 | 
			
		||||
                } elseif ($labels->has('coolify.serviceId')) {
 | 
			
		||||
                    $serviceId = $labels->get('coolify.serviceId');
 | 
			
		||||
                    $subType = $labels->get('coolify.service.subType');
 | 
			
		||||
                    $subId = $labels->get('coolify.service.subId');
 | 
			
		||||
                    if ($subType === 'application' && $this->isRunning($containerStatus)) {
 | 
			
		||||
                        $this->foundServiceApplicationIds->push($subId);
 | 
			
		||||
                        $this->updateServiceSubStatus($serviceId, $subType, $subId, $containerStatus);
 | 
			
		||||
                    } elseif ($subType === 'database' && $this->isRunning($containerStatus)) {
 | 
			
		||||
                        $this->foundServiceDatabaseIds->push($subId);
 | 
			
		||||
                        $this->updateServiceSubStatus($serviceId, $subType, $subId, $containerStatus);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                } else {
 | 
			
		||||
                    $uuid = $labels->get('com.docker.compose.service');
 | 
			
		||||
                    $type = $labels->get('coolify.type');
 | 
			
		||||
                    if ($name === 'coolify-proxy' && $this->isRunning($containerStatus)) {
 | 
			
		||||
                        $this->foundProxy = true;
 | 
			
		||||
                    } elseif ($type === 'service' && $this->isRunning($containerStatus)) {
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if ($this->allDatabaseUuids->contains($uuid) && $this->isRunning($containerStatus)) {
 | 
			
		||||
                            $this->foundDatabaseUuids->push($uuid);
 | 
			
		||||
                            if ($this->allTcpProxyUuids->contains($uuid) && $this->isRunning($containerStatus)) {
 | 
			
		||||
                                $this->updateDatabaseStatus($uuid, $containerStatus, tcpProxy: true);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                $this->updateDatabaseStatus($uuid, $containerStatus, tcpProxy: false);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->updateProxyStatus();
 | 
			
		||||
 | 
			
		||||
        $this->updateNotFoundApplicationStatus();
 | 
			
		||||
        $this->updateNotFoundApplicationPreviewStatus();
 | 
			
		||||
        $this->updateNotFoundDatabaseStatus();
 | 
			
		||||
        $this->updateNotFoundServiceStatus();
 | 
			
		||||
 | 
			
		||||
        $this->updateAdditionalServersStatus();
 | 
			
		||||
 | 
			
		||||
        $this->checkLogDrainContainer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateApplicationStatus(string $applicationId, string $containerStatus)
 | 
			
		||||
    {
 | 
			
		||||
        $application = $this->applications->where('id', $applicationId)->first();
 | 
			
		||||
        if (! $application) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $application->status = $containerStatus;
 | 
			
		||||
        $application->save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateApplicationPreviewStatus(string $applicationId, string $containerStatus)
 | 
			
		||||
    {
 | 
			
		||||
        $application = $this->previews->where('id', $applicationId)->first();
 | 
			
		||||
        if (! $application) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $application->status = $containerStatus;
 | 
			
		||||
        $application->save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateNotFoundApplicationStatus()
 | 
			
		||||
    {
 | 
			
		||||
        $notFoundApplicationIds = $this->allApplicationIds->diff($this->foundApplicationIds);
 | 
			
		||||
        if ($notFoundApplicationIds->isNotEmpty()) {
 | 
			
		||||
            $notFoundApplicationIds->each(function ($applicationId) {
 | 
			
		||||
                $application = Application::find($applicationId);
 | 
			
		||||
                if ($application) {
 | 
			
		||||
                    $application->status = 'exited';
 | 
			
		||||
                    $application->save();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateNotFoundApplicationPreviewStatus()
 | 
			
		||||
    {
 | 
			
		||||
        $notFoundApplicationPreviewsIds = $this->allApplicationPreviewsIds->diff($this->foundApplicationPreviewsIds);
 | 
			
		||||
        if ($notFoundApplicationPreviewsIds->isNotEmpty()) {
 | 
			
		||||
            $notFoundApplicationPreviewsIds->each(function ($applicationPreviewId) {
 | 
			
		||||
                $applicationPreview = ApplicationPreview::find($applicationPreviewId);
 | 
			
		||||
                if ($applicationPreview) {
 | 
			
		||||
                    $applicationPreview->status = 'exited';
 | 
			
		||||
                    $applicationPreview->save();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateProxyStatus()
 | 
			
		||||
    {
 | 
			
		||||
        // If proxy is not found, start it
 | 
			
		||||
        if ($this->server->isProxyShouldRun()) {
 | 
			
		||||
            if ($this->foundProxy === false) {
 | 
			
		||||
                try {
 | 
			
		||||
                    if (CheckProxy::run($this->server)) {
 | 
			
		||||
                        StartProxy::run($this->server, false);
 | 
			
		||||
                        $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (\Throwable $e) {
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
			
		||||
                instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateDatabaseStatus(string $databaseUuid, string $containerStatus, bool $tcpProxy = false)
 | 
			
		||||
    {
 | 
			
		||||
        $database = $this->databases->where('uuid', $databaseUuid)->first();
 | 
			
		||||
        if (! $database) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        $database->status = $containerStatus;
 | 
			
		||||
        $database->save();
 | 
			
		||||
        if ($this->isRunning($containerStatus) && $tcpProxy) {
 | 
			
		||||
            $tcpProxyContainerFound = $this->containers->filter(function ($value, $key) use ($databaseUuid) {
 | 
			
		||||
                return data_get($value, 'name') === "$databaseUuid-proxy" && data_get($value, 'state') === 'running';
 | 
			
		||||
            })->first();
 | 
			
		||||
            if (! $tcpProxyContainerFound) {
 | 
			
		||||
                StartDatabaseProxy::dispatch($database);
 | 
			
		||||
                $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
 | 
			
		||||
            } else {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateNotFoundDatabaseStatus()
 | 
			
		||||
    {
 | 
			
		||||
        $notFoundDatabaseUuids = $this->allDatabaseUuids->diff($this->foundDatabaseUuids);
 | 
			
		||||
        if ($notFoundDatabaseUuids->isNotEmpty()) {
 | 
			
		||||
            $notFoundDatabaseUuids->each(function ($databaseUuid) {
 | 
			
		||||
                $database = $this->databases->where('uuid', $databaseUuid)->first();
 | 
			
		||||
                if ($database) {
 | 
			
		||||
                    $database->status = 'exited';
 | 
			
		||||
                    $database->save();
 | 
			
		||||
                    if ($database->is_public) {
 | 
			
		||||
                        StopDatabaseProxy::dispatch($database);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateServiceSubStatus(string $serviceId, string $subType, string $subId, string $containerStatus)
 | 
			
		||||
    {
 | 
			
		||||
        $service = $this->services->where('id', $serviceId)->first();
 | 
			
		||||
        if (! $service) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if ($subType === 'application') {
 | 
			
		||||
            $application = $service->applications()->where('id', $subId)->first();
 | 
			
		||||
            $application->status = $containerStatus;
 | 
			
		||||
            $application->save();
 | 
			
		||||
        } elseif ($subType === 'database') {
 | 
			
		||||
            $database = $service->databases()->where('id', $subId)->first();
 | 
			
		||||
            $database->status = $containerStatus;
 | 
			
		||||
            $database->save();
 | 
			
		||||
        } else {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateNotFoundServiceStatus()
 | 
			
		||||
    {
 | 
			
		||||
        $notFoundServiceApplicationIds = $this->allServiceApplicationIds->diff($this->foundServiceApplicationIds);
 | 
			
		||||
        $notFoundServiceDatabaseIds = $this->allServiceDatabaseIds->diff($this->foundServiceDatabaseIds);
 | 
			
		||||
        if ($notFoundServiceApplicationIds->isNotEmpty()) {
 | 
			
		||||
            $notFoundServiceApplicationIds->each(function ($serviceApplicationId) {
 | 
			
		||||
                $application = ServiceApplication::find($serviceApplicationId);
 | 
			
		||||
                if ($application) {
 | 
			
		||||
                    $application->status = 'exited';
 | 
			
		||||
                    $application->save();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if ($notFoundServiceDatabaseIds->isNotEmpty()) {
 | 
			
		||||
            $notFoundServiceDatabaseIds->each(function ($serviceDatabaseId) {
 | 
			
		||||
                $database = ServiceDatabase::find($serviceDatabaseId);
 | 
			
		||||
                if ($database) {
 | 
			
		||||
                    $database->status = 'exited';
 | 
			
		||||
                    $database->save();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function updateAdditionalServersStatus()
 | 
			
		||||
    {
 | 
			
		||||
        $this->allApplicationsWithAdditionalServers->each(function ($application) {
 | 
			
		||||
            ComplexStatusCheck::run($application);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function isRunning(string $containerStatus)
 | 
			
		||||
    {
 | 
			
		||||
        return str($containerStatus)->contains('running');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkLogDrainContainer()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) {
 | 
			
		||||
            StartLogDrain::dispatch($this->server);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -31,7 +31,6 @@ class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            send_user_an_email($mail, $this->email);
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification("SendConfirmationForWaitlistJob failed for {$this->email} with error: ".$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Notifications\Dto\DiscordMessage;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
@@ -29,7 +30,7 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
    public int $maxExceptions = 5;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $text,
 | 
			
		||||
        public DiscordMessage $message,
 | 
			
		||||
        public string $webhookUrl
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
@@ -38,9 +39,6 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
     */
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        $payload = [
 | 
			
		||||
            'content' => $this->text,
 | 
			
		||||
        ];
 | 
			
		||||
        Http::post($this->webhookUrl, $payload);
 | 
			
		||||
        Http::post($this->webhookUrl, $this->message->toPayload());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,11 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Database\StartDatabaseProxy;
 | 
			
		||||
use App\Actions\Docker\GetContainersStatus;
 | 
			
		||||
use App\Actions\Proxy\CheckProxy;
 | 
			
		||||
use App\Actions\Proxy\StartProxy;
 | 
			
		||||
use App\Actions\Server\InstallLogDrain;
 | 
			
		||||
use App\Models\ApplicationPreview;
 | 
			
		||||
use App\Actions\Server\StartLogDrain;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Models\ServiceDatabase;
 | 
			
		||||
use App\Notifications\Container\ContainerRestarted;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
@@ -17,7 +14,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Arr;
 | 
			
		||||
 | 
			
		||||
class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
@@ -47,74 +43,64 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if ($this->server->serverStatus() === false) {
 | 
			
		||||
                return 'Server is not reachable or not ready.';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->applications = $this->server->applications();
 | 
			
		||||
            $this->databases = $this->server->databases();
 | 
			
		||||
            $this->services = $this->server->services()->get();
 | 
			
		||||
            $this->previews = $this->server->previews();
 | 
			
		||||
 | 
			
		||||
            $up = $this->serverStatus();
 | 
			
		||||
            if (! $up) {
 | 
			
		||||
                ray('Server is not reachable.');
 | 
			
		||||
 | 
			
		||||
                return 'Server is not reachable.';
 | 
			
		||||
            }
 | 
			
		||||
            if (! $this->server->isFunctional()) {
 | 
			
		||||
                ray('Server is not ready.');
 | 
			
		||||
 | 
			
		||||
                return 'Server is not ready.';
 | 
			
		||||
            }
 | 
			
		||||
            if (! $this->server->isSwarmWorker() && ! $this->server->isBuildServer()) {
 | 
			
		||||
                ['containers' => $this->containers, 'containerReplicates' => $containerReplicates] = $this->server->getContainers();
 | 
			
		||||
                if (is_null($this->containers)) {
 | 
			
		||||
                    return 'No containers found.';
 | 
			
		||||
                }
 | 
			
		||||
                ServerStorageCheckJob::dispatch($this->server);
 | 
			
		||||
                GetContainersStatus::run($this->server, $this->containers, $containerReplicates);
 | 
			
		||||
 | 
			
		||||
                if ($this->server->isSentinelEnabled()) {
 | 
			
		||||
                    CheckAndStartSentinelJob::dispatch($this->server);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ($this->server->isLogDrainEnabled()) {
 | 
			
		||||
                    $this->checkLogDrainContainer();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ($this->server->proxySet() && ! $this->server->proxy->force_stop) {
 | 
			
		||||
                    $this->server->proxyType();
 | 
			
		||||
                    $foundProxyContainer = $this->containers->filter(function ($value, $key) {
 | 
			
		||||
                        if ($this->server->isSwarm()) {
 | 
			
		||||
                            return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
			
		||||
                        } else {
 | 
			
		||||
                            return data_get($value, 'Name') === '/coolify-proxy';
 | 
			
		||||
                        }
 | 
			
		||||
                    })->first();
 | 
			
		||||
                    if (! $foundProxyContainer) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            $shouldStart = CheckProxy::run($this->server);
 | 
			
		||||
                            if ($shouldStart) {
 | 
			
		||||
                                StartProxy::run($this->server, false);
 | 
			
		||||
                                $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (\Throwable $e) {
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
 | 
			
		||||
                        $this->server->save();
 | 
			
		||||
                        $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
			
		||||
                        instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function serverStatus()
 | 
			
		||||
    {
 | 
			
		||||
        ['uptime' => $uptime] = $this->server->validateConnection(false);
 | 
			
		||||
        if ($uptime) {
 | 
			
		||||
            if ($this->server->unreachable_notification_sent === true) {
 | 
			
		||||
                $this->server->update(['unreachable_notification_sent' => false]);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // $this->server->team?->notify(new Unreachable($this->server));
 | 
			
		||||
            foreach ($this->applications as $application) {
 | 
			
		||||
                $application->update(['status' => 'exited']);
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($this->databases as $database) {
 | 
			
		||||
                $database->update(['status' => 'exited']);
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($this->services as $service) {
 | 
			
		||||
                $apps = $service->applications()->get();
 | 
			
		||||
                $dbs = $service->databases()->get();
 | 
			
		||||
                foreach ($apps as $app) {
 | 
			
		||||
                    $app->update(['status' => 'exited']);
 | 
			
		||||
                }
 | 
			
		||||
                foreach ($dbs as $db) {
 | 
			
		||||
                    $db->update(['status' => 'exited']);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkLogDrainContainer()
 | 
			
		||||
    {
 | 
			
		||||
        $foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
 | 
			
		||||
@@ -123,295 +109,10 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
        if ($foundLogDrainContainer) {
 | 
			
		||||
            $status = data_get($foundLogDrainContainer, 'State.Status');
 | 
			
		||||
            if ($status !== 'running') {
 | 
			
		||||
                InstallLogDrain::dispatch($this->server);
 | 
			
		||||
                StartLogDrain::dispatch($this->server);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            InstallLogDrain::dispatch($this->server);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function containerStatus()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $foundApplications = [];
 | 
			
		||||
        $foundApplicationPreviews = [];
 | 
			
		||||
        $foundDatabases = [];
 | 
			
		||||
        $foundServices = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->containers as $container) {
 | 
			
		||||
            if ($this->server->isSwarm()) {
 | 
			
		||||
                $labels = data_get($container, 'Spec.Labels');
 | 
			
		||||
                $uuid = data_get($labels, 'coolify.name');
 | 
			
		||||
            } else {
 | 
			
		||||
                $labels = data_get($container, 'Config.Labels');
 | 
			
		||||
            }
 | 
			
		||||
            $containerStatus = data_get($container, 'State.Status');
 | 
			
		||||
            $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
 | 
			
		||||
            $containerStatus = "$containerStatus ($containerHealth)";
 | 
			
		||||
            $labels = Arr::undot(format_docker_labels_to_json($labels));
 | 
			
		||||
            $applicationId = data_get($labels, 'coolify.applicationId');
 | 
			
		||||
            if ($applicationId) {
 | 
			
		||||
                $pullRequestId = data_get($labels, 'coolify.pullRequestId');
 | 
			
		||||
                if ($pullRequestId) {
 | 
			
		||||
                    if (str($applicationId)->contains('-')) {
 | 
			
		||||
                        $applicationId = str($applicationId)->before('-');
 | 
			
		||||
                    }
 | 
			
		||||
                    $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
 | 
			
		||||
                    if ($preview) {
 | 
			
		||||
                        $foundApplicationPreviews[] = $preview->id;
 | 
			
		||||
                        $statusFromDb = $preview->status;
 | 
			
		||||
                        if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
                            $preview->update(['status' => $containerStatus]);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        //Notify user that this container should not be there.
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $application = $this->applications->where('id', $applicationId)->first();
 | 
			
		||||
                    if ($application) {
 | 
			
		||||
                        $foundApplications[] = $application->id;
 | 
			
		||||
                        $statusFromDb = $application->status;
 | 
			
		||||
                        if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
                            $application->update(['status' => $containerStatus]);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        //Notify user that this container should not be there.
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $uuid = data_get($labels, 'com.docker.compose.service');
 | 
			
		||||
                $type = data_get($labels, 'coolify.type');
 | 
			
		||||
 | 
			
		||||
                if ($uuid) {
 | 
			
		||||
                    if ($type === 'service') {
 | 
			
		||||
                        $database_id = data_get($labels, 'coolify.service.subId');
 | 
			
		||||
                        if ($database_id) {
 | 
			
		||||
                            $service_db = ServiceDatabase::where('id', $database_id)->first();
 | 
			
		||||
                            if ($service_db) {
 | 
			
		||||
                                $uuid = data_get($service_db, 'service.uuid');
 | 
			
		||||
                                if ($uuid) {
 | 
			
		||||
                                    $isPublic = data_get($service_db, 'is_public');
 | 
			
		||||
                                    if ($isPublic) {
 | 
			
		||||
                                        $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
 | 
			
		||||
                                            if ($this->server->isSwarm()) {
 | 
			
		||||
                                                return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                return data_get($value, 'Name') === "/$uuid-proxy";
 | 
			
		||||
                                            }
 | 
			
		||||
                                        })->first();
 | 
			
		||||
                                        if (! $foundTcpProxy) {
 | 
			
		||||
                                            StartDatabaseProxy::run($service_db);
 | 
			
		||||
                                            // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $database = $this->databases->where('uuid', $uuid)->first();
 | 
			
		||||
                        if ($database) {
 | 
			
		||||
                            $isPublic = data_get($database, 'is_public');
 | 
			
		||||
                            $foundDatabases[] = $database->id;
 | 
			
		||||
                            $statusFromDb = $database->status;
 | 
			
		||||
                            if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
                                $database->update(['status' => $containerStatus]);
 | 
			
		||||
                            }
 | 
			
		||||
                            if ($isPublic) {
 | 
			
		||||
                                $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
 | 
			
		||||
                                    if ($this->server->isSwarm()) {
 | 
			
		||||
                                        return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        return data_get($value, 'Name') === "/$uuid-proxy";
 | 
			
		||||
                                    }
 | 
			
		||||
                                })->first();
 | 
			
		||||
                                if (! $foundTcpProxy) {
 | 
			
		||||
                                    StartDatabaseProxy::run($database);
 | 
			
		||||
                                    $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // Notify user that this container should not be there.
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (data_get($container, 'Name') === '/coolify-db') {
 | 
			
		||||
                    $foundDatabases[] = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $serviceLabelId = data_get($labels, 'coolify.serviceId');
 | 
			
		||||
            if ($serviceLabelId) {
 | 
			
		||||
                $subType = data_get($labels, 'coolify.service.subType');
 | 
			
		||||
                $subId = data_get($labels, 'coolify.service.subId');
 | 
			
		||||
                $service = $this->services->where('id', $serviceLabelId)->first();
 | 
			
		||||
                if (! $service) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                if ($subType === 'application') {
 | 
			
		||||
                    $service = $service->applications()->where('id', $subId)->first();
 | 
			
		||||
                } else {
 | 
			
		||||
                    $service = $service->databases()->where('id', $subId)->first();
 | 
			
		||||
                }
 | 
			
		||||
                if ($service) {
 | 
			
		||||
                    $foundServices[] = "$service->id-$service->name";
 | 
			
		||||
                    $statusFromDb = $service->status;
 | 
			
		||||
                    if ($statusFromDb !== $containerStatus) {
 | 
			
		||||
                        // ray('Updating status: ' . $containerStatus);
 | 
			
		||||
                        $service->update(['status' => $containerStatus]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $exitedServices = collect([]);
 | 
			
		||||
        foreach ($this->services as $service) {
 | 
			
		||||
            $apps = $service->applications()->get();
 | 
			
		||||
            $dbs = $service->databases()->get();
 | 
			
		||||
            foreach ($apps as $app) {
 | 
			
		||||
                if (in_array("$app->id-$app->name", $foundServices)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $exitedServices->push($app);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($dbs as $db) {
 | 
			
		||||
                if (in_array("$db->id-$db->name", $foundServices)) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $exitedServices->push($db);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $exitedServices = $exitedServices->unique('id');
 | 
			
		||||
        foreach ($exitedServices as $exitedService) {
 | 
			
		||||
            if (str($exitedService->status)->startsWith('exited')) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $name = data_get($exitedService, 'name');
 | 
			
		||||
            $fqdn = data_get($exitedService, 'fqdn');
 | 
			
		||||
            if ($name) {
 | 
			
		||||
                if ($fqdn) {
 | 
			
		||||
                    $containerName = "$name, available at $fqdn";
 | 
			
		||||
                } else {
 | 
			
		||||
                    $containerName = $name;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if ($fqdn) {
 | 
			
		||||
                    $containerName = $fqdn;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $containerName = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $projectUuid = data_get($service, 'environment.project.uuid');
 | 
			
		||||
            $serviceUuid = data_get($service, 'uuid');
 | 
			
		||||
            $environmentName = data_get($service, 'environment.name');
 | 
			
		||||
 | 
			
		||||
            if ($projectUuid && $serviceUuid && $environmentName) {
 | 
			
		||||
                $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
 | 
			
		||||
            } else {
 | 
			
		||||
                $url = null;
 | 
			
		||||
            }
 | 
			
		||||
            // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
            $exitedService->update(['status' => 'exited']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
 | 
			
		||||
        foreach ($notRunningApplications as $applicationId) {
 | 
			
		||||
            $application = $this->applications->where('id', $applicationId)->first();
 | 
			
		||||
            if (str($application->status)->startsWith('exited')) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $application->update(['status' => 'exited']);
 | 
			
		||||
 | 
			
		||||
            $name = data_get($application, 'name');
 | 
			
		||||
            $fqdn = data_get($application, 'fqdn');
 | 
			
		||||
 | 
			
		||||
            $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
			
		||||
 | 
			
		||||
            $projectUuid = data_get($application, 'environment.project.uuid');
 | 
			
		||||
            $applicationUuid = data_get($application, 'uuid');
 | 
			
		||||
            $environment = data_get($application, 'environment.name');
 | 
			
		||||
 | 
			
		||||
            if ($projectUuid && $applicationUuid && $environment) {
 | 
			
		||||
                $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
 | 
			
		||||
            } else {
 | 
			
		||||
                $url = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
        }
 | 
			
		||||
        $notRunningApplicationPreviews = $this->previews->pluck('id')->diff($foundApplicationPreviews);
 | 
			
		||||
        foreach ($notRunningApplicationPreviews as $previewId) {
 | 
			
		||||
            $preview = $this->previews->where('id', $previewId)->first();
 | 
			
		||||
            if (str($preview->status)->startsWith('exited')) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $preview->update(['status' => 'exited']);
 | 
			
		||||
 | 
			
		||||
            $name = data_get($preview, 'name');
 | 
			
		||||
            $fqdn = data_get($preview, 'fqdn');
 | 
			
		||||
 | 
			
		||||
            $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
			
		||||
 | 
			
		||||
            $projectUuid = data_get($preview, 'application.environment.project.uuid');
 | 
			
		||||
            $environmentName = data_get($preview, 'application.environment.name');
 | 
			
		||||
            $applicationUuid = data_get($preview, 'application.uuid');
 | 
			
		||||
 | 
			
		||||
            if ($projectUuid && $applicationUuid && $environmentName) {
 | 
			
		||||
                $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
 | 
			
		||||
            } else {
 | 
			
		||||
                $url = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
        }
 | 
			
		||||
        $notRunningDatabases = $this->databases->pluck('id')->diff($foundDatabases);
 | 
			
		||||
        foreach ($notRunningDatabases as $database) {
 | 
			
		||||
            $database = $this->databases->where('id', $database)->first();
 | 
			
		||||
            if (str($database->status)->startsWith('exited')) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $database->update(['status' => 'exited']);
 | 
			
		||||
 | 
			
		||||
            $name = data_get($database, 'name');
 | 
			
		||||
            $fqdn = data_get($database, 'fqdn');
 | 
			
		||||
 | 
			
		||||
            $containerName = $name;
 | 
			
		||||
 | 
			
		||||
            $projectUuid = data_get($database, 'environment.project.uuid');
 | 
			
		||||
            $environmentName = data_get($database, 'environment.name');
 | 
			
		||||
            $databaseUuid = data_get($database, 'uuid');
 | 
			
		||||
 | 
			
		||||
            if ($projectUuid && $databaseUuid && $environmentName) {
 | 
			
		||||
                $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
 | 
			
		||||
            } else {
 | 
			
		||||
                $url = null;
 | 
			
		||||
            }
 | 
			
		||||
            // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if proxy is running
 | 
			
		||||
        $this->server->proxyType();
 | 
			
		||||
        $foundProxyContainer = $this->containers->filter(function ($value, $key) {
 | 
			
		||||
            if ($this->server->isSwarm()) {
 | 
			
		||||
                return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
			
		||||
            } else {
 | 
			
		||||
                return data_get($value, 'Name') === '/coolify-proxy';
 | 
			
		||||
            }
 | 
			
		||||
        })->first();
 | 
			
		||||
        if (! $foundProxyContainer) {
 | 
			
		||||
            try {
 | 
			
		||||
                $shouldStart = CheckProxy::run($this->server);
 | 
			
		||||
                if ($shouldStart) {
 | 
			
		||||
                    StartProxy::run($this->server, false);
 | 
			
		||||
                    $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
			
		||||
                }
 | 
			
		||||
            } catch (\Throwable $e) {
 | 
			
		||||
                ray($e);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
 | 
			
		||||
            $this->server->save();
 | 
			
		||||
            $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
			
		||||
            instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
			
		||||
            StartLogDrain::dispatch($this->server);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								app/Jobs/ServerCleanupMux.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								app/Jobs/ServerCleanupMux.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Helpers\SshMultiplexingHelper;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
class ServerCleanupMux implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public $tries = 1;
 | 
			
		||||
 | 
			
		||||
    public $timeout = 60;
 | 
			
		||||
 | 
			
		||||
    public function backoff(): int
 | 
			
		||||
    {
 | 
			
		||||
        return isDev() ? 1 : 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(public Server $server) {}
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if ($this->server->serverStatus() === false) {
 | 
			
		||||
                return 'Server is not reachable or not ready.';
 | 
			
		||||
            }
 | 
			
		||||
            SshMultiplexingHelper::removeMuxFile($this->server);
 | 
			
		||||
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -32,9 +32,7 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            $servers_count = $servers->count();
 | 
			
		||||
            $limit = data_get($this->team->limits, 'serverLimit', 2);
 | 
			
		||||
            $number_of_servers_to_disable = $servers_count - $limit;
 | 
			
		||||
            ray('ServerLimitCheckJob', $this->team->uuid, $servers_count, $limit, $number_of_servers_to_disable);
 | 
			
		||||
            if ($number_of_servers_to_disable > 0) {
 | 
			
		||||
                ray('Disabling servers');
 | 
			
		||||
                $servers = $servers->sortbyDesc('created_at');
 | 
			
		||||
                $servers_to_disable = $servers->take($number_of_servers_to_disable);
 | 
			
		||||
                $servers_to_disable->each(function ($server) {
 | 
			
		||||
@@ -51,7 +49,6 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('ServerLimitCheckJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public int|string|null $disk_usage = null;
 | 
			
		||||
 | 
			
		||||
    public $tries = 3;
 | 
			
		||||
 | 
			
		||||
    public function backoff(): int
 | 
			
		||||
    {
 | 
			
		||||
        return isDev() ? 1 : 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(public Server $server) {}
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        if (! $this->server->isServerReady($this->tries)) {
 | 
			
		||||
            throw new \RuntimeException('Server is not ready.');
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            if ($this->server->isFunctional()) {
 | 
			
		||||
                $this->remove_unnecessary_coolify_yaml();
 | 
			
		||||
                if ($this->server->isSentinelEnabled()) {
 | 
			
		||||
                    $this->server->checkSentinel();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            // send_internal_notification('ServerStatusJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function remove_unnecessary_coolify_yaml()
 | 
			
		||||
    {
 | 
			
		||||
        // This will remote the coolify.yaml file from the server as it is not needed on cloud servers
 | 
			
		||||
        if (isCloud() && $this->server->id !== 0) {
 | 
			
		||||
            $file = $this->server->proxyPath().'/dynamic/coolify.yaml';
 | 
			
		||||
 | 
			
		||||
            return instant_remote_process([
 | 
			
		||||
                "rm -f $file",
 | 
			
		||||
            ], $this->server, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,12 +3,14 @@
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Notifications\Server\HighDiskUsage;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Facades\RateLimiter;
 | 
			
		||||
 | 
			
		||||
class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
@@ -18,40 +20,46 @@ class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
 | 
			
		||||
    public $timeout = 60;
 | 
			
		||||
 | 
			
		||||
    public $containers;
 | 
			
		||||
 | 
			
		||||
    public $applications;
 | 
			
		||||
 | 
			
		||||
    public $databases;
 | 
			
		||||
 | 
			
		||||
    public $services;
 | 
			
		||||
 | 
			
		||||
    public $previews;
 | 
			
		||||
 | 
			
		||||
    public function backoff(): int
 | 
			
		||||
    {
 | 
			
		||||
        return isDev() ? 1 : 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(public Server $server) {}
 | 
			
		||||
    public function __construct(public Server $server, public ?int $percentage = null) {}
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (! $this->server->isFunctional()) {
 | 
			
		||||
                ray('Server is not ready.');
 | 
			
		||||
 | 
			
		||||
                return 'Server is not ready.';
 | 
			
		||||
            }
 | 
			
		||||
            $team = $this->server->team;
 | 
			
		||||
            $percentage = $this->server->storageCheck();
 | 
			
		||||
            if ($percentage > 1) {
 | 
			
		||||
                ray('Server storage is at '.$percentage.'%');
 | 
			
		||||
            $team = data_get($this->server, 'team');
 | 
			
		||||
            $serverDiskUsageNotificationThreshold = data_get($this->server, 'settings.server_disk_usage_notification_threshold');
 | 
			
		||||
 | 
			
		||||
            if (is_null($this->percentage)) {
 | 
			
		||||
                $this->percentage = $this->server->storageCheck();
 | 
			
		||||
            }
 | 
			
		||||
            if (! $this->percentage) {
 | 
			
		||||
                return 'No percentage could be retrieved.';
 | 
			
		||||
            }
 | 
			
		||||
            if ($this->percentage > $serverDiskUsageNotificationThreshold) {
 | 
			
		||||
                $executed = RateLimiter::attempt(
 | 
			
		||||
                    'high-disk-usage:'.$this->server->id,
 | 
			
		||||
                    $maxAttempts = 0,
 | 
			
		||||
                    function () use ($team, $serverDiskUsageNotificationThreshold) {
 | 
			
		||||
                        $team->notify(new HighDiskUsage($this->server, $this->percentage, $serverDiskUsageNotificationThreshold));
 | 
			
		||||
                    },
 | 
			
		||||
                    $decaySeconds = 3600,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                if (! $executed) {
 | 
			
		||||
                    return 'Too many messages sent!';
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                RateLimiter::hit('high-disk-usage:'.$this->server->id, 600);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return handleError($e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,14 +27,12 @@ class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            ]);
 | 
			
		||||
            $mail->subject('Your last payment was failed for Coolify Cloud.');
 | 
			
		||||
            $this->team->members()->each(function ($member) use ($mail) {
 | 
			
		||||
                ray($member);
 | 
			
		||||
                if ($member->isAdmin()) {
 | 
			
		||||
                    send_user_an_email($mail, $member->email);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('SubscriptionInvoiceFailedJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,14 +30,12 @@ class SubscriptionTrialEndedJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            ]);
 | 
			
		||||
            $this->team->members()->each(function ($member) use ($mail) {
 | 
			
		||||
                if ($member->isAdmin()) {
 | 
			
		||||
                    ray('Sending trial ended email to '.$member->email);
 | 
			
		||||
                    send_user_an_email($mail, $member->email);
 | 
			
		||||
                    send_internal_notification('Trial reminder email sent to '.$member->email);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('SubscriptionTrialEndsSoonJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,14 +30,12 @@ class SubscriptionTrialEndsSoonJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
            ]);
 | 
			
		||||
            $this->team->members()->each(function ($member) use ($mail) {
 | 
			
		||||
                if ($member->isAdmin()) {
 | 
			
		||||
                    ray('Sending trial ending email to '.$member->email);
 | 
			
		||||
                    send_user_an_email($mail, $member->email);
 | 
			
		||||
                    send_internal_notification('Trial reminder email sent to '.$member->email);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('SubscriptionTrialEndsSoonJob failed with: '.$e->getMessage());
 | 
			
		||||
            ray($e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ class MaintenanceModeDisabledNotification
 | 
			
		||||
 | 
			
		||||
    public function handle(EventsMaintenanceModeDisabled $event): void
 | 
			
		||||
    {
 | 
			
		||||
        ray('Maintenance mode disabled!');
 | 
			
		||||
        $files = Storage::disk('webhooks-during-maintenance')->files();
 | 
			
		||||
        $files = collect($files);
 | 
			
		||||
        $files = $files->sort();
 | 
			
		||||
@@ -41,7 +40,6 @@ class MaintenanceModeDisabledNotification
 | 
			
		||||
                $instance = new $class;
 | 
			
		||||
                $instance->$method($request);
 | 
			
		||||
            } catch (\Throwable $th) {
 | 
			
		||||
                ray($th);
 | 
			
		||||
            } finally {
 | 
			
		||||
                Storage::disk('webhooks-during-maintenance')->delete($file);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,5 @@ class MaintenanceModeEnabledNotification
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle the event.
 | 
			
		||||
     */
 | 
			
		||||
    public function handle(EventsMaintenanceModeEnabled $event): void
 | 
			
		||||
    {
 | 
			
		||||
        ray('Maintenance mode enabled!');
 | 
			
		||||
    }
 | 
			
		||||
    public function handle(EventsMaintenanceModeEnabled $event): void {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,18 @@ class Index extends Component
 | 
			
		||||
 | 
			
		||||
    public $search = '';
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        if (! isCloud()) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (auth()->user()->id !== 0) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
        $this->getSubscribers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function submitSearch()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->search !== '') {
 | 
			
		||||
@@ -38,17 +50,6 @@ class Index extends Component
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        if (! isCloud()) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
        if (auth()->user()->id !== 0) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
        $this->getSubscribers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSubscribers()
 | 
			
		||||
    {
 | 
			
		||||
        $this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,8 +73,6 @@ class Index extends Component
 | 
			
		||||
        }
 | 
			
		||||
        $this->privateKeyName = generate_random_name();
 | 
			
		||||
        $this->remoteServerName = generate_random_name();
 | 
			
		||||
        $this->remoteServerPort = $this->remoteServerPort;
 | 
			
		||||
        $this->remoteServerUser = $this->remoteServerUser;
 | 
			
		||||
        if (isDev()) {
 | 
			
		||||
            $this->privateKey = '-----BEGIN OPENSSH PRIVATE KEY-----
 | 
			
		||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
 | 
			
		||||
@@ -87,26 +85,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
 | 
			
		||||
            $this->remoteServerDescription = 'Created by Coolify';
 | 
			
		||||
            $this->remoteServerHost = 'coolify-testing-host';
 | 
			
		||||
        }
 | 
			
		||||
        // if ($this->currentState === 'create-project') {
 | 
			
		||||
        //     $this->getProjects();
 | 
			
		||||
        // }
 | 
			
		||||
        // if ($this->currentState === 'create-resource') {
 | 
			
		||||
        //     $this->selectExistingServer();
 | 
			
		||||
        //     $this->selectExistingProject();
 | 
			
		||||
        // }
 | 
			
		||||
        // if ($this->currentState === 'private-key') {
 | 
			
		||||
        //     $this->setServerType('remote');
 | 
			
		||||
        // }
 | 
			
		||||
        // if ($this->currentState === 'create-server') {
 | 
			
		||||
        //     $this->selectExistingPrivateKey();
 | 
			
		||||
        // }
 | 
			
		||||
        // if ($this->currentState === 'validate-server') {
 | 
			
		||||
        //     $this->selectExistingServer();
 | 
			
		||||
        // }
 | 
			
		||||
        // if ($this->currentState === 'select-existing-server') {
 | 
			
		||||
        //     $this->selectExistingServer();
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function explanation()
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ class Form extends Component
 | 
			
		||||
    public function delete()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if ($this->destination->getMorphClass() === 'App\Models\StandaloneDocker') {
 | 
			
		||||
            if ($this->destination->getMorphClass() === \App\Models\StandaloneDocker::class) {
 | 
			
		||||
                if ($this->destination->attachedTo()) {
 | 
			
		||||
                    return $this->dispatch('error', 'You must delete all resources before deleting this destination.');
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ class Show extends Component
 | 
			
		||||
            return ! $alreadyAddedNetworks->contains('network', $network['Name']);
 | 
			
		||||
        });
 | 
			
		||||
        if ($this->networks->count() === 0) {
 | 
			
		||||
            $this->dispatch('success', 'No new networks found.');
 | 
			
		||||
            $this->dispatch('success', 'No new destinations found on this server.');
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ namespace App\Livewire;
 | 
			
		||||
 | 
			
		||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
 | 
			
		||||
use Illuminate\Support\Facades\Hash;
 | 
			
		||||
use Illuminate\Validation\Rules\Password;
 | 
			
		||||
use Livewire\Component;
 | 
			
		||||
 | 
			
		||||
class ForcePasswordReset extends Component
 | 
			
		||||
@@ -16,14 +17,19 @@ class ForcePasswordReset extends Component
 | 
			
		||||
 | 
			
		||||
    public string $password_confirmation;
 | 
			
		||||
 | 
			
		||||
    protected $rules = [
 | 
			
		||||
        'email' => 'required|email',
 | 
			
		||||
        'password' => 'required|min:8',
 | 
			
		||||
        'password_confirmation' => 'required|same:password',
 | 
			
		||||
    ];
 | 
			
		||||
    public function rules(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'email' => ['required', 'email'],
 | 
			
		||||
            'password' => ['required', Password::defaults(), 'confirmed'],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        if (auth()->user()->force_password_reset === false) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
        $this->email = auth()->user()->email;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +40,10 @@ class ForcePasswordReset extends Component
 | 
			
		||||
 | 
			
		||||
    public function submit()
 | 
			
		||||
    {
 | 
			
		||||
        if (auth()->user()->force_password_reset === false) {
 | 
			
		||||
            return redirect()->route('dashboard');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $this->rateLimit(10);
 | 
			
		||||
            $this->validate();
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user