commit
2b59630872
414 changed files with 14436 additions and 3151 deletions
|
|
@ -22,3 +22,4 @@ yarn-error.log
|
|||
/_data
|
||||
.rnd
|
||||
/.ssh
|
||||
.ignition.json
|
||||
|
|
|
|||
17
.github/workflows/chore-lock-closed-issues-discussions-and-prs.yml
vendored
Normal file
17
.github/workflows/chore-lock-closed-issues-discussions-and-prs.yml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
name: Lock closed Issues, Discussions, and PRs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 1 * * *'
|
||||
|
||||
jobs:
|
||||
lock-threads:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Lock threads after 30 days of inactivity
|
||||
uses: dessant/lock-threads@v5
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-inactive-days: '30'
|
||||
pr-inactive-days: '30'
|
||||
discussion-inactive-days: '30'
|
||||
28
.github/workflows/chore-manage-stale-issues-and-prs.yml
vendored
Normal file
28
.github/workflows/chore-manage-stale-issues-and-prs.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
name: Manage Stale Issues and PRs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
jobs:
|
||||
manage-stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Manage stale issues and PRs
|
||||
uses: actions/stale@v9
|
||||
id: stale
|
||||
with:
|
||||
stale-issue-message: 'This issue will be automatically closed in a few days if no response is received. Please provide an update with the requested information.'
|
||||
stale-pr-message: 'This pull request will be automatically closed in a few days if no response is received. Please update your PR or comment if you would like to continue working on it.'
|
||||
close-issue-message: 'This issue has been automatically closed due to inactivity.'
|
||||
close-pr-message: 'This pull request has been automatically closed due to inactivity.'
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
stale-issue-label: '⏱︎ Stale'
|
||||
stale-pr-label: '⏱︎ Stale'
|
||||
only-labels: '💤 Waiting for feedback'
|
||||
remove-stale-when-updated: true
|
||||
operations-per-run: 100
|
||||
labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback'
|
||||
close-issue-reason: 'not_planned'
|
||||
exempt-all-milestones: false
|
||||
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:
|
||||
|
|
|
|||
94
.github/workflows/coolify-helper.yml
vendored
94
.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,26 +98,45 @@ 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:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}
|
||||
|
||||
|
|
|
|||
143
.github/workflows/coolify-production-build.yml
vendored
Normal file
143
.github/workflows/coolify-production-build.yml
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
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 }}
|
||||
labels: |
|
||||
coolify.managed=true
|
||||
|
||||
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
|
||||
labels: |
|
||||
coolify.managed=true
|
||||
|
||||
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 }}
|
||||
146
.github/workflows/coolify-realtime-next.yml
vendored
Normal file
146
.github/workflows/coolify-realtime-next.yml
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
name: Coolify Realtime Development
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- .github/workflows/coolify-realtime-next.yml
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
- docker/coolify-realtime/terminal-server.js
|
||||
- docker/coolify-realtime/package.json
|
||||
- docker/coolify-realtime/soketi-entrypoint.sh
|
||||
|
||||
env:
|
||||
GITHUB_REGISTRY: ghcr.io
|
||||
DOCKER_REGISTRY: docker.io
|
||||
IMAGE_NAME: "coollabsio/coolify-realtime"
|
||||
|
||||
jobs:
|
||||
amd64:
|
||||
runs-on: ubuntu-latest
|
||||
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: 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 and Push Image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/coolify-realtime/Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
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:
|
||||
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: 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 and Push Image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/coolify-realtime/Dockerfile
|
||||
platforms: linux/aarch64
|
||||
push: true
|
||||
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:
|
||||
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 ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|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 }}-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_DEV_RELEASE_CHANNEL }}
|
||||
93
.github/workflows/coolify-realtime.yml
vendored
93
.github/workflows/coolify-realtime.yml
vendored
|
|
@ -1,8 +1,8 @@
|
|||
name: Coolify Realtime (v4)
|
||||
name: Coolify Realtime
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "next" ]
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- .github/workflows/coolify-realtime.yml
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
129
.github/workflows/coolify-staging-build.yml
vendored
Normal file
129
.github/workflows/coolify-staging-build.yml
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
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 }}
|
||||
labels: |
|
||||
coolify.managed=true
|
||||
|
||||
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
|
||||
labels: |
|
||||
coolify.managed=true
|
||||
|
||||
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 }}
|
||||
44
.github/workflows/docker-image.yml
vendored
44
.github/workflows/docker-image.yml
vendored
|
|
@ -1,44 +0,0 @@
|
|||
name: Docker Image CI
|
||||
|
||||
on:
|
||||
# push:
|
||||
# branches: [ "main" ]
|
||||
# pull_request:
|
||||
# branches: [ "*" ]
|
||||
push:
|
||||
branches: ["this-does-not-exist"]
|
||||
pull_request:
|
||||
branches: ["this-does-not-exist"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
/usr/local/share/ca-certificates
|
||||
/var/cache/apt/archives
|
||||
/var/lib/apt/lists
|
||||
~/.cache
|
||||
key: ${{ runner.os }}-docker-${{ hashFiles('**/Dockerfile') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-docker-
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cp .env.example .env
|
||||
docker run --rm -u "$(id -u):$(id -g)" \
|
||||
-v "$(pwd):/app" \
|
||||
-w /app composer:2 \
|
||||
composer install --ignore-platform-reqs
|
||||
./vendor/bin/spin build
|
||||
- name: Start the stack
|
||||
run: |
|
||||
./vendor/bin/spin up -d
|
||||
./vendor/bin/spin exec coolify php artisan key:generate
|
||||
./vendor/bin/spin exec coolify php artisan migrate:fresh --seed
|
||||
- name: Test (missing E2E tests)
|
||||
run: |
|
||||
./vendor/bin/spin exec coolify php artisan test
|
||||
25
.github/workflows/fix-php-code-style-issues
vendored
25
.github/workflows/fix-php-code-style-issues
vendored
|
|
@ -1,25 +0,0 @@
|
|||
name: Fix PHP code style issues
|
||||
|
||||
on: [push]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
php-code-styling:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Fix PHP code style issues
|
||||
uses: aglipanci/laravel-pint-action@2.4
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Fix styling
|
||||
93
.github/workflows/pr-build.yml
vendored
93
.github/workflows/pr-build.yml
vendored
|
|
@ -1,93 +0,0 @@
|
|||
name: PR Build (v4)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
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
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
actions: 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/amd64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}
|
||||
aarch64:
|
||||
runs-on: [self-hosted, arm64]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
actions: 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.event.number }}-aarch64
|
||||
merge-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
actions: 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.event.number }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}
|
||||
- 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
|
|
@ -32,3 +32,4 @@ _ide_helper_models.php
|
|||
.rnd
|
||||
/.ssh
|
||||
scripts/load-test/*
|
||||
.ignition.json
|
||||
|
|
|
|||
136
CONTRIBUTING.md
136
CONTRIBUTING.md
|
|
@ -6,19 +6,16 @@ # Contributing to Coolify
|
|||
|
||||
## Table of Contents
|
||||
|
||||
- [Contributing to Coolify](#contributing-to-coolify)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [1. Setup Development Environment](#1-setup-development-environment)
|
||||
- [2. Verify Installation (Optional)](#2-verify-installation-optional)
|
||||
- [3. Fork and Setup Local Repository](#3-fork-and-setup-local-repository)
|
||||
- [4. Set up Environment Variables](#4-set-up-environment-variables)
|
||||
- [5. Start Coolify](#5-start-coolify)
|
||||
- [6. Start Development](#6-start-development)
|
||||
- [7. Development Notes](#7-development-notes)
|
||||
- [8. Create a Pull Request](#8-create-a-pull-request)
|
||||
- [Additional Contribution Guidelines](#additional-contribution-guidelines)
|
||||
- [Contributing a New Service](#contributing-a-new-service)
|
||||
- [Contributing to Documentation](#contributing-to-documentation)
|
||||
1. [Setup Development Environment](#1-setup-development-environment)
|
||||
2. [Verify Installation](#2-verify-installation-optional)
|
||||
3. [Fork and Setup Local Repository](#3-fork-and-setup-local-repository)
|
||||
4. [Set up Environment Variables](#4-set-up-environment-variables)
|
||||
5. [Start Coolify](#5-start-coolify)
|
||||
6. [Start Development](#6-start-development)
|
||||
7. [Create a Pull Request](#7-create-a-pull-request)
|
||||
8. [Development Notes](#development-notes)
|
||||
9. [Resetting Development Environment](#resetting-development-environment)
|
||||
10. [Additional Contribution Guidelines](#additional-contribution-guidelines)
|
||||
|
||||
## 1. Setup Development Environment
|
||||
|
||||
|
|
@ -29,15 +26,15 @@ ## 1. Setup Development Environment
|
|||
|
||||
1. Install `docker-ce`, Docker Desktop (or similar):
|
||||
- Docker CE (recommended):
|
||||
- Install Windows Subsystem for Linux v2 (WSL2) by following this guide: [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install)
|
||||
- After installing WSL2, install Docker CE for your Linux distribution by following this guide: [Install Docker Engine](https://docs.docker.com/engine/install/)
|
||||
- Install Windows Subsystem for Linux v2 (WSL2) by following this guide: [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install?ref=coolify)
|
||||
- After installing WSL2, install Docker CE for your Linux distribution by following this guide: [Install Docker Engine](https://docs.docker.com/engine/install/?ref=coolify)
|
||||
- Make sure to choose the appropriate Linux distribution (e.g., Ubuntu) when following the Docker installation guide
|
||||
- Install Docker Desktop (easier):
|
||||
- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/)
|
||||
- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/?ref=coolify)
|
||||
- Ensure WSL2 backend is enabled in Docker Desktop settings
|
||||
|
||||
2. Install Spin:
|
||||
- Follow the instructions to install Spin on Windows from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-windows#download-and-install-spin-into-wsl2)
|
||||
- Follow the instructions to install Spin on Windows from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-windows#download-and-install-spin-into-wsl2?ref=coolify)
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -46,12 +43,12 @@ ## 1. Setup Development Environment
|
|||
|
||||
1. Install Orbstack, Docker Desktop (or similar):
|
||||
- Orbstack (recommended, as it is a faster and lighter alternative to Docker Desktop):
|
||||
- Download and install [Orbstack](https://docs.orbstack.dev/quick-start#installation)
|
||||
- Download and install [Orbstack](https://docs.orbstack.dev/quick-start#installation?ref=coolify)
|
||||
- Docker Desktop:
|
||||
- Download and install [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/)
|
||||
- Download and install [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/?ref=coolify)
|
||||
|
||||
2. Install Spin:
|
||||
- Follow the instructions to install Spin on MacOS from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-macos/#download-and-install-spin)
|
||||
- Follow the instructions to install Spin on MacOS from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-macos/#download-and-install-spin?ref=coolify)
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -60,12 +57,12 @@ ## 1. Setup Development Environment
|
|||
|
||||
1. Install Docker Engine, Docker Desktop (or similar):
|
||||
- Docker Engine (recommended, as there is no VM overhead):
|
||||
- Follow the official [Docker Engine installation guide](https://docs.docker.com/engine/install/) for your Linux distribution
|
||||
- Follow the official [Docker Engine installation guide](https://docs.docker.com/engine/install/?ref=coolify) for your Linux distribution
|
||||
- Docker Desktop:
|
||||
- If you want a GUI, you can use [Docker Desktop for Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||
- If you want a GUI, you can use [Docker Desktop for Linux](https://docs.docker.com/desktop/install/linux-install/?ref=coolify)
|
||||
|
||||
2. Install Spin:
|
||||
- Follow the instructions to install Spin on Linux from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-linux#configure-docker-permissions)
|
||||
- Follow the instructions to install Spin on Linux from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-linux#configure-docker-permissions?ref=coolify)
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -89,14 +86,14 @@ ## 3. Fork and Setup Local Repository
|
|||
|
||||
| Editor | Platform | Download Link |
|
||||
|--------|----------|---------------|
|
||||
| Visual Studio Code (recommended free) | Windows/macOS/Linux | [Download](https://code.visualstudio.com/download) |
|
||||
| Cursor (recommended but paid) | Windows/macOS/Linux | [Download](https://www.cursor.com/) |
|
||||
| Zed (very fast) | macOS/Linux | [Download](https://zed.dev/download) |
|
||||
| Visual Studio Code (recommended free) | Windows/macOS/Linux | [Download](https://code.visualstudio.com/download?ref=coolify) |
|
||||
| Cursor (recommended but paid) | Windows/macOS/Linux | [Download](https://www.cursor.com/?ref=coolify) |
|
||||
| Zed (very fast) | macOS/Linux | [Download](https://zed.dev/download?ref=coolify) |
|
||||
|
||||
3. Clone the Coolify Repository from your fork to your local machine
|
||||
- Use `git clone` in the command line, or
|
||||
- Use GitHub Desktop (recommended):
|
||||
- Download and install from [https://desktop.github.com/](https://desktop.github.com/)
|
||||
- Download and install from [https://desktop.github.com/](https://desktop.github.com/?ref=coolify)
|
||||
- Open GitHub Desktop and login with your GitHub account
|
||||
- Click on `File` -> `Clone Repository` select `github.com` as the repository location, then select your forked Coolify repository, choose the local path and then click `Clone`
|
||||
|
||||
|
|
@ -149,7 +146,36 @@ ## 6. Start Development
|
|||
> TELESCOPE_ENABLED=true
|
||||
> ```
|
||||
|
||||
## 7. Development Notes
|
||||
## 7. Create a Pull Request
|
||||
|
||||
1. After making changes or adding a new service:
|
||||
- Commit your changes to your forked repository.
|
||||
- Push the changes to your GitHub account.
|
||||
|
||||
2. Creating the Pull Request (PR):
|
||||
- Navigate to the main Coolify repository on GitHub.
|
||||
- Click the "Pull requests" tab.
|
||||
- Click the green "New pull request" button.
|
||||
- Choose your fork and branch as the compare branch.
|
||||
- Click "Create pull request".
|
||||
|
||||
3. Filling out the PR details:
|
||||
- Give your PR a descriptive title.
|
||||
- Use the Pull Request Template provided and fill in the details.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Always set the base branch for your PR to the `next` branch of the Coolify repository, not the `main` branch.
|
||||
|
||||
4. Submit your PR:
|
||||
- Review your changes one last time.
|
||||
- Click "Create pull request" to submit.
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure your PR is out of draft mode as soon as it's ready for review. PRs that are in draft mode for a long time may be closed by maintainers.
|
||||
|
||||
After submission, maintainers will review your PR and may request changes or provide feedback.
|
||||
|
||||
## Development Notes
|
||||
|
||||
When working on Coolify, keep the following in mind:
|
||||
|
||||
|
|
@ -168,35 +194,41 @@ ## 7. Development Notes
|
|||
> [!IMPORTANT]
|
||||
> Forgetting to migrate the database can cause problems, so make it a habit to run migrations after pulling changes or switching branches.
|
||||
|
||||
## 8. Create a Pull Request
|
||||
## Resetting Development Environment
|
||||
|
||||
1. After making changes or adding a new service:
|
||||
- Commit your changes to your forked repository.
|
||||
- Push the changes to your GitHub account.
|
||||
If you encounter issues or break your database or something else, follow these steps to start from a clean slate (works since `v4.0.0-beta.342`):
|
||||
|
||||
2. Creating the Pull Request (PR):
|
||||
- Navigate to the main Coolify repository on GitHub.
|
||||
- Click the "Pull requests" tab.
|
||||
- Click the green "New pull request" button.
|
||||
- Choose your fork and branch as the compare branch.
|
||||
- Click "Create pull request".
|
||||
1. Stop all running containers `ctrl + c`.
|
||||
|
||||
3. Filling out the PR details:
|
||||
- Give your PR a descriptive title.
|
||||
- In the description, explain the changes you've made.
|
||||
- Reference any related issues by using keywords like "Fixes #123" or "Closes #456".
|
||||
2. Remove all Coolify containers:
|
||||
```bash
|
||||
docker rm coolify coolify-db coolify-redis coolify-realtime coolify-testing-host coolify-minio coolify-vite-1 coolify-mail
|
||||
```
|
||||
|
||||
3. Remove Coolify volumes (it is possible that the volumes have no `coolify` prefix on your machine, in that case remove the prefix from the command):
|
||||
```bash
|
||||
docker volume rm coolify_dev_backups_data coolify_dev_postgres_data coolify_dev_redis_data coolify_dev_coolify_data coolify_dev_minio_data
|
||||
```
|
||||
|
||||
4. Remove unused images:
|
||||
```bash
|
||||
docker image prune -a
|
||||
```
|
||||
|
||||
5. Start Coolify again:
|
||||
```bash
|
||||
spin up
|
||||
```
|
||||
|
||||
6. Run database migrations and seeders:
|
||||
```bash
|
||||
docker exec -it coolify php artisan migrate:fresh --seed
|
||||
```
|
||||
|
||||
After completing these steps, you'll have a fresh development setup.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Always set the base branch for your PR to the `next` branch of the Coolify repository, not the `main` branch.
|
||||
|
||||
4. Submit your PR:
|
||||
- Review your changes one last time.
|
||||
- Click "Create pull request" to submit.
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure your PR is out of draft mode as soon as it's ready for review. PRs that are in draft mode for a long time may be closed by maintainers.
|
||||
|
||||
After submission, maintainers will review your PR and may request changes or provide feedback.
|
||||
> Always run database migrations and seeders after switching branches or pulling updates to ensure your local database structure matches the current codebase and includes necessary seed data.
|
||||
|
||||
## Additional Contribution Guidelines
|
||||
|
||||
|
|
|
|||
113
RELEASE.md
113
RELEASE.md
|
|
@ -2,35 +2,120 @@ # Coolify Release Guide
|
|||
|
||||
This guide outlines the release process for Coolify, intended for developers and those interested in understanding how releases are managed and deployed.
|
||||
|
||||
## Table of Contents
|
||||
- [Release Process](#release-process)
|
||||
- [Version Types](#version-types)
|
||||
- [Stable](#stable)
|
||||
- [Nightly](#nightly)
|
||||
- [Beta](#beta)
|
||||
- [Version Availability](#version-availability)
|
||||
- [Self-Hosted](#self-hosted)
|
||||
- [Cloud](#cloud)
|
||||
- [Manually Update to Specific Versions](#manually-update-to-specific-versions)
|
||||
|
||||
## Release Process
|
||||
|
||||
1. **Development on `next` or separate branches**
|
||||
- Changes, fixes and new features are developed on the `next` or even separate branches.
|
||||
1. **Development on `next` or Feature Branches**
|
||||
- Improvements, fixes, and new features are developed on the `next` branch or separate feature branches.
|
||||
|
||||
2. **Merging to `main`**
|
||||
- Once changes are ready, they are merged from `next` into the `main` branch.
|
||||
- Once ready, changes are merged from the `next` branch into the `main` branch.
|
||||
|
||||
3. **Building the release**
|
||||
- After merging to `main`, a new release is built.
|
||||
- Note: A push to `main` does not automatically mean a new version is released.
|
||||
3. **Building the Release**
|
||||
- After merging to `main`, GitHub Actions automatically builds release images for all architectures and pushes them to the GitHub Container Registry with the version tag and the `latest` tag.
|
||||
|
||||
4. **Creating a GitHub release**
|
||||
- A new release is created on GitHub with the new version details.
|
||||
4. **Creating a GitHub Release**
|
||||
- A new GitHub release is manually created with details of the changes made in the version.
|
||||
|
||||
5. **Updating the CDN**
|
||||
- The final step is updating the version information on the CDN:
|
||||
[https://cdn.coollabs.io/coolify/versions.json](https://cdn.coollabs.io/coolify/versions.json)
|
||||
- To make a new version publicly available, the version information on the CDN needs to be updated: [https://cdn.coollabs.io/coolify/versions.json](https://cdn.coollabs.io/coolify/versions.json)
|
||||
|
||||
> [!NOTE]
|
||||
> The CDN update may not occur immediately after the GitHub release. It can happen hours or even days later due to additional testing, stability checks, or potential hotfixes.
|
||||
> The CDN update may not occur immediately after the GitHub release. It can take hours or even days due to additional testing, stability checks, or potential hotfixes. **The update becomes available only after the CDN is updated.**
|
||||
|
||||
## Version Types
|
||||
|
||||
<details>
|
||||
<summary><strong>Stable (coming soon)</strong></summary>
|
||||
|
||||
- **Stable**
|
||||
- The production version suitable for stable, production environments (generally recommended).
|
||||
- **Update Frequency:** Every 2 to 4 weeks, with more frequent possible hotfixes.
|
||||
- **Release Size:** Larger but less frequent releases. Multiple nightly versions are consolidated into a single stable release.
|
||||
- **Versioning Scheme:** Follows semantic versioning (e.g., `v4.0.0`).
|
||||
- **Installation Command:**
|
||||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Nightly</strong></summary>
|
||||
|
||||
- **Nightly**
|
||||
- The latest development version, suitable for testing the latest changes and experimenting with new features.
|
||||
- **Update Frequency:** Daily or bi-weekly updates.
|
||||
- **Release Size:** Smaller, more frequent releases.
|
||||
- **Versioning Scheme:** TO BE DETERMINED
|
||||
- **Installation Command:**
|
||||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify-nightly/install.sh | bash -s next
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Beta</strong></summary>
|
||||
|
||||
- **Beta**
|
||||
- Test releases for the upcoming stable version.
|
||||
- **Purpose:** Allows users to test and provide feedback on new features and changes before they become stable.
|
||||
- **Update Frequency:** Available if we think beta testing is necessary.
|
||||
- **Release Size:** Same size as stable release as it will become the next stabe release after some time.
|
||||
- **Versioning Scheme:** Follows semantic versioning (e.g., `4.1.0-beta.1`).
|
||||
- **Installation Command:**
|
||||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
> [!WARNING]
|
||||
> Do not use nightly/beta builds in production as there is no guarantee of stability.
|
||||
|
||||
## Version Availability
|
||||
|
||||
It's important to understand that a new version released on GitHub may not immediately become available for users to update (through manual or auto-update).
|
||||
When a new version is released and a new GitHub release is created, it doesn't immediately become available for your instance. Here's how version availability works for different instance types.
|
||||
|
||||
### Self-Hosted
|
||||
|
||||
- **Update Frequency:** More frequent updates, especially on the nightly release channel.
|
||||
- **Update Availability:** New versions are available once the CDN has been updated.
|
||||
- **Update Methods:**
|
||||
1. **Manual Update in Instance Settings:**
|
||||
- Go to `Settings > Update Check Frequency` and click the `Check Manually` button.
|
||||
- If an update is available, an upgrade button will appear on the sidebar.
|
||||
2. **Automatic Update:**
|
||||
- If enabled, the instance will update automatically at the time set in the settings.
|
||||
3. **Re-run Installation Script:**
|
||||
- Run the installation script again to upgrade to the latest version available on the CDN:
|
||||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you see a new release on GitHub but haven't received the update, it's likely because the CDN hasn't been updated yet. This is intentional and ensures stability and allows for hotfixes before the new version is officially released.
|
||||
> If a new release is available on GitHub but your instance hasn't updated yet or no upgrade button is shown in the UI, the CDN might not have been updated yet. This intentional delay ensures stability and allows for hotfixes before official release.
|
||||
|
||||
### Cloud
|
||||
|
||||
- **Update Frequency:** Less frequent as it's a managed service.
|
||||
- **Update Availability:** New versions are available once Andras has updated the cloud version manually.
|
||||
- **Update Method:**
|
||||
- Updates are managed by Andras, who ensures each cloud version is thoroughly tested and stable before releasing it.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The cloud version of Coolify may be several versions behind the latest GitHub releases even if the CDN is updated. This is intentional to ensure stability and reliability for cloud users and Andras will manully update the cloud version when the update is ready.
|
||||
|
||||
## Manually Update to Specific Versions
|
||||
|
||||
|
|
@ -42,4 +127,4 @@ ## Manually Update to Specific Versions
|
|||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash -s <version>
|
||||
```
|
||||
-> Replace `<version>` with the version you want to update to (for example `4.0.0-beta.332`).
|
||||
Replace `<version>` with the version you want to update to (for example `4.0.0-beta.332`).
|
||||
|
|
|
|||
17
app/Actions/Application/GenerateConfig.php
Normal file
17
app/Actions/Application/GenerateConfig.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class GenerateConfig
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Application $application, bool $is_json = false)
|
||||
{
|
||||
ray()->clearAll();
|
||||
return $application->generateConfig(is_json: $is_json);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,9 +46,6 @@ public function handle(StandaloneDragonfly $database)
|
|||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'ulimits' => [
|
||||
'memlock' => '-1',
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -651,8 +651,9 @@ private function old_way()
|
|||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
|
||||
// Check if proxy is running
|
||||
$this->server->proxyType();
|
||||
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';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
|
@ -20,7 +19,7 @@ class CreateNewUser implements CreatesNewUsers
|
|||
*/
|
||||
public function create(array $input): User
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
abort(403);
|
||||
}
|
||||
|
|
@ -48,7 +47,7 @@ public function create(array $input): User
|
|||
$team = $user->teams()->first();
|
||||
|
||||
// Disable registration after first user is created
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$settings->is_registration_enabled = false;
|
||||
$settings->save();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Actions\License;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
|
@ -13,7 +12,7 @@ class CheckResaleLicense
|
|||
public function handle()
|
||||
{
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (isDev()) {
|
||||
$settings->update([
|
||||
'is_resale_license_active' => true,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public function handle(Server $server, bool $reset = false)
|
|||
];
|
||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
|
||||
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
|
||||
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value();
|
||||
}
|
||||
if (! $proxy_configuration || is_null($proxy_configuration)) {
|
||||
throw new \Exception('Could not generate proxy configuration');
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class CheckProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Server $server, $fromUI = false)
|
||||
// It should return if the proxy should be started (true) or not (false)
|
||||
public function handle(Server $server, $fromUI = false): bool
|
||||
{
|
||||
if (! $server->isFunctional()) {
|
||||
return false;
|
||||
|
|
@ -62,22 +65,42 @@ public function handle(Server $server, $fromUI = false)
|
|||
$ip = 'host.docker.internal';
|
||||
}
|
||||
|
||||
$connection80 = @fsockopen($ip, '80');
|
||||
$connection443 = @fsockopen($ip, '443');
|
||||
$port80 = is_resource($connection80) && fclose($connection80);
|
||||
$port443 = is_resource($connection443) && fclose($connection443);
|
||||
if ($port80) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
$portsToCheck = ['80', '443'];
|
||||
|
||||
try {
|
||||
if ($server->proxyType() !== ProxyTypes::NONE->value) {
|
||||
$proxyCompose = CheckConfiguration::run($server);
|
||||
if (isset($proxyCompose)) {
|
||||
$yaml = Yaml::parse($proxyCompose);
|
||||
$portsToCheck = [];
|
||||
if ($server->proxyType() === ProxyTypes::TRAEFIK->value) {
|
||||
$ports = data_get($yaml, 'services.traefik.ports');
|
||||
} elseif ($server->proxyType() === ProxyTypes::CADDY->value) {
|
||||
$ports = data_get($yaml, 'services.caddy.ports');
|
||||
}
|
||||
if (isset($ports)) {
|
||||
foreach ($ports as $port) {
|
||||
$portsToCheck[] = str($port)->before(':')->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
$portsToCheck = [];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
ray($e->getMessage());
|
||||
}
|
||||
if ($port443) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
if (count($portsToCheck) === 0) {
|
||||
return false;
|
||||
}
|
||||
foreach ($portsToCheck as $port) {
|
||||
$connection = @fsockopen($ip, $port);
|
||||
if (is_resource($connection) && fclose($connection)) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public function handle(Server $server, bool $async = true, bool $force = false):
|
|||
}
|
||||
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->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value();
|
||||
$server->save();
|
||||
if ($server->isSwarm()) {
|
||||
$commands = $commands->merge([
|
||||
|
|
@ -35,7 +35,7 @@ public function handle(Server $server, bool $async = true, bool $force = false):
|
|||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
'docker stack deploy -c docker-compose.yml coolify-proxy',
|
||||
"echo 'Proxy started successfully.'",
|
||||
"echo 'Successfully started coolify-proxy.'",
|
||||
]);
|
||||
} else {
|
||||
$caddfile = 'import /dynamic/*.caddy';
|
||||
|
|
@ -46,12 +46,14 @@ public function handle(Server $server, bool $async = true, bool $force = false):
|
|||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
'docker compose pull',
|
||||
"echo 'Stopping existing coolify-proxy.'",
|
||||
'docker stop -t 10 coolify-proxy || true',
|
||||
'docker rm coolify-proxy || true',
|
||||
'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 'Proxy started successfully.'",
|
||||
"echo 'Successfully started coolify-proxy.'",
|
||||
]);
|
||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
|
@ -12,28 +11,29 @@ class CleanupDocker
|
|||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
$settings = instanceSettings();
|
||||
$helperImageVersion = data_get($settings, 'helper_version');
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$helperImageWithVersion = "$helperImage:$helperImageVersion";
|
||||
|
||||
$commands = $this->getCommands();
|
||||
$commands = [
|
||||
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',
|
||||
'docker image prune -af --filter "label!=coolify.managed=true"',
|
||||
'docker builder prune -af',
|
||||
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi -f",
|
||||
];
|
||||
|
||||
$serverSettings = $server->settings;
|
||||
if ($serverSettings->delete_unused_volumes) {
|
||||
$commands[] = 'docker volume prune -af';
|
||||
}
|
||||
|
||||
if ($serverSettings->delete_unused_networks) {
|
||||
$commands[] = 'docker network prune -f';
|
||||
}
|
||||
|
||||
foreach ($commands as $command) {
|
||||
instant_remote_process([$command], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
private function getCommands(): array
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$helperImageVersion = data_get($settings, 'helper_version');
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$helperImageWithVersion = config('coolify.helper_image').':'.$helperImageVersion;
|
||||
|
||||
$commonCommands = [
|
||||
'docker container prune -f --filter "label=coolify.managed=true"',
|
||||
'docker image prune -af --filter "label!=coolify.managed=true"',
|
||||
'docker builder prune -af',
|
||||
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi",
|
||||
];
|
||||
|
||||
return $commonCommands;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public function handle(Server $server)
|
|||
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": {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
|
@ -9,18 +10,48 @@ class StartSentinel
|
|||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
||||
public function handle(Server $server, $version = 'next', bool $restart = false)
|
||||
{
|
||||
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;
|
||||
$metrics_history = data_get($server, 'settings.sentinel_metrics_history_days');
|
||||
$refresh_rate = data_get($server, 'settings.sentinel_metrics_refresh_rate_seconds');
|
||||
$push_interval = data_get($server, 'settings.sentinel_push_interval_seconds');
|
||||
$token = data_get($server, 'settings.sentinel_token');
|
||||
$endpoint = data_get($server, 'settings.sentinel_custom_url');
|
||||
$mount_dir = '/data/coolify/sentinel';
|
||||
$image = "ghcr.io/coollabsio/sentinel:$version";
|
||||
if (! $endpoint) {
|
||||
throw new \Exception('You should set FQDN in Instance Settings.');
|
||||
}
|
||||
$environments = [
|
||||
'TOKEN' => $token,
|
||||
'PUSH_ENDPOINT' => $endpoint,
|
||||
'PUSH_INTERVAL_SECONDS' => $push_interval,
|
||||
'COLLECTOR_ENABLED' => $server->isMetricsEnabled() ? 'true' : 'false',
|
||||
'COLLECTOR_REFRESH_RATE_SECONDS' => $refresh_rate,
|
||||
'COLLECTOR_RETENTION_PERIOD_DAYS' => $metrics_history,
|
||||
];
|
||||
if (isDev()) {
|
||||
data_set($environments, 'DEBUG', 'true');
|
||||
$mount_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/sentinel';
|
||||
$image = 'sentinel';
|
||||
}
|
||||
$docker_environments = '-e "' . implode('" -e "', array_map(fn($key, $value) => "$key=$value", array_keys($environments), $environments)) . '"';
|
||||
|
||||
$docker_command = "docker run -d $docker_environments --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v $mount_dir:/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 $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 $mount_dir",
|
||||
$docker_command,
|
||||
"chown -R 9999:root $mount_dir",
|
||||
"chmod -R 700 $mount_dir",
|
||||
], $server);
|
||||
|
||||
$server->settings->is_sentinel_enabled = true;
|
||||
$server->settings->save();
|
||||
$server->sentinelUpdateAt();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Carbon\Carbon;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StopSentinel
|
||||
|
|
@ -12,5 +13,6 @@ class StopSentinel
|
|||
public function handle(Server $server)
|
||||
{
|
||||
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
|
||||
$server->sentinelUpdateAt(isReset: true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Actions\Server;
|
||||
|
||||
use App\Jobs\PullHelperImageJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
|
@ -20,7 +19,7 @@ class UpdateCoolify
|
|||
public function handle($manual_update = false)
|
||||
{
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$this->server = Server::find(0);
|
||||
if (! $this->server) {
|
||||
return;
|
||||
|
|
|
|||
50
app/Console/Commands/CheckApplicationDeploymentQueue.php
Normal file
50
app/Console/Commands/CheckApplicationDeploymentQueue.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CheckApplicationDeploymentQueue extends Command
|
||||
{
|
||||
protected $signature = 'check:deployment-queue {--force} {--seconds=3600}';
|
||||
|
||||
protected $description = 'Check application deployment queue.';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$seconds = $this->option('seconds');
|
||||
$deployments = ApplicationDeploymentQueue::whereIn('status', [
|
||||
ApplicationDeploymentStatus::IN_PROGRESS,
|
||||
ApplicationDeploymentStatus::QUEUED,
|
||||
])->where('created_at', '<=', now()->subSeconds($seconds))->get();
|
||||
if ($deployments->isEmpty()) {
|
||||
$this->info('No deployments found in the last '.$seconds.' seconds.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info('Found '.$deployments->count().' deployments created in the last '.$seconds.' seconds.');
|
||||
|
||||
foreach ($deployments as $deployment) {
|
||||
if ($this->option('force')) {
|
||||
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
|
||||
$this->cancelDeployment($deployment);
|
||||
} else {
|
||||
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
|
||||
if ($this->confirm('Do you want to cancel this deployment?', true)) {
|
||||
$this->cancelDeployment($deployment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function cancelDeployment(ApplicationDeploymentQueue $deployment)
|
||||
{
|
||||
$deployment->update(['status' => ApplicationDeploymentStatus::FAILED]);
|
||||
if ($deployment->server?->isFunctional()) {
|
||||
remote_process(['docker rm -f '.$deployment->deployment_uuid], $deployment->server, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
class CleanupApplicationDeploymentQueue extends Command
|
||||
{
|
||||
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
||||
protected $signature = 'cleanup:deployment-queue {--team-id=}';
|
||||
|
||||
protected $description = 'CleanupApplicationDeploymentQueue';
|
||||
protected $description = 'Cleanup application deployment queue.';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Jobs\CleanupHelperContainersJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledTask;
|
||||
|
|
@ -47,6 +48,17 @@ private function cleanup_stucked_resources()
|
|||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$applicationsDeploymentQueue = ApplicationDeploymentQueue::get();
|
||||
foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) {
|
||||
if (is_null($applicationDeploymentQueue->application)) {
|
||||
echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n";
|
||||
$applicationDeploymentQueue->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($applications as $application) {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,13 @@ public function init()
|
|||
echo "Generating APP_KEY.\n";
|
||||
Artisan::call('key:generate');
|
||||
}
|
||||
|
||||
// Generate STORAGE link if not exists
|
||||
if (! file_exists(public_path('storage'))) {
|
||||
echo "Generating STORAGE link.\n";
|
||||
Artisan::call('storage:link');
|
||||
}
|
||||
|
||||
// Seed database if it's empty
|
||||
$settings = InstanceSettings::find(0);
|
||||
if (! $settings) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
|
|
@ -69,7 +68,7 @@ public function handle()
|
|||
} catch (\Throwable $e) {
|
||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! is_null(env('AUTOUPDATE', null))) {
|
||||
if (env('AUTOUPDATE') == true) {
|
||||
$settings->update(['is_auto_update_enabled' => true]);
|
||||
|
|
@ -196,7 +195,7 @@ private function send_alive_signal()
|
|||
{
|
||||
$id = config('app.id');
|
||||
$version = config('version');
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$do_not_track = data_get($settings, 'do_not_track');
|
||||
if ($do_not_track == true) {
|
||||
echo "Skipping alive as do_not_track is enabled\n";
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ public function handle()
|
|||
$serviceTemplatesJson[$name] = $parsed;
|
||||
}
|
||||
}
|
||||
$serviceTemplatesJson = json_encode($serviceTemplatesJson);
|
||||
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
||||
$serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson.PHP_EOL);
|
||||
}
|
||||
|
||||
private function process_file($file)
|
||||
|
|
@ -78,7 +78,7 @@ private function process_file($file)
|
|||
if ($logo->count() > 0) {
|
||||
$logo = str($logo[0])->after('# logo:')->trim()->value();
|
||||
} else {
|
||||
$logo = 'svgs/unknown.svg';
|
||||
$logo = 'svgs/coolify.png';
|
||||
}
|
||||
$minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values();
|
||||
if ($minversion->count() > 0) {
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@
|
|||
use App\Jobs\ScheduledTaskJob;
|
||||
use App\Jobs\ServerCheckJob;
|
||||
use App\Jobs\UpdateCoolifyJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledTask;
|
||||
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
|
||||
{
|
||||
|
|
@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel
|
|||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
$this->all_servers = Server::all();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
|
||||
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ protected function schedule(Schedule $schedule): void
|
|||
|
||||
private function pull_images($schedule)
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||
foreach ($servers as $server) {
|
||||
if ($server->isSentinelEnabled()) {
|
||||
|
|
@ -88,7 +88,7 @@ private function pull_images($schedule)
|
|||
|
||||
private function schedule_updates($schedule)
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
|
||||
$updateCheckFrequency = $settings->update_check_frequency;
|
||||
$schedule->job(new CheckForUpdatesJob)
|
||||
|
|
@ -115,7 +115,11 @@ private function check_resources($schedule)
|
|||
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
||||
$last_sentinel_update = $server->sentinel_updated_at;
|
||||
if (Carbon::parse($last_sentinel_update)->isBefore(now()->subMinutes(4))) {
|
||||
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
||||
}
|
||||
// $schedule->job(new ServerStorageCheckJob($server))->everyMinute()->onOneServer();
|
||||
$serverTimezone = $server->settings->server_timezone;
|
||||
if ($server->settings->force_docker_cleanup) {
|
||||
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
|
||||
|
|
|
|||
14
app/Enums/ContainerStatusTypes.php
Normal file
14
app/Enums/ContainerStatusTypes.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ContainerStatusTypes: string
|
||||
{
|
||||
case PAUSED = 'paused';
|
||||
case RESTARTING = 'restarting';
|
||||
case REMOVING = 'removing';
|
||||
case RUNNING = 'running';
|
||||
case DEAD = 'dead';
|
||||
case CREATED = 'created';
|
||||
case EXITED = 'exited';
|
||||
}
|
||||
8
app/Enums/StaticImageTypes.php
Normal file
8
app/Enums/StaticImageTypes.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum StaticImageTypes: string
|
||||
{
|
||||
case NGINX_ALPINE = 'nginx:alpine';
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ public function register(): void
|
|||
if ($e instanceof RuntimeException) {
|
||||
return;
|
||||
}
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
if ($this->settings->do_not_track) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,9 @@ public static function generateScpCommand(Server $server, string $source, string
|
|||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||
|
||||
$scp_command = "timeout $timeout scp ";
|
||||
|
||||
if ($server->isIpv6()) {
|
||||
$scp_command .= '-6 ';
|
||||
}
|
||||
if (self::isMultiplexingEnabled()) {
|
||||
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
||||
self::ensureMultiplexedConnection($server);
|
||||
|
|
@ -136,8 +138,8 @@ public static function generateSshCommand(Server $server, string $command)
|
|||
|
||||
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
|
||||
|
||||
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
|
||||
$delimiter = Hash::make($command);
|
||||
$delimiter = base64_encode($delimiter);
|
||||
$command = str_replace($delimiter, '', $command);
|
||||
|
||||
$ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ public function applications(Request $request)
|
|||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
|
||||
|
|
@ -177,6 +178,7 @@ public function applications(Request $request)
|
|||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -235,6 +237,7 @@ public function create_public_application(Request $request)
|
|||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
|
||||
|
|
@ -279,6 +282,7 @@ public function create_public_application(Request $request)
|
|||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -337,6 +341,7 @@ public function create_private_gh_app_application(Request $request)
|
|||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
|
||||
|
|
@ -381,6 +386,7 @@ public function create_private_gh_app_application(Request $request)
|
|||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -468,6 +474,7 @@ public function create_private_deploy_key_application(Request $request)
|
|||
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
|
||||
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -552,6 +559,7 @@ public function create_dockerfile_application(Request $request)
|
|||
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
|
||||
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -602,6 +610,7 @@ public function create_dockerimage_application(Request $request)
|
|||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -627,7 +636,7 @@ public function create_dockercompose_application(Request $request)
|
|||
|
||||
private function create_application(Request $request, $type)
|
||||
{
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths'];
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image'];
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
|
|
@ -665,6 +674,8 @@ private function create_application(Request $request, $type)
|
|||
$fqdn = $request->domains;
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
$githubAppUuid = $request->github_app_uuid;
|
||||
$useBuildServer = $request->use_build_server;
|
||||
$isStatic = $request->is_static;
|
||||
|
||||
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
|
||||
if (! $project) {
|
||||
|
|
@ -693,8 +704,7 @@ private function create_application(Request $request, $type)
|
|||
if ($request->build_pack === 'dockercompose') {
|
||||
$request->offsetSet('ports_exposes', '80');
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
$validationRules = [
|
||||
'git_repository' => 'string|required',
|
||||
'git_branch' => 'string|required',
|
||||
'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
|
||||
|
|
@ -702,19 +712,21 @@ private function create_application(Request $request, $type)
|
|||
'docker_compose_location' => 'string',
|
||||
'docker_compose_raw' => 'string|nullable',
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_custom_start_command' => 'string|nullable',
|
||||
'docker_compose_custom_build_command' => 'string|nullable',
|
||||
]);
|
||||
];
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $validator->errors(),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$return = $this->validateDataApplications($request, $server);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
|
|
@ -738,6 +750,14 @@ private function create_application(Request $request, $type)
|
|||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->save();
|
||||
if (isset($isStatic)) {
|
||||
$application->settings->is_static = $isStatic;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
$application->refresh();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
|
|
@ -771,8 +791,7 @@ private function create_application(Request $request, $type)
|
|||
if ($request->build_pack === 'dockercompose') {
|
||||
$request->offsetSet('ports_exposes', '80');
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
$validationRules = [
|
||||
'git_repository' => 'string|required',
|
||||
'git_branch' => 'string|required',
|
||||
'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
|
||||
|
|
@ -781,10 +800,10 @@ private function create_application(Request $request, $type)
|
|||
'watch_paths' => 'string|nullable',
|
||||
'docker_compose_location' => 'string',
|
||||
'docker_compose_raw' => 'string|nullable',
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_custom_start_command' => 'string|nullable',
|
||||
'docker_compose_custom_build_command' => 'string|nullable',
|
||||
]);
|
||||
];
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
@ -833,6 +852,10 @@ private function create_application(Request $request, $type)
|
|||
$application->environment_id = $environment->id;
|
||||
$application->source_type = $githubApp->getMorphClass();
|
||||
$application->source_id = $githubApp->id;
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
|
|
@ -867,8 +890,8 @@ private function create_application(Request $request, $type)
|
|||
if ($request->build_pack === 'dockercompose') {
|
||||
$request->offsetSet('ports_exposes', '80');
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
|
||||
$validationRules = [
|
||||
'git_repository' => 'string|required',
|
||||
'git_branch' => 'string|required',
|
||||
'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
|
||||
|
|
@ -877,10 +900,10 @@ private function create_application(Request $request, $type)
|
|||
'watch_paths' => 'string|nullable',
|
||||
'docker_compose_location' => 'string',
|
||||
'docker_compose_raw' => 'string|nullable',
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_custom_start_command' => 'string|nullable',
|
||||
'docker_compose_custom_build_command' => 'string|nullable',
|
||||
]);
|
||||
];
|
||||
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
|
|
@ -925,6 +948,10 @@ private function create_application(Request $request, $type)
|
|||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
|
|
@ -956,10 +983,13 @@ private function create_application(Request $request, $type)
|
|||
if (! $request->has('name')) {
|
||||
$request->offsetSet('name', 'dockerfile-'.new Cuid2);
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
|
||||
$validationRules = [
|
||||
'dockerfile' => 'string|required',
|
||||
]);
|
||||
];
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
@ -1004,6 +1034,10 @@ private function create_application(Request $request, $type)
|
|||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
|
|
@ -1034,12 +1068,14 @@ private function create_application(Request $request, $type)
|
|||
if (! $request->has('name')) {
|
||||
$request->offsetSet('name', 'docker-image-'.new Cuid2);
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
$validationRules = [
|
||||
'docker_registry_image_name' => 'string|required',
|
||||
'docker_registry_image_tag' => 'string',
|
||||
'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required',
|
||||
]);
|
||||
];
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
@ -1062,6 +1098,10 @@ private function create_application(Request $request, $type)
|
|||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
|
|
@ -1108,10 +1148,12 @@ private function create_application(Request $request, $type)
|
|||
if (! $request->has('name')) {
|
||||
$request->offsetSet('name', 'service'.new Cuid2);
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
$validationRules = [
|
||||
'docker_compose_raw' => 'string|required',
|
||||
]);
|
||||
];
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
@ -1259,16 +1301,10 @@ public function application_by_uuid(Request $request)
|
|||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'cleanup',
|
||||
in: 'query',
|
||||
description: 'Delete configurations and volumes.',
|
||||
required: false,
|
||||
schema: new OA\Schema(
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
)
|
||||
),
|
||||
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
|
|
@ -1316,10 +1352,14 @@ public function delete_by_uuid(Request $request)
|
|||
'message' => 'Application not found',
|
||||
], 404);
|
||||
}
|
||||
|
||||
DeleteResourceJob::dispatch(
|
||||
resource: $application,
|
||||
deleteConfigurations: $cleanup,
|
||||
deleteVolumes: $cleanup);
|
||||
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Application deletion request queued.',
|
||||
|
|
@ -1404,6 +1444,7 @@ public function delete_by_uuid(Request $request)
|
|||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
|
|
@ -1460,10 +1501,9 @@ public function update_by_uuid(Request $request)
|
|||
], 404);
|
||||
}
|
||||
$server = $application->destination->server;
|
||||
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy'];
|
||||
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server'];
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
$validationRules = [
|
||||
'name' => 'string|max:255',
|
||||
'description' => 'string|nullable',
|
||||
'static_image' => 'string',
|
||||
|
|
@ -1473,7 +1513,9 @@ public function update_by_uuid(Request $request)
|
|||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_custom_start_command' => 'string|nullable',
|
||||
'docker_compose_custom_build_command' => 'string|nullable',
|
||||
]);
|
||||
];
|
||||
$validationRules = array_merge($validationRules, sharedDataApplications());
|
||||
$validator = customApiValidator($request->all(), $validationRules);
|
||||
|
||||
// Validate ports_exposes
|
||||
if ($request->has('ports_exposes')) {
|
||||
|
|
@ -1538,6 +1580,13 @@ public function update_by_uuid(Request $request)
|
|||
}
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
|
||||
$use_build_server = $request->use_build_server;
|
||||
|
||||
if (isset($use_build_server)) {
|
||||
$application->settings->is_build_server_enabled = $use_build_server;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$data = $request->all();
|
||||
|
|
|
|||
|
|
@ -1541,16 +1541,10 @@ public function create_database(Request $request, NewDatabaseTypes $type)
|
|||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'cleanup',
|
||||
in: 'query',
|
||||
description: 'Delete configurations and volumes.',
|
||||
required: false,
|
||||
schema: new OA\Schema(
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
)
|
||||
),
|
||||
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
|
|
@ -1595,10 +1589,14 @@ public function delete_by_uuid(Request $request)
|
|||
if (! $database) {
|
||||
return response()->json(['message' => 'Database not found.'], 404);
|
||||
}
|
||||
|
||||
DeleteResourceJob::dispatch(
|
||||
resource: $database,
|
||||
deleteConfigurations: $cleanup,
|
||||
deleteVolumes: $cleanup);
|
||||
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Database deletion request queued.',
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public function enable_api(Request $request)
|
|||
if ($teamId !== '0') {
|
||||
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$settings->update(['is_api_enabled' => true]);
|
||||
|
||||
return response()->json(['message' => 'API enabled.'], 200);
|
||||
|
|
@ -138,7 +138,7 @@ public function disable_api(Request $request)
|
|||
if ($teamId !== '0') {
|
||||
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$settings->update(['is_api_enabled' => false]);
|
||||
|
||||
return response()->json(['message' => 'API disabled.'], 200);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ private function removeSensitiveDataFromSettings($settings)
|
|||
return serializeApiResponse($settings);
|
||||
}
|
||||
$settings = $settings->makeHidden([
|
||||
'metrics_token',
|
||||
'sentinel_token',
|
||||
]);
|
||||
|
||||
return serializeApiResponse($settings);
|
||||
|
|
@ -308,7 +308,7 @@ public function domains_by_server(Request $request)
|
|||
$projects = Project::where('team_id', $teamId)->get();
|
||||
$domains = collect();
|
||||
$applications = $projects->pluck('applications')->flatten();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($applications->count() > 0) {
|
||||
foreach ($applications as $application) {
|
||||
$ip = $application->destination->server->ip;
|
||||
|
|
|
|||
|
|
@ -432,6 +432,10 @@ public function service_by_uuid(Request $request)
|
|||
tags: ['Services'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')),
|
||||
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
|
|
@ -476,7 +480,14 @@ public function delete_by_uuid(Request $request)
|
|||
if (! $service) {
|
||||
return response()->json(['message' => 'Service not found.'], 404);
|
||||
}
|
||||
DeleteResourceJob::dispatch($service);
|
||||
|
||||
DeleteResourceJob::dispatch(
|
||||
resource: $service,
|
||||
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Service deletion request queued.',
|
||||
|
|
@ -516,7 +527,8 @@ public function delete_by_uuid(Request $request)
|
|||
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -619,7 +631,8 @@ public function envs(Request $request)
|
|||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -738,7 +751,8 @@ public function update_env_by_uuid(Request $request)
|
|||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -853,7 +867,8 @@ public function create_bulk_envs(Request $request)
|
|||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -953,7 +968,8 @@ public function create_env(Request $request)
|
|||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -1025,9 +1041,11 @@ public function delete_env_by_uuid(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Service starting request queued.'],
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -1101,9 +1119,11 @@ public function action_deploy(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'],
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
@ -1177,9 +1197,11 @@ public function action_stop(Request $request)
|
|||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'],
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
|
@ -22,7 +21,7 @@ public function callback(string $provider)
|
|||
$oauthUser = get_socialite_provider($provider)->user();
|
||||
$user = User::whereEmail($oauthUser->email)->first();
|
||||
if (! $user) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
abort(403, 'Registration is disabled');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public function handle(Request $request, Closure $next): Response
|
|||
if (isCloud()) {
|
||||
return $next($request);
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->is_api_enabled === false) {
|
||||
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
|
|
@ -962,7 +961,7 @@ private function save_environment_variables()
|
|||
}
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
if ($this->application->compose_parsing_version === '3') {
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$envs->push("COOLIFY_URL={$this->application->fqdn}");
|
||||
} else {
|
||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||
|
|
@ -970,7 +969,7 @@ private function save_environment_variables()
|
|||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
if ($this->application->compose_parsing_version === '3') {
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$envs->push("COOLIFY_FQDN={$url}");
|
||||
} else {
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
|
|
@ -1334,7 +1333,7 @@ private function create_workdir()
|
|||
|
||||
private function prepare_builder_image()
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$helperImage = "{$helperImage}:{$settings->helper_version}";
|
||||
// Get user home directory
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
|
@ -22,7 +21,7 @@ public function handle(): void
|
|||
if (isDev() || isCloud()) {
|
||||
return;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
if ($response->successful()) {
|
||||
$versions = $response->json();
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Events\BackupCreated;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\S3Storage;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
|
|
@ -25,7 +23,6 @@
|
|||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Str;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
|
|
@ -64,32 +61,32 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||
public function __construct($backup)
|
||||
{
|
||||
$this->backup = $backup;
|
||||
$this->team = Team::find($backup->team_id);
|
||||
if (is_null($this->team)) {
|
||||
return;
|
||||
}
|
||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->service->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
} else {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->destination->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
// Check if team is exists
|
||||
if (is_null($this->team)) {
|
||||
$this->backup->update(['status' => 'failed']);
|
||||
StopDatabase::run($this->database);
|
||||
$this->database->delete();
|
||||
$this->team = Team::find($this->backup->team_id);
|
||||
if (! $this->team) {
|
||||
$this->backup->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->service->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
} else {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->destination->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
}
|
||||
if (is_null($this->server)) {
|
||||
throw new \Exception('Server not found?!');
|
||||
}
|
||||
if (is_null($this->database)) {
|
||||
throw new \Exception('Database not found?!');
|
||||
}
|
||||
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
|
||||
|
|
@ -239,7 +236,6 @@ public function handle(): void
|
|||
}
|
||||
}
|
||||
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
|
||||
|
||||
if ($this->database->name === 'coolify-db') {
|
||||
$databasesToBackup = ['coolify'];
|
||||
$this->directory_name = $this->container_name = 'coolify-db';
|
||||
|
|
@ -252,6 +248,9 @@ public function handle(): void
|
|||
try {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->backup->dump_all) {
|
||||
$this->backup_file = '/pg-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||
}
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
|
|
@ -280,6 +279,9 @@ public function handle(): void
|
|||
$this->backup_standalone_mongodb($database);
|
||||
} elseif (str($databaseType)->contains('mysql')) {
|
||||
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->backup->dump_all) {
|
||||
$this->backup_file = '/mysql-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||
}
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
|
|
@ -289,6 +291,9 @@ public function handle(): void
|
|||
$this->backup_standalone_mysql($database);
|
||||
} elseif (str($databaseType)->contains('mariadb')) {
|
||||
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||
if ($this->backup->dump_all) {
|
||||
$this->backup_file = '/mariadb-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||
}
|
||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
|
|
@ -327,7 +332,9 @@ public function handle(): void
|
|||
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
|
||||
throw $e;
|
||||
} finally {
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
if ($this->team) {
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +393,11 @@ private function backup_standalone_postgresql(string $database): void
|
|||
if ($this->postgres_password) {
|
||||
$backupCommand .= " -e PGPASSWORD=$this->postgres_password";
|
||||
}
|
||||
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
|
||||
if ($this->backup->dump_all) {
|
||||
$backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
|
||||
} else {
|
||||
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
|
||||
}
|
||||
|
||||
$commands[] = $backupCommand;
|
||||
ray($commands);
|
||||
|
|
@ -407,8 +418,11 @@ private function backup_standalone_mysql(string $database): void
|
|||
{
|
||||
try {
|
||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
|
||||
ray($commands);
|
||||
if ($this->backup->dump_all) {
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress | gzip > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
|
||||
}
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
$this->backup_output = trim($this->backup_output);
|
||||
if ($this->backup_output === '') {
|
||||
|
|
@ -426,7 +440,11 @@ private function backup_standalone_mariadb(string $database): void
|
|||
{
|
||||
try {
|
||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
|
||||
if ($this->backup->dump_all) {
|
||||
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress > $this->backup_location";
|
||||
} 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);
|
||||
|
|
@ -468,34 +486,6 @@ private function remove_old_backups(): void
|
|||
}
|
||||
}
|
||||
|
||||
// private function upload_to_s3(): void
|
||||
// {
|
||||
// try {
|
||||
// if (is_null($this->s3)) {
|
||||
// return;
|
||||
// }
|
||||
// $key = $this->s3->key;
|
||||
// $secret = $this->s3->secret;
|
||||
// // $region = $this->s3->region;
|
||||
// $bucket = $this->s3->bucket;
|
||||
// $endpoint = $this->s3->endpoint;
|
||||
// $this->s3->testConnection(shouldSave: true);
|
||||
// $configName = new Cuid2;
|
||||
|
||||
// $s3_copy_dir = str($this->backup_location)->replace(backup_dir(), '/var/www/html/storage/app/backups/');
|
||||
// $commands[] = "docker exec coolify bash -c 'mc config host add {$configName} {$endpoint} $key $secret'";
|
||||
// $commands[] = "docker exec coolify bash -c 'mc cp $s3_copy_dir {$configName}/{$bucket}{$this->backup_dir}/'";
|
||||
// instant_remote_process($commands, $this->server);
|
||||
// $this->add_to_backup_output('Uploaded to S3.');
|
||||
// } catch (\Throwable $e) {
|
||||
// $this->add_to_backup_output($e->getMessage());
|
||||
// throw $e;
|
||||
// } finally {
|
||||
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc config remove {$configName}'";
|
||||
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc alias rm {$configName}'";
|
||||
// instant_remote_process($removeConfigCommands, $this->server, false);
|
||||
// }
|
||||
// }
|
||||
private function upload_to_s3(): void
|
||||
{
|
||||
try {
|
||||
|
|
@ -517,10 +507,27 @@ private function upload_to_s3(): void
|
|||
$this->ensureHelperImageAvailable();
|
||||
|
||||
$fullImageName = $this->getFullImageName();
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||
|
||||
if (isDev()) {
|
||||
if ($this->database->name === 'coolify-db') {
|
||||
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/coolify/coolify-db-'.$this->server->ip.$this->backup_file;
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
} else {
|
||||
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name.$this->backup_file;
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
}
|
||||
} else {
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
|
||||
}
|
||||
if ($this->s3->isHetzner()) {
|
||||
$endpointWithoutBucket = 'https://'.str($endpoint)->after('https://')->after('.')->value();
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
|
||||
} else {
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||
}
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||
instant_remote_process($commands, $this->server);
|
||||
|
||||
$this->add_to_backup_output('Uploaded to S3.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
|
|
@ -562,7 +569,7 @@ private function pullHelperImage(string $fullImageName): void
|
|||
|
||||
private function getFullImageName(): string
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$latestVersion = $settings->helper_version;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||
|
||||
public function __construct(
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource,
|
||||
public bool $deleteConfigurations,
|
||||
public bool $deleteVolumes,
|
||||
public bool $dockerCleanup,
|
||||
public bool $deleteConnectedNetworks
|
||||
public bool $deleteConfigurations = true,
|
||||
public bool $deleteVolumes = true,
|
||||
public bool $dockerCleanup = true,
|
||||
public bool $deleteConnectedNetworks = true
|
||||
) {}
|
||||
|
||||
public function handle()
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
|||
|
||||
public ?string $usageBefore = null;
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
public function __construct(public Server $server, public bool $manualCleanup = false) {}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
|
|
@ -31,8 +31,9 @@ public function handle(): void
|
|||
if (! $this->server->isFunctional()) {
|
||||
return;
|
||||
}
|
||||
if ($this->server->settings->force_docker_cleanup) {
|
||||
Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
|
||||
|
||||
if ($this->manualCleanup || $this->server->settings->force_docker_cleanup) {
|
||||
Log::info('DockerCleanupJob '.($this->manualCleanup ? 'manual' : 'force').' cleanup on '.$this->server->name);
|
||||
CleanupDocker::run(server: $this->server);
|
||||
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
|
|
@ -26,7 +25,7 @@ public function handle(): void
|
|||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
if ($response->successful()) {
|
||||
$versions = $response->json();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$latest_version = data_get($versions, 'coolify.helper.version');
|
||||
$current_version = $settings->helper_version;
|
||||
if (version_compare($latest_version, $current_version, '>')) {
|
||||
|
|
|
|||
404
app/Jobs/PushServerUpdateJob.php
Normal file
404
app/Jobs/PushServerUpdateJob.php
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
<?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\InstallLogDrain;
|
||||
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 Illuminate\Bus\Queueable;
|
||||
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 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()
|
||||
{
|
||||
try {
|
||||
if (! $this->data) {
|
||||
throw new \Exception('No data provided');
|
||||
}
|
||||
$data = collect($this->data);
|
||||
|
||||
$this->serverStatus();
|
||||
|
||||
$this->server->sentinelUpdateAt();
|
||||
|
||||
$this->containers = collect(data_get($data, 'containers'));
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
ray('allServiceApplicationIds', ['allServiceApplicationIds' => $this->allServiceApplicationIds]);
|
||||
|
||||
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) {
|
||||
ray()->error($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)) {
|
||||
ray("Service: $uuid, $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();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function serverStatus(){
|
||||
if ($this->server->isFunctional() === false) {
|
||||
throw new \Exception('Server is not ready.');
|
||||
}
|
||||
if ($this->server->status() === false) {
|
||||
throw new \Exception('Server is not reachable.');
|
||||
}
|
||||
}
|
||||
private function updateApplicationStatus(string $applicationId, string $containerStatus)
|
||||
{
|
||||
$application = $this->applications->where('id', $applicationId)->first();
|
||||
if (! $application) {
|
||||
return;
|
||||
}
|
||||
$application->status = $containerStatus;
|
||||
$application->save();
|
||||
ray('Application updated', ['application_id' => $applicationId, 'status' => $containerStatus]);
|
||||
}
|
||||
|
||||
private function updateApplicationPreviewStatus(string $applicationId, string $containerStatus)
|
||||
{
|
||||
$application = $this->previews->where('id', $applicationId)->first();
|
||||
if (! $application) {
|
||||
return;
|
||||
}
|
||||
$application->status = $containerStatus;
|
||||
$application->save();
|
||||
ray('Application preview updated', ['application_id' => $applicationId, 'status' => $containerStatus]);
|
||||
}
|
||||
|
||||
private function updateNotFoundApplicationStatus()
|
||||
{
|
||||
$notFoundApplicationIds = $this->allApplicationIds->diff($this->foundApplicationIds);
|
||||
if ($notFoundApplicationIds->isNotEmpty()) {
|
||||
ray('Not found application ids', ['application_ids' => $notFoundApplicationIds]);
|
||||
$notFoundApplicationIds->each(function ($applicationId) {
|
||||
ray('Updating application status', ['application_id' => $applicationId, 'status' => 'exited']);
|
||||
$application = Application::find($applicationId);
|
||||
if ($application) {
|
||||
$application->status = 'exited';
|
||||
$application->save();
|
||||
ray('Application status updated', ['application_id' => $applicationId, 'status' => 'exited']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function updateNotFoundApplicationPreviewStatus()
|
||||
{
|
||||
$notFoundApplicationPreviewsIds = $this->allApplicationPreviewsIds->diff($this->foundApplicationPreviewsIds);
|
||||
if ($notFoundApplicationPreviewsIds->isNotEmpty()) {
|
||||
ray('Not found application previews ids', ['application_previews_ids' => $notFoundApplicationPreviewsIds]);
|
||||
$notFoundApplicationPreviewsIds->each(function ($applicationPreviewId) {
|
||||
ray('Updating application preview status', ['application_preview_id' => $applicationPreviewId, 'status' => 'exited']);
|
||||
$applicationPreview = ApplicationPreview::find($applicationPreviewId);
|
||||
if ($applicationPreview) {
|
||||
$applicationPreview->status = 'exited';
|
||||
$applicationPreview->save();
|
||||
ray('Application preview status updated', ['application_preview_id' => $applicationPreviewId, 'status' => 'exited']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
logger()->error($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();
|
||||
ray('Database status updated', ['database_uuid' => $databaseUuid, 'status' => $containerStatus]);
|
||||
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) {
|
||||
ray('Starting TCP proxy for database', ['database_uuid' => $databaseUuid]);
|
||||
StartDatabaseProxy::dispatch($database);
|
||||
} else {
|
||||
ray('TCP proxy for database found in containers', ['database_uuid' => $databaseUuid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateNotFoundDatabaseStatus()
|
||||
{
|
||||
$notFoundDatabaseUuids = $this->allDatabaseUuids->diff($this->foundDatabaseUuids);
|
||||
if ($notFoundDatabaseUuids->isNotEmpty()) {
|
||||
ray('Not found database uuids', ['database_uuids' => $notFoundDatabaseUuids]);
|
||||
$notFoundDatabaseUuids->each(function ($databaseUuid) {
|
||||
ray('Updating database status', ['database_uuid' => $databaseUuid, 'status' => 'exited']);
|
||||
$database = $this->databases->where('uuid', $databaseUuid)->first();
|
||||
if ($database) {
|
||||
$database->status = 'exited';
|
||||
$database->save();
|
||||
ray('Database status updated', ['database_uuid' => $databaseUuid, 'status' => 'exited']);
|
||||
ray('Database is public', ['database_uuid' => $databaseUuid, 'is_public' => $database->is_public]);
|
||||
if ($database->is_public) {
|
||||
ray('Stopping TCP proxy for database', ['database_uuid' => $databaseUuid]);
|
||||
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();
|
||||
ray('Service application updated', ['service_id' => $serviceId, 'sub_type' => $subType, 'sub_id' => $subId, 'status' => $containerStatus]);
|
||||
} elseif ($subType === 'database') {
|
||||
$database = $service->databases()->where('id', $subId)->first();
|
||||
$database->status = $containerStatus;
|
||||
$database->save();
|
||||
ray('Service database updated', ['service_id' => $serviceId, 'sub_type' => $subType, 'sub_id' => $subId, 'status' => $containerStatus]);
|
||||
} else {
|
||||
ray()->warning('Unknown sub type', ['service_id' => $serviceId, 'sub_type' => $subType, 'sub_id' => $subId, 'status' => $containerStatus]);
|
||||
}
|
||||
}
|
||||
|
||||
private function updateNotFoundServiceStatus()
|
||||
{
|
||||
$notFoundServiceApplicationIds = $this->allServiceApplicationIds->diff($this->foundServiceApplicationIds);
|
||||
$notFoundServiceDatabaseIds = $this->allServiceDatabaseIds->diff($this->foundServiceDatabaseIds);
|
||||
if ($notFoundServiceApplicationIds->isNotEmpty()) {
|
||||
ray('Not found service application ids', ['service_application_ids' => $notFoundServiceApplicationIds]);
|
||||
$notFoundServiceApplicationIds->each(function ($serviceApplicationId) {
|
||||
ray('Updating service application status', ['service_application_id' => $serviceApplicationId, 'status' => 'exited']);
|
||||
$application = ServiceApplication::find($serviceApplicationId);
|
||||
if ($application) {
|
||||
$application->status = 'exited';
|
||||
$application->save();
|
||||
ray('Service application status updated', ['service_application_id' => $serviceApplicationId, 'status' => 'exited']);
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($notFoundServiceDatabaseIds->isNotEmpty()) {
|
||||
ray('Not found service database ids', ['service_database_ids' => $notFoundServiceDatabaseIds]);
|
||||
$notFoundServiceDatabaseIds->each(function ($serviceDatabaseId) {
|
||||
ray('Updating service database status', ['service_database_id' => $serviceDatabaseId, 'status' => 'exited']);
|
||||
$database = ServiceDatabase::find($serviceDatabaseId);
|
||||
if ($database) {
|
||||
$database->status = 'exited';
|
||||
$database->save();
|
||||
ray('Service database status updated', ['service_database_id' => $serviceDatabaseId, 'status' => 'exited']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function updateAdditionalServersStatus()
|
||||
{
|
||||
$this->allApplicationsWithAdditionalServers->each(function ($application) {
|
||||
ray('Updating additional servers status for application', ['application_id' => $application->id]);
|
||||
ComplexStatusCheck::run($application);
|
||||
});
|
||||
}
|
||||
|
||||
private function isRunning(string $containerStatus)
|
||||
{
|
||||
return str($containerStatus)->contains('running');
|
||||
}
|
||||
|
||||
private function checkLogDrainContainer(){
|
||||
if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) {
|
||||
InstallLogDrain::dispatch($this->server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,7 +69,9 @@ public function handle()
|
|||
return 'No containers found.';
|
||||
}
|
||||
GetContainersStatus::run($this->server, $this->containers, $containerReplicates);
|
||||
$this->checkLogDrainContainer();
|
||||
if ($this->server->isLogDrainEnabled()) {
|
||||
$this->checkLogDrainContainer();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
|
@ -115,9 +117,6 @@ private function serverStatus()
|
|||
|
||||
private function checkLogDrainContainer()
|
||||
{
|
||||
if (! $this->server->isLogDrainEnabled()) {
|
||||
return;
|
||||
}
|
||||
$foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
|
||||
return data_get($value, 'Name') === '/coolify-log-drain';
|
||||
})->first();
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
|
|
|||
59
app/Jobs/ServerStorageCheckJob.php
Normal file
59
app/Jobs/ServerStorageCheckJob.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?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 ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
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 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.'%');
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Server\UpdateCoolify;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
|
|
@ -23,7 +22,7 @@ public function handle(): void
|
|||
{
|
||||
try {
|
||||
CheckForUpdatesJob::dispatchSync();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->new_version_available) {
|
||||
Log::info('No new version available. Skipping update.');
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public function mount()
|
|||
|
||||
public function cleanup_queue()
|
||||
{
|
||||
Artisan::queue('cleanup:application-deployment-queue', [
|
||||
Artisan::queue('cleanup:deployment-queue', [
|
||||
'--team-id' => currentTeam()->id,
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public function submit()
|
|||
]
|
||||
);
|
||||
$mail->subject("[HELP]: {$this->subject}");
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
$url = 'https://app.coolify.io/api/feedback';
|
||||
|
|
@ -61,6 +61,7 @@ public function submit()
|
|||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||
}
|
||||
$this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.');
|
||||
$this->reset('description', 'subject');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ public function submitResend()
|
|||
|
||||
public function copyFromInstanceSettings()
|
||||
{
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->smtp_enabled) {
|
||||
$team = currentTeam();
|
||||
$team->update([
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Actions\Application\GenerateConfig;
|
||||
use App\Models\Application;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class General extends Component
|
||||
|
|
@ -182,9 +184,7 @@ public function instantSave()
|
|||
$storage->save();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function loadComposeFile($isInit = false)
|
||||
|
|
@ -241,16 +241,6 @@ public function updatedApplicationBaseDirectory()
|
|||
}
|
||||
}
|
||||
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
|
||||
public function updatedApplicationBuildPack()
|
||||
{
|
||||
|
|
@ -287,18 +277,22 @@ public function getWildcardDomain()
|
|||
|
||||
public function resetDefaultLabels()
|
||||
{
|
||||
if ($this->application->settings->is_container_label_readonly_enabled) {
|
||||
return;
|
||||
try {
|
||||
if ($this->application->settings->is_container_label_readonly_enabled) {
|
||||
return;
|
||||
}
|
||||
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
|
||||
public function checkFqdns($showToaster = true)
|
||||
|
|
@ -320,7 +314,7 @@ public function checkFqdns($showToaster = true)
|
|||
public function set_redirect()
|
||||
{
|
||||
try {
|
||||
$has_www = collect($this->application->fqdns)->filter(fn ($fqdn) => str($fqdn)->contains('www.'))->count();
|
||||
$has_www = collect($this->application->fqdns)->filter(fn($fqdn) => str($fqdn)->contains('www.'))->count();
|
||||
if ($has_www === 0 && $this->application->redirect === 'www') {
|
||||
$this->dispatch('error', 'You want to redirect to www, but you do not have a www domain set.<br><br>Please add www to your domain list and as an A DNS record (if applicable).');
|
||||
|
||||
|
|
@ -337,15 +331,18 @@ public function set_redirect()
|
|||
public function submit($showToaster = true)
|
||||
{
|
||||
try {
|
||||
if ($this->application->isDirty('redirect')) {
|
||||
$this->set_redirect();
|
||||
}
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
Url::fromString($domain, ['http', 'https']);
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->resetDefaultLabels();
|
||||
|
||||
if ($this->application->isDirty('redirect')) {
|
||||
$this->set_redirect();
|
||||
}
|
||||
|
||||
$this->checkFqdns();
|
||||
|
||||
|
|
@ -408,9 +405,25 @@ public function submit($showToaster = true)
|
|||
$this->application->save();
|
||||
$showToaster && $this->dispatch('success', 'Application settings updated!');
|
||||
} catch (\Throwable $e) {
|
||||
$originalFqdn = $this->application->getOriginal('fqdn');
|
||||
if ($originalFqdn !== $this->application->fqdn) {
|
||||
$this->application->fqdn = $originalFqdn;
|
||||
}
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
}
|
||||
public function downloadConfig()
|
||||
{
|
||||
$config = GenerateConfig::run($this->application, true);
|
||||
$fileName = str($this->application->name)->slug()->append('_config.json');
|
||||
|
||||
return response()->streamDownload(function () use ($config) {
|
||||
echo $config;
|
||||
}, $fileName, [
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Disposition' => 'attachment; filename=' . $fileName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,14 @@ public function resetToDefault()
|
|||
public function generate_real_url()
|
||||
{
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
$firstFqdn = str($this->application->fqdn)->before(',');
|
||||
$url = Url::fromString($firstFqdn);
|
||||
$host = $url->getHost();
|
||||
$this->preview_url_template = str($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||
try {
|
||||
$firstFqdn = str($this->application->fqdn)->before(',');
|
||||
$url = Url::fromString($firstFqdn);
|
||||
$host = $url->getHost();
|
||||
$this->preview_url_template = str($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||
} catch (\Exception $e) {
|
||||
$this->dispatch('error', 'Invalid FQDN.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class BackupEdit extends Component
|
|||
'backup.save_s3' => 'required|boolean',
|
||||
'backup.s3_storage_id' => 'nullable|integer',
|
||||
'backup.databases_to_backup' => 'nullable',
|
||||
'backup.dump_all' => 'required|boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
|
|
@ -40,6 +41,7 @@ class BackupEdit extends Component
|
|||
'backup.save_s3' => 'Save to S3',
|
||||
'backup.s3_storage_id' => 'S3 Storage',
|
||||
'backup.databases_to_backup' => 'Databases to Backup',
|
||||
'backup.dump_all' => 'Backup All Databases',
|
||||
];
|
||||
|
||||
protected $messages = [
|
||||
|
|
@ -182,7 +184,7 @@ public function render()
|
|||
{
|
||||
return view('livewire.project.database.backup-edit', [
|
||||
'checkboxes' => [
|
||||
['id' => 'delete_associated_backups_locally', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from local storage.'],
|
||||
['id' => 'delete_associated_backups_locally', 'label' => __('database.delete_backups_locally')],
|
||||
// ['id' => 'delete_associated_backups_s3', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected S3 Storage.']
|
||||
// ['id' => 'delete_associated_backups_sftp', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected SFTP Storage.']
|
||||
],
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public function render()
|
|||
{
|
||||
return view('livewire.project.database.heading', [
|
||||
'checkboxes' => [
|
||||
['id' => 'docker_cleanup', 'label' => 'Cleanup docker build cache and unused images (next deployment could take longer).'],
|
||||
['id' => 'docker_cleanup', 'label' => __('resource.docker_cleanup')],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class ScheduledBackups extends Component
|
|||
public function mount(): void
|
||||
{
|
||||
if ($this->selectedBackupId) {
|
||||
$this->setSelectedBackup($this->selectedBackupId);
|
||||
$this->setSelectedBackup($this->selectedBackupId, true);
|
||||
}
|
||||
$this->parameters = get_route_parameters();
|
||||
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
|
|
@ -37,10 +37,13 @@ public function mount(): void
|
|||
$this->s3s = currentTeam()->s3s;
|
||||
}
|
||||
|
||||
public function setSelectedBackup($backupId)
|
||||
public function setSelectedBackup($backupId, $force = false)
|
||||
{
|
||||
if ($this->selectedBackupId === $backupId && ! $force) {
|
||||
return;
|
||||
}
|
||||
$this->selectedBackupId = $backupId;
|
||||
$this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId);
|
||||
$this->selectedBackup = $this->database->scheduledBackups->find($backupId);
|
||||
if (is_null($this->selectedBackup)) {
|
||||
$this->selectedBackupId = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ class Index extends Component
|
|||
public function mount()
|
||||
{
|
||||
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
$this->projects = Project::ownedByCurrentTeam()->get()->map(function ($project) {
|
||||
$project->settingsRoute = route('project.edit', ['project_uuid' => $project->uuid]);
|
||||
|
||||
return $project;
|
||||
});
|
||||
$this->servers = Server::ownedByCurrentTeam()->count();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,12 @@ class PublicGitRepository extends Component
|
|||
|
||||
public bool $isStatic = false;
|
||||
|
||||
public bool $checkCoolifyConfig = true;
|
||||
|
||||
public ?string $publish_directory = null;
|
||||
|
||||
// In case of docker compose
|
||||
public ?string $base_directory = null;
|
||||
public string $base_directory = '/';
|
||||
|
||||
public ?string $docker_compose_location = '/docker-compose.yaml';
|
||||
// End of docker compose
|
||||
|
|
@ -97,6 +99,7 @@ public function updatedBaseDirectory()
|
|||
$this->base_directory = '/'.$this->base_directory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function updatedDockerComposeLocation()
|
||||
|
|
@ -275,6 +278,7 @@ public function submit()
|
|||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination_class,
|
||||
'build_pack' => $this->build_pack,
|
||||
'base_directory' => $this->base_directory,
|
||||
];
|
||||
} else {
|
||||
$application_init = [
|
||||
|
|
@ -289,6 +293,7 @@ public function submit()
|
|||
'source_id' => $this->git_source->id,
|
||||
'source_type' => $this->git_source->getMorphClass(),
|
||||
'build_pack' => $this->build_pack,
|
||||
'base_directory' => $this->base_directory,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -303,11 +308,15 @@ public function submit()
|
|||
|
||||
$application->settings->is_static = $this->isStatic;
|
||||
$application->settings->save();
|
||||
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
$application->fqdn = $fqdn;
|
||||
$application->save();
|
||||
|
||||
if ($this->checkCoolifyConfig) {
|
||||
// $config = loadConfigFromGit($this->repository_url, $this->git_branch, $this->base_directory, $this->query['server_id'], auth()->user()->currentTeam()->id);
|
||||
// if ($config) {
|
||||
// $application->setConfig($config);
|
||||
// }
|
||||
}
|
||||
return redirect()->route('project.application.configuration', [
|
||||
'application_uuid' => $application->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -32,8 +32,11 @@ class Index extends Component
|
|||
|
||||
public $services = [];
|
||||
|
||||
public array $parameters;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||
if (! $project) {
|
||||
return redirect()->route('dashboard');
|
||||
|
|
@ -44,7 +47,6 @@ public function mount()
|
|||
}
|
||||
$this->project = $project;
|
||||
$this->environment = $environment;
|
||||
|
||||
$this->applications = $this->environment->applications->load(['tags']);
|
||||
$this->applications = $this->applications->map(function ($application) {
|
||||
if (data_get($application, 'environment.project.uuid')) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Models\ServiceApplication;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
class EditDomain extends Component
|
||||
{
|
||||
|
|
@ -20,21 +21,16 @@ public function mount()
|
|||
{
|
||||
$this->application = ServiceApplication::find($this->applicationId);
|
||||
}
|
||||
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
Url::fromString($domain, ['http', 'https']);
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
check_domain_usage(resource: $this->application);
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
|
|
@ -44,12 +40,15 @@ public function submit()
|
|||
} else {
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->application->service->parse();
|
||||
$this->dispatch('refresh');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
$originalFqdn = $this->application->getOriginal('fqdn');
|
||||
if ($originalFqdn !== $this->application->fqdn) {
|
||||
$this->application->fqdn = $originalFqdn;
|
||||
}
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public function getListeners()
|
|||
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||
"envsUpdated" => '$refresh',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -108,8 +109,23 @@ public function restart()
|
|||
|
||||
return;
|
||||
}
|
||||
StopService::run(service: $this->service, dockerCleanup: false);
|
||||
$this->service->parse();
|
||||
$this->dispatch('imagePulled');
|
||||
$activity = StartService::run($this->service);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
|
||||
public function pullAndRestartEvent()
|
||||
{
|
||||
$this->checkDeployments();
|
||||
if ($this->isDeploymentProgress) {
|
||||
$this->dispatch('error', 'There is a deployment in progress.');
|
||||
|
||||
return;
|
||||
}
|
||||
PullImage::run($this->service);
|
||||
StopService::run($this->service);
|
||||
StopService::run(service: $this->service, dockerCleanup: false);
|
||||
$this->service->parse();
|
||||
$this->dispatch('imagePulled');
|
||||
$activity = StartService::run($this->service);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
class ServiceApplicationView extends Component
|
||||
{
|
||||
|
|
@ -31,13 +32,7 @@ class ServiceApplicationView extends Component
|
|||
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
|
|
@ -83,6 +78,14 @@ public function mount()
|
|||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
Url::fromString($domain, ['http', 'https']);
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
|
||||
check_domain_usage(resource: $this->application);
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
|
|
@ -92,10 +95,13 @@ public function submit()
|
|||
} else {
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('generateDockerCompose');
|
||||
} catch (\Throwable $e) {
|
||||
$originalFqdn = $this->application->getOriginal('fqdn');
|
||||
if ($originalFqdn !== $this->application->fqdn) {
|
||||
$this->application->fqdn = $originalFqdn;
|
||||
}
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public function mount()
|
|||
$value = data_get($field, 'value');
|
||||
$rules = data_get($field, 'rules', 'nullable');
|
||||
$isPassword = data_get($field, 'isPassword', false);
|
||||
$customHelper = data_get($field, 'customHelper', false);
|
||||
$this->fields->put($key, [
|
||||
'serviceName' => $serviceName,
|
||||
'key' => $key,
|
||||
|
|
@ -41,6 +42,7 @@ public function mount()
|
|||
'value' => $value,
|
||||
'isPassword' => $isPassword,
|
||||
'rules' => $rules,
|
||||
'customHelper' => $customHelper,
|
||||
]);
|
||||
|
||||
$this->rules["fields.$key.value"] = $rules;
|
||||
|
|
|
|||
|
|
@ -91,10 +91,12 @@ public function mount()
|
|||
|
||||
public function delete($password)
|
||||
{
|
||||
if (! Hash::check($password, Auth::user()->password)) {
|
||||
$this->addError('password', 'The provided password is incorrect.');
|
||||
if (isProduction()) {
|
||||
if (! Hash::check($password, Auth::user()->password)) {
|
||||
$this->addError('password', 'The provided password is incorrect.');
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->resource) {
|
||||
|
|
|
|||
|
|
@ -48,14 +48,6 @@ public function mount()
|
|||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
// if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) {
|
||||
// $type = str($this->value)->after('{{')->before('.')->value;
|
||||
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
|
||||
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
|
||||
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
$this->dispatch('saveKey', [
|
||||
'key' => $this->key,
|
||||
'value' => $this->value,
|
||||
|
|
|
|||
|
|
@ -53,30 +53,16 @@ public function instantSave()
|
|||
|
||||
public function sortEnvironmentVariables()
|
||||
{
|
||||
if ($this->resource->type() === 'application') {
|
||||
$this->resource->load(['environment_variables', 'environment_variables_preview']);
|
||||
} else {
|
||||
$this->resource->load(['environment_variables']);
|
||||
if (! data_get($this->resource, 'settings.is_env_sorting_enabled')) {
|
||||
if ($this->resource->environment_variables) {
|
||||
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('order')->values();
|
||||
}
|
||||
|
||||
if ($this->resource->environment_variables_preview) {
|
||||
$this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('order')->values();
|
||||
}
|
||||
}
|
||||
|
||||
$sortBy = data_get($this->resource, 'settings.is_env_sorting_enabled') ? 'key' : 'order';
|
||||
|
||||
$sortFunction = function ($variables) use ($sortBy) {
|
||||
if (! $variables) {
|
||||
return $variables;
|
||||
}
|
||||
if ($sortBy === 'key') {
|
||||
return $variables->sortBy(function ($item) {
|
||||
return strtolower($item->key);
|
||||
}, SORT_NATURAL | SORT_FLAG_CASE)->values();
|
||||
} else {
|
||||
return $variables->sortBy('order')->values();
|
||||
}
|
||||
};
|
||||
|
||||
$this->resource->environment_variables = $sortFunction($this->resource->environment_variables);
|
||||
$this->resource->environment_variables_preview = $sortFunction($this->resource->environment_variables_preview);
|
||||
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +107,8 @@ public function submit($data = null)
|
|||
$this->sortEnvironmentVariables();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->refreshEnvs();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class Show extends Component
|
|||
'env.is_literal' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
'env.real_value' => 'nullable',
|
||||
'env.is_required' => 'required|boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
|
|
@ -46,6 +47,7 @@ class Show extends Component
|
|||
'env.is_multiline' => 'Multiline',
|
||||
'env.is_literal' => 'Literal',
|
||||
'env.is_shown_once' => 'Shown Once',
|
||||
'env.is_required' => 'Required',
|
||||
];
|
||||
|
||||
public function refresh()
|
||||
|
|
@ -109,14 +111,14 @@ public function submit()
|
|||
} else {
|
||||
$this->validate();
|
||||
}
|
||||
// if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) {
|
||||
// $type = str($this->env->value)->after('{{')->before('.')->value;
|
||||
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
|
||||
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
|
||||
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
if ($this->env->is_required && str($this->env->real_value)->isEmpty()) {
|
||||
$oldValue = $this->env->getOriginal('value');
|
||||
$this->env->value = $oldValue;
|
||||
$this->dispatch('error', 'Required environment variable cannot be empty.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->serialize();
|
||||
$this->env->save();
|
||||
$this->dispatch('success', 'Environment variable updated.');
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
class ExecuteContainerCommand extends Component
|
||||
{
|
||||
public $selected_container = 'default';
|
||||
|
||||
public $container;
|
||||
|
||||
public Collection $containers;
|
||||
|
|
@ -83,11 +85,14 @@ public function loadContainers()
|
|||
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||
}
|
||||
foreach ($containers as $container) {
|
||||
$payload = [
|
||||
'server' => $server,
|
||||
'container' => $container,
|
||||
];
|
||||
$this->containers = $this->containers->push($payload);
|
||||
// if container state is running
|
||||
if (data_get($container, 'State') === 'running') {
|
||||
$payload = [
|
||||
'server' => $server,
|
||||
'container' => $container,
|
||||
];
|
||||
$this->containers = $this->containers->push($payload);
|
||||
}
|
||||
}
|
||||
} elseif (data_get($this->parameters, 'database_uuid')) {
|
||||
if ($this->resource->isRunning()) {
|
||||
|
|
@ -100,7 +105,6 @@ public function loadContainers()
|
|||
}
|
||||
} elseif (data_get($this->parameters, 'service_uuid')) {
|
||||
$this->resource->applications()->get()->each(function ($application) {
|
||||
ray($application);
|
||||
if ($application->isRunning()) {
|
||||
$this->containers->push([
|
||||
'server' => $this->resource->server,
|
||||
|
|
@ -131,9 +135,14 @@ public function loadContainers()
|
|||
#[On('connectToContainer')]
|
||||
public function connectToContainer()
|
||||
{
|
||||
if ($this->selected_container === 'default') {
|
||||
$this->dispatch('error', 'Please select a container.');
|
||||
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$container_name = data_get($this->container, 'container.Names');
|
||||
if (is_null($container_name)) {
|
||||
$container = collect($this->containers)->firstWhere('container.Names', $this->selected_container);
|
||||
if (is_null($container)) {
|
||||
throw new \RuntimeException('Container not found.');
|
||||
}
|
||||
$server = data_get($this->container, 'server');
|
||||
|
|
@ -141,11 +150,11 @@ public function connectToContainer()
|
|||
if ($server->isForceDisabled()) {
|
||||
throw new \RuntimeException('Server is disabled.');
|
||||
}
|
||||
|
||||
$this->dispatch('send-terminal-command',
|
||||
true,
|
||||
$container_name,
|
||||
$server->uuid,
|
||||
$this->dispatch(
|
||||
'send-terminal-command',
|
||||
isset($container),
|
||||
data_get($container, 'container.Names'),
|
||||
data_get($container, 'server.uuid')
|
||||
);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
|
|
|||
|
|
@ -31,13 +31,8 @@ public function pollData()
|
|||
public function loadData()
|
||||
{
|
||||
try {
|
||||
$metrics = $this->resource->getMetrics($this->interval);
|
||||
$cpuMetrics = collect($metrics)->map(function ($metric) {
|
||||
return [$metric[0], $metric[1]];
|
||||
});
|
||||
$memoryMetrics = collect($metrics)->map(function ($metric) {
|
||||
return [$metric[0], $metric[2]];
|
||||
});
|
||||
$cpuMetrics = $this->resource->getCpuMetrics($this->interval);
|
||||
$memoryMetrics = $this->resource->getMemoryMetrics($this->interval);
|
||||
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
|
||||
'seriesData' => $cpuMetrics,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ public function sendTerminalCommand($isContainer, $identifier, $serverUuid)
|
|||
if ($status !== 'running') {
|
||||
return;
|
||||
}
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
} else {
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi');
|
||||
}
|
||||
|
||||
// ssh command is sent back to frontend then to websocket
|
||||
|
|
|
|||
41
app/Livewire/Project/Shared/UploadConfig.php
Normal file
41
app/Livewire/Project/Shared/UploadConfig.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Shared;
|
||||
|
||||
use App\Models\Application;
|
||||
use Livewire\Component;
|
||||
|
||||
class UploadConfig extends Component
|
||||
{
|
||||
public $config;
|
||||
public $applicationId;
|
||||
public function mount() {
|
||||
if (isDev()) {
|
||||
$this->config = '{
|
||||
"build_pack": "nixpacks",
|
||||
"base_directory": "/nodejs",
|
||||
"publish_directory": "/",
|
||||
"ports_exposes": "3000",
|
||||
"settings": {
|
||||
"is_static": false
|
||||
}
|
||||
}';
|
||||
}
|
||||
}
|
||||
public function uploadConfig()
|
||||
{
|
||||
try {
|
||||
$application = Application::findOrFail($this->applicationId);
|
||||
$application->setConfig($this->config);
|
||||
$this->dispatch('success', 'Application settings updated');
|
||||
} catch (\Exception $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.upload-config');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Livewire\Security;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Livewire\Component;
|
||||
|
||||
class ApiTokens extends Component
|
||||
|
|
@ -14,8 +15,12 @@ class ApiTokens extends Component
|
|||
|
||||
public bool $readOnly = true;
|
||||
|
||||
public bool $rootAccess = false;
|
||||
|
||||
public array $permissions = ['read-only'];
|
||||
|
||||
public $isApiEnabled;
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.security.api-tokens');
|
||||
|
|
@ -23,6 +28,7 @@ public function render()
|
|||
|
||||
public function mount()
|
||||
{
|
||||
$this->isApiEnabled = InstanceSettings::get()->is_api_enabled;
|
||||
$this->tokens = auth()->user()->tokens->sortByDesc('created_at');
|
||||
}
|
||||
|
||||
|
|
@ -31,12 +37,11 @@ public function updatedViewSensitiveData()
|
|||
if ($this->viewSensitiveData) {
|
||||
$this->permissions[] = 'view:sensitive';
|
||||
$this->permissions = array_diff($this->permissions, ['*']);
|
||||
$this->rootAccess = false;
|
||||
} else {
|
||||
$this->permissions = array_diff($this->permissions, ['view:sensitive']);
|
||||
}
|
||||
if (count($this->permissions) == 0) {
|
||||
$this->permissions = ['*'];
|
||||
}
|
||||
$this->makeSureOneIsSelected();
|
||||
}
|
||||
|
||||
public function updatedReadOnly()
|
||||
|
|
@ -44,11 +49,30 @@ public function updatedReadOnly()
|
|||
if ($this->readOnly) {
|
||||
$this->permissions[] = 'read-only';
|
||||
$this->permissions = array_diff($this->permissions, ['*']);
|
||||
$this->rootAccess = false;
|
||||
} else {
|
||||
$this->permissions = array_diff($this->permissions, ['read-only']);
|
||||
}
|
||||
if (count($this->permissions) == 0) {
|
||||
$this->makeSureOneIsSelected();
|
||||
}
|
||||
|
||||
public function updatedRootAccess()
|
||||
{
|
||||
if ($this->rootAccess) {
|
||||
$this->permissions = ['*'];
|
||||
$this->readOnly = false;
|
||||
$this->viewSensitiveData = false;
|
||||
} else {
|
||||
$this->readOnly = true;
|
||||
$this->permissions = ['read-only'];
|
||||
}
|
||||
}
|
||||
|
||||
public function makeSureOneIsSelected()
|
||||
{
|
||||
if (count($this->permissions) == 0) {
|
||||
$this->permissions = ['read-only'];
|
||||
$this->readOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,12 +82,6 @@ public function addNewToken()
|
|||
$this->validate([
|
||||
'description' => 'required|min:3|max:255',
|
||||
]);
|
||||
// if ($this->viewSensitiveData) {
|
||||
// $this->permissions[] = 'view:sensitive';
|
||||
// }
|
||||
// if ($this->readOnly) {
|
||||
// $this->permissions[] = 'read-only';
|
||||
// }
|
||||
$token = auth()->user()->createToken($this->description, $this->permissions);
|
||||
$this->tokens = auth()->user()->tokens;
|
||||
session()->flash('token', $token->plainTextToken);
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@ public function loadData()
|
|||
try {
|
||||
$cpuMetrics = $this->server->getCpuMetrics($this->interval);
|
||||
$memoryMetrics = $this->server->getMemoryMetrics($this->interval);
|
||||
$cpuMetrics = collect($cpuMetrics)->map(function ($metric) {
|
||||
return [$metric[0], $metric[1]];
|
||||
});
|
||||
$memoryMetrics = collect($memoryMetrics)->map(function ($metric) {
|
||||
return [$metric[0], $metric[1]];
|
||||
});
|
||||
// $cpuMetrics = collect($cpuMetrics)->map(function ($metric) {
|
||||
// return [$metric[0], $metric[1]];
|
||||
// });
|
||||
// $memoryMetrics = collect($memoryMetrics)->map(function ($metric) {
|
||||
// return [$metric[0], $metric[1]];
|
||||
// });
|
||||
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
|
||||
'seriesData' => $cpuMetrics,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ public function alreadyConfigured()
|
|||
public function submit()
|
||||
{
|
||||
try {
|
||||
if (str($this->ssh_domain)->contains('https://')) {
|
||||
$this->ssh_domain = str($this->ssh_domain)->replace('https://', '')->replace('http://', '')->trim();
|
||||
// remove / from the end
|
||||
$this->ssh_domain = str($this->ssh_domain)->replace('/', '');
|
||||
}
|
||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
|
||||
$server->settings->is_cloudflare_tunnel = true;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Jobs\DockerCleanupJob;
|
||||
use App\Jobs\PullSentinelImageJob;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Component;
|
||||
|
||||
class Form extends Component
|
||||
|
|
@ -24,6 +26,10 @@ class Form extends Component
|
|||
|
||||
public $timezones;
|
||||
|
||||
public $delete_unused_volumes = false;
|
||||
|
||||
public $delete_unused_networks = false;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
|
|
@ -49,15 +55,19 @@ public function getListeners()
|
|||
'server.settings.concurrent_builds' => 'required|integer|min:1',
|
||||
'server.settings.dynamic_timeout' => 'required|integer|min:1',
|
||||
'server.settings.is_metrics_enabled' => 'required|boolean',
|
||||
'server.settings.metrics_token' => 'required',
|
||||
'server.settings.metrics_refresh_rate_seconds' => 'required|integer|min:1',
|
||||
'server.settings.metrics_history_days' => 'required|integer|min:1',
|
||||
'server.settings.sentinel_token' => 'required',
|
||||
'server.settings.sentinel_metrics_refresh_rate_seconds' => 'required|integer|min:1',
|
||||
'server.settings.sentinel_metrics_history_days' => 'required|integer|min:1',
|
||||
'server.settings.sentinel_push_interval_seconds' => 'required|integer|min:10',
|
||||
'wildcard_domain' => 'nullable|url',
|
||||
'server.settings.is_server_api_enabled' => 'required|boolean',
|
||||
'server.settings.sentinel_custom_url' => 'nullable|url',
|
||||
'server.settings.is_sentinel_enabled' => 'required|boolean',
|
||||
'server.settings.server_timezone' => 'required|string|timezone',
|
||||
'server.settings.force_docker_cleanup' => 'required|boolean',
|
||||
'server.settings.docker_cleanup_frequency' => 'required_if:server.settings.force_docker_cleanup,true|string',
|
||||
'server.settings.docker_cleanup_threshold' => 'required_if:server.settings.force_docker_cleanup,false|integer|min:1|max:100',
|
||||
'server.settings.delete_unused_volumes' => 'boolean',
|
||||
'server.settings.delete_unused_networks' => 'boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
|
|
@ -74,11 +84,15 @@ public function getListeners()
|
|||
'server.settings.concurrent_builds' => 'Concurrent Builds',
|
||||
'server.settings.dynamic_timeout' => 'Dynamic Timeout',
|
||||
'server.settings.is_metrics_enabled' => 'Metrics',
|
||||
'server.settings.metrics_token' => 'Metrics Token',
|
||||
'server.settings.metrics_refresh_rate_seconds' => 'Metrics Interval',
|
||||
'server.settings.metrics_history_days' => 'Metrics History',
|
||||
'server.settings.is_server_api_enabled' => 'Server API',
|
||||
'server.settings.sentinel_token' => 'Metrics Token',
|
||||
'server.settings.sentinel_metrics_refresh_rate_seconds' => 'Metrics Interval',
|
||||
'server.settings.sentinel_metrics_history_days' => 'Metrics History',
|
||||
'server.settings.sentinel_push_interval_seconds' => 'Push Interval',
|
||||
'server.settings.is_sentinel_enabled' => 'Server API',
|
||||
'server.settings.sentinel_custom_url' => 'Sentinel URL',
|
||||
'server.settings.server_timezone' => 'Server Timezone',
|
||||
'server.settings.delete_unused_volumes' => 'Delete Unused Volumes',
|
||||
'server.settings.delete_unused_networks' => 'Delete Unused Networks',
|
||||
];
|
||||
|
||||
public function mount(Server $server)
|
||||
|
|
@ -88,6 +102,24 @@ public function mount(Server $server)
|
|||
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
||||
$this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold;
|
||||
$this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency;
|
||||
$this->server->settings->delete_unused_volumes = $server->settings->delete_unused_volumes;
|
||||
$this->server->settings->delete_unused_networks = $server->settings->delete_unused_networks;
|
||||
}
|
||||
|
||||
public function checkSyncStatus(){
|
||||
$this->server->refresh();
|
||||
$this->server->settings->refresh();
|
||||
}
|
||||
|
||||
public function regenerateSentinelToken()
|
||||
{
|
||||
try {
|
||||
$this->server->generateSentinelToken();
|
||||
$this->server->settings->refresh();
|
||||
$this->dispatch('success', 'Sentinel token regenerated. Please restart your Sentinel.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function updated($field)
|
||||
|
|
@ -120,40 +152,47 @@ public function updatedServerSettingsIsBuildServer()
|
|||
$this->dispatch('proxyStatusUpdated');
|
||||
}
|
||||
|
||||
public function checkPortForServerApi()
|
||||
{
|
||||
try {
|
||||
if ($this->server->settings->is_server_api_enabled === true) {
|
||||
$this->server->checkServerApi();
|
||||
$this->dispatch('success', 'Server API is reachable.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
public function updatedServerSettingsIsSentinelEnabled($value){
|
||||
if($value === false){
|
||||
StopSentinel::dispatch($this->server);
|
||||
$this->server->settings->is_metrics_enabled = false;
|
||||
$this->server->settings->save();
|
||||
$this->server->sentinelUpdateAt(isReset: true);
|
||||
} else {
|
||||
StartSentinel::run($this->server);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedServerSettingsIsMetricsEnabled(){
|
||||
$this->restartSentinel();
|
||||
}
|
||||
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer(false);
|
||||
|
||||
$this->server->settings->save();
|
||||
$this->server->save();
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
$this->dispatch('refreshServerShow');
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
PullSentinelImageJob::dispatchSync($this->server);
|
||||
ray('Sentinel is enabled');
|
||||
if ($this->server->settings->isDirty('is_metrics_enabled')) {
|
||||
$this->dispatch('reloadWindow');
|
||||
}
|
||||
if ($this->server->settings->isDirty('is_server_api_enabled') && $this->server->settings->is_server_api_enabled === true) {
|
||||
ray('Starting sentinel');
|
||||
}
|
||||
} else {
|
||||
ray('Sentinel is not enabled');
|
||||
StopSentinel::dispatch($this->server);
|
||||
}
|
||||
|
||||
// if ($this->server->isSentinelEnabled()) {
|
||||
// PullSentinelImageJob::dispatchSync($this->server);
|
||||
// ray('Sentinel is enabled');
|
||||
// if ($this->server->settings->isDirty('is_metrics_enabled')) {
|
||||
// $this->dispatch('reloadWindow');
|
||||
// }
|
||||
// if ($this->server->settings->isDirty('is_sentinel_enabled') && $this->server->settings->is_sentinel_enabled === true) {
|
||||
// ray('Starting sentinel');
|
||||
// }
|
||||
// } else {
|
||||
// ray('Sentinel is not enabled');
|
||||
// StopSentinel::dispatch($this->server);
|
||||
// }
|
||||
$this->server->settings->save();
|
||||
// $this->checkPortForServerApi();
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
|
@ -166,7 +205,7 @@ public function restartSentinel()
|
|||
try {
|
||||
$version = get_latest_sentinel_version();
|
||||
StartSentinel::run($this->server, $version, true);
|
||||
$this->dispatch('success', 'Sentinel restarted.');
|
||||
$this->dispatch('success', 'Sentinel started.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
|
@ -232,22 +271,24 @@ public function submit()
|
|||
$newTimezone = $this->server->settings->server_timezone;
|
||||
if ($currentTimezone !== $newTimezone || $currentTimezone === '') {
|
||||
$this->server->settings->server_timezone = $newTimezone;
|
||||
$this->server->settings->save();
|
||||
}
|
||||
|
||||
$this->server->settings->save();
|
||||
$this->server->save();
|
||||
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedServerSettingsServerTimezone($value)
|
||||
public function manualCleanup()
|
||||
{
|
||||
$this->server->settings->server_timezone = $value;
|
||||
$this->server->settings->save();
|
||||
$this->dispatch('success', 'Server timezone updated.');
|
||||
try {
|
||||
DockerCleanupJob::dispatch($this->server, true);
|
||||
$this->dispatch('success', 'Manual cleanup job started. Depending on the amount of data, this might take a while.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function manualCloudflareConfig()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
|
|
@ -44,11 +44,18 @@ public function checkProxy(bool $notification = false)
|
|||
}
|
||||
$this->numberOfPolls++;
|
||||
}
|
||||
CheckProxy::run($this->server, true);
|
||||
$shouldStart = CheckProxy::run($this->server, true);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
}
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
if ($this->server->proxy->status === 'running') {
|
||||
$this->polling = false;
|
||||
$notification && $this->dispatch('success', 'Proxy is running.');
|
||||
} elseif ($this->server->proxy->status === 'exited' and ! $this->server->proxy->force_stop) {
|
||||
$notification && $this->dispatch('error', 'Proxy has exited.');
|
||||
} elseif ($this->server->proxy->force_stop) {
|
||||
$notification && $this->dispatch('error', 'Proxy is stopped manually.');
|
||||
} else {
|
||||
$notification && $this->dispatch('error', 'Proxy is not running.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class Index extends Component
|
|||
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
||||
|
||||
protected Server $server;
|
||||
public $timezones;
|
||||
|
||||
protected $rules = [
|
||||
'settings.fqdn' => 'nullable',
|
||||
|
|
@ -53,14 +54,14 @@ class Index extends Component
|
|||
'settings.is_auto_update_enabled' => 'Auto Update Enabled',
|
||||
'auto_update_frequency' => 'Auto Update Frequency',
|
||||
'update_check_frequency' => 'Update Check Frequency',
|
||||
'settings.instance_timezone' => 'Instance Timezone',
|
||||
];
|
||||
|
||||
public $timezones;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->do_not_track = $this->settings->do_not_track;
|
||||
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
||||
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
||||
|
|
@ -162,7 +163,7 @@ public function checkManually()
|
|||
{
|
||||
CheckForUpdatesJob::dispatchSync();
|
||||
$this->dispatch('updateAvailable');
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->new_version_available) {
|
||||
$this->dispatch('success', 'New version available!');
|
||||
} else {
|
||||
|
|
@ -170,12 +171,6 @@ public function checkManually()
|
|||
}
|
||||
}
|
||||
|
||||
public function updatedSettingsInstanceTimezone($value)
|
||||
{
|
||||
$this->settings->instance_timezone = $value;
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Instance timezone updated.');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public function mount()
|
|||
abort(404);
|
||||
}
|
||||
$this->instance_id = config('app.id');
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class SettingsBackup extends Component
|
|||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
||||
if ($this->database) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class SettingsEmail extends Component
|
|||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->emails = auth()->user()->email;
|
||||
} else {
|
||||
return redirect()->route('dashboard');
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ public function mount()
|
|||
return redirect()->route('source.all');
|
||||
}
|
||||
$this->applications = $this->github_app->applications;
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
|
||||
$this->name = str($this->github_app->name)->kebab();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class Create extends Component
|
|||
|
||||
public function mount()
|
||||
{
|
||||
$this->name = generate_random_name();
|
||||
$this->name = substr(generate_random_name(), 0, 34); // GitHub Apps names can only be 34 characters long
|
||||
}
|
||||
|
||||
public function createGitHubApp()
|
||||
|
|
|
|||
|
|
@ -43,15 +43,17 @@ class Create extends Component
|
|||
'endpoint' => 'Endpoint',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
public function updatedEndpoint($value)
|
||||
{
|
||||
if (isDev()) {
|
||||
$this->name = 'Local MinIO';
|
||||
$this->description = 'Local MinIO';
|
||||
$this->key = 'minioadmin';
|
||||
$this->secret = 'minioadmin';
|
||||
$this->bucket = 'local';
|
||||
$this->endpoint = 'http://coolify-minio:9000';
|
||||
if (! str($value)->startsWith('https://') && ! str($value)->startsWith('http://')) {
|
||||
$this->endpoint = 'https://'.$value;
|
||||
$value = $this->endpoint;
|
||||
}
|
||||
|
||||
if (str($value)->contains('your-objectstorage.com') && ! isset($this->bucket)) {
|
||||
$this->bucket = str($value)->after('//')->before('.');
|
||||
} elseif (str($value)->contains('your-objectstorage.com')) {
|
||||
$this->bucket = $this->bucket ?: str($value)->after('//')->before('.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public function mount()
|
|||
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||
return redirect()->route('subscription.show');
|
||||
}
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue