diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 0505d67e3..000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node/.devcontainer/base.Dockerfile - -# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster -ARG VARIANT="16-bullseye" -FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} - -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends - -# [Optional] Uncomment if you want to install an additional version of node using nvm -# ARG EXTRA_NODE_VERSION=10 -# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" - -# [Optional] Uncomment if you want to install more global node modules -RUN su node -c "npm install -g pnpm" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index eda081b6c..000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,36 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node -{ - "name": "Node.js", - "build": { - "dockerfile": "Dockerfile", - // Update 'VARIANT' to pick a Node version: 18, 16, 14. - // Append -bullseye or -buster to pin to an OS version. - // Use -bullseye variants on local arm64/Apple Silicon. - "args": { - "VARIANT": "18-bullseye" - } - }, - // Set *default* container specific settings.json values on container create. - "settings": {}, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "ms-azuretools.vscode-docker", - "dbaeumer.vscode-eslint", - "svelte.svelte-vscode", - "ardenivanov.svelte-intellisense", - "Prisma.prisma", - "bradlc.vscode-tailwindcss", - "waderyan.gitblame" - ], - // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [3000, 3001], - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "cp apps/api/.env.example apps/api/.env && pnpm install && pnpm db:push && pnpm db:seed", - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "node", - "features": { - "docker-in-docker": "20.10", - "github-cli": "latest" - } -} diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 1b0bcf1c2..000000000 --- a/.dockerignore +++ /dev/null @@ -1,16 +0,0 @@ -.DS_Store -node_modules -.pnpm-store -build -.svelte-kit -package -.env -.env.* -!.env.example -dist -apps/api/db/*.db -local-serve -apps/api/db/migration.db-journal -apps/api/core* -logs -others/certificates diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..8f0de65c5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..ab8f68072 --- /dev/null +++ b/.env.example @@ -0,0 +1,39 @@ +############################################################################################################ +# Development Environment + +# Userid for the user that will run the application inside the container +USERID= +############################################################################################################ + +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=pgsql +DB_HOST=postgres +DB_PORT=5432 +DB_DATABASE=coolify +DB_USERNAME=sail +DB_PASSWORD=password + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..fcb21d396 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index f96d5b45c..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -open_collective: coollabsio -github: coollabsio \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/--bug-report.yaml b/.github/ISSUE_TEMPLATE/--bug-report.yaml deleted file mode 100644 index 65f2ed40d..000000000 --- a/.github/ISSUE_TEMPLATE/--bug-report.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: 🐞 Bug report -description: Create a bug report to help us improve coolify -title: "[Bug]: " -labels: [Bug] -assignees: -- andrasbacsai -- vasani-arpit -body: -- type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! Please fill the form in English. -- type: checkboxes - attributes: - label: Is there an existing issue for this? - options: - - label: I have searched the existing issues - required: true -- type: input - id: repository - attributes: - label: Example public repository - description: "An example public git repository to reproduce the issue easily (if applicable)." - placeholder: "ex: https://github.com/coollabsio/coolify" - validations: - required: false -- type: textarea - attributes: - label: Description - description: A concise description of what you're experiencing and what you expect. - placeholder: | - When I do , happens and I see the error message attached below: - ```...``` - What I expect is - validations: - required: true -- type: textarea - attributes: - label: Steps To Reproduce - description: Add steps to reproduce this behaviour, include console / network logs & videos - placeholder: | - 1. Go to '...' - 2. Click on '....' - 3. Scroll down to '....' - 4. See error - validations: - required: true -- type: input - id: version - attributes: - label: Version - description: "The version of your coolify Instance" - placeholder: "2.5.2" - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/--feature-request.yaml b/.github/ISSUE_TEMPLATE/--feature-request.yaml deleted file mode 100644 index 9e217ff23..000000000 --- a/.github/ISSUE_TEMPLATE/--feature-request.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: 🛠️ Feature request -description: Suggest an idea to improve coolify -title: '[Feature]: ' -labels: [Enhancement] -assignees: - - andrasbacsai - - vasani-arpit -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to request a feature for coolify! Please also add your request here to get feedback from the community: https://feedback.coolify.io/! - - type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search to see if an issue related to this feature request already exists. - options: - - label: I have searched the existing issues - required: true - - type: textarea - attributes: - label: Summary - description: One paragraph description of the feature. - validations: - required: true - - type: textarea - attributes: - label: Why should this be worked on? - description: A concise description of the problems or use cases for this feature request. - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/--task.yaml b/.github/ISSUE_TEMPLATE/--task.yaml deleted file mode 100644 index a1ff080c5..000000000 --- a/.github/ISSUE_TEMPLATE/--task.yaml +++ /dev/null @@ -1,20 +0,0 @@ -name: 📝 Task -description: Create a task for the team to work on -title: "[Task]: " -labels: [Task] -body: -- type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search to see if an issue related to this already exists. - options: - - label: I have searched the existing issues - required: true -- type: textarea - attributes: - label: SubTasks - placeholder: | - - Sub Task 1 - - Sub Task 2 - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 4642a8549..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: true -contact_links: - - name: 🤔 Questions and Help - url: https://discord.com/invite/6rDM4fkymF - about: Reach out to us on discord or our github discussions page. - - name: 🙋‍♂️ Service request - url: https://feedback.coolify.io/ - about: Want to request a new service or build pack? For example Wordpress, Hasura, Appwrite, Angular etc... diff --git a/.github/workflows/production-release-dockerhub.yml b/.github/workflows/production-release-dockerhub.yml deleted file mode 100644 index 0ffe330eb..000000000 --- a/.github/workflows/production-release-dockerhub.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: Production Release to DockerHub - -on: - release: - types: [released] - -jobs: - arm64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/arm64 - push: true - tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 - cache-from: type=registry,ref=coollabsio/coolify:buildcache-arm64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-arm64,mode=max - amd64: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - push: true - tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}} - cache-from: type=registry,ref=coollabsio/coolify:buildcache-amd64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-amd64,mode=max - aarch64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/aarch64 - push: true - tags: coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 - cache-from: type=registry,ref=coollabsio/coolify:buildcache-aarch64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-aarch64,mode=max - merge-manifest: - runs-on: ubuntu-latest - needs: [amd64, arm64, aarch64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Create & publish manifest - run: | - docker buildx imagetools create --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-arm64 --append coollabsio/coolify:${{steps.package-version.outputs.current-version}}-aarch64 --tag coollabsio/coolify:${{steps.package-version.outputs.current-version}} - docker buildx imagetools create coollabsio/coolify:${{steps.package-version.outputs.current-version}} --tag coollabsio/coolify:latest - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }} diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml deleted file mode 100644 index 1adc0cb47..000000000 --- a/.github/workflows/production-release.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Production Release to ghcr.io - -on: - release: - types: [released] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: "coollabsio/coolify" - -jobs: - amd64: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=semver,pattern={{version}} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - aarch64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=semver,pattern={{version}}-aarch64 - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/aarch64 - push: true - tags: ${{ steps.meta.outputs.tags }}-aarch64 - labels: ${{ steps.meta.outputs.labels }} - merge-manifest: - runs-on: ubuntu-latest - needs: [amd64, aarch64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=semver,pattern={{version}} - - name: Create & publish manifest - run: | - docker buildx imagetools create --append ${{ fromJSON(steps.meta.outputs.json).tags[0] }}-aarch64 --tag ${{ fromJSON(steps.meta.outputs.json).tags[0] }} - docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }} diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml deleted file mode 100644 index e69dd009c..000000000 --- a/.github/workflows/release-candidate.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Release Candidate to ghcr.io - -on: - release: - types: [prereleased] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: "coollabsio/coolify" - -jobs: - amd64: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - aarch64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/aarch64 - push: true - tags: ${{ steps.meta.outputs.tags }}-aarch64 - labels: ${{ steps.meta.outputs.labels }} - merge-manifest: - runs-on: ubuntu-latest - needs: [amd64, aarch64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - name: Create & publish manifest - run: | - docker buildx imagetools create --append ${{ steps.meta.outputs.tags }}-aarch64 --tag ${{ steps.meta.outputs.tags }} - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} diff --git a/.github/workflows/staging-release-dockerhub.yml b/.github/workflows/staging-release-dockerhub.yml deleted file mode 100644 index 36dd332cf..000000000 --- a/.github/workflows/staging-release-dockerhub.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Staging Release to DockerHub - -on: - push: - branches: - - "next" - -jobs: - arm64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: "next" - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/arm64 - push: true - tags: coollabsio/coolify:next-arm64 - cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-arm64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-arm64,mode=max - amd64: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: "next" - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Get current package version - uses: martinbeentjes/npm-get-version-action@v1.2.3 - id: package-version - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - push: true - tags: coollabsio/coolify:next - cache-from: type=registry,ref=coollabsio/coolify:buildcache-next-amd64 - cache-to: type=registry,ref=coollabsio/coolify:buildcache-next-amd64,mode=max - merge-manifest: - runs-on: ubuntu-latest - needs: [arm64, amd64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Create & publish manifest - run: | - docker buildx imagetools create --append coollabsio/coolify:next-arm64 --tag coollabsio/coolify:next - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} diff --git a/.github/workflows/staging-release.yml b/.github/workflows/staging-release.yml deleted file mode 100644 index 15d3e5393..000000000 --- a/.github/workflows/staging-release.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Staging Release to ghcr.io - -on: - push: - branches-ignore: - - "main" -env: - REGISTRY: ghcr.io - IMAGE_NAME: "coollabsio/coolify" - -jobs: - amd64: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: "next" - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - aarch64: - runs-on: [self-hosted, arm64] - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: "next" - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/aarch64 - push: true - tags: ${{ steps.meta.outputs.tags }}-aarch64 - labels: ${{ steps.meta.outputs.labels }} - merge-manifest: - runs-on: ubuntu-latest - needs: [amd64, aarch64] - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to ghcr.io - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Create & publish manifest - run: | - docker buildx imagetools create --append ${{ steps.meta.outputs.tags }}-aarch64 --tag ${{ steps.meta.outputs.tags }} - - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} diff --git a/.gitignore b/.gitignore index 6ace702bf..92d1e443d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,21 @@ -.DS_Store -node_modules -.pnpm-store -/apps/ui/build -/build -.svelte-kit -package +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/vendor .env -.env.* -!.env.example -dist -apps/api/db/*.db -apps/api/db/migration.db-journal -apps/api/core* -apps/server/build -apps/backup/backups/* -!apps/backup/backups/.gitkeep -/logs -others/certificates -backups/* -!backups/.gitkeep - -# Trpc -apps/server/db/*.db -apps/server/db/*.db-journal \ No newline at end of file +.env.backup +.env.production +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode +/.npm +/.bash_history diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index e365fb489..000000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM gitpod/workspace-full:2022-08-17-18-37-55 -RUN brew install buildpacks/tap/pack \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 940733ac9..000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,14 +0,0 @@ -# This configuration file was automatically generated by Gitpod. -# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) -# and commit this file to your remote git repository to share the goodness with others. -#image: -# file: .gitpod.Dockerfile -#tasks: -# - init: pnpm install && pnpm db:push && pnpm db:seed -# command: pnpm dev - -ports: - - port: 3001 - visibility: public - - port: 3000 - visibility: public diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 65a483d79..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "i18n-ally.localesPaths": [ - "src/lib/locales" - ], - "i18n-ally.keystyle": "nested", - "i18n-ally.extract.ignoredByFiles": { - "src\\routes\\__layout.svelte": [ - "Coolify", - "coolLabs logo" - ] - }, - "i18n-ally.sourceLanguage": "en", - "i18n-ally.enabledFrameworks": [ - "svelte" - ], - "i18n-ally.enabledParsers": [ - "js", - "ts", - "json" - ], - "i18n-ally.extract.autoDetect": true -} \ No newline at end of file diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md deleted file mode 100644 index c57d817fe..000000000 --- a/CONTRIBUTION.md +++ /dev/null @@ -1,48 +0,0 @@ -# Contributing - -> "First, thanks for considering to contribute to my project. - It really means a lot! 😁" - [@andrasbacsai](https://github.com/andrasbacsai) - -You can ask for guidance anytime on our -[Discord server](https://coollabs.io/discord) in the `#contribution` channel. - -You'll need a set of skills to [get started](docs/contribution/GettingStarted.md). - -## 1) Setup your development environment - -- 🌟 [Container based](docs/dev_setup/Container.md) ← *Recommended* -- 📦 [DockerContainer](docs/dev_setup/DockerContiner.md) *WIP -- 🐙 [Github Codespaces](docs/dev_setup/GithubCodespaces.md) -- ☁️ [GitPod](docs/dev_setup/GitPod.md) -- 🍏 [Local Mac](docs/dev_setup/Mac.md) - -## 2) Basic requirements - -- [Install Pnpm](https://pnpm.io/installation) -- [Install Docker Engine](https://docs.docker.com/engine/install/) -- [Setup Docker Compose Plugin](https://docs.docker.com/compose/install/) -- [Setup GIT LFS Support](https://git-lfs.github.com/) - -## 3) Setup Coolify - -- Copy `apps/api/.env.example` to `apps/api/.env` -- Edit `apps/api/.env`, set the `COOLIFY_APP_ID` environment variable to something cool. -- Run `pnpm install` to install dependencies. -- Run `pnpm db:push` to create a local SQlite database. This will apply all migrations at `db/dev.db`. -- Run `pnpm db:seed` seed the database. -- Run `pnpm dev` start coding. - -```sh -# Or... Copy and paste commands below: -cp apps/api/.env.example apps/api/.env -pnpm install -pnpm db:push -pnpm db:seed -pnpm dev -``` - -## 4) Start Coding - -You should be able to access `http://localhost:3000`. - -1. Click `Register` and setup your first user. diff --git a/Dockerfile b/Dockerfile index 80f56f06a..fd4edd2bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,53 +1,26 @@ -ARG PNPM_VERSION=7.11.0 +FROM serversideup/php:8.2-fpm-nginx -FROM node:18-slim as build -WORKDIR /app +ARG NODE_VERSION=18 +ARG POSTGRES_VERSION=15 -RUN apt update && apt -y install curl -RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION} +RUN apt-get update \ + && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g npm -COPY . . -RUN pnpm install -RUN pnpm build +RUN apt-get install -y php-pgsql -# Production build -FROM node:18-slim -WORKDIR /app -ENV NODE_ENV production -ARG TARGETPLATFORM +RUN apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* -# https://download.docker.com/linux/static/stable/ -ARG DOCKER_VERSION=20.10.18 -# https://github.com/docker/compose/releases -# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug. -ARG DOCKER_COMPOSE_VERSION=2.6.1 -# https://github.com/buildpacks/pack/releases -ARG PACK_VERSION=0.27.0 - -RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3 -RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/ -RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION} -RUN npm install -g npm@${PNPM_VERSION} - -RUN mkdir -p ~/.docker/cli-plugins/ - -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack - -RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack - -COPY --from=build /app/apps/api/build/ . -# COPY --from=build /app/others/fluentbit/ ./fluentbit -COPY --from=build /app/apps/ui/build/ ./public -COPY --from=build /app/apps/api/prisma/ ./prisma -COPY --from=build /app/apps/api/package.json . -COPY --from=build /app/docker-compose.yaml . -COPY --from=build /app/apps/api/tags.json . -COPY --from=build /app/apps/api/templates.json . - -RUN pnpm install -p - -EXPOSE 3000 -ENV CHECKPOINT_DISABLE=1 -CMD pnpm start +# Adding test ssh key +RUN mkdir -p ~/.ssh +RUN echo "-----BEGIN OPENSSH PRIVATE KEY-----\n\ + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n\ + QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk\n\ + hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA\n\ + AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV\n\ + uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==\n\ + -----END OPENSSH PRIVATE KEY-----" >> ~/.ssh/id_ed25519 +RUN chmod 0600 ~/.ssh/id_ed25519 \ No newline at end of file diff --git a/Dockerfile-dev b/Dockerfile-dev deleted file mode 100644 index 13b3a8560..000000000 --- a/Dockerfile-dev +++ /dev/null @@ -1,31 +0,0 @@ -FROM node:18-slim -ENV NODE_ENV development -ARG TARGETPLATFORM -ARG PNPM_VERSION=7.11.0 -ARG NPM_VERSION=8.19.1 -# https://download.docker.com/linux/static/stable/ -ARG DOCKER_VERSION=20.10.18 -# https://github.com/docker/compose/releases -# Reverted to 2.6.1 because of this https://github.com/docker/compose/issues/9704. 2.9.0 still has a bug. -ARG DOCKER_COMPOSE_VERSION=2.6.1 -# https://github.com/buildpacks/pack/releases -ARG PACK_VERSION=0.27.0 -WORKDIR /app - -RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION} - -RUN apt update && apt -y install --no-install-recommends ca-certificates git git-lfs openssh-client curl jq cmake sqlite3 openssl psmisc python3 -RUN apt-get clean autoclean && apt-get autoremove --yes && rm -rf /var/lib/{apt,dpkg,cache,log}/ -RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION} -RUN npm install -g npm@${PNPM_VERSION} - -RUN mkdir -p ~/.docker/cli-plugins/ - -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-compose-linux-$DOCKER_COMPOSE_VERSION -o ~/.docker/cli-plugins/docker-compose -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/pack-$PACK_VERSION -o /usr/local/bin/pack - -RUN chmod +x ~/.docker/cli-plugins/docker-compose /usr/bin/docker /usr/local/bin/pack - -EXPOSE 3000 -ENV CHECKPOINT_DISABLE=1 diff --git a/README.md b/README.md index 3628d68fd..a8ca09a6c 100644 --- a/README.md +++ b/README.md @@ -2,130 +2,22 @@ # Coolify An open-source & self-hostable Heroku / Netlify alternative. -## Live Demo +# v4 -https://demo.coolify.io/ +You are checking the next-gen of Coolify here, aka v4. Hi 👋 -(If it is unresponsive, that means someone overloaded the server. 😄) - -## Feedback - -If you have a new service / build pack you would like to add, raise an idea [here](https://feedback.coolify.io/) to get feedback from the community! - ---- - -## How to install - -For more details goto the [docs](https://docs.coollabs.io/coolify/installation). - -Installation is automated with the following command: - -```bash -wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh -``` - -If you would like no questions during installation: - -```bash -wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh -f -``` - ---- - -## Features - -### Git Sources - -Self-hosted versions also! - - - - -### Destinations - -Deploy your resource to: - -- Local Docker Engine -- Remote Docker Engine - -### Applications - - - - - - - - - - - - - - - - - - -### Databases - - - - - - - - -### Services - -- [Appwrite](https://appwrite.io) -- [WordPress](https://docs.coollabs.io/coolify/services/wordpress) -- [Ghost](https://ghost.org) -- [Plausible Analytics](https://docs.coollabs.io/coolify/services/plausible-analytics) -- [NocoDB](https://nocodb.com) -- [VSCode Server](https://github.com/cdr/code-server) -- [MinIO](https://min.io) -- [VaultWarden](https://github.com/dani-garcia/vaultwarden) -- [LanguageTool](https://languagetool.org) -- [n8n](https://n8n.io) -- [Uptime Kuma](https://github.com/louislam/uptime-kuma) -- [MeiliSearch](https://github.com/meilisearch/meilisearch) -- [Umami](https://github.com/mikecao/umami) -- [Fider](https://fider.io) -- [Hasura](https://hasura.io) -- [GlitchTip](https://glitchtip.com) -- And more... +Thinks will be added here incrementally through PR's. ## Support - Mastodon: [@andrasbacsai@fosstodon.org](https://fosstodon.org/@andrasbacsai) - Telegram: [@andrasbacsai](https://t.me/andrasbacsai) -- Twitter: [@andrasbacsai](https://twitter.com/andrasbacsai) +- Twitter: [@andrasbacsai](https://twitter.com/heyandras) - Email: [andras@coollabs.io](mailto:andras@coollabs.io) - Discord: [Invitation](https://coollabs.io/discord) --- -## ⚗️ Expertise Contributions - -Coolify is developed under the [Apache License](./LICENSE) and you can help to make it grow. -Our community will be glad to have you on board! - -Learn how to contribute to Coolify as as ... - -→ [👩🏾‍💻 Software developer](./CONTRIBUTION.md) - -→ [🧑🏻‍🏫 Translator](./docs/contribution/Translating.md) - - - ---- - ## 💰 Financial Contributors Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)] diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 000000000..e6b9960ec --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,27 @@ +command('inspire')->hourly(); + } + + /** + * Register the commands for the application. + */ + protected function commands(): void + { + $this->load(__DIR__.'/Commands'); + + require base_path('routes/console.php'); + } +} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 000000000..b1c262c6d --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,48 @@ +, \Psr\Log\LogLevel::*> + */ + protected $levels = [ + // + ]; + + /** + * A list of the exception types that are not reported. + * + * @var array> + */ + protected $dontReport = [ + // + ]; + + /** + * A list of the inputs that are never flashed to the session on validation exceptions. + * + * @var array + */ + protected $dontFlash = [ + 'current_password', + 'password', + 'password_confirmation', + ]; + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->reportable(function (Throwable $e) { + // + }); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 000000000..77ec359ab --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,12 @@ + + */ + protected $middleware = [ + // \App\Http\Middleware\TrustHosts::class, + \App\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array> + */ + protected $middlewareGroups = [ + 'web' => [ + \App\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \App\Http\Middleware\VerifyCsrfToken::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + + 'api' => [ + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + ]; + + /** + * The application's middleware aliases. + * + * Aliases may be used to conveniently assign middleware to routes and groups. + * + * @var array + */ + protected $middlewareAliases = [ + 'auth' => \App\Http\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, + 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, + 'signed' => \App\Http\Middleware\ValidateSignature::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + ]; +} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php new file mode 100644 index 000000000..d4ef6447a --- /dev/null +++ b/app/Http/Middleware/Authenticate.php @@ -0,0 +1,17 @@ +expectsJson() ? null : route('login'); + } +} diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 000000000..867695bdc --- /dev/null +++ b/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php new file mode 100644 index 000000000..74cbd9a9e --- /dev/null +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 000000000..afc78c4e5 --- /dev/null +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,30 @@ +check()) { + return redirect(RouteServiceProvider::HOME); + } + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php new file mode 100644 index 000000000..88cadcaaf --- /dev/null +++ b/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,19 @@ + + */ + protected $except = [ + 'current_password', + 'password', + 'password_confirmation', + ]; +} diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php new file mode 100644 index 000000000..c9c58bddc --- /dev/null +++ b/app/Http/Middleware/TrustHosts.php @@ -0,0 +1,20 @@ + + */ + public function hosts(): array + { + return [ + $this->allSubdomainsOfApplicationUrl(), + ]; + } +} diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php new file mode 100644 index 000000000..3391630ec --- /dev/null +++ b/app/Http/Middleware/TrustProxies.php @@ -0,0 +1,28 @@ +|string|null + */ + protected $proxies; + + /** + * The headers that should be used to detect proxies. + * + * @var int + */ + protected $headers = + Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; +} diff --git a/app/Http/Middleware/ValidateSignature.php b/app/Http/Middleware/ValidateSignature.php new file mode 100644 index 000000000..093bf64af --- /dev/null +++ b/app/Http/Middleware/ValidateSignature.php @@ -0,0 +1,22 @@ + + */ + protected $except = [ + // 'fbclid', + // 'utm_campaign', + // 'utm_content', + // 'utm_medium', + // 'utm_source', + // 'utm_term', + ]; +} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php new file mode 100644 index 000000000..9e8652172 --- /dev/null +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -0,0 +1,17 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 000000000..23b406346 --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,44 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'email_verified_at' => 'datetime', + ]; +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 000000000..452e6b65b --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ + + */ + protected $policies = [ + // 'App\Models\Model' => 'App\Policies\ModelPolicy', + ]; + + /** + * Register any authentication / authorization services. + */ + public function boot(): void + { + // + } +} diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 000000000..2be04f5d9 --- /dev/null +++ b/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,19 @@ +> + */ + protected $listen = [ + Registered::class => [ + SendEmailVerificationNotification::class, + ], + ]; + + /** + * Register any events for your application. + */ + public function boot(): void + { + // + } + + /** + * Determine if events and listeners should be automatically discovered. + */ + public function shouldDiscoverEvents(): bool + { + return false; + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php new file mode 100644 index 000000000..bc4910996 --- /dev/null +++ b/app/Providers/RouteServiceProvider.php @@ -0,0 +1,48 @@ +configureRateLimiting(); + + $this->routes(function () { + Route::middleware('api') + ->prefix('api') + ->group(base_path('routes/api.php')); + + Route::middleware('web') + ->group(base_path('routes/web.php')); + }); + } + + /** + * Configure the rate limiters for the application. + */ + protected function configureRateLimiting(): void + { + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); + }); + } +} diff --git a/apps/api/.env.example b/apps/api/.env.example deleted file mode 100644 index 598d659b3..000000000 --- a/apps/api/.env.example +++ /dev/null @@ -1,9 +0,0 @@ -COOLIFY_APP_ID=local-dev -# 32 bits long secret key -COOLIFY_SECRET_KEY=12341234123412341234123412341234 -COOLIFY_DATABASE_URL=file:../db/dev.db - -COOLIFY_IS_ON=docker -COOLIFY_WHITE_LABELED=false -COOLIFY_WHITE_LABELED_ICON= -COOLIFY_AUTO_UPDATE= diff --git a/apps/api/.eslintignore b/apps/api/.eslintignore deleted file mode 100644 index 70dc23b71..000000000 --- a/apps/api/.eslintignore +++ /dev/null @@ -1,9 +0,0 @@ -seed.js -.DS_Store -node_modules -build -.env -.env.* -!.env.example -dist -dev.db \ No newline at end of file diff --git a/apps/api/.eslintrc b/apps/api/.eslintrc deleted file mode 100644 index 92583263b..000000000 --- a/apps/api/.eslintrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "prettier"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "prettier" - ] -} diff --git a/apps/api/.gitignore b/apps/api/.gitignore deleted file mode 100644 index 8614cdee9..000000000 --- a/apps/api/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -node_modules -build -.svelte-kit -package -.env -.env.* -!.env.example -dist -dev.db -client -testTemplate.yaml -testTags.json \ No newline at end of file diff --git a/apps/api/.prettierrc b/apps/api/.prettierrc deleted file mode 100644 index ff2677efd..000000000 --- a/apps/api/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100 -} diff --git a/apps/api/db/.gitkeep b/apps/api/db/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/api/devTags.json b/apps/api/devTags.json deleted file mode 100644 index a788801e2..000000000 --- a/apps/api/devTags.json +++ /dev/null @@ -1,983 +0,0 @@ -[ - { - "name": "appsmith", - "image": "appsmith/appsmith-ce", - "tags": [ - "v1.9.3", - "v1.9.1", - "v1.8.15", - "v1.8.12", - "v1.8.10", - "v1.8.9", - "v1.8.7", - "v1.8.5", - "v1.8.3", - "v1.8.0", - "v1.7.13", - "v1.7.11", - "v1.7.8", - "v1.7.6", - "v1.7.4", - "v1.7.2", - "v1.7.1", - "v1.6.22", - "v1.6.20", - "v1.6.19", - "v1.6.17", - "v1.6.15", - "v1.6.13", - "v1.6.11", - "v1.6.9", - "v1.6.7", - "v1.6.5", - "v1.6.3", - "v1.6.1", - "v1.5.30" - ] - }, - { - "name": "appwrite", - "image": "appwrite/appwrite", - "tags": [ - "1.2.0", - "1.1.2", - "1.1.0", - "1.0.3", - "1.0.1", - "1.0.0", - "0.15.2", - "0.15.0", - "0.14.2", - "0.14.0", - "0.13.4", - "0.13.2", - "0.13.0", - "0.12.3", - "0.12.1", - "0.12.0", - "0.11.2", - "0.11.0", - "0.10.4", - "0.10.2", - "0.10.0", - "0.9.3", - "0.9.1", - "0.8.0", - "0.7.1", - "0.6.2", - "0.6.0", - "0.5.2", - "0.5.0", - "0.3.1" - ] - }, - { - "name": "directus-postgresql", - "image": "directus/directus", - "tags": [ - "9.22.3", - "9.22.0", - "9.21.0", - "9.20.4", - "9.20.2", - "9.20.0", - "9.19.2", - "9.18.0", - "9.17.4", - "9.17.2", - "9.17.0", - "9.16.0", - "9.15.0", - "9.14.5", - "9.14.3", - "9.14.0", - "9.13.0", - "9.12.2", - "9.12.0", - "9.11.0", - "9.10.0", - "9.9.0", - "9.8.0", - "9.7.0", - "9.6.0", - "9.5.2", - "9.5.0", - "9.4.2", - "9.4.0", - "9.3.0" - ] - }, - { - "name": "fider", - "image": "getfider/fider", - "tags": [ - "stable", - "master", - "main", - "dev", - "SHA_ee6e83cfaadadaa56ab76e089e01f5631af3506f", - "SHA_deb4f9b4f561d890d8a80e6872fea9a98a265cc6", - "SHA_d5cc307909d43447200483d76b5db74d8ed8349e", - "SHA_d1674476577a7fd3c88fc29f91c3f35f5bd6a260", - "SHA_d107cbb157abca6576110080736213efe0955cff", - "SHA_c9c55b2f5b33a76015241b97e03cfac1254b42a7", - "SHA_bcf451a3cb02d5c8a489fd30309249296057b084", - "SHA_bbfe419639514f949a042807addf0fde7d4de225", - "SHA_adc3afc4c7bcf96931a5f90cab65c282d860dbfd", - "SHA_ab5283ae95334f10b5041402dce79e333c472015", - "SHA_a3f4cb5ed0a4ee2d726705fc426636364aac17a1", - "SHA_a18224142bf51bc6463c3d22f45f62287902e9a6", - "SHA_8e5cff30d95963eaee2587488d351e0d658c8195", - "SHA_8cabe2817ce7ccaf2f0a9fdbb1b5d3411de87f81", - "SHA_7851f9da566132d87fa2a63004e78c3bc9c09c6c", - "SHA_6c0f2bed1754e9d579eb9575129a6e3dbc529c32", - "SHA_603508c8790d6a6fb1e852df1a58ead8e5b3ea6c", - "SHA_55efacf164a4749b50ee68ae8925e7dc9dfa3a0c", - "SHA_4bdd291ce61e5f5dfc063fa1b2d9be8c9ff1d4c4", - "SHA_3fba9cb6a9ceab0c78c6cff3220610f591f657cb", - "SHA_3d635b57606a9885babe91fe975b11429e0f2c38", - "SHA_3b794edbd9789a8aa38ecd3714bc536a675d3058", - "SHA_3570c454ad3252b690608f7bf8051737d8519f8a", - "SHA_263e2709fd145f3ea511e5557e170102899995b0", - "SHA_255c30ed012fc4c39ffc97efc1d3b00425b17c72", - "SHA_17f92b16ef790003338f0926fc8d791a9a61333c" - ] - }, - { - "name": "ghost-mariadb", - "image": "bitnami/ghost", - "tags": [ - "5.30.1", - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.4", - "5.26.3", - "5.26.2", - "5.26.1", - "5.26.0", - "5.25.5", - "5.25.4", - "5.25.3", - "5.25.2", - "5.25.1", - "5.25.0", - "5.24.2", - "5.24.1", - "5.24.0", - "5.23.0", - "5.22.11", - "5.22.10", - "5.22.9", - "5.22.8", - "5.22.7", - "5.22.6", - "5.22.5", - "5.22.4", - "5.22.3", - "5.22.2", - "4.48.8" - ] - }, - { - "name": "ghost-mysql", - "image": "library/ghost", - "tags": [ - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.4", - "5.26.3", - "5.26.2", - "5.26.1", - "5.25.5", - "5.25.3", - "5.25.2", - "5.25.1", - "5.25.0", - "5.24.2", - "5.24.1", - "5.23.0", - "5.22.11", - "5.22.10", - "5.22.9", - "5.22.8", - "5.22.4", - "5.22.1", - "5.20.0", - "5.19.3", - "5.19.0", - "5.18.0", - "5.17.2", - "5.17.1", - "5.17.0", - "5.16.2" - ] - }, - { - "name": "ghost-only", - "image": "library/ghost", - "tags": [ - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.4", - "5.26.3", - "5.26.2", - "5.26.1", - "5.25.5", - "5.25.3", - "5.25.2", - "5.25.1", - "5.25.0", - "5.24.2", - "5.24.1", - "5.23.0", - "5.22.11", - "5.22.10", - "5.22.9", - "5.22.8", - "5.22.4", - "5.22.1", - "5.20.0", - "5.19.3", - "5.19.0", - "5.18.0", - "5.17.2", - "5.17.1", - "5.17.0", - "5.16.2" - ] - }, - { - "name": "gitea", - "image": "gitea/gitea", - "tags": [ - "1.18.0", - "1.17.4", - "1.17.3", - "1.17.2", - "1.17.1", - "1.17.0", - "1.16.9", - "1.16.8", - "1.16.7", - "1.16.6", - "1.16.5", - "1.16.4", - "1.16.3", - "1.16.2", - "1.16.1", - "1.16.0", - "1.15.11", - "1.15.10", - "1.15.9", - "1.15.8", - "1.15.7", - "1.15.6", - "1.15.5", - "1.15.4", - "1.15.3", - "1.15.2", - "1.15.1", - "1.15.0", - "1.14.7", - "1.14.6" - ] - }, - { - "name": "glitchtip", - "image": "glitchtip/glitchtip", - "tags": [ - "v3.0.2", - "v3.0.0", - "v2.0.7", - "v2.0.5", - "v2.0.2", - "v2.0.0", - "v1.12.4", - "v1.12.2", - "v1.12.0", - "v1.10.3", - "v1.10.1", - "v1.9.2", - "v1.9.0", - "v1.8.4", - "v1.8.2", - "v1.8.0", - "v1.7.1", - "v1.6.4", - "v1.6.2", - "v1.6.0", - "v1.5.3", - "v1.5.1", - "v1.4.1", - "v1.3.3", - "v1.3.1", - "v1.2.6", - "v1.2.4", - "v1.2.2", - "v1.2.0", - "v1.1.2" - ] - }, - { - "name": "grafana", - "image": "grafana/grafana", - "tags": [ - "9.3.2", - "9.3.1", - "9.3.0", - "9.2.8", - "9.2.7", - "9.2.6", - "9.2.5", - "9.2.4", - "9.2.3", - "9.2.2", - "9.2.1", - "9.2.0", - "9.1.8", - "9.1.7", - "9.1.6", - "9.1.5", - "9.1.4", - "9.1.3", - "9.1.2", - "9.1.1", - "9.1.0", - "9.0.9", - "9.0.8", - "9.0.7", - "9.0.6", - "9.0.5", - "9.0.4", - "9.0.3", - "9.0.2", - "9.0.1" - ] - }, - { - "name": "hasura", - "image": "hasura/graphql-engine", - "tags": [ - "v2.16.1", - "v2.16.0", - "v2.15.2", - "v2.14.1", - "v2.13.2", - "v2.12.1", - "v2.11.3", - "v2.10.2", - "v2.9.0", - "v2.8.4", - "v2.8.3", - "v2.8.2", - "v2.8.1", - "v2.8.0", - "v2.7.0", - "v2.6.2", - "v2.6.1", - "v2.6.0", - "v2.5.2", - "v2.5.1", - "v2.5.0", - "v2.4.0", - "v2.3.1", - "v2.3.0", - "v2.2.2", - "v2.2.1", - "v2.2.0", - "v2.1.1", - "v2.1.0", - "v2.0.10" - ] - }, - { - "name": "keycloak", - "image": "quay.io/keycloak/keycloak", - "tags": [ - "9.0.3", - "9.0.0", - "8.0.1", - "7.0.0", - "6.0.1", - "6.0.0", - "20.0.3", - "20.0.2", - "20.0.1", - "20.0.0", - "19.0.3", - "19.0.1", - "19.0.0", - "18.0.1", - "18.0.0", - "17.0.1", - "17.0.0", - "16.1.0", - "15.1.1", - "15.0.2", - "15.0.0", - "13.0.1", - "12.0.4", - "12.0.2", - "12.0.0", - "11.0.2", - "11.0.0", - "10.0.1" - ] - }, - { - "name": "languagetool", - "image": "silviof/docker-languagetool", - "tags": ["latest", "6.0", "5.8", "5.7", "5.6", "5.5", "5.4", "5.3"] - }, - { - "name": "lavalink", - "image": "fredboat/lavalink", - "tags": ["3.7.0", "3.6.1", "3.5.1", "v2.0.1"] - }, - { - "name": "libretranslate", - "image": "libretranslate/libretranslate", - "tags": ["v1.3.8", "v1.3.6", "v1.3.4", "v1.3.2", "v1.3.0", "v1.2.8"] - }, - { - "name": "meilisearch", - "image": "getmeili/meilisearch", - "tags": [ - "v0.30.5", - "v0.30.3", - "v0.30.1", - "v0.30.0", - "v0.29.3", - "v0.29.1", - "v0.29.0", - "v0.28.1", - "v0.28.0", - "v0.27.1", - "v0.27.0", - "v0.26.1", - "v0.26.0", - "v0.25.1", - "v0.25.0", - "v0.23.1", - "v0.23.0", - "v0.21.1", - "v0.21.0", - "v0.20.0", - "v0.19.0", - "v0.18.1", - "v0.18.0", - "v0.17.0", - "v0.16.0", - "0.14.1", - "v0.14.1", - "v0.14.0", - "v0.12.0", - "v0.11.0" - ] - }, - { - "name": "minio", - "image": "minio/minio", - "tags": [ - "RELEASE.2023-01-12T02-06-16Z", - "RELEASE.2023-01-06T18-11-18Z", - "RELEASE.2023-01-02T09-40-09Z", - "RELEASE.2022-12-12T19-27-27Z", - "RELEASE.2022-12-07T00-56-37Z", - "RELEASE.2022-12-02T19-19-22Z", - "RELEASE.2022-11-29T23-40-49Z", - "RELEASE.2022-11-26T22-43-32Z", - "RELEASE.2022-11-17T23-20-09Z", - "RELEASE.2022-11-11T03-44-20Z", - "RELEASE.2022-11-10T18-20-21Z", - "RELEASE.2022-11-08T05-27-07Z", - "RELEASE.2022-10-29T06-21-33Z", - "RELEASE.2022-10-24T18-35-07Z.hotfix.7906ac5be", - "RELEASE.2022-10-24T18-35-07Z", - "RELEASE.2022-10-21T22-37-48Z", - "RELEASE.2022-10-20T00-55-09Z", - "RELEASE.2022-10-15T19-57-03Z", - "RELEASE.2022-10-08T20-11-00Z", - "RELEASE.2022-10-05T14-58-27Z", - "RELEASE.2022-10-02T19-29-29Z", - "RELEASE.2022-09-25T15-44-53Z", - "RELEASE.2022-09-22T18-57-27Z", - "RELEASE.2022-09-17T00-09-45Z.hotfix.f76e5da9f", - "RELEASE.2022-09-17T00-09-45Z.fips", - "RELEASE.2022-09-07T22-25-02Z.fips", - "RELEASE.2022-09-01T23-53-36Z.fips", - "RELEASE.2022-08-26T19-53-15Z.fips", - "RELEASE.2022-08-25T07-17-05Z.fips", - "RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb" - ] - }, - { - "name": "n8n", - "image": "n8nio/n8n", - "tags": [ - "0.210.1", - "0.210.0", - "0.209.4", - "0.209.3", - "0.209.2", - "0.209.1", - "0.209.0", - "0.208.1", - "0.208.0", - "0.207.1", - "0.207.0", - "0.206.1", - "0.205.0", - "0.204.0", - "0.203.1", - "0.202.1", - "0.202.0", - "0.201.0", - "0.200.1", - "0.199.0", - "0.198.1", - "0.198.0", - "0.197.1", - "0.197.0", - "0.196.0", - "0.195.5", - "0.195.4", - "0.195.3", - "0.195.2", - "0.195.1" - ] - }, - { - "name": "nocodb", - "image": "nocodb/nocodb", - "tags": [ - "0.101.2", - "0.101.0", - "0.100.1", - "0.99.2", - "0.99.0", - "0.98.3", - "0.98.1", - "0.97.0", - "0.96.3", - "0.96.1", - "0.92.4", - "0.92.0", - "0.91.8", - "0.91.6", - "0.91.1", - "0.90.11", - "0.90.8", - "0.90.5", - "0.90.3", - "0.90.1", - "0.84.16", - "0.84.14", - "0.84.10", - "0.84.9", - "0.84.7", - "0.84.3", - "0.83.8", - "0.83.5", - "0.83.2", - "0.83.0" - ] - }, - { - "name": "openblocks", - "image": "openblocksdev/openblocks-ce", - "tags": ["1.1.3", "1.1.1", "1.0.21"] - }, - { - "name": "plausibleanalytics-arm", - "image": "plausible/analytics", - "tags": [ - "v1.5.1", - "v1.5.0", - "v1.4.3", - "v1.4.1", - "v1.4.0", - "v1.3.0", - "v1.2.0", - "v1.1.0", - "v1.0.0" - ] - }, - { - "name": "plausibleanalytics", - "image": "plausible/analytics", - "tags": [ - "v1.5.1", - "v1.5.0", - "v1.4.3", - "v1.4.1", - "v1.4.0", - "v1.3.0", - "v1.2.0", - "v1.1.0", - "v1.0.0" - ] - }, - { "name": "pocketbase", "image": "coollabsio/pocketbase", "tags": ["0.11.0", "0.10.2", "0.8.0"] }, - { - "name": "searxng", - "image": "searxng/searxng", - "tags": [ - "2023.01.15-52d41559", - "2023.01.15-13b0c251", - "2023.01.14-b720a495", - "2023.01.14-449aebae", - "2023.01.14-18d895ff", - "2023.01.09-afd71a6c", - "2023.01.09-a90ed481", - "2023.01.08-54e63839", - "2023.01.08-4e735b28", - "2023.01.08-217395b8", - "2023.01.08-0c429d70", - "2023.01.07-cb7b0916", - "2023.01.07-a98c5156", - "2023.01.07-633ba8b1", - "2023.01.07-4e355564", - "2023.01.06-b241015e", - "2023.01.06-269a72ee", - "2023.01.05-aba969cc", - "2022.12.30-b6d98be7", - "2022.12.30-647a0aa9", - "2022.12.30-17516290", - "2022.12.29-d531f893", - "2022.12.29-9b31976c", - "2022.12.29-76cd808a", - "2022.12.29-3ec58b06", - "2022.12.29-174e6851", - "2022.12.26-0d489617", - "2022.12.23-e8f72d70", - "2022.12.23-a2d506d4", - "2022.12.22-d75ae7c8" - ] - }, - { - "name": "trilium", - "image": "zadam/trilium", - "tags": [ - "0.58.4", - "0.57.4", - "0.57.2", - "0.56.1", - "0.55.1", - "0.54.2", - "0.53.2", - "0.52.4", - "0.52.2", - "0.51.2", - "0.50.3", - "0.50.1", - "0.49.5", - "0.49.3", - "0.48.9", - "0.48.7", - "0.48.4", - "0.48.2", - "0.47.8", - "0.47.6", - "0.47.4", - "0.47.2", - "0.46.7", - "0.46.5", - "0.45.10", - "0.45.9", - "0.45.7", - "0.45.5", - "0.45.3", - "0.44.8" - ] - }, - { - "name": "umami-postgresql", - "image": "ghcr.io/umami-software/umami", - "tags": [ - "postgresql-v1.39.5", - "postgresql-v1.39.4", - "postgresql-v1.39.3", - "postgresql-v1.39.2", - "postgresql-v1.39.1", - "postgresql-v1.39.0", - "postgresql-v1.38.0", - "postgresql-v1.37.0", - "postgresql-v1.36.1", - "postgresql-v1.36.0", - "postgresql-v1.35.0", - "postgresql-v1.34.0", - "postgresql-v1.33.3", - "postgresql-latest", - "mysql-v1.39.5", - "mysql-v1.39.4", - "mysql-v1.39.3", - "mysql-v1.39.2", - "mysql-v1.39.1", - "mysql-v1.39.0", - "mysql-v1.38.0", - "mysql-v1.37.0", - "mysql-v1.36.1", - "mysql-v1.36.0", - "mysql-v1.35.0", - "mysql-v1.34.0", - "mysql-v1.33.3", - "mysql-latest" - ] - }, - { - "name": "umami", - "image": "ghcr.io/umami-software/umami", - "tags": [ - "postgresql-v1.39.5", - "postgresql-v1.39.4", - "postgresql-v1.39.3", - "postgresql-v1.39.2", - "postgresql-v1.39.1", - "postgresql-v1.39.0", - "postgresql-v1.38.0", - "postgresql-v1.37.0", - "postgresql-v1.36.1", - "postgresql-v1.36.0", - "postgresql-v1.35.0", - "postgresql-v1.34.0", - "postgresql-v1.33.3", - "postgresql-latest", - "mysql-v1.39.5", - "mysql-v1.39.4", - "mysql-v1.39.3", - "mysql-v1.39.2", - "mysql-v1.39.1", - "mysql-v1.39.0", - "mysql-v1.38.0", - "mysql-v1.37.0", - "mysql-v1.36.1", - "mysql-v1.36.0", - "mysql-v1.35.0", - "mysql-v1.34.0", - "mysql-v1.33.3", - "mysql-latest" - ] - }, - { - "name": "uptimekuma", - "image": "louislam/uptime-kuma", - "tags": [ - "1.19.4", - "1.19.3", - "1.19.2", - "1.19.1", - "1.19.0", - "1.18.5", - "1.18.4", - "1.18.3", - "1.18.2", - "1.18.1", - "1.18.0", - "1.17.1", - "1.17.0", - "1.16.1", - "1.16.0", - "1.15.1", - "1.15.0", - "1.14.1", - "1.14.0", - "1.13.2", - "1.13.1", - "1.13.0", - "1.12.1", - "1.11.4", - "1.11.3", - "1.11.2", - "1.11.1", - "1.11.0", - "1.10.2", - "1.10.1" - ] - }, - { - "name": "vaultwarden", - "image": "vaultwarden/server", - "tags": [ - "1.27.0", - "1.26.0", - "1.25.2", - "1.25.1", - "1.25.0", - "1.24.0", - "1.23.1", - "1.23.0", - "1.22.2", - "1.22.1", - "1.22.0", - "1.21.0" - ] - }, - { - "name": "vscodeserver", - "image": "codercom/code-server", - "tags": [ - "4.9.1", - "4.9.0", - "4.8.3", - "4.8.2", - "4.8.1", - "4.8.0", - "4.7.0", - "4.6.0", - "4.5.1", - "4.4.0", - "4.2.0", - "4.0.2", - "3.11.1", - "3.10.2", - "3.10.0", - "3.9.3", - "3.9.1", - "3.8.1", - "3.7.4", - "3.7.2", - "3.7.0", - "3.6.1", - "3.5.0", - "3.4.0", - "3.3.0", - "3.2.0", - "3.1.1", - "3.1.0", - "3.0.2", - "3.0.0" - ] - }, - { - "name": "weblate", - "image": "weblate/weblate", - "tags": [ - "latest", - "edge-2023-01-13-e824b551f23c3679467e38b06366744a06aa3b0c", - "edge-2023-01-13-468b996565e6b62edb78d40b515c476e0d860273", - "edge-2023-01-12-fe3d58b14f119eb5501220e9f096949c2e1ec2d3", - "edge-2023-01-12-112f75f9ee9e118ad493215f89742e6e091be8d0", - "edge-2023-01-11-f7bb190993e329d1529694e8cc7f5e0a80ccd615", - "edge-2023-01-11-e8ef3183aa7723f32c2b60c7c3b89910f2c7c593", - "edge-2023-01-11-155231f6cde18a65e3f35093d66dd0ce93aa7154", - "edge-2023-01-10-e47516e4022f87c019e61998b556b69111187aa9", - "edge-2023-01-10-98c6b38c746165adb27b2a8e93a74fa9ab64f17c", - "edge-2023-01-10-1df5c9dd96a6d8650f6881942fecbe33e1884295", - "edge-2023-01-09-7029b7b6c630be7cdac07d1629573dd2b81bc05f", - "edge-2023-01-09-4b05a878aa25b2c544a4e77027769b5934ec561f", - "edge-2023-01-07-df50259ff209720b0fd3c983bd7a5d01b564149c", - "edge-2023-01-07-719b3034de0ed369cd63554ea652f11142b9a540", - "edge-2023-01-07-14d4aec6cd8e1e6e0b7c1dc3a9cf4a74ccfc5e37", - "edge-2023-01-07-00f2d980c4388d799ba0df3a27c10bf941b7edd3", - "edge-2023-01-04-e92fe933b22e36e92f6de57a0ecb16752852d815", - "edge-2023-01-04-4c413c6ba33c39a6b7f3238f255bc09c69e96345", - "edge-2023-01-02-fd57652cce9dd2a12c7ff1dc644f5a969e57ea76", - "edge-2023-01-02-738d270784bca0cfaaef59e6cfb8c307f68a9939", - "edge-2023-01-02-3f6a7a183bbda4dc2e8d7e468370adc6f9e50527", - "edge-2023-01-01-5580a7a4755bf71403c85999fb4dff2078293403", - "edge-2022-12-29-8f422f23930880809a26d1207f9e43315ef54ad2", - "edge-2022-12-28-375fd99195711641246ccd09e3ce394d6928eaef", - "edge-2022-12-25-358c8ce6071f2781ae027bbc3a46d906ce08d9e7", - "edge-2022-12-24-3e1503494ce06ad6ff32f02db1a7d59224e5c860", - "edge-2022-12-21-cac4b09f943fe97700e3a33b7caf23277d2fcc11", - "edge-2022-12-21-3a8dd1bf66a7295f3512346bc1c97d55c5649dcf", - "edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099" - ] - }, - { - "name": "whoogle", - "image": "benbusby/whoogle-search", - "tags": [ - "0.8.0", - "0.7.3", - "0.7.1", - "0.6.0", - "0.5.3", - "0.5.1", - "0.4.1", - "0.3.2", - "v0.3.0", - "0.1.2", - "0.1.0" - ] - }, - { - "name": "wordpress-only", - "image": "library/wordpress", - "tags": [ - "php8.2-fpm-alpine", - "php8.2-fpm", - "php8.2-apache", - "php8.2", - "php8.1-fpm-alpine", - "php8.1-fpm", - "php8.1-apache", - "php8.1", - "php8.0-fpm-alpine", - "php8.0-fpm", - "php8.0-apache", - "php8.0", - "php7.4-fpm-alpine", - "php7.4-fpm", - "php7.4-apache", - "php7.4", - "php7.3-fpm-alpine", - "php7.3-fpm", - "php7.3-apache", - "php7.3", - "php7.2-fpm-alpine", - "php7.2-fpm", - "php7.2-apache", - "php7.2", - "php7.1-fpm-alpine", - "php7.1-fpm", - "php7.1-apache", - "php7.1", - "php7.0-fpm-alpine", - "php7.0-fpm" - ] - }, - { - "name": "wordpress", - "image": "library/wordpress", - "tags": [ - "php8.2-fpm-alpine", - "php8.2-fpm", - "php8.2-apache", - "php8.2", - "php8.1-fpm-alpine", - "php8.1-fpm", - "php8.1-apache", - "php8.1", - "php8.0-fpm-alpine", - "php8.0-fpm", - "php8.0-apache", - "php8.0", - "php7.4-fpm-alpine", - "php7.4-fpm", - "php7.4-apache", - "php7.4", - "php7.3-fpm-alpine", - "php7.3-fpm", - "php7.3-apache", - "php7.3", - "php7.2-fpm-alpine", - "php7.2-fpm", - "php7.2-apache", - "php7.2", - "php7.1-fpm-alpine", - "php7.1-fpm", - "php7.1-apache", - "php7.1", - "php7.0-fpm-alpine", - "php7.0-fpm" - ] - } -] diff --git a/apps/api/devTemplates.yaml b/apps/api/devTemplates.yaml deleted file mode 100644 index e4bcde325..000000000 --- a/apps/api/devTemplates.yaml +++ /dev/null @@ -1,3581 +0,0 @@ -- templateVersion: 1.0.0 - defaultVersion: "9.22" - documentation: https://docs.directus.io/getting-started/introduction.html - type: directus-postgresql - name: Directus - subname: (PostgreSQL) - description: >- - Directus is a free and open-source headless CMS framework for managing custom SQL-based databases. - labels: - - CMS - - headless - services: - $$id: - name: Directus - depends_on: - - $$id-postgresql - - $$id-redis - image: directus/directus:$$core_version - volumes: - - $$id-uploads:/directus/uploads - - $$id-database:/directus/database - - $$id-extensions:/directus/extensions - environment: - - KEY=$$secret_key - - SECRET=$$secret_secret - - DB_CLIENT=pg - - DB_CONNECTION_STRING=$$secret_db_connection_string - - CACHE_ENABLED=true - - CACHE_STORE=redis - - CACHE_REDIS=$$secret_cache_redis - - ADMIN_EMAIL=$$config_admin_email - - ADMIN_PASSWORD=$$secret_admin_password - - CACHE_AUTO_PURGE=true - - PUBLIC_URL=$$config_public_url - ports: - - "8055" - $$id-postgresql: - name: Directus PostgreSQL - depends_on: [] - image: postgres:14-alpine - volumes: - - $$id-postgresql-data:/var/lib/postgresql/data - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - $$id-redis: - name: Directus Redis - depends_on: [] - image: redis:7.0.4-alpine - command: "--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5" - volumes: - - "$$id-redis:/data" - environment: [] - variables: - - id: $$config_public_url - name: PUBLIC_URL - label: Public URL - defaultValue: $$generate_fqdn - description: "" - - id: $$secret_db_connection_string - name: DB_CONNECTION_STRING - label: Directus Database Url - defaultValue: postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$config_postgres_db - main: $$id-postgresql - name: POSTGRES_DB - label: Database - defaultValue: directus - description: "" - - id: $$config_postgres_user - main: $$id-postgresql - name: POSTGRES_USER - label: User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - main: $$id-postgresql - name: POSTGRES_PASSWORD - label: Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$secret_cache_redis - name: CACHE_REDIS - label: Redis Url - defaultValue: redis://$$id-redis:6379 - description: "" - - id: $$config_admin_email - name: ADMIN_EMAIL - label: Initial Admin Email - defaultValue: "admin@example.com" - description: "The email address of the first user that is automatically created. You can change it later in Directus." - - id: $$secret_admin_password - name: ADMIN_PASSWORD - label: Initial Admin Password - defaultValue: $$generate_password - description: "The password of the first user that is automatically created." - showOnConfiguration: true - - id: $$secret_key - name: KEY - label: Key - defaultValue: $$generate_password - description: "Unique identifier for the project." - showOnConfiguration: true - - id: $$secret_secret - name: SECRET - label: Secret - defaultValue: $$generate_password - description: "Secret string for the project." - showOnConfiguration: true - -- templateVersion: 1.0.0 - defaultVersion: v1.3.8 - documentation: https://github.com/LibreTranslate/LibreTranslate - description: Free and Open Source Machine Translation API. 100% self-hosted, offline capable and easy to setup. - type: libretranslate - name: Libretranslate - labels: - - translator - - argos - - python - - libretranslate - services: - $$id: - name: Libretranslate - image: libretranslate/libretranslate:$$core_version - environment: - - LT_HOST=0.0.0.0 - - LT_SUGGESTIONS=true - - LT_CHAR_LIMIT=$$config_lt_char_limit - - LT_REQ_LIMIT=$$config_lt_req_limit - - LT_BATCH_LIMIT=$$config_lt_batch_limit - - LT_GA_ID=$$config_lt_ga_id - - LT_DISABLE_WEB_UI=$$config_lt_web_ui - volumes: - - $$id-libretranslate:/libretranslate - ports: - - "5000" - variables: - - id: $$config_lt_char_limit - name: LT_CHAR_LIMIT - label: Char limit - defaultValue: "5000" - description: "Set character limit." - - id: $$config_lt_req_limit - name: LT_REQ_LIMIT - label: Request limit - defaultValue: "5000" - description: "Set maximum number of requests per minute per client." - - id: $$config_lt_batch_limit - name: LT_BATCH_LIMIT - label: Batch Limit - defaultValue: "5000" - description: "Set maximum number of texts to translate in a batch request." - - id: $$config_lt_ga_id - name: LT_GA_ID - label: Google Analytics ID - defaultValue: "" - description: "Enable Google Analytics on the API client page by providing an ID" - - id: $$config_lt_web_ui - name: LT_DISABLE_WEB_UI - label: Web UI - defaultValue: "false" - description: "Disable or enable web ui. True or false." -- templateVersion: 1.0.0 - defaultVersion: 0.8.0 - documentation: https://github.com/benbusby/whoogle-search - type: whoogle - name: Whoogle Search - description: A self-hosted, ad-free, privacy-respecting metasearch engine - labels: - - search - - google - services: - $$id: - name: Whoogle Search - documentation: https://github.com/benbusby/whoogle-search - depends_on: [] - image: benbusby/whoogle-search:$$core_version - cap_drop: - - ALL - environment: - - WHOOGLE_USER=$$config_whoogle_username - - WHOOGLE_PASS=$$secret_whoogle_password - - WHOOGLE_CONFIG_PREFERENCES_KEY=$$secret_whoogle_preferences_key - ulimits: - nofile: - soft: 262144 - hard: 262144 - ports: - - "5000" - variables: - - id: $$config_whoogle_username - name: WHOOGLE_USER - label: Whoogle User - defaultValue: $$generate_username - description: "Username to log into Whoogle" - - id: $$secret_whoogle_password - name: WHOOGLE_PASSWORD - label: Whoogle Password - defaultValue: $$generate_password - description: "Password to log into Whoogle" - showOnConfiguration: true - - id: $$secret_whoogle_preferences_key - name: WHOOGLE_CONFIG_PREFERENCES_KEY - label: Whoogle preferences key - defaultValue: $$generate_password - description: "password to encrypt preferences" -- templateVersion: 1.0.0 - defaultVersion: 1.1.5 - documentation: https://docs.openblocks.dev/ - type: openblocks - name: Openblocks - description: The Open Source Retool Alternative - services: - $$id: - image: openblocksdev/openblocks-ce:$$core_version - volumes: - - $$id-stacks-data:/openblocks-stacks - ports: - - "3000" -- templateVersion: 1.0.0 - defaultVersion: "0.12.3" - documentation: https://pocketbase.io/docs/ - type: pocketbase - name: Pocketbase - description: "Open Source realtime backend in 1 file" - services: - $$id: - image: ghcr.io/coollabsio/pocketbase:$$core_version - volumes: - - $$id-data:/app/pb_data - ports: - - "8080" -- templateVersion: 1.0.0 - defaultVersion: v1.5.1 - documentation: https://plausible.io/doc/ - type: plausibleanalytics-arm - name: Plausible Analytics (ARM) - description: A lightweight and open-source website analytics tool. - labels: - - analytics - - statistics - - plausible - - gdpr - - no-cookie - - google analytics - services: - $$id: - name: Plausible Analytics - command: >- - sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate - && /entrypoint.sh db init-admin && /entrypoint.sh run" - depends_on: - - $$id-postgresql - - $$id-clickhouse - image: plausible/analytics:$$core_version - environment: - - ADMIN_USER_EMAIL=$$config_admin_user_email - - ADMIN_USER_NAME=$$config_admin_user_name - - ADMIN_USER_PWD=$$secret_admin_user_pwd - - BASE_URL=$$config_base_url - - SECRET_KEY_BASE=$$secret_secret_key_base - - DISABLE_AUTH=$$config_disable_auth - - DISABLE_REGISTRATION=$$config_disable_registration - - DATABASE_URL=$$secret_database_url - - CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url - ports: - - "8000" - $$id-postgresql: - name: PostgreSQL - image: postgres:14-alpine - volumes: - - $$id-postgresql-data:/var/lib/postgresql/data - environment: - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_DB=$$config_postgres_db - $$id-clickhouse: - name: Clickhouse - volumes: - - $$id-clickhouse-data:/var/lib/clickhouse - image: clickhouse/clickhouse-server:22.6-alpine - ulimits: - nofile: - soft: 262144 - hard: 262144 - files: - - location: /etc/clickhouse-server/users.d/logging.xml - content: >- - warningtrue - - location: /etc/clickhouse-server/config.d/logging.xml - content: >- - 00 - - location: /docker-entrypoint-initdb.d/init.query - content: CREATE DATABASE IF NOT EXISTS plausible; - - location: /docker-entrypoint-initdb.d/init-db.sh - content: >- - clickhouse client --queries-file - /docker-entrypoint-initdb.d/init.query - variables: - - id: $$config_base_url - name: BASE_URL - label: Base URL - defaultValue: $$generate_fqdn - description: >- - You must set this to the FQDN of the Plausible Analytics instance. This is - used to generate the links to the Plausible Analytics instance. - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL for PostgreSQL - defaultValue: >- - postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$secret_clickhouse_database_url - name: CLICKHOUSE_DATABASE_URL - label: Database URL for Clickhouse - defaultValue: http://$$id-clickhouse:8123/plausible - description: "" - - id: $$config_admin_user_email - name: ADMIN_USER_EMAIL - label: Admin Email Address - defaultValue: admin@example.com - description: This is the admin email. Please change it. - - id: $$config_admin_user_name - name: ADMIN_USER_NAME - label: Admin User Name - defaultValue: $$generate_username - description: This is the admin username. Please change it. - - id: $$secret_admin_user_pwd - name: ADMIN_USER_PWD - label: Admin User Password - defaultValue: $$generate_password - description: This is the admin password. Please change it. - showOnConfiguration: true - - id: $$secret_secret_key_base - name: SECRET_KEY_BASE - label: Secret Key Base - defaultValue: $$generate_hex(64) - description: "" - - id: $$config_disable_auth - name: DISABLE_AUTH - label: Authentication - defaultValue: "false" - description: "" - - id: $$config_disable_registration - name: DISABLE_REGISTRATION - label: Registration - defaultValue: "true" - description: "" - - id: $$config_postgres_user - main: $$id-postgresql - name: POSTGRES_USER - label: PostgreSQL Username - defaultValue: postgresql - description: "" - - id: $$secret_postgres_password - main: $$id-postgresql - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgres_db - main: $$id-postgresql - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: plausible - description: "" - - id: $$config_scriptName - name: SCRIPT_NAME - label: Custom Script Name - defaultValue: plausible.js - description: This is the default script name. -- templateVersion: 1.0.0 - defaultVersion: "1.18" - documentation: https://docs.gitea.io - type: gitea - name: Gitea - description: Gitea is a community managed lightweight code hosting solution written in Go. - labels: - - storage - - git - services: - $$id: - name: Gitea - documentation: https://docs.gitea.io - image: gitea/gitea:$$core_version - volumes: - - $$id-data:/data - - /etc/timezone:/etc/timezone:ro - - /etc/localtime:/etc/localtime:ro - environment: - - USER_UID=1000 - - USER_GID=1000 - - DOMAIN=$$config_domain - - SSH_DOMAIN=$$config_ssh_domain - - ROOT_URL=$$config_root_url - - SECRET_KEY=$$secret_secret_key - - INTERNAL_TOKEN=$$secret_internal_token - - SSH_PORT=22 - - START_SSH_SERVER=$$config_start_ssh_server - ports: - - "3000" - - "22" - proxy: - - port: "22" - hostPort: $$config_hostport_ssh - variables: - - id: $$config_hostport_ssh - name: SSH_PORT - label: SSH Port - defaultValue: "8022" - description: "" - required: true - - id: $$config_domain - name: DOMAIN - label: Domain - defaultValue: $$generate_domain - description: "" - - id: $$config_ssh_domain - name: SSH_DOMAIN - label: SSH Domain - defaultValue: $$generate_domain - description: "" - - id: $$config_start_ssh_server - name: START_SSH_SERVER - label: Start SSH Server - defaultValue: "true" - description: "" - - id: $$config_root_url - name: ROOT_URL - label: Root URL of Gitea - defaultValue: $$generate_fqdn_slash - description: "" - - id: $$secret_secret_key - name: SECRET_KEY - label: Secret Key - defaultValue: $$generate_hex(32) - description: "" - - id: $$secret_internal_token - name: INTERNAL_TOKEN - label: Internal JWT Token - defaultValue: $$generate_token - description: "" -- templateVersion: 1.0.0 - defaultVersion: "20.0" - documentation: https://www.keycloak.org/documentation - type: keycloak - name: Keycloak - description: "Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more." - labels: - - authentication - - authorization - - oidconnect - - saml2 - services: - $$id: - name: Keycloak - command: start --db=postgres --features=token-exchange --import-realm - depends_on: - - $$id-postgresql - image: "quay.io/keycloak/keycloak:$$core_version" - volumes: - - $$id-import:/opt/keycloak/data/import - environment: - - KC_HEALTH_ENABLED=true - - KC_PROXY=edge - - KC_DB=postgres - - KC_HOSTNAME=$$config_keycloak_domain - - KEYCLOAK_ADMIN=$$config_admin_user - - KEYCLOAK_ADMIN_PASSWORD=$$secret_keycloak_admin_password - - KC_DB_PASSWORD=$$secret_postgres_password - - KC_DB_USERNAME=$$config_postgres_user - - KC_DB_URL=$$secret_keycloak_database_url - ports: - - "8080" - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:14-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - variables: - - id: $$config_keycloak_domain - name: KEYCLOAK_DOMAIN - label: Keycloak Domain - defaultValue: $$generate_domain - description: "" - - id: $$secret_keycloak_database_url - name: KEYCLOAK_DATABASE_URL - label: Keycloak Database Url - defaultValue: >- - jdbc:postgresql://$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$config_admin_user - name: KEYCLOAK_ADMIN - label: Keycloak Admin User - defaultValue: $$generate_username - description: "" - - id: $$secret_keycloak_admin_password - name: KEYCLOAK_ADMIN_PASSWORD - label: Keycloak Admin Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgres_user - main: $$id-postgresql - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - main: $$id-postgresql - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgres_db - main: $$id-postgresql - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: keycloak - description: "" -- templateVersion: 1.0.0 - defaultVersion: v3.7 - documentation: https://github.com/freyacodes/Lavalink - description: Standalone audio sending node based on Lavaplayer. - type: lavalink - name: Lavalink - labels: - - discord - - discord bot - - audio - - lavalink - - jda - services: - $$id: - name: Lavalink - image: fredboat/lavalink:$$core_version - environment: [] - volumes: - - $$id-lavalink:/lavalink - ports: - - $$config_port - files: - - location: /opt/Lavalink/application.yml - content: >- - server: - port: 2333 - address: 0.0.0.0 - lavalink: - server: - password: "$$secret_password" - sources: - youtube: true - bandcamp: true - soundcloud: true - twitch: true - vimeo: true - http: true - local: false - - logging: - file: - path: ./logs/ - - level: - root: INFO - lavalink: INFO - - logback: - rollingpolicy: - max-file-size: 1GB - max-history: 30 - variables: - - id: $$secret_password - name: PASSWORD - label: Password - defaultValue: $$generate_password - required: true -- templateVersion: 1.0.0 - defaultVersion: v1.9.6 - documentation: https://docs.appsmith.com/getting-started/setup/instance-configuration/ - type: appsmith - name: Appsmith - description: "Fastest way to build internal apps over any database or API." - services: - $$id: - image: appsmith/appsmith-ce:$$core_version - environment: - - APPSMITH_MAIL_ENABLED=$$config_appsmith_mail_enabled - - APPSMITH_DISABLE_TELEMETRY=$$config_appsmith_disable_telemetry - - APPSMITH_DISABLE_INTERCOM=$$config_appsmith_disable_intercom - volumes: - - $$id-stacks-data:/appsmith-stacks - ports: - - "80" - variables: - - id: $$config_appsmith_mail_enabled - name: APPSMITH_MAIL_ENABLED - label: Enable Mail - defaultValue: "false" - description: "" - - id: $$config_appsmith_disable_telemetry - name: APPSMITH_DISABLE_TELEMETRY - label: Disable Telemetry - defaultValue: "true" - description: "" - - id: $$config_appsmith_disable_intercom - name: APPSMITH_DISABLE_INTERCOM - label: Disable Intercom - defaultValue: "true" - description: "" -- templateVersion: 1.0.0 - defaultVersion: 0.58.4 - documentation: https://hub.docker.com/r/zadam/trilium - description: "A hierarchical note taking application with focus on building large personal knowledge bases." - labels: - - personal - - knowledge - - notes - - wiki - type: trilium - name: Trilium Notes - services: - $$id: - image: zadam/trilium:$$core_version - environment: [] - volumes: - - $$id-trilium:/home/node/trilium-data - ports: - - "8080" - variables: [] -- templateVersion: 1.0.0 - defaultVersion: 1.19.4 - documentation: https://hub.docker.com/r/louislam/uptime-kuma - description: A free & fancy self-hosted monitoring tool. - labels: - - uptime - type: uptimekuma - name: UptimeKuma - services: - $$id: - image: louislam/uptime-kuma:$$core_version - environment: [] - volumes: - - $$id-uptimekuma:/app/data - ports: - - "3001" - variables: [] -- templateVersion: 1.0.0 - defaultVersion: "6.0" - documentation: https://hub.docker.com/r/silviof/docker-languagetool - description: "A multilingual grammar, style and spell checker." - type: languagetool - name: LanguageTool - services: - $$id: - image: silviof/docker-languagetool:$$core_version - environment: [] - volumes: - - $$id-ngrams:/ngrams - ports: - - "8010" - variables: [] -- templateVersion: 1.0.0 - defaultVersion: 1.27.0 - documentation: https://hub.docker.com/r/vaultwarden/server - description: "Bitwarden compatible server written in Rust." - type: vaultwarden - name: VaultWarden - labels: - - bitwarden - - password manager - services: - $$id: - image: vaultwarden/server:$$core_version - environment: [] - volumes: - - $$id-data:/data - ports: - - "80" - variables: [] -- templateVersion: 1.0.0 - defaultVersion: 9.3.2 - documentation: https://hub.docker.com/r/grafana/grafana - type: grafana - name: Grafana - description: >- - Grafana allows you to query, visualize, alert on and understand your metrics. - labels: - - monitoring - - metrics - - dashboard - services: - $$id: - image: grafana/grafana:$$core_version - environment: [] - volumes: - - $$id-config:/etc/grafana - - $$id-grafana:/var/lib/grafana - ports: - - "3000" - variables: [] -- templateVersion: 1.0.0 - defaultVersion: 1.2.0 - documentation: https://appwrite.io/docs - type: appwrite - name: Appwrite - description: Secure Backend Server for Web, Mobile & Flutter Developers. - labels: - - serverless - - backend - - storage - - api - services: - "$$id": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_WORKER_PER_CORE=$$config__app_worker_per_core - - _APP_LOCALE=$$config__app_locale - - _APP_CONSOLE_WHITELIST_ROOT=$$config__app_console_whitelist_root - - _APP_CONSOLE_WHITELIST_EMAILS=$$config__app_console_whitelist_emails - - _APP_CONSOLE_WHITELIST_IPS=$$config__app_console_whitelist_ips - - _APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name - - _APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address - - _APP_SYSTEM_RESPONSE_FORMAT=$$config__app_system_response_format - - _APP_OPTIONS_ABUSE=$$config__app_options_abuse - - _APP_OPTIONS_FORCE_HTTPS=$$config__app_options_force_https - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_DOMAIN=$$config__app_domain - - _APP_DOMAIN_TARGET=$$config__app_domain_target - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_SMTP_HOST=$$config__app_smtp_host - - _APP_SMTP_PORT=$$config__app_smtp_port - - _APP_SMTP_SECURE=$$config__app_smtp_secure - - _APP_SMTP_USERNAME=$$config__app_smtp_username - - _APP_SMTP_PASSWORD=$$secret__app_smtp_password - - _APP_USAGE_STATS=$$config__app_usage_stats - - _APP_INFLUXDB_HOST=$$config__app_influxdb_host - - _APP_INFLUXDB_PORT=$$config__app_influxdb_port - - _APP_STORAGE_LIMIT=$$config__app_storage_limit - - _APP_STORAGE_PREVIEW_LIMIT=$$config__app_storage_preview_limit - - _APP_STORAGE_ANTIVIRUS=$$config__app_storage_antivirus_enabled - - _APP_STORAGE_ANTIVIRUS_HOST=$$config__app_storage_antivirus_host - - _APP_STORAGE_ANTIVIRUS_PORT=$$config__app_storage_antivirus_port - - _APP_STORAGE_DEVICE=$$config__app_storage_device - - _APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key - - _APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret - - _APP_STORAGE_S3_REGION=$$config__app_storage_s3_region - - _APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket - - _APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key - - _APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret - - _APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region - - _APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket - - _APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key - - _APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret - - _APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region - - _APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket - - _APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key - - _APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret - - _APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region - - _APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket - - _APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key - - _APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret - - _APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region - - _APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket - - _APP_FUNCTIONS_SIZE_LIMIT=$$config__app_functions_size_limit - - _APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout - - _APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout - - _APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers - - _APP_FUNCTIONS_CPUS=$$config__app_functions_cpus - - _APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated - - _APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap - - _APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes - - _APP_EXECUTOR_SECRET=$$secret__app_executor_secret - - _APP_EXECUTOR_HOST=$$config__app_executor_host - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - _APP_STATSD_HOST=$$config__app_statsd_host - - _APP_STATSD_PORT=$$config__app_statsd_port - - _APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval - - _APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution - - _APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache - - _APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse - - _APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit - - _APP_SMS_PROVIDER=$$config__app_sms_provider - - _APP_SMS_FROM=$$config__app_sms_from - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: - - "$$id-uploads:/storage/uploads" - - "$$id-cache:/storage/cache" - - "$$id-config:/storage/config" - - "$$id-certificates:/storage/certificates" - - "$$id-functions:/storage/functions" - ports: - - "80" - proxy: - - port: "80" - "$$id-executor": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout - - _APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout - - _APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers - - _APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes - - _APP_FUNCTIONS_CPUS=$$config__app_functions_cpus - - _APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated - - _APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap - - _APP_FUNCTIONS_INACTIVE_THRESHOLD=$$config__app_functions_inactive_threshold - - _APP_EXECUTOR_SECRET=$$secret__app_executor_secret - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - _APP_STORAGE_DEVICE=$$config__app_storage_device - - _APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key - - _APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret - - _APP_STORAGE_S3_REGION=$$config__app_storage_s3_region - - _APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket - - _APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key - - _APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret - - _APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region - - _APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket - - _APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key - - _APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret - - _APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region - - _APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket - - _APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key - - _APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret - - _APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region - - _APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket - - _APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key - - _APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret - - _APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region - - _APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket - - DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username - - DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: - - "$$id-functions:/storage/functions" - - "$$id-builds:/storage/builds" - - "/var/run/docker.sock:/var/run/docker.sock" - - "/tmp:/tmp:rw" - entrypoint: executor - "$$id-influxdb": - image: appwrite/influxdb:1.5.0 - environment: [] - volumes: - - "$$id-influxdb:/var/lib/influxdb" - "$$id-maintenance": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_DOMAIN=$$config__app_domain - - _APP_DOMAIN_TARGET=$$config__app_domain_target - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval - - _APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution - - _APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache - - _APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse - - _APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: maintenance - "$$id-mariadb": - image: mariadb:10.7 - command: "--innodb-flush-method fsync" - environment: - - MARIADB_ROOT_PASSWORD=$$secret__app_db_root_pass - - MARIADB_DATABASE=$$config__app_db_schema - - MARIADB_USER=$$config__app_db_user - - MARIADB_PASSWORD=$$secret__app_db_pass - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: - - "$$id-mariadb:/var/lib/mysql" - "$$id-realtime": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_WORKER_PER_CORE=$$config__app_worker_per_core - - _APP_OPTIONS_ABUSE=$$config__app_options_abuse - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_USAGE_STATS=$$config__app_usage_stats - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: realtime - proxy: - - port: "80" - pathPrefix: "/v1/realtime" - "$$id-redis": - image: redis:7.0.4-alpine - command: - "--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples - 5" - environment: [] - volumes: - - "$$id-redis:/data" - "$$id-schedule": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: schedule - "$$id-telegraf": - image: appwrite/telegraf:1.4.0 - environment: - - _APP_INFLUXDB_HOST=$$config__app_influxdb_host - - _APP_INFLUXDB_PORT=$$config__app_influxdb_port - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: - - "$$id-influxdb:/var/lib/influxdb" - "$$id-usage-database": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_INFLUXDB_HOST=$$config__app_influxdb_host - - _APP_INFLUXDB_PORT=$$config__app_influxdb_port - - _APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval - - _APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: usage --type database - "$$id-usage": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_INFLUXDB_HOST=$$config__app_influxdb_host - - _APP_INFLUXDB_PORT=$$config__app_influxdb_port - - _APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval - - _APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: usage --type timeseries - "$$id-worker-audits": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-audits - "$$id-worker-builds": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_EXECUTOR_SECRET=$$secret__app_executor_secret - - _APP_EXECUTOR_HOST=$$config__app_executor_host - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-builds - "$$id-worker-certificates": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_DOMAIN=$$config__app_domain - - _APP_DOMAIN_TARGET=$$config__app_domain_target - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: - - "$$id-config:/storage/config" - - "$$id-certificates:/storage/certificates" - entrypoint: worker-certificates - "$$id-worker-databases": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-databases - "$$id-worker-deletes": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_STORAGE_DEVICE=$$config__app_storage_device - - _APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key - - _APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret - - _APP_STORAGE_S3_REGION=$$config__app_storage_s3_region - - _APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket - - _APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key - - _APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret - - _APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region - - _APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket - - _APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key - - _APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret - - _APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region - - _APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket - - _APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key - - _APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret - - _APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region - - _APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket - - _APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key - - _APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret - - _APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region - - _APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - _APP_EXECUTOR_SECRET=$$secret__app_executor_secret - - _APP_EXECUTOR_HOST=$$config__app_executor_host - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: - - "$$id-uploads:/storage/uploads" - - "$$id-cache:/storage/cache" - - "$$id-functions:/storage/functions" - - "$$id-builds:/storage/builds" - - "$$id-certificates:/storage/certificates" - entrypoint: worker-deletes - "$$id-worker-functions": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_DB_HOST=$$config__app_db_host - - _APP_DB_PORT=$$config__app_db_port - - _APP_DB_SCHEMA=$$config__app_db_schema - - _APP_DB_USER=$$config__app_db_user - - _APP_DB_PASS=$$secret__app_db_pass - - _APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout - - _APP_EXECUTOR_SECRET=$$secret__app_executor_secret - - _APP_EXECUTOR_HOST=$$config__app_executor_host - - _APP_USAGE_STATS=$$config__app_usage_stats - - DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username - - DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-functions - "$$id-worker-mails": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name - - _APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_SMTP_HOST=$$config__app_smtp_host - - _APP_SMTP_PORT=$$config__app_smtp_port - - _APP_SMTP_SECURE=$$config__app_smtp_secure - - _APP_SMTP_USERNAME=$$config__app_smtp_username - - _APP_SMTP_PASSWORD=$$secret__app_smtp_password - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-mails - "$$id-worker-messaging": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_SMS_PROVIDER=$$config__app_sms_provider - - _APP_SMS_FROM=$$config__app_sms_from - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-messaging - "$$id-worker-webhooks": - image: appwrite/appwrite:$$core_version - environment: - - _APP_ENV=$$config__app_env - - _APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1 - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address - - _APP_REDIS_HOST=$$config__app_redis_host - - _APP_REDIS_PORT=$$config__app_redis_port - - _APP_REDIS_USER=$$config__app_redis_user - - _APP_REDIS_PASS=$$secret__app_redis_pass - - _APP_LOGGING_PROVIDER=$$config__app_logging_provider - - _APP_LOGGING_CONFIG=$$config__app_logging_config - - OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network - volumes: [] - entrypoint: worker-webhooks - variables: - - id: "$$config__app_influxdb_host" - name: _APP_INFLUXDB_HOST - label: InfluxDB | _APP_INFLUXDB_HOST - defaultValue: "$$id-influxdb" - description: "" - - id: "$$config__app_influxdb_port" - name: _APP_INFLUXDB_PORT - label: InfluxDB | _APP_INFLUXDB_PORT - defaultValue: "8086" - description: InfluxDB server TCP port. - - id: "$$config__app_env" - name: _APP_ENV - label: General | _APP_ENV - defaultValue: production - description: Set your server running environment. - - id: "$$config__app_worker_per_core" - name: _APP_WORKER_PER_CORE - label: General | _APP_WORKER_PER_CORE - defaultValue: "6" - description: - Internal Worker per core for the API, Realtime and Executor containers. - Can be configured to optimize performance. - - id: "$$config__app_locale" - name: _APP_LOCALE - label: General | _APP_LOCALE - defaultValue: en - description: Set your Appwrite's locale. By default, the locale is set to 'en'. - - id: "$$config__app_console_whitelist_root" - name: _APP_CONSOLE_WHITELIST_ROOT - label: General | _APP_CONSOLE_WHITELIST_ROOT - defaultValue: enabled - description: - This option allows you to disable the creation of new users on the - Appwrite console. When enabled only 1 user will be able to use the registration - form. New users can be added by inviting them to your project. By default this - option is enabled. - - id: "$$config__app_console_whitelist_emails" - name: _APP_CONSOLE_WHITELIST_EMAILS - label: General | _APP_CONSOLE_WHITELIST_EMAILS - defaultValue: "" - description: - This option allows you to limit creation of new users on the Appwrite - console. This option is very useful for small teams or sole developers. To enable - it, pass a list of allowed email addresses separated by a comma. - - id: "$$config__app_console_whitelist_ips" - name: _APP_CONSOLE_WHITELIST_IPS - label: General | _APP_CONSOLE_WHITELIST_IPS - defaultValue: "" - description: - This last option allows you to limit creation of users in Appwrite - console for users sharing the same set of IP addresses. This option is very - useful for team working with a VPN service or a company IP.\n\nTo enable/activate - this option, pass a list of allowed IP addresses separated by a comma. - - id: "$$config__app_system_email_name" - name: _APP_SYSTEM_EMAIL_NAME - label: General | _APP_SYSTEM_EMAIL_NAME - defaultValue: Appwrite - description: - This is the sender name value that will appear on email messages - sent to developers from the Appwrite console. You can use url encoded strings - for spaces and special chars. - - id: "$$config__app_system_email_address" - name: _APP_SYSTEM_EMAIL_ADDRESS - label: General | _APP_SYSTEM_EMAIL_ADDRESS - defaultValue: team@appwrite.io - description: - This is the sender email address that will appear on email messages - sent to developers from the Appwrite console. You should choose an email address - that is allowed to be used from your SMTP server to avoid the server email ending - in the users' SPAM folders. - - id: "$$config__app_system_security_email_address" - name: _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - label: General | _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - defaultValue: certs@appwrite.io - description: - This is the email address used to issue SSL certificates for custom - domains or the user agent in your webhooks payload. - - id: "$$config__app_system_response_format" - name: _APP_SYSTEM_RESPONSE_FORMAT - label: General | _APP_SYSTEM_RESPONSE_FORMAT - defaultValue: "" - description: - Use this environment variable to set the default Appwrite HTTP response - format to support an older version of Appwrite. This option is useful to overcome - breaking changes between versions. You can also use the X-Appwrite-Response-Format - HTTP request header to overwrite the response for a specific request. This variable - accepts any valid Appwrite version. To use the current version format, leave - the value of the variable empty. - - id: "$$config__app_options_abuse" - name: _APP_OPTIONS_ABUSE - label: General | _APP_OPTIONS_ABUSE - defaultValue: enabled - description: - Allows you to disable abuse checks and API rate limiting. By default, - set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not - recommended to disable this check-in a production environment. - - id: "$$config__app_options_force_https" - name: _APP_OPTIONS_FORCE_HTTPS - label: General | _APP_OPTIONS_FORCE_HTTPS - defaultValue: disabled - description: - Allows you to force HTTPS connection to your API. This feature redirects - any HTTP call to HTTPS and adds the 'Strict-Transport-Security' header to all - HTTP responses. - - id: "$$secret__app_openssl_key_v1" - name: _APP_OPENSSL_KEY_V1 - label: General | _APP_OPENSSL_KEY_V1 - defaultValue: "$$generate_hex(256)" - description: - This is your server private secret key that is used to encrypt all - sensitive data on your server. Appwrite server encrypts all secret data on your - server like webhooks, HTTP passwords, user sessions, and storage files. Keep - it a secret and have a backup for it. - - id: "$$config__app_domain" - name: _APP_DOMAIN - label: General | _APP_DOMAIN - defaultValue: $$generate_domain - description: - Your Appwrite domain address. When setting a public suffix domain, - Appwrite will attempt to issue a valid SSL certificate automatically. When used - with a dev domain, Appwrite will assign a self-signed SSL certificate. The default - value is 'localhost'. - - id: "$$config__app_domain_target" - name: _APP_DOMAIN_TARGET - label: General | _APP_DOMAIN_TARGET - defaultValue: $$generate_fqdn - description: - A DNS A record hostname to serve as a CNAME target for your Appwrite - custom domains. You can use the same value as used for the Appwrite '_APP_DOMAIN' - variable. The default value is 'localhost'. - - id: "$$config__app_redis_host" - name: _APP_REDIS_HOST - label: Redis | _APP_REDIS_HOST - defaultValue: "$$id-redis" - description: "" - - id: "$$config__app_redis_port" - name: _APP_REDIS_PORT - label: Redis | _APP_REDIS_PORT - defaultValue: "6379" - description: Redis server TCP port. - - id: "$$config__app_redis_user" - name: _APP_REDIS_USER - label: Redis | _APP_REDIS_USER - defaultValue: "" - description: - Redis server user. This is an optional variable. Default value is - an empty string. - - id: "$$secret__app_redis_pass" - name: _APP_REDIS_PASS - label: Redis | _APP_REDIS_PASS - defaultValue: "" - description: - Redis server password. This is an optional variable. Default value - is an empty string. - - id: "$$config__app_db_host" - name: _APP_DB_HOST - label: MariaDB | _APP_DB_HOST - defaultValue: "$$id-mariadb" - description: "" - - id: "$$config__app_db_port" - name: _APP_DB_PORT - label: MariaDB | _APP_DB_PORT - defaultValue: "3306" - description: MariaDB server TCP port. - - id: "$$config__app_db_schema" - name: _APP_DB_SCHEMA - label: MariaDB | _APP_DB_SCHEMA - defaultValue: appwrite - description: MariaDB server database schema. - - id: "$$config__app_db_user" - name: _APP_DB_USER - label: MariaDB | _APP_DB_USER - defaultValue: user - description: MariaDB server user name. - - id: "$$secret__app_db_root_pass" - name: MARIADB_ROOT_PASSWORD - label: MariaDB | MARIADB_ROOT_PASSWORD - defaultValue: "$$generate_hex(16)" - description: MariaDB server root user password. - - id: "$$secret__app_db_pass" - name: _APP_DB_PASS - label: MariaDB | _APP_DB_PASS - defaultValue: "$$generate_hex(16)" - description: MariaDB server user password. - - id: "$$config__app_smtp_host" - name: _APP_SMTP_HOST - label: SMTP | _APP_SMTP_HOST - defaultValue: "" - description: - SMTP server host name address. Use an empty string to disable all - mail sending from the server. The default value for this variable is an empty - string. - - id: "$$config__app_smtp_port" - name: _APP_SMTP_PORT - label: SMTP | _APP_SMTP_PORT - defaultValue: "" - description: SMTP server TCP port. Empty by default. - - id: "$$config__app_smtp_secure" - name: _APP_SMTP_SECURE - label: SMTP | _APP_SMTP_SECURE - defaultValue: "" - description: - SMTP secure connection protocol. Empty by default, change to 'tls' - if running on a secure connection. - - id: "$$config__app_smtp_username" - name: _APP_SMTP_USERNAME - label: SMTP | _APP_SMTP_USERNAME - defaultValue: "" - description: SMTP server user name. Empty by default. - - id: "$$secret__app_smtp_password" - name: _APP_SMTP_PASSWORD - label: SMTP | _APP_SMTP_PASSWORD - defaultValue: "" - description: SMTP server user password. Empty by default. - - id: "$$config__app_usage_stats" - name: _APP_USAGE_STATS - label: General | _APP_USAGE_STATS - defaultValue: enabled - description: - This variable allows you to disable the collection and displaying - of usage stats. This value is set to 'enabled' by default, to disable the usage - stats set the value to 'disabled'. When disabled, it's recommended to turn off - the Worker Usage, Influxdb and Telegraf containers for better resource usage. - - id: "$$config__app_storage_limit" - name: _APP_STORAGE_LIMIT - label: Storage | _APP_STORAGE_LIMIT - defaultValue: "30000000" - description: - Maximum file size allowed for file upload. The default value is 30MB. - You should pass your size limit value in bytes. - - id: "$$config__app_storage_preview_limit" - name: _APP_STORAGE_PREVIEW_LIMIT - label: Storage | _APP_STORAGE_PREVIEW_LIMIT - defaultValue: "20000000" - description: - Maximum file size allowed for file image preview. The default value - is 20MB. You should pass your size limit value in bytes. - - id: "$$config__app_storage_antivirus_enabled" - name: _APP_STORAGE_ANTIVIRUS - label: Storage | _APP_STORAGE_ANTIVIRUS - defaultValue: disabled - description: - This variable allows you to disable the internal anti-virus scans. - This value is set to 'disabled' by default, to enable the scans set the value - to 'enabled'. Before enabling, you must add the ClamAV service and depend on - it on main Appwrite service. - - id: "$$config__app_storage_antivirus_host" - name: _APP_STORAGE_ANTIVIRUS_HOST - label: Storage | _APP_STORAGE_ANTIVIRUS_HOST - defaultValue: clamav - description: ClamAV server host name address. - - id: "$$config__app_storage_antivirus_port" - name: _APP_STORAGE_ANTIVIRUS_PORT - label: Storage | _APP_STORAGE_ANTIVIRUS_PORT - defaultValue: "3310" - description: ClamAV server TCP port. - - id: "$$config__app_storage_device" - name: _APP_STORAGE_DEVICE - label: Storage | _APP_STORAGE_DEVICE - defaultValue: Local - description: - Select default storage device. The default value is 'Local'. List - of supported adapters are 'Local', 'S3', 'DOSpaces', 'Backblaze', 'Linode' and - 'Wasabi'. - - id: "$$secret__app_storage_s3_access_key" - name: _APP_STORAGE_S3_ACCESS_KEY - label: Storage | _APP_STORAGE_S3_ACCESS_KEY - defaultValue: "" - description: - AWS S3 storage access key. Required when the storage adapter is set - to S3. You can get your access key from your AWS console. - - id: "$$secret__app_storage_s3_secret" - name: _APP_STORAGE_S3_SECRET - label: Storage | _APP_STORAGE_S3_SECRET - defaultValue: "" - description: - AWS S3 storage secret key. Required when the storage adapter is set - to S3. You can get your secret key from your AWS console. - - id: "$$config__app_storage_s3_region" - name: _APP_STORAGE_S3_REGION - label: Storage | _APP_STORAGE_S3_REGION - defaultValue: us-east-1 - description: - AWS S3 storage region. Required when storage adapter is set to S3. - You can find your region info for your bucket from AWS console. - - id: "$$config__app_storage_s3_bucket" - name: _APP_STORAGE_S3_BUCKET - label: Storage | _APP_STORAGE_S3_BUCKET - defaultValue: "" - description: - AWS S3 storage bucket. Required when storage adapter is set to S3. - You can create buckets in your AWS console. - - id: "$$secret__app_storage_do_spaces_access_key" - name: _APP_STORAGE_DO_SPACES_ACCESS_KEY - label: Storage | _APP_STORAGE_DO_SPACES_ACCESS_KEY - defaultValue: "" - description: - DigitalOcean spaces access key. Required when the storage adapter - is set to DOSpaces. You can get your access key from your DigitalOcean console. - - id: "$$secret__app_storage_do_spaces_secret" - name: _APP_STORAGE_DO_SPACES_SECRET - label: Storage | _APP_STORAGE_DO_SPACES_SECRET - defaultValue: "" - description: - DigitalOcean spaces secret key. Required when the storage adapter - is set to DOSpaces. You can get your secret key from your DigitalOcean console. - - id: "$$config__app_storage_do_spaces_region" - name: _APP_STORAGE_DO_SPACES_REGION - label: Storage | _APP_STORAGE_DO_SPACES_REGION - defaultValue: us-east-1 - description: - DigitalOcean spaces region. Required when storage adapter is set - to DOSpaces. You can find your region info for your space from DigitalOcean - console. - - id: "$$config__app_storage_do_spaces_bucket" - name: _APP_STORAGE_DO_SPACES_BUCKET - label: Storage | _APP_STORAGE_DO_SPACES_BUCKET - defaultValue: "" - description: - DigitalOcean spaces bucket. Required when storage adapter is set - to DOSpaces. You can create spaces in your DigitalOcean console. - - id: "$$secret__app_storage_backblaze_access_key" - name: _APP_STORAGE_BACKBLAZE_ACCESS_KEY - label: Storage | _APP_STORAGE_BACKBLAZE_ACCESS_KEY - defaultValue: "" - description: - Backblaze access key. Required when the storage adapter is set to - Backblaze. Your Backblaze keyID will be your access key. You can get your keyID - from your Backblaze console. - - id: "$$secret__app_storage_backblaze_secret" - name: _APP_STORAGE_BACKBLAZE_SECRET - label: Storage | _APP_STORAGE_BACKBLAZE_SECRET - defaultValue: "" - description: - Backblaze secret key. Required when the storage adapter is set to - Backblaze. Your Backblaze applicationKey will be your secret key. You can get - your applicationKey from your Backblaze console. - - id: "$$config__app_storage_backblaze_region" - name: _APP_STORAGE_BACKBLAZE_REGION - label: Storage | _APP_STORAGE_BACKBLAZE_REGION - defaultValue: us-west-004 - description: - Backblaze region. Required when storage adapter is set to Backblaze. - You can find your region info from your Backblaze console. - - id: "$$config__app_storage_backblaze_bucket" - name: _APP_STORAGE_BACKBLAZE_BUCKET - label: Storage | _APP_STORAGE_BACKBLAZE_BUCKET - defaultValue: "" - description: - Backblaze bucket. Required when storage adapter is set to Backblaze. - You can create your bucket from your Backblaze console. - - id: "$$secret__app_storage_linode_access_key" - name: _APP_STORAGE_LINODE_ACCESS_KEY - label: Storage | _APP_STORAGE_LINODE_ACCESS_KEY - defaultValue: "" - description: - Linode object storage access key. Required when the storage adapter - is set to Linode. You can get your access key from your Linode console. - - id: "$$secret__app_storage_linode_secret" - name: _APP_STORAGE_LINODE_SECRET - label: Storage | _APP_STORAGE_LINODE_SECRET - defaultValue: "" - description: - Linode object storage secret key. Required when the storage adapter - is set to Linode. You can get your secret key from your Linode console. - - id: "$$config__app_storage_linode_region" - name: _APP_STORAGE_LINODE_REGION - label: Storage | _APP_STORAGE_LINODE_REGION - defaultValue: eu-central-1 - description: - Linode object storage region. Required when storage adapter is set - to Linode. You can find your region info from your Linode console. - - id: "$$config__app_storage_linode_bucket" - name: _APP_STORAGE_LINODE_BUCKET - label: Storage | _APP_STORAGE_LINODE_BUCKET - defaultValue: "" - description: - Linode object storage bucket. Required when storage adapter is set - to Linode. You can create buckets in your Linode console. - - id: "$$secret__app_storage_wasabi_access_key" - name: _APP_STORAGE_WASABI_ACCESS_KEY - label: Storage | _APP_STORAGE_WASABI_ACCESS_KEY - defaultValue: "" - description: - Wasabi access key. Required when the storage adapter is set to Wasabi. - You can get your access key from your Wasabi console. - - id: "$$secret__app_storage_wasabi_secret" - name: _APP_STORAGE_WASABI_SECRET - label: Storage | _APP_STORAGE_WASABI_SECRET - defaultValue: "" - description: - Wasabi secret key. Required when the storage adapter is set to Wasabi. - You can get your secret key from your Wasabi console. - - id: "$$config__app_storage_wasabi_region" - name: _APP_STORAGE_WASABI_REGION - label: Storage | _APP_STORAGE_WASABI_REGION - defaultValue: eu-central-1 - description: - Wasabi region. Required when storage adapter is set to Wasabi. You - can find your region info from your Wasabi console. - - id: "$$config__app_storage_wasabi_bucket" - name: _APP_STORAGE_WASABI_BUCKET - label: Storage | _APP_STORAGE_WASABI_BUCKET - defaultValue: "" - description: - Wasabi bucket. Required when storage adapter is set to Wasabi. You - can create buckets in your Wasabi console. - - id: "$$config__app_functions_size_limit" - name: _APP_FUNCTIONS_SIZE_LIMIT - label: Functions | _APP_FUNCTIONS_SIZE_LIMIT - defaultValue: "30000000" - description: The maximum size deployment in bytes. The default value is 30MB. - - id: "$$config__app_functions_timeout" - name: _APP_FUNCTIONS_TIMEOUT - label: Functions | _APP_FUNCTIONS_TIMEOUT - defaultValue: "900" - description: - The maximum number of seconds allowed as a timeout value when creating - a new function. The default value is 900 seconds. - - id: "$$config__app_functions_build_timeout" - name: _APP_FUNCTIONS_BUILD_TIMEOUT - label: Functions | _APP_FUNCTIONS_BUILD_TIMEOUT - defaultValue: "900" - description: - The maximum number of seconds allowed as a timeout value when building - a new function. The default value is 900 seconds. - - id: "$$config__app_functions_containers" - name: _APP_FUNCTIONS_CONTAINERS - label: Functions | _APP_FUNCTIONS_CONTAINERS - defaultValue: "10" - description: - The maximum number of containers Appwrite is allowed to keep alive - in the background for function environments. Running containers allow faster - execution time as there is no need to recreate each container every time a function - gets executed. The default value is 10. - - id: "$$config__app_functions_cpus" - name: _APP_FUNCTIONS_CPUS - label: Functions | _APP_FUNCTIONS_CPUS - defaultValue: "" - description: - The maximum number of CPU core a single cloud function is allowed - to use. Please note that setting a value higher than available cores will result - in a function error, which might result in an error. The default value is empty. - When it's empty, CPU limit will be disabled. - - id: "$$config__app_functions_memory_allocated" - name: _APP_FUNCTIONS_MEMORY - label: Functions | _APP_FUNCTIONS_MEMORY - defaultValue: "" - description: - The maximum amount of memory a single cloud function is allowed to - use in megabytes. The default value is empty. When it's empty, memory limit - will be disabled. - - id: "$$config__app_functions_memory_swap" - name: _APP_FUNCTIONS_MEMORY_SWAP - label: Functions | _APP_FUNCTIONS_MEMORY_SWAP - defaultValue: "" - description: - The maximum amount of swap memory a single cloud function is allowed - to use in megabytes. The default value is empty. When it's empty, swap memory - limit will be disabled. - - id: "$$config__app_functions_runtimes" - name: _APP_FUNCTIONS_RUNTIMES - label: Functions | _APP_FUNCTIONS_RUNTIMES - defaultValue: node-18.0 - description: |- - This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space. - To enable/activate this option, pass a list of allowed environments separated by a comma. - Currently, supported environments are: node-14.5, node-16.0, node-18.0, php-8.0, php-8.1, ruby-3.0, ruby-3.1, python-3.8, python-3.9, python-3.10, deno-1.21, deno-1.24, dart-2.15, dart-2.16, dart-2.17, dotnet-3.1, dotnet-6.0, java-8.0, java-11.0, java-17.0, java-18.0, swift-5.5, kotlin-1.6, cpp-17.0 - - id: "$$secret__app_executor_secret" - name: _APP_EXECUTOR_SECRET - label: Functions | _APP_EXECUTOR_SECRET - defaultValue: "$$generate_hex(16)" - description: - The secret key used by Appwrite to communicate with the function - executor. - - id: "$$config__app_executor_host" - name: _APP_EXECUTOR_HOST - label: "" - defaultValue: http://$$id-executor/v1 - description: "" - - id: "$$config__app_logging_provider" - name: _APP_LOGGING_PROVIDER - label: General | _APP_LOGGING_PROVIDER - defaultValue: "" - description: - This variable allows you to enable logging errors to 3rd party providers. - This value is empty by default, to enable the logger set the value to one of - 'sentry', 'raygun', 'appsignal', 'logowl' - - id: "$$config__app_logging_config" - name: _APP_LOGGING_CONFIG - label: General | _APP_LOGGING_CONFIG - defaultValue: "" - description: - This variable configures authentication to 3rd party error logging - providers. If using Sentry, this should be 'SENTRY_API_KEY;SENTRY_APP_ID'. If - using Raygun, this should be Raygun API key. If using AppSignal, this should - be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket. - - id: "$$config__app_statsd_host" - name: _APP_STATSD_HOST - label: "" - defaultValue: "$$id-telegraf" - description: "" - - id: "$$config__app_statsd_port" - name: _APP_STATSD_PORT - label: StatsD | _APP_STATSD_PORT - defaultValue: "8125" - description: StatsD server TCP port. - - id: "$$config__app_maintenance_interval" - name: _APP_MAINTENANCE_INTERVAL - label: Functions | _APP_MAINTENANCE_INTERVAL - defaultValue: "86400" - description: - Interval value containing the number of seconds that the Appwrite - maintenance process should wait before executing system cleanups and optimizations. - The default value is 86400 seconds (1 day). - - id: "$$config__app_maintenance_retention_execution" - name: _APP_MAINTENANCE_RETENTION_EXECUTION - label: Functions | _APP_MAINTENANCE_RETENTION_EXECUTION - defaultValue: "1209600" - description: - The maximum duration (in seconds) upto which to retain execution - logs. The default value is 1209600 seconds (14 days). - - id: "$$config__app_maintenance_retention_cache" - name: _APP_MAINTENANCE_RETENTION_CACHE - label: Functions | _APP_MAINTENANCE_RETENTION_CACHE - defaultValue: "2592000" - description: - The maximum duration (in seconds) upto which to retain cached files. - The default value is 2592000 seconds (30 days). - - id: "$$config__app_maintenance_retention_abuse" - name: _APP_MAINTENANCE_RETENTION_ABUSE - label: Functions | _APP_MAINTENANCE_RETENTION_ABUSE - defaultValue: "86400" - description: - The maximum duration (in seconds) upto which to retain abuse logs. - The default value is 86400 seconds (1 day). - - id: "$$config__app_maintenance_retention_audit" - name: _APP_MAINTENANCE_RETENTION_AUDIT - label: Functions | _APP_MAINTENANCE_RETENTION_AUDIT - defaultValue: "1209600" - description: - The maximum duration (in seconds) upto which to retain audit logs. - The default value is 1209600 seconds (14 days). - - id: "$$config__app_sms_provider" - name: _APP_SMS_PROVIDER - label: Phone | _APP_SMS_PROVIDER - defaultValue: "" - description: - "Provider used for delivering SMS for Phone authentication. Use the - following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. Available providers - are twilio, text-magic, telesign, msg91, and vonage." - - id: "$$config__app_sms_from" - name: _APP_SMS_FROM - label: Phone | _APP_SMS_FROM - defaultValue: "" - description: - Phone number used for sending out messages. Must start with a leading - '+' and maximum of 15 digits without spaces (+123456789). - - id: "$$config__app_functions_inactive_threshold" - name: _APP_FUNCTIONS_INACTIVE_THRESHOLD - label: Functions | _APP_FUNCTIONS_INACTIVE_THRESHOLD - defaultValue: "60" - description: - The minimum time a function can be inactive before it's container - is shutdown and put to sleep. The default value is 60 seconds - - id: "$$config_open_runtimes_network" - name: OPEN_RUNTIMES_NETWORK - label: "" - defaultValue: "$$generate_network" - description: "" - - id: "$$config_dockerhub_pull_username" - name: DOCKERHUB_PULL_USERNAME - label: Functions | DOCKERHUB_PULL_USERNAME - defaultValue: "" - description: - The username for hub.docker.com. This variable is used to pull images - from hub.docker.com. - - id: "$$secret_dockerhub_pull_password" - name: DOCKERHUB_PULL_PASSWORD - label: Functions | DOCKERHUB_PULL_PASSWORD - defaultValue: "" - description: - The password for hub.docker.com. This variable is used to pull images - from hub.docker.com. - - id: "$$config__app_usage_timeseries_interval" - name: _APP_USAGE_TIMESERIES_INTERVAL - label: General | _APP_USAGE_TIMESERIES_INTERVAL - defaultValue: "30" - description: - Interval value containing the number of seconds that the Appwrite - usage process should wait before aggregating stats and syncing it to mariadb - from InfluxDB. The default value is 30 seconds. - - id: "$$config__app_usage_database_interval" - name: _APP_USAGE_DATABASE_INTERVAL - label: General | _APP_USAGE_DATABASE_INTERVAL - defaultValue: "900" - description: - Interval value containing the number of seconds that the Appwrite - usage process should wait before aggregating stats from data in Appwrite Database. - The default value is 15 minutes. -- templateVersion: 1.0.0 - defaultVersion: latest - documentation: https://docs.weblate.org/en/latest/admin/install/docker.html - description: "A copylefted libre software web-based continuous localization system." - type: weblate - name: Weblate - labels: - - translate - - localization - services: - $$id: - name: Weblate - depends_on: - - $$id-postgresql - - $$id-redis - image: "weblate/weblate:$$core_version" - volumes: - - "$$id-data:/app/data" - environment: - - WEBLATE_SITE_DOMAIN=$$config_weblate_site_domain - - WEBLATE_ADMIN_PASSWORD=$$secret_weblate_admin_password - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_DATABASE=$$config_postgres_db - - POSTGRES_HOST=$$id-postgresql - - POSTGRES_PORT=5432 - - REDIS_HOST=$$id-redis - ports: - - "8080" - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:14-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - $$id-redis: - name: Redis - depends_on: [] - image: "redis:7-alpine" - volumes: - - "$$id-redis-data:/data" - environment: [] - ports: [] - variables: - - id: $$config_weblate_site_domain - name: WEBLATE_SITE_DOMAIN - label: Weblate Domain - defaultValue: $$generate_domain - description: "" - - id: $$secret_weblate_admin_password - name: WEBLATE_ADMIN_PASSWORD - label: Weblate Admin Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgres_user - main: $$id-postgresql - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - main: $$id-postgresql - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgres_db - main: $$id-postgresql - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: weblate - description: "" -- templateVersion: 1.0.0 - defaultVersion: 2023.01.15-52d41559 - documentation: https://docs.searxng.org/ - type: searxng - name: SearXNG - description: "Free internet metasearch engine which aggregates results from more than 70 search services." - services: - $$id: - name: SearXNG - depends_on: - - $$id-redis - image: "searxng/searxng:$$core_version" - volumes: - - "$$id-searxng:/etc/searxng" - environment: - - SEARXNG_BASE_URL=$$config_searxng_base_url - ports: - - "8080" - cap_drop: - - ALL - cap_add: - - CHOWN - - SETGID - - SETUID - - DAC_OVERRIDE - files: - - location: /etc/searxng/settings.yml - content: |2- - - # see https://docs.searxng.org/admin/engines/settings.html#use-default-settings - use_default_settings: true - server: - secret_key: $$secret_secret_key - limiter: true - image_proxy: true - ui: - static_use_hash: true - redis: - url: redis://:$$secret_redis_password@$$id-redis:6379/0 - $$id-redis: - name: Redis - command: >- - redis-server --requirepass $$secret_redis_password --save "" - --appendonly "no" - depends_on: [] - image: "redis:7-alpine" - volumes: - - "$$id-redis-data:/data" - environment: - - REDIS_PASSWORD=$$secret_redis_password - ports: [] - cap_drop: - - ALL - cap_add: - - SETGID - - SETUID - - DAC_OVERRIDE - variables: - - id: $$config_searxng_base_url - name: SEARXNG_BASE_URL - label: SearXNG Base URL - defaultValue: $$generate_fqdn - description: "" - - id: $$secret_secret_key - name: SECRET_KEY - label: Secret Key - defaultValue: $$generate_hex(64) - description: "" - - id: $$secret_redis_password - name: REDIS_PASSWORD - label: Redis Password - defaultValue: $$generate_password - description: "" -- templateVersion: 1.0.0 - defaultVersion: v3.0.2 - documentation: https://glitchtip.com/documentation - type: glitchtip - name: GlitchTip - description: "Simple, open source error tracking." - labels: - - sentry - - bugsnag - services: - $$id: - name: GlitchTip - depends_on: - - $$id-postgresql - - $$id-redis - image: "glitchtip/glitchtip:$$core_version" - volumes: [] - environment: - - PORT=$$config_port - - GLITCHTIP_DOMAIN=$$config_glitchtip_domain - - SECRET_KEY=$$secret_secret_key - - DATABASE_URL=$$secret_database_url - - REDIS_URL=$$secret_redis_url - - DEFAULT_FROM_EMAIL=$$config_default_from_email - - EMAIL_URL=$$secret_email_url - - EMAIL_HOST=$$config_email_host - - EMAIL_PORT=$$config_email_port - - EMAIL_HOST_USER=$$config_email_host_user - - EMAIL_HOST_PASSWORD=$$secret_email_host_password - - EMAIL_USE_TLS=$$config_email_use_tls - - EMAIL_USE_SSL=$$config_email_use_ssl - - EMAIL_BACKEND=$$config_email_backend - - MAILGUN_API_KEY=$$secret_mailgun_api_key - - SENDGRID_API_KEY=$$secret_sendgrid_api_key - - ENABLE_OPEN_USER_REGISTRATION=$$config_enable_open_user_registration - - DJANGO_SUPERUSER_EMAIL=$$config_django_superuser_email - - DJANGO_SUPERUSER_PASSWORD=$$secret_django_superuser_password - - DJANGO_SUPERUSER_USERNAME=$$config_django_superuser_username - - CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency - ports: - - "8000" - $$id-worker: - name: Celery Worker - command: ./bin/run-celery-with-beat.sh - depends_on: - - $$id-postgresql - - $$id-redis - image: "glitchtip/glitchtip:$$core_version" - environment: - - GLITCHTIP_DOMAIN=$$config_glitchtip_domain - - SECRET_KEY=$$secret_secret_key - - DATABASE_URL=$$secret_database_url - - REDIS_URL=$$secret_redis_url - - DEFAULT_FROM_EMAIL=$$config_default_from_email - - EMAIL_URL=$$secret_email_url - - CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency - ports: [] - $$id-migrate: - exclude: true - name: Migrate - command: ./manage.py migrate - depends_on: - - $$id-postgresql - - $$id-redis - image: "glitchtip/glitchtip:$$core_version" - environment: - - GLITCHTIP_DOMAIN=$$config_glitchtip_domain - - SECRET_KEY=$$secret_secret_key - - DATABASE_URL=$$secret_database_url - - REDIS_URL=$$secret_redis_url - - DEFAULT_FROM_EMAIL=$$config_default_from_email - - EMAIL_URL=$$secret_email_url - ports: [] - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:14-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - $$id-redis: - name: Redis - depends_on: [] - image: "redis:7-alpine" - volumes: - - "$$id-postgresql-redis-data:/data" - environment: [] - ports: [] - variables: - - id: $$config_django_superuser_username - name: DJANGO_SUPERUSER_USERNAME - label: Django Superuser Username - defaultValue: $$generate_username - description: "" - - id: $$secret_django_superuser_password - name: DJANGO_SUPERUSER_PASSWORD - label: Django Superuser Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_port - name: PORT - label: GlitchTip Port - defaultValue: "8000" - description: "" - - id: $$config_celery_worker_concurrency - main: $$id-worker - name: CELERY_WORKER_CONCURRENCY - label: Celery Worker Concurrency - defaultValue: "2" - description: "" - - id: $$config_glitchtip_domain - name: GLITCHTIP_DOMAIN - label: GlitchTip Domain - defaultValue: $$generate_fqdn - description: "" - - id: $$secret_email_url - name: EMAIL_URL - label: SMTP Email URL - defaultValue: >- - smtp://$$config_email_host_user:$$secret_email_host_password@$$config_email_host:$$config_email_port - description: "" - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL for PostgreSQL - defaultValue: >- - postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$secret_redis_url - name: REDIS_URL - label: Redis URL - defaultValue: "redis://$$id-redis:6379/0" - description: "" - - id: $$config_default_from_email - name: DEFAULT_FROM_EMAIL - label: Default Email Address - defaultValue: noreply@example.com - description: "" - - id: $$config_email_host - name: EMAIL_HOST - label: Email SMTP Host - defaultValue: "" - description: "" - - id: $$config_email_port - name: EMAIL_PORT - label: Email SMTP Port - defaultValue: "25" - description: "" - - id: $$config_email_host_user - name: EMAIL_HOST_USER - label: Email SMTP User - defaultValue: "" - description: "" - - id: $$secret_email_host_password - name: EMAIL_HOST_PASSWORD - label: Email SMTP Password - defaultValue: "" - description: "" - - id: $$config_email_use_tls - name: EMAIL_USE_TLS - label: Email Use TLS - defaultValue: "false" - description: "" - - id: $$config_email_use_ssl - name: EMAIL_USE_SSL - label: Email Use SSL - defaultValue: "false" - description: "" - - id: $$secret_email_smtp_password - name: EMAIL_SMTP_PASSWORD - label: SMTP Password - defaultValue: "" - description: "" - - id: $$config_email_backend - name: EMAIL_BACKEND - label: Email Backend - defaultValue: "" - description: "" - - id: $$secret_mailgun_api_key - name: MAILGUN_API_KEY - label: Mailgun API Key - defaultValue: "" - description: "" - showOnConfiguration: true - - id: $$secret_sendgrid_api_key - name: SENDGRID_API_KEY - label: Sendgrid API Key - defaultValue: "" - description: "" - showOnConfiguration: true - - id: $$config_enable_open_user_registration - name: ENABLE_OPEN_USER_REGISTRATION - label: Enable Open User Registration - defaultValue: "true" - description: "" - - id: $$config_django_superuser_email - name: DJANGO_SUPERUSER_EMAIL - label: Django Superuser Email - defaultValue: noreply@example.com - description: "" - - id: $$config_postgres_user - main: $$id-postgresql - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - main: $$id-postgresql - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - - id: $$config_postgres_db - main: $$id-postgresql - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: glitchtip - description: "" -- templateVersion: 1.0.0 - defaultVersion: v2.16.1 - documentation: https://hasura.io/docs/latest/index/ - type: hasura - name: Hasura - description: "Instant realtime GraphQL APIs on any Postgres application, existing or new." - labels: - - graphql - - database - services: - $$id: - name: Hasura - depends_on: - - $$id-postgresql - image: "hasura/graphql-engine:$$core_version" - volumes: [] - environment: - - HASURA_GRAPHQL_ENABLE_CONSOLE=$$config_hasura_graphql_enable_console - - >- - HASURA_GRAPHQL_METADATA_DATABASE_URL=$$secret_hasura_graphql_metadata_database_url - - HASURA_GRAPHQL_ADMIN_SECRET=$$secret_hasura_graphql_admin_secret - ports: - - "8080" - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:12-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - variables: - - id: $$config_hasura_graphql_enable_console - name: HASURA_GRAPHQL_ENABLE_CONSOLE - label: Enable Hasura Console - defaultValue: "true" - description: "" - - id: $$secret_hasura_graphql_metadata_database_url - name: HASURA_GRAPHQL_METADATA_DATABASE_URL - label: Hasura Metadata Database URL - defaultValue: >- - postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$secret_hasura_graphql_admin_secret - name: HASURA_GRAPHQL_ADMIN_SECRET - label: Hasura Admin Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgres_user - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - - id: $$config_postgres_db - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: hasura - description: "" -- templateVersion: 1.0.0 - defaultVersion: postgresql-v1.39.5 - documentation: https://umami.is/docs/getting-started - type: umami-postgresql - name: Umami - subname: (PostgreSQL) - description: >- - A simple, easy to use, self-hosted web analytics solution. - services: - $$id: - name: Umami - depends_on: - - $$id-postgresql - image: "ghcr.io/umami-software/umami:$$core_version" - volumes: [] - environment: - - ADMIN_PASSWORD=$$secret_admin_password - - DATABASE_URL=$$secret_database_url - - DATABASE_TYPE=$$config_database_type - - HASH_SALT=$$secret_hash_salt - ports: - - "3000" - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:12-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - files: - - location: /docker-entrypoint-initdb.d/schema.postgresql.sql - content: |2- - - -- CreateTable - CREATE TABLE "account" ( - "user_id" SERIAL NOT NULL, - "username" VARCHAR(255) NOT NULL, - "password" VARCHAR(60) NOT NULL, - "is_admin" BOOLEAN NOT NULL DEFAULT false, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - - PRIMARY KEY ("user_id") - ); - - -- CreateTable - CREATE TABLE "event" ( - "event_id" SERIAL NOT NULL, - "website_id" INTEGER NOT NULL, - "session_id" INTEGER NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "url" VARCHAR(500) NOT NULL, - "event_type" VARCHAR(50) NOT NULL, - "event_value" VARCHAR(50) NOT NULL, - - PRIMARY KEY ("event_id") - ); - - -- CreateTable - CREATE TABLE "pageview" ( - "view_id" SERIAL NOT NULL, - "website_id" INTEGER NOT NULL, - "session_id" INTEGER NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "url" VARCHAR(500) NOT NULL, - "referrer" VARCHAR(500), - - PRIMARY KEY ("view_id") - ); - - -- CreateTable - CREATE TABLE "session" ( - "session_id" SERIAL NOT NULL, - "session_uuid" UUID NOT NULL, - "website_id" INTEGER NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "hostname" VARCHAR(100), - "browser" VARCHAR(20), - "os" VARCHAR(20), - "device" VARCHAR(20), - "screen" VARCHAR(11), - "language" VARCHAR(35), - "country" CHAR(2), - - PRIMARY KEY ("session_id") - ); - - -- CreateTable - CREATE TABLE "website" ( - "website_id" SERIAL NOT NULL, - "website_uuid" UUID NOT NULL, - "user_id" INTEGER NOT NULL, - "name" VARCHAR(100) NOT NULL, - "domain" VARCHAR(500), - "share_id" VARCHAR(64), - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - - PRIMARY KEY ("website_id") - ); - - -- CreateIndex - CREATE UNIQUE INDEX "account.username_unique" ON "account"("username"); - - -- CreateIndex - CREATE INDEX "event_created_at_idx" ON "event"("created_at"); - - -- CreateIndex - CREATE INDEX "event_session_id_idx" ON "event"("session_id"); - - -- CreateIndex - CREATE INDEX "event_website_id_idx" ON "event"("website_id"); - - -- CreateIndex - CREATE INDEX "pageview_created_at_idx" ON "pageview"("created_at"); - - -- CreateIndex - CREATE INDEX "pageview_session_id_idx" ON "pageview"("session_id"); - - -- CreateIndex - CREATE INDEX "pageview_website_id_created_at_idx" ON "pageview"("website_id", "created_at"); - - -- CreateIndex - CREATE INDEX "pageview_website_id_idx" ON "pageview"("website_id"); - - -- CreateIndex - CREATE INDEX "pageview_website_id_session_id_created_at_idx" ON "pageview"("website_id", "session_id", "created_at"); - - -- CreateIndex - CREATE UNIQUE INDEX "session.session_uuid_unique" ON "session"("session_uuid"); - - -- CreateIndex - CREATE INDEX "session_created_at_idx" ON "session"("created_at"); - - -- CreateIndex - CREATE INDEX "session_website_id_idx" ON "session"("website_id"); - - -- CreateIndex - CREATE UNIQUE INDEX "website.website_uuid_unique" ON "website"("website_uuid"); - - -- CreateIndex - CREATE UNIQUE INDEX "website.share_id_unique" ON "website"("share_id"); - - -- CreateIndex - CREATE INDEX "website_user_id_idx" ON "website"("user_id"); - - -- AddForeignKey - ALTER TABLE "event" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "event" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "pageview" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "pageview" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "session" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "website" ADD FOREIGN KEY ("user_id") REFERENCES "account"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; - - insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true); - variables: - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL for PostgreSQL - defaultValue: >- - postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$secret_hash_salt - name: HASH_SALT - label: Hash Salt - defaultValue: $$generate_hex(64) - description: "" - - id: $$config_database_type - name: DATABASE_TYPE - label: Database Type - defaultValue: "postgresql" - description: "" - - id: $$config_postgres_user - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - - id: $$config_postgres_db - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: umami - description: "" - - id: $$secret_admin_password - name: ADMIN_PASSWORD - label: Initial Admin Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true -- templateVersion: 1.0.0 - ignore: true - defaultVersion: postgresql-v1.39.5 - documentation: https://umami.is/docs/getting-started - type: umami - name: Umami - subname: (PostgreSQL) - description: >- - A simple, easy to use, self-hosted web analytics solution. - services: - $$id: - name: Umami - depends_on: - - $$id-postgresql - image: "ghcr.io/umami-software/umami:$$core_version" - volumes: [] - environment: - - ADMIN_PASSWORD=$$secret_admin_password - - DATABASE_URL=$$secret_database_url - - DATABASE_TYPE=$$config_database_type - - HASH_SALT=$$secret_hash_salt - ports: - - "3000" - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:12-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - ports: [] - files: - - location: /docker-entrypoint-initdb.d/schema.postgresql.sql - content: |2- - - -- CreateTable - CREATE TABLE "account" ( - "user_id" SERIAL NOT NULL, - "username" VARCHAR(255) NOT NULL, - "password" VARCHAR(60) NOT NULL, - "is_admin" BOOLEAN NOT NULL DEFAULT false, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - - PRIMARY KEY ("user_id") - ); - - -- CreateTable - CREATE TABLE "event" ( - "event_id" SERIAL NOT NULL, - "website_id" INTEGER NOT NULL, - "session_id" INTEGER NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "url" VARCHAR(500) NOT NULL, - "event_type" VARCHAR(50) NOT NULL, - "event_value" VARCHAR(50) NOT NULL, - - PRIMARY KEY ("event_id") - ); - - -- CreateTable - CREATE TABLE "pageview" ( - "view_id" SERIAL NOT NULL, - "website_id" INTEGER NOT NULL, - "session_id" INTEGER NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "url" VARCHAR(500) NOT NULL, - "referrer" VARCHAR(500), - - PRIMARY KEY ("view_id") - ); - - -- CreateTable - CREATE TABLE "session" ( - "session_id" SERIAL NOT NULL, - "session_uuid" UUID NOT NULL, - "website_id" INTEGER NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "hostname" VARCHAR(100), - "browser" VARCHAR(20), - "os" VARCHAR(20), - "device" VARCHAR(20), - "screen" VARCHAR(11), - "language" VARCHAR(35), - "country" CHAR(2), - - PRIMARY KEY ("session_id") - ); - - -- CreateTable - CREATE TABLE "website" ( - "website_id" SERIAL NOT NULL, - "website_uuid" UUID NOT NULL, - "user_id" INTEGER NOT NULL, - "name" VARCHAR(100) NOT NULL, - "domain" VARCHAR(500), - "share_id" VARCHAR(64), - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - - PRIMARY KEY ("website_id") - ); - - -- CreateIndex - CREATE UNIQUE INDEX "account.username_unique" ON "account"("username"); - - -- CreateIndex - CREATE INDEX "event_created_at_idx" ON "event"("created_at"); - - -- CreateIndex - CREATE INDEX "event_session_id_idx" ON "event"("session_id"); - - -- CreateIndex - CREATE INDEX "event_website_id_idx" ON "event"("website_id"); - - -- CreateIndex - CREATE INDEX "pageview_created_at_idx" ON "pageview"("created_at"); - - -- CreateIndex - CREATE INDEX "pageview_session_id_idx" ON "pageview"("session_id"); - - -- CreateIndex - CREATE INDEX "pageview_website_id_created_at_idx" ON "pageview"("website_id", "created_at"); - - -- CreateIndex - CREATE INDEX "pageview_website_id_idx" ON "pageview"("website_id"); - - -- CreateIndex - CREATE INDEX "pageview_website_id_session_id_created_at_idx" ON "pageview"("website_id", "session_id", "created_at"); - - -- CreateIndex - CREATE UNIQUE INDEX "session.session_uuid_unique" ON "session"("session_uuid"); - - -- CreateIndex - CREATE INDEX "session_created_at_idx" ON "session"("created_at"); - - -- CreateIndex - CREATE INDEX "session_website_id_idx" ON "session"("website_id"); - - -- CreateIndex - CREATE UNIQUE INDEX "website.website_uuid_unique" ON "website"("website_uuid"); - - -- CreateIndex - CREATE UNIQUE INDEX "website.share_id_unique" ON "website"("share_id"); - - -- CreateIndex - CREATE INDEX "website_user_id_idx" ON "website"("user_id"); - - -- AddForeignKey - ALTER TABLE "event" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "event" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "pageview" ADD FOREIGN KEY ("session_id") REFERENCES "session"("session_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "pageview" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "session" ADD FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE; - - -- AddForeignKey - ALTER TABLE "website" ADD FOREIGN KEY ("user_id") REFERENCES "account"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; - - insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true); - variables: - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL for PostgreSQL - defaultValue: >- - postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db - description: "" - - id: $$secret_hash_salt - name: HASH_SALT - label: Hash Salt - defaultValue: $$generate_hex(64) - description: "" - - id: $$config_database_type - name: DATABASE_TYPE - label: Database Type - defaultValue: "postgresql" - description: "" - - id: $$config_postgres_user - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - - id: $$config_postgres_db - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: umami - description: "" - - id: $$secret_admin_password - name: ADMIN_PASSWORD - label: Initial Admin Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true -- templateVersion: 1.0.0 - defaultVersion: v0.30.5 - documentation: https://docs.meilisearch.com/learn/getting_started/quick_start.html - type: meilisearch - name: MeiliSearch - description: >- - A lightning Fast, Ultra Relevant, and Typo-Tolerant Search Engine. - services: - $$id: - name: MeiliSearch - documentation: https://docs.meilisearch.com/ - depends_on: [] - image: "getmeili/meilisearch:$$core_version" - volumes: - - "$$id-datams:/meili_data/data.ms" - - "$$id-data:/meili_data" - - "$$id-snapshot:/snapshot" - - "$$id-dump:/dumps" - environment: - - MEILI_MASTER_KEY=$$secret_meili_master_key - ports: - - "7700" - variables: - - id: $$secret_meili_master_key - name: MEILI_MASTER_KEY - label: Master Key - defaultValue: $$generate_hex(64) - description: "" - showOnConfiguration: true -- templateVersion: 1.0.0 - ignore: true - defaultVersion: 5.30.0 - documentation: https://docs.ghost.org - arch: amd64 - type: ghost-mariadb - name: Ghost - subname: (MariaDB) - description: >- - Free and open source blogging platform. - labels: - - cms - - blog - services: - $$id: - name: Ghost - depends_on: - - $$id-mariadb - image: "bitnami/ghost:$$core_version" - volumes: - - "$$id-ghost:/bitnami/ghost" - environment: - - url=$$config_url - - GHOST_HOST=$$config_ghost_host - - GHOST_ENABLE_HTTPS=$$config_ghost_enable_https - - GHOST_EMAIL=$$config_ghost_email - - GHOST_PASSWORD=$$secret_ghost_password - - GHOST_DATABASE_HOST=$$config_ghost_database_host - - GHOST_DATABASE_USER=$$config_mariadb_user - - GHOST_DATABASE_PASSWORD=$$secret_ghost_database_password - - GHOST_DATABASE_NAME=$$config_mariadb_database - - GHOST_DATABASE_PORT_NUMBER=3306 - ports: - - "2368" - $$id-mariadb: - name: MariaDB - depends_on: [] - image: "bitnami/mariadb:latest" - volumes: - - "$$id-mariadb:/bitnami/mariadb" - environment: - - MARIADB_USER=$$config_mariadb_user - - MARIADB_PASSWORD=$$secret_mariadb_password - - MARIADB_DATABASE=$$config_mariadb_database - - MARIADB_ROOT_USER=$$config_mariadb_root_user - - MARIADB_ROOT_PASSWORD=$$secret_mariadb_root_password - ports: [] - variables: - - id: $$config_url - name: url - label: URL - defaultValue: $$generate_fqdn - description: "" - - id: $$config_ghost_host - name: GHOST_HOST - label: Ghost Host - defaultValue: $$generate_domain - description: "" - - id: $$config_ghost_enable_https - name: GHOST_ENABLE_HTTPS - label: Ghost Enable HTTPS - defaultValue: "no" - description: "" - - id: $$config_ghost_email - name: GHOST_EMAIL - label: Ghost Default Email - defaultValue: admin@example.com - description: "" - - id: $$secret_ghost_password - name: GHOST_PASSWORD - label: Ghost Default Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_ghost_database_host - name: GHOST_DATABASE_HOST - label: Ghost Database Host - defaultValue: $$id-mariadb - description: "" - - id: $$config_ghost_database_user - name: GHOST_DATABASE_USER - label: MariaDB User - defaultValue: $$config_mariadb_user - description: "" - - id: $$secret_ghost_database_password - name: GHOST_DATABASE_PASSWORD - label: MariaDB Password - defaultValue: $$secret_mariadb_password - description: "" - - id: $$config_ghost_database_name - name: GHOST_DATABASE_NAME - label: MariaDB Database - defaultValue: $$config_mariadb_database - description: "" - - id: $$config_mariadb_user - name: MARIADB_USER - label: MariaDB User - defaultValue: $$generate_username - description: "" - - id: $$secret_mariadb_password - name: MARIADB_PASSWORD - label: MariaDB Password - defaultValue: $$generate_password - description: "" - - id: $$config_mariadb_database - name: MARIADB_DATABASE - label: MariaDB Database - defaultValue: ghost - description: "" - - id: $$config_mariadb_root_user - name: MARIADB_ROOT_USER - label: MariaDB Root User - defaultValue: $$generate_username - description: "" - - id: $$secret_mariadb_root_password - name: MARIADB_ROOT_PASSWORD - label: MariaDB Root Password - defaultValue: $$generate_password - description: "" -- templateVersion: 1.0.0 - defaultVersion: 5.30.0 - documentation: https://docs.ghost.org - type: ghost-only - name: Ghost - subname: (without Database) - description: >- - Free and open source blogging platform. - services: - $$id: - name: Ghost - image: "ghost:$$core_version" - volumes: - - "$$id-ghost:/var/lib/ghost/content" - environment: - - url=$$config_url - - database__client=$$config_database__client - - database__connection__host=$$config_database__connection__host - - database__connection__user=$$config_database__connection__user - - database__connection__password=$$secret_database__connection__password - - database__connection__database=$$config_database__connection__database - ports: - - "2368" - variables: - - id: $$config_url - name: url - label: URL - defaultValue: $$generate_fqdn - description: "" - - id: $$config_database__client - name: database__client - label: Database Client - defaultValue: mysql - description: "" - required: true - - id: $$config_database__connection__host - name: database__connection__host - label: Database Host - defaultValue: "" - description: "" - required: true - placeholder: "db.coolify.io" - - id: $$config_database__connection__user - name: database__connection__user - label: Database User - defaultValue: "" - description: "" - placeholder: "ghost" - required: true - - id: $$secret_database__connection__password - name: database__connection__password - label: Database Password - defaultValue: "" - description: "" - placeholder: "superSecretP4ssword" - showOnConfiguration: true - required: true - - id: $$config_database__connection__database - name: database__connection__database - label: Database Name - defaultValue: "" - description: "" - placeholder: "ghost_db" - required: true -- templateVersion: 1.0.0 - defaultVersion: 5.30.0 - documentation: https://docs.ghost.org - type: ghost-mysql - name: Ghost - subname: (MySQL) - description: >- - Ghost is a free and open source blogging platform. - services: - $$id: - name: Ghost - depends_on: - - $$id-mysql - image: "ghost:$$core_version" - volumes: - - "$$id-ghost:/var/lib/ghost/content" - environment: - - url=$$config_url - - database__client=$$config_database__client - - database__connection__host=$$config_database__connection__host - - database__connection__user=$$config_mysql_user - - database__connection__password=$$secret_mysql_password - - database__connection__database=$$config_mysql_database - ports: - - "2368" - $$id-mysql: - name: MySQL - depends_on: [] - image: "mysql:8.0" - volumes: - - "$$id-mysql:/var/lib/mysql" - environment: - - MYSQL_USER=$$config_mysql_user - - MYSQL_PASSWORD=$$secret_mysql_password - - MYSQL_DATABASE=$$config_mysql_database - - MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password - ports: [] - variables: - - id: $$config_url - name: url - label: URL - defaultValue: $$generate_fqdn - description: "" - - id: $$config_database__client - name: database__client - label: Database Client - defaultValue: mysql - description: "" - readOnly: true - - id: $$config_database__connection__host - name: database__connection__host - label: Database Host - defaultValue: $$id-mysql - description: "" - - id: $$config_mysql_user - main: $$id-mysql - name: MYSQL_USER - label: MySQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_mysql_password - main: $$id-mysql - name: MYSQL_PASSWORD - label: MySQL Password - defaultValue: $$generate_password - description: "" - - id: $$config_mysql_database - main: $$id-mysql - name: MYSQL_DATABASE - label: MySQL Database - defaultValue: ghost - description: "" - - id: $$secret_mysql_root_password - name: MYSQL_ROOT_PASSWORD - label: MySQL Root Password - defaultValue: $$generate_password - description: "" -- templateVersion: 1.0.0 - defaultVersion: php8.2 - documentation: https://wordpress.org/ - type: wordpress - name: WordPress - subname: (MySQL) - description: A content management system based on PHP. - labels: - - "wordpress" - - "php" - - "cms" - services: - $$id: - name: WordPress - depends_on: - - $$id-mysql - image: "wordpress:$$core_version" - volumes: - - "$$id-wordpress-data:/var/www/html" - environment: - - WORDPRESS_DB_HOST=$$config_wordpress_db_host - - WORDPRESS_DB_USER=$$config_mysql_user - - WORDPRESS_DB_PASSWORD=$$secret_mysql_password - - WORDPRESS_DB_NAME=$$config_mysql_database - - WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra - ports: - - "80" - $$id-mysql: - name: MySQL - depends_on: [] - image: "bitnami/mysql:5.7" - imageArm: "mysql:8.0" - volumes: - - "$$id-mysql-data:/bitnami/mysql/data" - volumesArm: - - "$$id-mysql-data:/var/lib/mysql" - environment: - - MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password - - MYSQL_ROOT_USER=$$config_mysql_root_user - - MYSQL_DATABASE=$$config_mysql_database - - MYSQL_USER=$$config_mysql_user - - MYSQL_PASSWORD=$$secret_mysql_password - variables: - - id: $$config_wordpress_db_host - name: WORDPRESS_DB_HOST - label: Database Host - defaultValue: $$id-mysql - description: "" - readOnly: true - - id: $$config_wordpress_config_extra - name: WORDPRESS_CONFIG_EXTRA - label: WordPress Config Extra - defaultValue: "" - description: "" - type: "textarea" - placeholder: | - define('WP_DEBUG', true); - define('WP_DEBUG_LOG', true); - define('WP_DEBUG_DISPLAY', false); - @ini_set('display_errors', 0); - - id: $$secret_mysql_root_password - name: MYSQL_ROOT_PASSWORD - label: MySQL Root Password - defaultValue: $$generate_password - description: "" - readOnly: true - - id: $$config_mysql_root_user - name: MYSQL_ROOT_USER - label: MySQL Root User - defaultValue: $$generate_username - description: "" - readOnly: true - - id: $$config_mysql_database - name: MYSQL_DATABASE - label: MySQL Database - defaultValue: wordpress - description: "" - readOnly: true - - id: $$config_mysql_user - name: MYSQL_USER - label: MySQL User - defaultValue: $$generate_username - description: "" - readOnly: true - - id: $$secret_mysql_password - name: MYSQL_PASSWORD - label: MySQL Password - defaultValue: $$generate_password - description: "" - readOnly: true -- templateVersion: 1.0.0 - defaultVersion: php8.2 - documentation: https://wordpress.org/ - type: wordpress-only - name: WordPress - subname: (without DB) - description: A content management system based on PHP. - labels: - - "wordpress" - - "php" - - "cms" - services: - $$id: - name: WordPress - image: "wordpress:$$core_version" - volumes: - - "$$id-wordpress-data:/var/www/html" - environment: - - WORDPRESS_DB_HOST=$$config_wordpress_db_host - - WORDPRESS_DB_PORT=$$config_wordpress_db_port - - WORDPRESS_DB_USER=$$config_wordpress_db_user - - WORDPRESS_DB_PASSWORD=$$secret_wordpress_db_password - - WORDPRESS_DB_NAME=$$config_wordpress_db_name - - WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra - ports: - - "80" - variables: - - id: $$config_wordpress_db_host - name: WORDPRESS_DB_HOST - label: Database Host - defaultValue: "" - description: "" - placeholder: "db.coollabs.io" - required: true - - id: $$config_wordpress_db_port - name: WORDPRESS_DB_PORT - label: Database Port - defaultValue: "" - description: "" - placeholder: "3306" - required: true - - id: $$config_wordpress_db_user - name: WORDPRESS_DB_USER - label: Database User - defaultValue: "" - description: "" - placeholder: "wordpress" - required: true - - id: $$secret_wordpress_db_password - name: WORDPRESS_DB_PASSWORD - label: Database Password - defaultValue: "" - description: "" - placeholder: "supers3cr3tpassw0rd!" - required: true - showOnConfiguration: true - - id: $$config_wordpress_db_name - name: WORDPRESS_DB_NAME - label: Database Name - defaultValue: "" - description: "" - placeholder: "wordpress" - required: true - - id: $$config_wordpress_config_extra - name: WORDPRESS_CONFIG_EXTRA - label: Extra Config - defaultValue: "" - description: "" - type: "textarea" - placeholder: | - define('WP_DEBUG', true); - define('WP_DEBUG_LOG', true); - define('WP_DEBUG_DISPLAY', false); - @ini_set('display_errors', 0); -- templateVersion: 1.0.0 - defaultVersion: 4.9.1 - documentation: https://coder.com/docs/coder-oss/latest - type: vscodeserver - name: VSCode Server - description: >- - Visual Studio Code on a remote server, accessible through the browser. - labels: - - vscode - - ide - services: - $$id: - name: VSCode Server - depends_on: [] - image: "codercom/code-server:$$core_version" - volumes: - - "$$id-vscodeserver-data:/home/coder" - - "$$id-keys-directory:/root/.ssh" - - "$$id-theme-and-plugin-directory:/root/.local/share/code-server" - environment: - - PASSWORD=$$secret_password - ports: - - "8080" - variables: - - id: $$secret_password - name: PASSWORD - label: Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true -- templateVersion: 1.0.0 - defaultVersion: RELEASE.2023-01-12T02-06-16Z - documentation: https://min.io/docs/minio - type: minio - name: MinIO - description: "A cloud storage server compatible with Amazon S3." - labels: - - storage - - s3 - services: - $$id: - name: MinIO - command: "server /data --console-address :9001" - depends_on: [] - image: "minio/minio:$$core_version" - volumes: - - "$$id-minio-data:/data" - - "$$id-data-write:/files" - environment: - - MINIO_SERVER_URL=$$config_coolify_fqdn_minio_console - - MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url - - MINIO_DOMAIN=$$config_minio_domain - - MINIO_ROOT_USER=$$config_minio_root_user - - MINIO_ROOT_PASSWORD=$$secret_minio_root_password - ports: - - "9000" - - "9001" - proxy: - - port: "9000" - domain: $$config_coolify_fqdn_minio_console - - port: "9001" - variables: - - id: $$config_coolify_fqdn_minio_console - name: MINIO_SERVER_URL - label: MinIO Server URL - defaultValue: "" - description: "Specify the URL hostname the MinIO Console should use for connecting to the MinIO Server." - required: true - - id: $$config_minio_browser_redirect_url - name: MINIO_BROWSER_REDIRECT_URL - label: Browser Redirect URL - defaultValue: $$generate_fqdn - description: "" - - id: $$config_minio_domain - name: MINIO_DOMAIN - label: Domain - defaultValue: $$generate_domain - description: "" - - id: $$config_minio_root_user - name: MINIO_ROOT_USER - label: Root User - defaultValue: $$generate_username - description: "" - - id: $$secret_minio_root_password - name: MINIO_ROOT_PASSWORD - label: Root User Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true -- templateVersion: 1.0.0 - defaultVersion: stable - documentation: https://fider.io/docs - type: fider - name: Fider - description: A platform to collect and organize customer feedback. - labels: - - suggestion - - feedback - services: - $$id: - name: Fider - image: "getfider/fider:$$core_version" - depends_on: - - $$id-postgresql - environment: - - BASE_URL=$$config_base_url - - DATABASE_URL=$$secret_database_url - - JWT_SECRET=$$secret_jwt_secret - - EMAIL_NOREPLY=$$config_email_noreply - - EMAIL_MAILGUN_API=$$secret_email_mailgun_api - - EMAIL_MAILGUN_REGION=$$config_email_mailgun_region - - EMAIL_MAILGUN_DOMAIN=$$config_email_mailgun_domain - - EMAIL_SMTP_HOST=$$config_email_smtp_host - - EMAIL_SMTP_PORT=$$config_email_smtp_port - - EMAIL_SMTP_USER=$$config_email_smtp_user - - EMAIL_SMTP_PASSWORD=$$secret_email_smtp_password - - EMAIL_SMTP_ENABLE_STARTTLS=$$config_email_smtp_enable_starttls - ports: - - "3000" - $$id-postgresql: - name: PostgreSQL - depends_on: [] - image: "postgres:12-alpine" - volumes: - - "$$id-postgresql-data:/var/lib/postgresql/data" - environment: - - POSTGRES_USER=$$config_postgres_user - - POSTGRES_PASSWORD=$$secret_postgres_password - - POSTGRES_DB=$$config_postgres_db - variables: - - id: $$config_base_url - name: BASE_URL - label: Base URL - defaultValue: $$generate_fqdn - description: "" - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL for PostgreSQL - defaultValue: >- - postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db?sslmode=disable - description: "" - - id: $$secret_jwt_secret - name: JWT_SECRET - label: JWT Secret - defaultValue: $$generate_hex(64) - description: "" - - id: $$config_email_noreply - name: EMAIL_NOREPLY - label: No Reply Email Address - defaultValue: noreply@example.com - description: "" - - id: $$secret_email_mailgun_api - name: EMAIL_MAILGUN_API - label: Mailgun API Key - defaultValue: "" - description: "" - showOnConfiguration: true - - id: $$config_email_mailgun_region - name: EMAIL_MAILGUN_REGION - label: Mailgun Region - defaultValue: EU - description: "" - - id: $$config_email_mailgun_domain - name: EMAIL_MAILGUN_DOMAIN - label: Mailgun Domain - defaultValue: "" - description: "" - - id: $$config_email_smtp_host - name: EMAIL_SMTP_HOST - label: SMTP Host - defaultValue: "" - description: "" - - id: $$config_email_smtp_port - name: EMAIL_SMTP_PORT - label: SMTP Port - defaultValue: "587" - description: "" - - id: $$config_email_smtp_user - name: EMAIL_SMTP_USER - label: SMTP User - defaultValue: "" - description: "" - - id: $$secret_email_smtp_password - name: EMAIL_SMTP_PASSWORD - label: SMTP Password - defaultValue: "" - description: "" - showOnConfiguration: true - - id: $$config_email_smtp_enable_starttls - name: EMAIL_SMTP_ENABLE_STARTTLS - label: SMTP Enable StartTLS - defaultValue: "false" - description: "" - - id: $$config_postgres_user - name: POSTGRES_USER - label: PostgreSQL User - defaultValue: $$generate_username - description: "" - - id: $$secret_postgres_password - name: POSTGRES_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - - id: $$config_postgres_db - name: POSTGRES_DB - label: PostgreSQL Database - defaultValue: $$generate_username - description: "" -- templateVersion: 1.0.0 - defaultVersion: 0.215.1 - documentation: https://docs.n8n.io - type: n8n - name: n8n.io - description: A free and open node based Workflow Automation Tool. - labels: - - workflow - - automation - - ifttt - - zapier - - nodered - services: - $$id: - name: N8n - depends_on: [] - image: "n8nio/n8n:$$core_version" - volumes: - - "$$id-data:/root/.n8n" - - "$$id-data-write:/files" - - "/var/run/docker.sock:/var/run/docker.sock" - environment: - - WEBHOOK_URL=$$config_webhook_url - ports: - - "5678" - variables: - - id: $$config_webhook_url - name: WEBHOOK_URL - label: Webhook URL - defaultValue: $$generate_fqdn - description: "" -- templateVersion: 1.0.0 - defaultVersion: v1.5.1 - documentation: https://plausible.io/doc/ - arch: amd64 - type: plausibleanalytics - name: Plausible Analytics - description: A lightweight and open-source website analytics tool. - labels: - - analytics - - statistics - - plausible - - gdpr - - no-cookie - - google analytics - services: - $$id: - name: Plausible Analytics - command: >- - sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db - migrate && /entrypoint.sh db init-admin && /entrypoint.sh run" - depends_on: - - $$id-postgresql - - $$id-clickhouse - image: "plausible/analytics:$$core_version" - environment: - - ADMIN_USER_EMAIL=$$config_admin_user_email - - ADMIN_USER_NAME=$$config_admin_user_name - - ADMIN_USER_PWD=$$secret_admin_user_pwd - - BASE_URL=$$config_base_url - - SECRET_KEY_BASE=$$secret_secret_key_base - - DISABLE_AUTH=$$config_disable_auth - - DISABLE_REGISTRATION=$$config_disable_registration - - DATABASE_URL=$$secret_database_url - - CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url - ports: - - "8000" - $$id-postgresql: - name: PostgreSQL - image: "bitnami/postgresql:13" - volumes: - - "$$id-postgresql-data:/bitnami/postgresql" - environment: - - POSTGRESQL_PASSWORD=$$secret_postgresql_password - - POSTGRESQL_USERNAME=$$config_postgresql_username - - POSTGRESQL_DATABASE=$$config_postgresql_database - $$id-clickhouse: - name: Clickhouse - volumes: - - "$$id-clickhouse-data:/var/lib/clickhouse" - image: "clickhouse/clickhouse-server:22.6-alpine" - ulimits: - nofile: - soft: 262144 - hard: 262144 - files: - - location: /etc/clickhouse-server/users.d/logging.xml - content: >- - warningtrue - - location: /etc/clickhouse-server/config.d/logging.xml - content: >- - 00 - - location: /docker-entrypoint-initdb.d/init.query - content: CREATE DATABASE IF NOT EXISTS plausible; - - location: /docker-entrypoint-initdb.d/init-db.sh - content: >- - clickhouse client --queries-file - /docker-entrypoint-initdb.d/init.query - variables: - - id: $$config_base_url - name: BASE_URL - label: Base URL - defaultValue: $$generate_fqdn - description: >- - You must set this to the FQDN of the Plausible Analytics instance. This - is used to generate the links to the Plausible Analytics instance. - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL for PostgreSQL - defaultValue: >- - postgresql://$$config_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$config_postgresql_database - description: "" - - id: $$secret_clickhouse_database_url - name: CLICKHOUSE_DATABASE_URL - label: Database URL for Clickhouse - defaultValue: "http://$$id-clickhouse:8123/plausible" - description: "" - - id: $$config_admin_user_email - name: ADMIN_USER_EMAIL - label: Admin Email Address - defaultValue: admin@example.com - description: This is the admin email. Please change it. - - id: $$config_admin_user_name - name: ADMIN_USER_NAME - label: Admin User Name - defaultValue: $$generate_username - description: This is the admin username. Please change it. - - id: $$secret_admin_user_pwd - name: ADMIN_USER_PWD - label: Admin User Password - defaultValue: $$generate_password - description: This is the admin password. Please change it. - showOnConfiguration: true - - id: $$secret_secret_key_base - name: SECRET_KEY_BASE - label: Secret Key Base - defaultValue: $$generate_hex(64) - description: "" - - id: $$config_disable_auth - name: DISABLE_AUTH - label: Authentication - defaultValue: "false" - description: "" - - id: $$config_disable_registration - name: DISABLE_REGISTRATION - label: Registration - defaultValue: "true" - description: "" - - id: $$config_postgresql_username - main: $$id-postgresql - name: POSTGRESQL_USERNAME - label: PostgreSQL Username - defaultValue: postgresql - description: "" - - id: $$secret_postgresql_password - main: $$id-postgresql - name: POSTGRESQL_PASSWORD - label: PostgreSQL Password - defaultValue: $$generate_password - description: "" - showOnConfiguration: true - - id: $$config_postgresql_database - main: $$id-postgresql - name: POSTGRESQL_DATABASE - label: PostgreSQL Database - defaultValue: plausible - description: "" - - id: $$config_scriptName - name: SCRIPT_NAME - label: Custom Script Name - defaultValue: plausible.js - description: This is the default script name. -- templateVersion: 1.0.0 - defaultVersion: 0.104.2 - documentation: https://docs.nocodb.com - type: nocodb - name: NocoDB - description: >- - Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet. - labels: - - database - - airtable - - spreadsheet - services: - $$id: - name: NocoDB - image: nocodb/nocodb:$$core_version - environment: - - PORT=$$config_port - - NC_DB=$$config_nc_db - - DATABASE_URL=$$secret_database_url - - NC_PUBLIC_URL=$$config_public_url - - NC_AUTH_JWT_SECRET=$$secret_auth_jwt_secret - - NC_SENTRY_DSN=$$secret_sentry_dsn - - >- - NC_CONNECT_TO_EXTERNAL_DB_DISABLED=$$config_connect_to_external_db_disabled - - NC_DISABLE_TELE=$$config_disable_tele - volumes: - - $$id-data:/usr/app/data - ports: - - "8080" - variables: - - id: $$config_nc_db - name: NC_DB - label: Database - defaultValue: "" - description: >- - MySQL, PostgreSQL and MSSQL connection urls supported. If absent: A - local SQLite will be created in root folder. - - id: $$config_port - name: PORT - label: Port - defaultValue: "8080" - description: >- - - - id: $$secret_database_url - name: DATABASE_URL - label: Database URL - defaultValue: "" - description: >- - JDBC URL Format. Can be used instead of NC_DB. Used in 1-Click Heroku - deployment. - - id: $$config_public_url - name: NC_PUBLIC_URL - label: Public URL - defaultValue: "" - description: >- - Used for sending Email invitations. If absent: Best guess from http - request params. - - id: $$secret_auth_jwt_secret - name: NC_AUTH_JWT_SECRET - label: Auth JWT Secret - defaultValue: $$generate_hex(64) - description: >- - JWT secret used for auth and storing other secrets. If absent: A Random - secret will be generated. - - id: $$secret_sentry_dsn - name: NC_SENTRY_DSN - label: Sentry DSN - defaultValue: "" - description: For Sentry monitoring. - - id: $$config_connect_to_external_db_disabled - name: NC_CONNECT_TO_EXTERNAL_DB_DISABLED - label: Disable External Database - defaultValue: "0" - description: Disable Project creation with external database. (Enter "1" to disable). - - id: $$config_disable_tele - name: NC_DISABLE_TELE - label: NocoDB Disable Telemetry - defaultValue: "1" - description: Disable telemetry (Enter "1" to disable). diff --git a/apps/api/nodemon.json b/apps/api/nodemon.json deleted file mode 100644 index 8825a2262..000000000 --- a/apps/api/nodemon.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "watch": [ - "src" - ], - "ignore": [ - "src/**/*.test.ts" - ], - "ext": "ts,mjs,json,graphql", - "exec": "rimraf build && esbuild `find src \\( -name '*.ts' \\)` --platform=node --outdir=build --format=cjs && node build", - "legacyWatch": true -} \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json deleted file mode 100644 index 86f78ab39..000000000 --- a/apps/api/package.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "name": "api", - "description": "Coolify's Fastify API", - "license": "Apache-2.0", - "scripts": { - "db:generate": "prisma generate", - "db:push": "prisma db push && prisma generate", - "db:seed": "prisma db seed", - "db:studio": "prisma studio", - "db:migrate": "COOLIFY_DATABASE_URL=file:../db/migration.db prisma migrate dev --skip-seed --name", - "dev": "nodemon", - "build": "rimraf build && esbuild `find src \\( -name '*.ts' \\)| grep -v client/` --platform=node --outdir=build --format=cjs", - "format": "prettier --write 'src/**/*.{js,ts,json,md}'", - "lint": "prettier --check 'src/**/*.{js,ts,json,md}' && eslint --ignore-path .eslintignore .", - "start": "NODE_ENV=production pnpm prisma migrate deploy && pnpm prisma generate && pnpm prisma db seed && node index.js" - }, - "dependencies": { - "@breejs/ts-worker": "2.0.0", - "@fastify/autoload": "5.7.0", - "@fastify/cookie": "8.3.0", - "@fastify/cors": "8.2.0", - "@fastify/env": "4.2.0", - "@fastify/jwt": "6.5.0", - "@fastify/multipart": "7.4.1", - "@fastify/static": "6.6.0", - "@iarna/toml": "2.2.5", - "@ladjs/graceful": "3.2.1", - "@prisma/client": "4.8.1", - "@sentry/node": "7.30.0", - "@sentry/tracing": "7.30.0", - "axe": "11.2.1", - "bcryptjs": "2.4.3", - "bree": "9.1.3", - "cabin": "11.1.1", - "compare-versions": "5.0.1", - "csv-parse": "5.3.3", - "csvtojson": "2.0.10", - "cuid": "2.1.8", - "dayjs": "1.11.7", - "dockerode": "3.3.4", - "dotenv-extended": "2.9.0", - "execa": "6.1.0", - "fastify": "4.11.0", - "fastify-plugin": "4.3.0", - "fastify-socket.io": "4.0.0", - "generate-password": "1.7.0", - "got": "12.5.3", - "is-ip": "5.0.0", - "is-port-reachable": "4.0.0", - "js-yaml": "4.1.0", - "jsonwebtoken": "9.0.0", - "minimist": "^1.2.7", - "node-forge": "1.3.1", - "node-os-utils": "1.3.7", - "p-all": "4.0.0", - "p-throttle": "5.0.0", - "prisma": "4.8.1", - "public-ip": "6.0.1", - "pump": "3.0.0", - "shell-quote": "^1.7.4", - "socket.io": "4.5.4", - "ssh-config": "4.2.0", - "strip-ansi": "7.0.1", - "unique-names-generator": "4.7.1" - }, - "devDependencies": { - "@types/node": "18.11.18", - "@types/node-os-utils": "1.3.0", - "@typescript-eslint/eslint-plugin": "5.48.1", - "@typescript-eslint/parser": "5.48.1", - "esbuild": "0.16.16", - "eslint": "8.31.0", - "eslint-config-prettier": "8.6.0", - "eslint-plugin-prettier": "4.2.1", - "nodemon": "2.0.20", - "prettier": "2.8.2", - "rimraf": "3.0.2", - "tsconfig-paths": "4.1.2", - "types-fastify-socket.io": "0.0.1", - "typescript": "4.9.4" - }, - "prisma": { - "seed": "node prisma/seed.js" - } -} diff --git a/apps/api/prisma/migrations/20220131142425_init/migration.sql b/apps/api/prisma/migrations/20220131142425_init/migration.sql deleted file mode 100644 index 6607a48b5..000000000 --- a/apps/api/prisma/migrations/20220131142425_init/migration.sql +++ /dev/null @@ -1,443 +0,0 @@ --- CreateTable -CREATE TABLE "Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- CreateTable -CREATE TABLE "User" ( - "id" TEXT NOT NULL PRIMARY KEY, - "email" TEXT NOT NULL, - "type" TEXT NOT NULL, - "password" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- CreateTable -CREATE TABLE "Permission" ( - "id" TEXT NOT NULL PRIMARY KEY, - "userId" TEXT NOT NULL, - "teamId" TEXT NOT NULL, - "permission" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Permission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT "Permission_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "Team" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "databaseId" TEXT, - "serviceId" TEXT, - FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "TeamInvitation" ( - "id" TEXT NOT NULL PRIMARY KEY, - "uid" TEXT NOT NULL, - "email" TEXT NOT NULL, - "teamId" TEXT NOT NULL, - "teamName" TEXT NOT NULL, - "permission" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); - --- CreateTable -CREATE TABLE "Application" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "fqdn" TEXT, - "repository" TEXT, - "configHash" TEXT, - "branch" TEXT, - "buildPack" TEXT, - "projectId" INTEGER, - "port" INTEGER, - "installCommand" TEXT, - "buildCommand" TEXT, - "startCommand" TEXT, - "baseDirectory" TEXT, - "publishDirectory" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "destinationDockerId" TEXT, - "gitSourceId" TEXT, - CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "Secret" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "value" TEXT NOT NULL, - "isBuildSecret" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "applicationId" TEXT NOT NULL, - CONSTRAINT "Secret_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "BuildLog" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT, - "buildId" TEXT NOT NULL, - "line" TEXT NOT NULL, - "time" INTEGER NOT NULL -); - --- CreateTable -CREATE TABLE "Build" ( - "id" TEXT NOT NULL PRIMARY KEY, - "type" TEXT NOT NULL, - "applicationId" TEXT, - "destinationDockerId" TEXT, - "gitSourceId" TEXT, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - "commit" TEXT, - "status" TEXT DEFAULT 'queued', - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- CreateTable -CREATE TABLE "DestinationDocker" ( - "id" TEXT NOT NULL PRIMARY KEY, - "network" TEXT NOT NULL, - "name" TEXT NOT NULL, - "engine" TEXT NOT NULL, - "remoteEngine" BOOLEAN NOT NULL DEFAULT false, - "isCoolifyProxyUsed" BOOLEAN DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- CreateTable -CREATE TABLE "GitSource" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "type" TEXT, - "apiUrl" TEXT, - "htmlUrl" TEXT, - "organization" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "GithubApp" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT, - "appId" INTEGER, - "installationId" INTEGER, - "clientId" TEXT, - "clientSecret" TEXT, - "webhookSecret" TEXT, - "privateKey" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- CreateTable -CREATE TABLE "GitlabApp" ( - "id" TEXT NOT NULL PRIMARY KEY, - "oauthId" INTEGER NOT NULL, - "groupName" TEXT, - "deployKeyId" INTEGER, - "privateSshKey" TEXT, - "publicSshKey" TEXT, - "webhookToken" TEXT, - "appId" TEXT, - "appSecret" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- CreateTable -CREATE TABLE "Database" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "publicPort" INTEGER, - "defaultDatabase" TEXT, - "type" TEXT, - "version" TEXT, - "dbUser" TEXT, - "dbUserPassword" TEXT, - "rootUser" TEXT, - "rootUserPassword" TEXT, - "destinationDockerId" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Database_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "DatabaseSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "databaseId" TEXT NOT NULL, - "isPublic" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "DatabaseSettings_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "Service" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "fqdn" TEXT, - "type" TEXT, - "version" TEXT, - "destinationDockerId" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Service_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "PlausibleAnalytics" ( - "id" TEXT NOT NULL PRIMARY KEY, - "email" TEXT, - "username" TEXT, - "password" TEXT NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "secretKeyBase" TEXT, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "PlausibleAnalytics_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "Minio" ( - "id" TEXT NOT NULL PRIMARY KEY, - "rootUser" TEXT NOT NULL, - "rootUserPassword" TEXT NOT NULL, - "publicPort" INTEGER, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Minio_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "Vscodeserver" ( - "id" TEXT NOT NULL PRIMARY KEY, - "password" TEXT NOT NULL, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Vscodeserver_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "Wordpress" ( - "id" TEXT NOT NULL PRIMARY KEY, - "extraConfig" TEXT, - "tablePrefix" TEXT, - "mysqlUser" TEXT NOT NULL, - "mysqlPassword" TEXT NOT NULL, - "mysqlRootUser" TEXT NOT NULL, - "mysqlRootUserPassword" TEXT NOT NULL, - "mysqlDatabase" TEXT, - "mysqlPublicPort" INTEGER, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_TeamToUser" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_ApplicationToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "Application" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_GitSourceToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "GitSource" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_GithubAppToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "GithubApp" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_GitlabAppToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "GitlabApp" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_DestinationDockerToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "DestinationDocker" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_DatabaseToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "Database" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateTable -CREATE TABLE "_ServiceToTeam" ( - "A" TEXT NOT NULL, - "B" TEXT NOT NULL, - FOREIGN KEY ("A") REFERENCES "Service" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("B") REFERENCES "Team" ("id") ON DELETE CASCADE ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); - --- CreateIndex -CREATE UNIQUE INDEX "User_id_key" ON "User"("id"); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "Application_fqdn_key" ON "Application"("fqdn"); - --- CreateIndex -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); - --- CreateIndex -CREATE UNIQUE INDEX "Secret_name_key" ON "Secret"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "DestinationDocker_network_key" ON "DestinationDocker"("network"); - --- CreateIndex -CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId"); - --- CreateIndex -CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId"); - --- CreateIndex -CREATE UNIQUE INDEX "GithubApp_name_key" ON "GithubApp"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "GitlabApp_oauthId_key" ON "GitlabApp"("oauthId"); - --- CreateIndex -CREATE UNIQUE INDEX "GitlabApp_groupName_key" ON "GitlabApp"("groupName"); - --- CreateIndex -CREATE UNIQUE INDEX "DatabaseSettings_databaseId_key" ON "DatabaseSettings"("databaseId"); - --- CreateIndex -CREATE UNIQUE INDEX "PlausibleAnalytics_serviceId_key" ON "PlausibleAnalytics"("serviceId"); - --- CreateIndex -CREATE UNIQUE INDEX "Minio_serviceId_key" ON "Minio"("serviceId"); - --- CreateIndex -CREATE UNIQUE INDEX "Vscodeserver_serviceId_key" ON "Vscodeserver"("serviceId"); - --- CreateIndex -CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); - --- CreateIndex -CREATE UNIQUE INDEX "_TeamToUser_AB_unique" ON "_TeamToUser"("A", "B"); - --- CreateIndex -CREATE INDEX "_TeamToUser_B_index" ON "_TeamToUser"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_ApplicationToTeam_AB_unique" ON "_ApplicationToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_ApplicationToTeam_B_index" ON "_ApplicationToTeam"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_GitSourceToTeam_AB_unique" ON "_GitSourceToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_GitSourceToTeam_B_index" ON "_GitSourceToTeam"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_GithubAppToTeam_AB_unique" ON "_GithubAppToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_GithubAppToTeam_B_index" ON "_GithubAppToTeam"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_GitlabAppToTeam_AB_unique" ON "_GitlabAppToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_GitlabAppToTeam_B_index" ON "_GitlabAppToTeam"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_DestinationDockerToTeam_AB_unique" ON "_DestinationDockerToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_DestinationDockerToTeam_B_index" ON "_DestinationDockerToTeam"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_DatabaseToTeam_AB_unique" ON "_DatabaseToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_DatabaseToTeam_B_index" ON "_DatabaseToTeam"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_ServiceToTeam_AB_unique" ON "_ServiceToTeam"("A", "B"); - --- CreateIndex -CREATE INDEX "_ServiceToTeam_B_index" ON "_ServiceToTeam"("B"); diff --git a/apps/api/prisma/migrations/20220210104005_redis_aol/migration.sql b/apps/api/prisma/migrations/20220210104005_redis_aol/migration.sql deleted file mode 100644 index 8a7e41098..000000000 --- a/apps/api/prisma/migrations/20220210104005_redis_aol/migration.sql +++ /dev/null @@ -1,28 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Team" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "databaseId" TEXT, - "serviceId" TEXT -); -INSERT INTO "new_Team" ("createdAt", "databaseId", "id", "name", "serviceId", "updatedAt") SELECT "createdAt", "databaseId", "id", "name", "serviceId", "updatedAt" FROM "Team"; -DROP TABLE "Team"; -ALTER TABLE "new_Team" RENAME TO "Team"; -CREATE TABLE "new_DatabaseSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "databaseId" TEXT NOT NULL, - "isPublic" BOOLEAN NOT NULL DEFAULT false, - "appendOnly" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "DatabaseSettings_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_DatabaseSettings" ("createdAt", "databaseId", "id", "isPublic", "updatedAt") SELECT "createdAt", "databaseId", "id", "isPublic", "updatedAt" FROM "DatabaseSettings"; -DROP TABLE "DatabaseSettings"; -ALTER TABLE "new_DatabaseSettings" RENAME TO "DatabaseSettings"; -CREATE UNIQUE INDEX "DatabaseSettings_databaseId_key" ON "DatabaseSettings"("databaseId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220212142309_unique_secret_by_application/migration.sql b/apps/api/prisma/migrations/20220212142309_unique_secret_by_application/migration.sql deleted file mode 100644 index da4aa8e7c..000000000 --- a/apps/api/prisma/migrations/20220212142309_unique_secret_by_application/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[name,applicationId]` on the table `Secret` will be added. If there are existing duplicate values, this will fail. - -*/ --- DropIndex -DROP INDEX "Secret_name_key"; - --- CreateIndex -CREATE UNIQUE INDEX "Secret_name_applicationId_key" ON "Secret"("name", "applicationId"); diff --git a/apps/api/prisma/migrations/20220217211304_dualcerts/migration.sql b/apps/api/prisma/migrations/20220217211304_dualcerts/migration.sql deleted file mode 100644 index a6ea0a57d..000000000 --- a/apps/api/prisma/migrations/20220217211304_dualcerts/migration.sql +++ /dev/null @@ -1,47 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("createdAt", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "createdAt", "debug", "id", "previews", "updatedAt") SELECT "applicationId", "createdAt", "debug", "id", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -CREATE TABLE "new_Service" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "fqdn" TEXT, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "type" TEXT, - "version" TEXT, - "destinationDockerId" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Service_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_Service" ("createdAt", "destinationDockerId", "fqdn", "id", "name", "type", "updatedAt", "version") SELECT "createdAt", "destinationDockerId", "fqdn", "id", "name", "type", "updatedAt", "version" FROM "Service"; -DROP TABLE "Service"; -ALTER TABLE "new_Service" RENAME TO "Service"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220219231255_prmr_secrets/migration.sql b/apps/api/prisma/migrations/20220219231255_prmr_secrets/migration.sql deleted file mode 100644 index 728fcae36..000000000 --- a/apps/api/prisma/migrations/20220219231255_prmr_secrets/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Secret" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "value" TEXT NOT NULL, - "isPRMRSecret" BOOLEAN NOT NULL DEFAULT false, - "isBuildSecret" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "applicationId" TEXT NOT NULL, - CONSTRAINT "Secret_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_Secret" ("applicationId", "createdAt", "id", "isBuildSecret", "name", "updatedAt", "value") SELECT "applicationId", "createdAt", "id", "isBuildSecret", "name", "updatedAt", "value" FROM "Secret"; -DROP TABLE "Secret"; -ALTER TABLE "new_Secret" RENAME TO "Secret"; -CREATE UNIQUE INDEX "Secret_name_applicationId_isPRMRSecret_key" ON "Secret"("name", "applicationId", "isPRMRSecret"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220220141136_public_portrange/migration.sql b/apps/api/prisma/migrations/20220220141136_public_portrange/migration.sql deleted file mode 100644 index 6423e9761..000000000 --- a/apps/api/prisma/migrations/20220220141136_public_portrange/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220301101928_proxyhash/migration.sql b/apps/api/prisma/migrations/20220301101928_proxyhash/migration.sql deleted file mode 100644 index 87845b38b..000000000 --- a/apps/api/prisma/migrations/20220301101928_proxyhash/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "proxyHash" TEXT; diff --git a/apps/api/prisma/migrations/20220304141408_service_secrets/migration.sql b/apps/api/prisma/migrations/20220304141408_service_secrets/migration.sql deleted file mode 100644 index baa0c3f54..000000000 --- a/apps/api/prisma/migrations/20220304141408_service_secrets/migration.sql +++ /dev/null @@ -1,13 +0,0 @@ --- CreateTable -CREATE TABLE "ServiceSecret" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "value" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "serviceId" TEXT NOT NULL, - CONSTRAINT "ServiceSecret_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "ServiceSecret_name_serviceId_key" ON "ServiceSecret"("name", "serviceId"); diff --git a/apps/api/prisma/migrations/20220311213422_autodeploy/migration.sql b/apps/api/prisma/migrations/20220311213422_autodeploy/migration.sql deleted file mode 100644 index d534d9372..000000000 --- a/apps/api/prisma/migrations/20220311213422_autodeploy/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "autodeploy" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt") SELECT "applicationId", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220320141424_phpmodules/migration.sql b/apps/api/prisma/migrations/20220320141424_phpmodules/migration.sql deleted file mode 100644 index 6a17ff8a3..000000000 --- a/apps/api/prisma/migrations/20220320141424_phpmodules/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "phpModules" TEXT; diff --git a/apps/api/prisma/migrations/20220322135800_persistent_storage/migration.sql b/apps/api/prisma/migrations/20220322135800_persistent_storage/migration.sql deleted file mode 100644 index d26ae3f8b..000000000 --- a/apps/api/prisma/migrations/20220322135800_persistent_storage/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- CreateTable -CREATE TABLE "ApplicationPersistentStorage" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "path" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationPersistentStorage_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_key" ON "ApplicationPersistentStorage"("applicationId"); - --- CreateIndex -CREATE UNIQUE INDEX "ApplicationPersistentStorage_path_key" ON "ApplicationPersistentStorage"("path"); - --- CreateIndex -CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_path_key" ON "ApplicationPersistentStorage"("applicationId", "path"); diff --git a/apps/api/prisma/migrations/20220327180323_ghost/migration.sql b/apps/api/prisma/migrations/20220327180323_ghost/migration.sql deleted file mode 100644 index 3c1cec36f..000000000 --- a/apps/api/prisma/migrations/20220327180323_ghost/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ --- CreateTable -CREATE TABLE "Ghost" ( - "id" TEXT NOT NULL PRIMARY KEY, - "defaultEmail" TEXT NOT NULL, - "defaultPassword" TEXT NOT NULL, - "mariadbUser" TEXT NOT NULL, - "mariadbPassword" TEXT NOT NULL, - "mariadbRootUser" TEXT NOT NULL, - "mariadbRootUserPassword" TEXT NOT NULL, - "mariadbDatabase" TEXT, - "mariadbPublicPort" INTEGER, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Ghost_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Ghost_serviceId_key" ON "Ghost"("serviceId"); diff --git a/apps/api/prisma/migrations/20220402135305_python/migration.sql b/apps/api/prisma/migrations/20220402135305_python/migration.sql deleted file mode 100644 index 0c253d539..000000000 --- a/apps/api/prisma/migrations/20220402135305_python/migration.sql +++ /dev/null @@ -1,4 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "pythonModule" TEXT; -ALTER TABLE "Application" ADD COLUMN "pythonVariable" TEXT; -ALTER TABLE "Application" ADD COLUMN "pythonWSGI" TEXT; diff --git a/apps/api/prisma/migrations/20220402210645_meilisearch/migration.sql b/apps/api/prisma/migrations/20220402210645_meilisearch/migration.sql deleted file mode 100644 index 9e832b107..000000000 --- a/apps/api/prisma/migrations/20220402210645_meilisearch/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ --- CreateTable -CREATE TABLE "MeiliSearch" ( - "id" TEXT NOT NULL PRIMARY KEY, - "masterKey" TEXT NOT NULL, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "MeiliSearch_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "MeiliSearch_serviceId_key" ON "MeiliSearch"("serviceId"); diff --git a/apps/api/prisma/migrations/20220405151428_wordpress_sftp/migration.sql b/apps/api/prisma/migrations/20220405151428_wordpress_sftp/migration.sql deleted file mode 100644 index 6c3c4b907..000000000 --- a/apps/api/prisma/migrations/20220405151428_wordpress_sftp/migration.sql +++ /dev/null @@ -1,29 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Wordpress" ( - "id" TEXT NOT NULL PRIMARY KEY, - "extraConfig" TEXT, - "tablePrefix" TEXT, - "mysqlUser" TEXT NOT NULL, - "mysqlPassword" TEXT NOT NULL, - "mysqlRootUser" TEXT NOT NULL, - "mysqlRootUserPassword" TEXT NOT NULL, - "mysqlDatabase" TEXT, - "mysqlPublicPort" INTEGER, - "ftpEnabled" BOOLEAN NOT NULL DEFAULT false, - "ftpUser" TEXT, - "ftpPassword" TEXT, - "ftpPublicPort" INTEGER, - "ftpHostKey" TEXT, - "ftpHostKeyPrivate" TEXT, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress"; -DROP TABLE "Wordpress"; -ALTER TABLE "new_Wordpress" RENAME TO "Wordpress"; -CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220407220809_unique_storage_fix/migration.sql b/apps/api/prisma/migrations/20220407220809_unique_storage_fix/migration.sql deleted file mode 100644 index 9b645cc3b..000000000 --- a/apps/api/prisma/migrations/20220407220809_unique_storage_fix/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- DropIndex -DROP INDEX "ApplicationPersistentStorage_path_key"; - --- DropIndex -DROP INDEX "ApplicationPersistentStorage_applicationId_key"; diff --git a/apps/api/prisma/migrations/20220408070805_added_expose_port/migration.sql b/apps/api/prisma/migrations/20220408070805_added_expose_port/migration.sql deleted file mode 100644 index a23afd64a..000000000 --- a/apps/api/prisma/migrations/20220408070805_added_expose_port/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "exposePort" INTEGER; diff --git a/apps/api/prisma/migrations/20220418214843_persistent_storage_services/migration.sql b/apps/api/prisma/migrations/20220418214843_persistent_storage_services/migration.sql deleted file mode 100644 index f85fd31df..000000000 --- a/apps/api/prisma/migrations/20220418214843_persistent_storage_services/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ --- CreateTable -CREATE TABLE "ServicePersistentStorage" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "path" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path"); diff --git a/apps/api/prisma/migrations/20220419203408_multiply_dockerfile_locations/migration.sql b/apps/api/prisma/migrations/20220419203408_multiply_dockerfile_locations/migration.sql deleted file mode 100644 index ce32f0844..000000000 --- a/apps/api/prisma/migrations/20220419203408_multiply_dockerfile_locations/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "dockerFileLocation" TEXT; diff --git a/apps/api/prisma/migrations/20220420202031_deno_configurations/migration.sql b/apps/api/prisma/migrations/20220420202031_deno_configurations/migration.sql deleted file mode 100644 index 9d0e832dc..000000000 --- a/apps/api/prisma/migrations/20220420202031_deno_configurations/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "denoMainFile" TEXT; -ALTER TABLE "Application" ADD COLUMN "denoOptions" TEXT; diff --git a/apps/api/prisma/migrations/20220420210057_branch_for_builds/migration.sql b/apps/api/prisma/migrations/20220420210057_branch_for_builds/migration.sql deleted file mode 100644 index 8cd8e653d..000000000 --- a/apps/api/prisma/migrations/20220420210057_branch_for_builds/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Build" ADD COLUMN "branch" TEXT; diff --git a/apps/api/prisma/migrations/20220425071132_umami/migration.sql b/apps/api/prisma/migrations/20220425071132_umami/migration.sql deleted file mode 100644 index 259fb76cd..000000000 --- a/apps/api/prisma/migrations/20220425071132_umami/migration.sql +++ /dev/null @@ -1,17 +0,0 @@ --- CreateTable -CREATE TABLE "Umami" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "umamiAdminPassword" TEXT NOT NULL, - "hashSalt" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Umami_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Umami_serviceId_key" ON "Umami"("serviceId"); diff --git a/apps/api/prisma/migrations/20220425075326_auto_update_coolify/migration.sql b/apps/api/prisma/migrations/20220425075326_auto_update_coolify/migration.sql deleted file mode 100644 index a102973ee..000000000 --- a/apps/api/prisma/migrations/20220425075326_auto_update_coolify/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "proxyHash" TEXT, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220426125053_select_base_image/migration.sql b/apps/api/prisma/migrations/20220426125053_select_base_image/migration.sql deleted file mode 100644 index 37209d82f..000000000 --- a/apps/api/prisma/migrations/20220426125053_select_base_image/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "baseBuildImage" TEXT; -ALTER TABLE "Application" ADD COLUMN "baseImage" TEXT; diff --git a/apps/api/prisma/migrations/20220427133656_hasura/migration.sql b/apps/api/prisma/migrations/20220427133656_hasura/migration.sql deleted file mode 100644 index c679ad0fb..000000000 --- a/apps/api/prisma/migrations/20220427133656_hasura/migration.sql +++ /dev/null @@ -1,16 +0,0 @@ --- CreateTable -CREATE TABLE "Hasura" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "graphQLAdminPassword" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Hasura_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Hasura_serviceId_key" ON "Hasura"("serviceId"); diff --git a/apps/api/prisma/migrations/20220429202516_fider/migration.sql b/apps/api/prisma/migrations/20220429202516_fider/migration.sql deleted file mode 100644 index a6d31a24d..000000000 --- a/apps/api/prisma/migrations/20220429202516_fider/migration.sql +++ /dev/null @@ -1,25 +0,0 @@ --- CreateTable -CREATE TABLE "Fider" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "jwtSecret" TEXT NOT NULL, - "emailNoreply" TEXT, - "emailMailgunApiKey" TEXT, - "emailMailgunDomain" TEXT, - "emailMailgunRegion" TEXT, - "emailSmtpHost" TEXT, - "emailSmtpPort" INTEGER, - "emailSmtpUser" TEXT, - "emailSmtpPassword" TEXT, - "emailSmtpEnableStartTls" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Fider_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Fider_serviceId_key" ON "Fider"("serviceId"); diff --git a/apps/api/prisma/migrations/20220429214112_fider_correction/migration.sql b/apps/api/prisma/migrations/20220429214112_fider_correction/migration.sql deleted file mode 100644 index 429524f72..000000000 --- a/apps/api/prisma/migrations/20220429214112_fider_correction/migration.sql +++ /dev/null @@ -1,29 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Fider" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "jwtSecret" TEXT NOT NULL, - "emailNoreply" TEXT, - "emailMailgunApiKey" TEXT, - "emailMailgunDomain" TEXT, - "emailMailgunRegion" TEXT NOT NULL DEFAULT 'EU', - "emailSmtpHost" TEXT, - "emailSmtpPort" INTEGER, - "emailSmtpUser" TEXT, - "emailSmtpPassword" TEXT, - "emailSmtpEnableStartTls" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Fider_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_Fider" ("createdAt", "emailMailgunApiKey", "emailMailgunDomain", "emailMailgunRegion", "emailNoreply", "emailSmtpEnableStartTls", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUser", "id", "jwtSecret", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "serviceId", "updatedAt") SELECT "createdAt", "emailMailgunApiKey", "emailMailgunDomain", coalesce("emailMailgunRegion", 'EU') AS "emailMailgunRegion", "emailNoreply", "emailSmtpEnableStartTls", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUser", "id", "jwtSecret", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "serviceId", "updatedAt" FROM "Fider"; -DROP TABLE "Fider"; -ALTER TABLE "new_Fider" RENAME TO "Fider"; -CREATE UNIQUE INDEX "Fider_serviceId_key" ON "Fider"("serviceId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql b/apps/api/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql deleted file mode 100644 index cf57379ca..000000000 --- a/apps/api/prisma/migrations/20220430111953_ssl_dns_check_settings/migration.sql +++ /dev/null @@ -1,23 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "proxyHash" TEXT, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220430124553_expose_port_for_services/migration.sql b/apps/api/prisma/migrations/20220430124553_expose_port_for_services/migration.sql deleted file mode 100644 index fdbab5713..000000000 --- a/apps/api/prisma/migrations/20220430124553_expose_port_for_services/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Service" ADD COLUMN "exposePort" INTEGER; diff --git a/apps/api/prisma/migrations/20220509130501_custom_plausible_script/migration.sql b/apps/api/prisma/migrations/20220509130501_custom_plausible_script/migration.sql deleted file mode 100644 index 6c8c28ff4..000000000 --- a/apps/api/prisma/migrations/20220509130501_custom_plausible_script/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_PlausibleAnalytics" ( - "id" TEXT NOT NULL PRIMARY KEY, - "email" TEXT, - "username" TEXT, - "password" TEXT NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "secretKeyBase" TEXT, - "scriptName" TEXT NOT NULL DEFAULT 'plausible.js', - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "PlausibleAnalytics_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_PlausibleAnalytics" ("createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username") SELECT "createdAt", "email", "id", "password", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "serviceId", "updatedAt", "username" FROM "PlausibleAnalytics"; -DROP TABLE "PlausibleAnalytics"; -ALTER TABLE "new_PlausibleAnalytics" RENAME TO "PlausibleAnalytics"; -CREATE UNIQUE INDEX "PlausibleAnalytics_serviceId_key" ON "PlausibleAnalytics"("serviceId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220510081125_custom_wordpress_db/migration.sql b/apps/api/prisma/migrations/20220510081125_custom_wordpress_db/migration.sql deleted file mode 100644 index 5a1cd301c..000000000 --- a/apps/api/prisma/migrations/20220510081125_custom_wordpress_db/migration.sql +++ /dev/null @@ -1,32 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Wordpress" ( - "id" TEXT NOT NULL PRIMARY KEY, - "extraConfig" TEXT, - "tablePrefix" TEXT, - "ownMysql" BOOLEAN NOT NULL DEFAULT false, - "mysqlHost" TEXT, - "mysqlPort" INTEGER, - "mysqlUser" TEXT NOT NULL, - "mysqlPassword" TEXT NOT NULL, - "mysqlRootUser" TEXT NOT NULL, - "mysqlRootUserPassword" TEXT NOT NULL, - "mysqlDatabase" TEXT, - "mysqlPublicPort" INTEGER, - "ftpEnabled" BOOLEAN NOT NULL DEFAULT false, - "ftpUser" TEXT, - "ftpPassword" TEXT, - "ftpPublicPort" INTEGER, - "ftpHostKey" TEXT, - "ftpHostKeyPrivate" TEXT, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlPassword", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress"; -DROP TABLE "Wordpress"; -ALTER TABLE "new_Wordpress" RENAME TO "Wordpress"; -CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220517081328_traefik/migration.sql b/apps/api/prisma/migrations/20220517081328_traefik/migration.sql deleted file mode 100644 index a83281fa4..000000000 --- a/apps/api/prisma/migrations/20220517081328_traefik/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "proxyHash" TEXT, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "createdAt", "dualCerts", "fqdn", "id", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220519095648_minio_apifqdn/migration.sql b/apps/api/prisma/migrations/20220519095648_minio_apifqdn/migration.sql deleted file mode 100644 index a44712864..000000000 --- a/apps/api/prisma/migrations/20220519095648_minio_apifqdn/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Minio" ADD COLUMN "apiFqdn" TEXT; diff --git a/apps/api/prisma/migrations/20220708132655_deployment_type_for_applications/migration.sql b/apps/api/prisma/migrations/20220708132655_deployment_type_for_applications/migration.sql deleted file mode 100644 index 2776d1b88..000000000 --- a/apps/api/prisma/migrations/20220708132655_deployment_type_for_applications/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "deploymentType" TEXT; diff --git a/apps/api/prisma/migrations/20220712083523_custom_port_git_sources/migration.sql b/apps/api/prisma/migrations/20220712083523_custom_port_git_sources/migration.sql deleted file mode 100644 index f702291b2..000000000 --- a/apps/api/prisma/migrations/20220712083523_custom_port_git_sources/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_GitSource" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "type" TEXT, - "apiUrl" TEXT, - "htmlUrl" TEXT, - "customPort" INTEGER NOT NULL DEFAULT 22, - "organization" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource"; -DROP TABLE "GitSource"; -ALTER TABLE "new_GitSource" RENAME TO "GitSource"; -CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId"); -CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220718083646_moodle/migration.sql b/apps/api/prisma/migrations/20220718083646_moodle/migration.sql deleted file mode 100644 index eaed14a4a..000000000 --- a/apps/api/prisma/migrations/20220718083646_moodle/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ --- CreateTable -CREATE TABLE "Moodle" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "defaultUsername" TEXT NOT NULL, - "defaultPassword" TEXT NOT NULL, - "defaultEmail" TEXT NOT NULL, - "mariadbUser" TEXT NOT NULL, - "mariadbPassword" TEXT NOT NULL, - "mariadbRootUser" TEXT NOT NULL, - "mariadbRootUserPassword" TEXT NOT NULL, - "mariadbDatabase" TEXT NOT NULL, - "mariadbPublicPort" INTEGER, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Moodle_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Moodle_serviceId_key" ON "Moodle"("serviceId"); diff --git a/apps/api/prisma/migrations/20220718114551_remote_docker_engine/migration.sql b/apps/api/prisma/migrations/20220718114551_remote_docker_engine/migration.sql deleted file mode 100644 index e80df22f2..000000000 --- a/apps/api/prisma/migrations/20220718114551_remote_docker_engine/migration.sql +++ /dev/null @@ -1,21 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_DestinationDocker" ( - "id" TEXT NOT NULL PRIMARY KEY, - "network" TEXT NOT NULL, - "name" TEXT NOT NULL, - "engine" TEXT, - "remoteEngine" BOOLEAN NOT NULL DEFAULT false, - "remoteIpAddress" TEXT, - "remoteUser" TEXT, - "remotePort" INTEGER, - "isCoolifyProxyUsed" BOOLEAN DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_DestinationDocker" ("createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "updatedAt") SELECT "createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "updatedAt" FROM "DestinationDocker"; -DROP TABLE "DestinationDocker"; -ALTER TABLE "new_DestinationDocker" RENAME TO "DestinationDocker"; -CREATE UNIQUE INDEX "DestinationDocker_network_key" ON "DestinationDocker"("network"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220721084020_ssh_key/migration.sql b/apps/api/prisma/migrations/20220721084020_ssh_key/migration.sql deleted file mode 100644 index 8323af409..000000000 --- a/apps/api/prisma/migrations/20220721084020_ssh_key/migration.sql +++ /dev/null @@ -1,33 +0,0 @@ --- CreateTable -CREATE TABLE "SshKey" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "privateKey" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); - --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_DestinationDocker" ( - "id" TEXT NOT NULL PRIMARY KEY, - "network" TEXT NOT NULL, - "name" TEXT NOT NULL, - "engine" TEXT, - "remoteEngine" BOOLEAN NOT NULL DEFAULT false, - "remoteIpAddress" TEXT, - "remoteUser" TEXT, - "remotePort" INTEGER, - "remoteVerified" BOOLEAN NOT NULL DEFAULT false, - "isCoolifyProxyUsed" BOOLEAN DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "sshKeyId" TEXT, - CONSTRAINT "DestinationDocker_sshKeyId_fkey" FOREIGN KEY ("sshKeyId") REFERENCES "SshKey" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_DestinationDocker" ("createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "remoteIpAddress", "remotePort", "remoteUser", "updatedAt") SELECT "createdAt", "engine", "id", "isCoolifyProxyUsed", "name", "network", "remoteEngine", "remoteIpAddress", "remotePort", "remoteUser", "updatedAt" FROM "DestinationDocker"; -DROP TABLE "DestinationDocker"; -ALTER TABLE "new_DestinationDocker" RENAME TO "DestinationDocker"; -CREATE UNIQUE INDEX "DestinationDocker_network_key" ON "DestinationDocker"("network"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220722203927_ipaddress/migration.sql b/apps/api/prisma/migrations/20220722203927_ipaddress/migration.sql deleted file mode 100644 index 21d45ada3..000000000 --- a/apps/api/prisma/migrations/20220722203927_ipaddress/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "ipv4" TEXT; -ALTER TABLE "Setting" ADD COLUMN "ipv6" TEXT; diff --git a/apps/api/prisma/migrations/20220725191205_architecture/migration.sql b/apps/api/prisma/migrations/20220725191205_architecture/migration.sql deleted file mode 100644 index 2e0ff3e01..000000000 --- a/apps/api/prisma/migrations/20220725191205_architecture/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "arch" TEXT; diff --git a/apps/api/prisma/migrations/20220726121333_fix_ssh_key/migration.sql b/apps/api/prisma/migrations/20220726121333_fix_ssh_key/migration.sql deleted file mode 100644 index e6e47b197..000000000 --- a/apps/api/prisma/migrations/20220726121333_fix_ssh_key/migration.sql +++ /dev/null @@ -1,16 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_SshKey" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "privateKey" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "teamId" TEXT, - CONSTRAINT "SshKey_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_SshKey" ("createdAt", "id", "name", "privateKey", "updatedAt") SELECT "createdAt", "id", "name", "privateKey", "updatedAt" FROM "SshKey"; -DROP TABLE "SshKey"; -ALTER TABLE "new_SshKey" RENAME TO "SshKey"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220806090621_fqdn_not_unique_anymore/migration.sql b/apps/api/prisma/migrations/20220806090621_fqdn_not_unique_anymore/migration.sql deleted file mode 100644 index f1eab666b..000000000 --- a/apps/api/prisma/migrations/20220806090621_fqdn_not_unique_anymore/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- DropIndex -DROP INDEX "Application_fqdn_key"; diff --git a/apps/api/prisma/migrations/20220806102340_rde_ssh_local_port/migration.sql b/apps/api/prisma/migrations/20220806102340_rde_ssh_local_port/migration.sql deleted file mode 100644 index ae4c39846..000000000 --- a/apps/api/prisma/migrations/20220806102340_rde_ssh_local_port/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "DestinationDocker" ADD COLUMN "sshLocalPort" INTEGER; diff --git a/apps/api/prisma/migrations/20220815092230_glitchtip/migration.sql b/apps/api/prisma/migrations/20220815092230_glitchtip/migration.sql deleted file mode 100644 index dba98ab82..000000000 --- a/apps/api/prisma/migrations/20220815092230_glitchtip/migration.sql +++ /dev/null @@ -1,30 +0,0 @@ --- CreateTable -CREATE TABLE "GlitchTip" ( - "id" TEXT NOT NULL PRIMARY KEY, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "secretKeyBase" TEXT, - "defaultEmail" TEXT NOT NULL, - "defaultUsername" TEXT NOT NULL, - "defaultPassword" TEXT NOT NULL, - "defaultEmailFrom" TEXT NOT NULL DEFAULT 'glitchtip@domain.tdl', - "emailSmtpHost" TEXT DEFAULT 'domain.tdl', - "emailSmtpPort" INTEGER DEFAULT 25, - "emailSmtpUser" TEXT, - "emailSmtpPassword" TEXT, - "emailSmtpUseTls" BOOLEAN DEFAULT false, - "emailSmtpUseSsl" BOOLEAN DEFAULT false, - "emailBackend" TEXT, - "mailgunApiKey" TEXT, - "sendgridApiKey" TEXT, - "enableOpenUserRegistration" BOOLEAN NOT NULL DEFAULT true, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "GlitchTip_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "GlitchTip_serviceId_key" ON "GlitchTip"("serviceId"); diff --git a/apps/api/prisma/migrations/20220815133844_appwrite/migration.sql b/apps/api/prisma/migrations/20220815133844_appwrite/migration.sql deleted file mode 100644 index ec61dc434..000000000 --- a/apps/api/prisma/migrations/20220815133844_appwrite/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ --- CreateTable -CREATE TABLE "Appwrite" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "opensslKeyV1" TEXT NOT NULL, - "executorSecret" TEXT NOT NULL, - "redisPassword" TEXT NOT NULL, - "mariadbHost" TEXT, - "mariadbPort" INTEGER NOT NULL DEFAULT 3306, - "mariadbUser" TEXT NOT NULL, - "mariadbPassword" TEXT NOT NULL, - "mariadbRootUser" TEXT NOT NULL, - "mariadbRootUserPassword" TEXT NOT NULL, - "mariadbDatabase" TEXT NOT NULL, - "mariadbPublicPort" INTEGER, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Appwrite_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Appwrite_serviceId_key" ON "Appwrite"("serviceId"); diff --git a/apps/api/prisma/migrations/20220816133447_bot_deployments/migration.sql b/apps/api/prisma/migrations/20220816133447_bot_deployments/migration.sql deleted file mode 100644 index 3d6d92d0e..000000000 --- a/apps/api/prisma/migrations/20220816133447_bot_deployments/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "autodeploy" BOOLEAN NOT NULL DEFAULT true, - "isBot" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220817082342_custom_dns_servers/migration.sql b/apps/api/prisma/migrations/20220817082342_custom_dns_servers/migration.sql deleted file mode 100644 index 03588b549..000000000 --- a/apps/api/prisma/migrations/20220817082342_custom_dns_servers/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "DNSServers" TEXT; diff --git a/apps/api/prisma/migrations/20220818093615_public_repositories/migration.sql b/apps/api/prisma/migrations/20220818093615_public_repositories/migration.sql deleted file mode 100644 index 7f08c29fd..000000000 --- a/apps/api/prisma/migrations/20220818093615_public_repositories/migration.sql +++ /dev/null @@ -1,42 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_GitSource" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "forPublic" BOOLEAN NOT NULL DEFAULT false, - "type" TEXT, - "apiUrl" TEXT, - "htmlUrl" TEXT, - "customPort" INTEGER NOT NULL DEFAULT 22, - "organization" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource"; -DROP TABLE "GitSource"; -ALTER TABLE "new_GitSource" RENAME TO "GitSource"; -CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId"); -CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId"); -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "autodeploy" BOOLEAN NOT NULL DEFAULT true, - "isBot" BOOLEAN NOT NULL DEFAULT false, - "isPublicRepository" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220823070532_service_searxng/migration.sql b/apps/api/prisma/migrations/20220823070532_service_searxng/migration.sql deleted file mode 100644 index 81ecc81c8..000000000 --- a/apps/api/prisma/migrations/20220823070532_service_searxng/migration.sql +++ /dev/null @@ -1,13 +0,0 @@ --- CreateTable -CREATE TABLE "Searxng" ( - "id" TEXT NOT NULL PRIMARY KEY, - "secretKey" TEXT NOT NULL, - "redisPassword" TEXT NOT NULL, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Searxng_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Searxng_serviceId_key" ON "Searxng"("serviceId"); diff --git a/apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql b/apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql deleted file mode 100644 index 1535c2bf7..000000000 --- a/apps/api/prisma/migrations/20220825064811_concurrent_build_settings/migration.sql +++ /dev/null @@ -1,29 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "proxyHash" TEXT, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "DNSServers" TEXT, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1 -); -INSERT INTO "new_Setting" ("DNSServers", "arch", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "DNSServers", "arch", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql b/apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql deleted file mode 100644 index 78c51fc15..000000000 --- a/apps/api/prisma/migrations/20220825072007_build_queue_improvements/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Build" ( - "id" TEXT NOT NULL PRIMARY KEY, - "type" TEXT NOT NULL, - "applicationId" TEXT, - "destinationDockerId" TEXT, - "gitSourceId" TEXT, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - "commit" TEXT, - "pullmergeRequestId" TEXT, - "forceRebuild" BOOLEAN NOT NULL DEFAULT false, - "sourceBranch" TEXT, - "branch" TEXT, - "status" TEXT DEFAULT 'queued', - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Build" ("applicationId", "branch", "commit", "createdAt", "destinationDockerId", "gitSourceId", "githubAppId", "gitlabAppId", "id", "status", "type", "updatedAt") SELECT "applicationId", "branch", "commit", "createdAt", "destinationDockerId", "gitSourceId", "githubAppId", "gitlabAppId", "id", "status", "type", "updatedAt" FROM "Build"; -DROP TABLE "Build"; -ALTER TABLE "new_Build" RENAME TO "Build"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220831095714_service_weblate/migration.sql b/apps/api/prisma/migrations/20220831095714_service_weblate/migration.sql deleted file mode 100644 index c985b4ae2..000000000 --- a/apps/api/prisma/migrations/20220831095714_service_weblate/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- CreateTable -CREATE TABLE "Weblate" ( - "id" TEXT NOT NULL PRIMARY KEY, - "adminPassword" TEXT NOT NULL, - "postgresqlHost" TEXT NOT NULL, - "postgresqlPort" INTEGER NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Weblate_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Weblate_serviceId_key" ON "Weblate"("serviceId"); diff --git a/apps/api/prisma/migrations/20220902115640_service_taiga/migration.sql b/apps/api/prisma/migrations/20220902115640_service_taiga/migration.sql deleted file mode 100644 index 3035dd8ef..000000000 --- a/apps/api/prisma/migrations/20220902115640_service_taiga/migration.sql +++ /dev/null @@ -1,23 +0,0 @@ --- CreateTable -CREATE TABLE "Taiga" ( - "id" TEXT NOT NULL PRIMARY KEY, - "secretKey" TEXT NOT NULL, - "erlangSecret" TEXT NOT NULL, - "djangoAdminPassword" TEXT NOT NULL, - "djangoAdminUser" TEXT NOT NULL, - "rabbitMQUser" TEXT NOT NULL, - "rabbitMQPassword" TEXT NOT NULL, - "postgresqlHost" TEXT NOT NULL, - "postgresqlPort" INTEGER NOT NULL, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Taiga_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "Taiga_serviceId_key" ON "Taiga"("serviceId"); diff --git a/apps/api/prisma/migrations/20220905062318_database_branching/migration.sql b/apps/api/prisma/migrations/20220905062318_database_branching/migration.sql deleted file mode 100644 index d828a4c66..000000000 --- a/apps/api/prisma/migrations/20220905062318_database_branching/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "autodeploy" BOOLEAN NOT NULL DEFAULT true, - "isBot" BOOLEAN NOT NULL DEFAULT false, - "isPublicRepository" BOOLEAN NOT NULL DEFAULT false, - "isDBBranching" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220905113241_prisma_migration/migration.sql b/apps/api/prisma/migrations/20220905113241_prisma_migration/migration.sql deleted file mode 100644 index 324bcfff0..000000000 --- a/apps/api/prisma/migrations/20220905113241_prisma_migration/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ -/* - Warnings: - - - You are about to alter the column `time` on the `BuildLog` table. The data in that column could be lost. The data in that column will be cast from `Int` to `BigInt`. - -*/ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_BuildLog" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT, - "buildId" TEXT NOT NULL, - "line" TEXT NOT NULL, - "time" BIGINT NOT NULL -); -INSERT INTO "new_BuildLog" ("applicationId", "buildId", "id", "line", "time") SELECT "applicationId", "buildId", "id", "line", "time" FROM "BuildLog"; -DROP TABLE "BuildLog"; -ALTER TABLE "new_BuildLog" RENAME TO "BuildLog"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220905115321_application_connected_database/migration.sql b/apps/api/prisma/migrations/20220905115321_application_connected_database/migration.sql deleted file mode 100644 index 576c23bdf..000000000 --- a/apps/api/prisma/migrations/20220905115321_application_connected_database/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ --- CreateTable -CREATE TABLE "ApplicationConnectedDatabase" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "databaseId" TEXT, - "hostedDatabaseType" TEXT, - "hostedDatabaseHost" TEXT, - "hostedDatabasePort" INTEGER, - "hostedDatabaseName" TEXT, - "hostedDatabaseUser" TEXT, - "hostedDatabasePassword" TEXT, - "hostedDatabaseDBName" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationConnectedDatabase_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "ApplicationConnectedDatabase_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "ApplicationConnectedDatabase_applicationId_key" ON "ApplicationConnectedDatabase"("applicationId"); diff --git a/apps/api/prisma/migrations/20220906120112_enable_api_debug_logging/migration.sql b/apps/api/prisma/migrations/20220906120112_enable_api_debug_logging/migration.sql deleted file mode 100644 index 05fb3e285..000000000 --- a/apps/api/prisma/migrations/20220906120112_enable_api_debug_logging/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "isAPIDebuggingEnabled" BOOLEAN DEFAULT false; diff --git a/apps/api/prisma/migrations/20220907092244_database_secrets/migration.sql b/apps/api/prisma/migrations/20220907092244_database_secrets/migration.sql deleted file mode 100644 index 53ff2d19e..000000000 --- a/apps/api/prisma/migrations/20220907092244_database_secrets/migration.sql +++ /dev/null @@ -1,13 +0,0 @@ --- CreateTable -CREATE TABLE "DatabaseSecret" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "value" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "databaseId" TEXT NOT NULL, - CONSTRAINT "DatabaseSecret_databaseId_fkey" FOREIGN KEY ("databaseId") REFERENCES "Database" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "DatabaseSecret_name_databaseId_key" ON "DatabaseSecret"("name", "databaseId"); diff --git a/apps/api/prisma/migrations/20220913092100_preview_applications/migration.sql b/apps/api/prisma/migrations/20220913092100_preview_applications/migration.sql deleted file mode 100644 index 0ec1aafa0..000000000 --- a/apps/api/prisma/migrations/20220913092100_preview_applications/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- AlterTable -ALTER TABLE "Build" ADD COLUMN "previewApplicationId" TEXT; - --- CreateTable -CREATE TABLE "PreviewApplication" ( - "id" TEXT NOT NULL PRIMARY KEY, - "pullmergeRequestId" TEXT NOT NULL, - "sourceBranch" TEXT NOT NULL, - "isRandomDomain" BOOLEAN NOT NULL DEFAULT false, - "customDomain" TEXT, - "applicationId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "PreviewApplication_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "PreviewApplication_applicationId_key" ON "PreviewApplication"("applicationId"); diff --git a/apps/api/prisma/migrations/20220922064605_custom_certificates/migration.sql b/apps/api/prisma/migrations/20220922064605_custom_certificates/migration.sql deleted file mode 100644 index 804913038..000000000 --- a/apps/api/prisma/migrations/20220922064605_custom_certificates/migration.sql +++ /dev/null @@ -1,10 +0,0 @@ --- CreateTable -CREATE TABLE "Certificate" ( - "id" TEXT NOT NULL PRIMARY KEY, - "key" TEXT NOT NULL, - "cert" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "teamId" TEXT, - CONSTRAINT "Certificate_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); diff --git a/apps/api/prisma/migrations/20220923122227_custom_ssl_for_applications/migration.sql b/apps/api/prisma/migrations/20220923122227_custom_ssl_for_applications/migration.sql deleted file mode 100644 index b9261a955..000000000 --- a/apps/api/prisma/migrations/20220923122227_custom_ssl_for_applications/migration.sql +++ /dev/null @@ -1,23 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "autodeploy" BOOLEAN NOT NULL DEFAULT true, - "isBot" BOOLEAN NOT NULL DEFAULT false, - "isPublicRepository" BOOLEAN NOT NULL DEFAULT false, - "isDBBranching" BOOLEAN NOT NULL DEFAULT false, - "isCustomSSL" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20220928083348_system_wide_git_sources/migration.sql b/apps/api/prisma/migrations/20220928083348_system_wide_git_sources/migration.sql deleted file mode 100644 index a3657504d..000000000 --- a/apps/api/prisma/migrations/20220928083348_system_wide_git_sources/migration.sql +++ /dev/null @@ -1,26 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_GitSource" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "forPublic" BOOLEAN NOT NULL DEFAULT false, - "type" TEXT, - "apiUrl" TEXT, - "htmlUrl" TEXT, - "customPort" INTEGER NOT NULL DEFAULT 22, - "organization" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - "isSystemWide" BOOLEAN NOT NULL DEFAULT false, - CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "name", "organization", "type", "updatedAt" FROM "GitSource"; -DROP TABLE "GitSource"; -ALTER TABLE "new_GitSource" RENAME TO "GitSource"; -CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId"); -CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221002084246_no_unique_appid_on_previews/migration.sql b/apps/api/prisma/migrations/20221002084246_no_unique_appid_on_previews/migration.sql deleted file mode 100644 index 031f842cb..000000000 --- a/apps/api/prisma/migrations/20221002084246_no_unique_appid_on_previews/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- DropIndex -DROP INDEX "PreviewApplication_applicationId_key"; diff --git a/apps/api/prisma/migrations/20221002091630_forked_previews/migration.sql b/apps/api/prisma/migrations/20221002091630_forked_previews/migration.sql deleted file mode 100644 index 2312c011b..000000000 --- a/apps/api/prisma/migrations/20221002091630_forked_previews/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Build" ADD COLUMN "sourceRepository" TEXT; diff --git a/apps/api/prisma/migrations/20221005120323_initial_docker_compose/migration.sql b/apps/api/prisma/migrations/20221005120323_initial_docker_compose/migration.sql deleted file mode 100644 index bb93e1aaf..000000000 --- a/apps/api/prisma/migrations/20221005120323_initial_docker_compose/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "dockerComposeFile" TEXT; -ALTER TABLE "Application" ADD COLUMN "dockerComposeFileLocation" TEXT; diff --git a/apps/api/prisma/migrations/20221005132352_docker_compose_configuration/migration.sql b/apps/api/prisma/migrations/20221005132352_docker_compose_configuration/migration.sql deleted file mode 100644 index e7368dc1a..000000000 --- a/apps/api/prisma/migrations/20221005132352_docker_compose_configuration/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "dockerComposeConfiguration" TEXT; diff --git a/apps/api/prisma/migrations/20221017134342_standardized_service_configs/migration.sql b/apps/api/prisma/migrations/20221017134342_standardized_service_configs/migration.sql deleted file mode 100644 index 837017435..000000000 --- a/apps/api/prisma/migrations/20221017134342_standardized_service_configs/migration.sql +++ /dev/null @@ -1,13 +0,0 @@ --- CreateTable -CREATE TABLE "ServiceSetting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "name" TEXT NOT NULL, - "value" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); - --- CreateIndex -CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name"); diff --git a/apps/api/prisma/migrations/20221018090939_service_peristent_volumes_predefined/migration.sql b/apps/api/prisma/migrations/20221018090939_service_peristent_volumes_predefined/migration.sql deleted file mode 100644 index c7b935eb7..000000000 --- a/apps/api/prisma/migrations/20221018090939_service_peristent_volumes_predefined/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ServicePersistentStorage" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "path" TEXT NOT NULL, - "volumeName" TEXT, - "predefined" BOOLEAN NOT NULL DEFAULT false, - "containerId" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ServicePersistentStorage_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ServicePersistentStorage" ("createdAt", "id", "path", "serviceId", "updatedAt") SELECT "createdAt", "id", "path", "serviceId", "updatedAt" FROM "ServicePersistentStorage"; -DROP TABLE "ServicePersistentStorage"; -ALTER TABLE "new_ServicePersistentStorage" RENAME TO "ServicePersistentStorage"; -CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_path_key" ON "ServicePersistentStorage"("serviceId", "path"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221021112429_serivce_settings_extended/migration.sql b/apps/api/prisma/migrations/20221021112429_serivce_settings_extended/migration.sql deleted file mode 100644 index b2eddcfd9..000000000 --- a/apps/api/prisma/migrations/20221021112429_serivce_settings_extended/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ -/* - Warnings: - - - Added the required column `variableName` to the `ServiceSetting` table without a default value. This is not possible if the table is not empty. - -*/ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ServiceSetting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "serviceId" TEXT NOT NULL, - "name" TEXT NOT NULL, - "value" TEXT NOT NULL, - "variableName" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ServiceSetting_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ServiceSetting" ("createdAt", "id", "name", "serviceId", "updatedAt", "value") SELECT "createdAt", "id", "name", "serviceId", "updatedAt", "value" FROM "ServiceSetting"; -DROP TABLE "ServiceSetting"; -ALTER TABLE "new_ServiceSetting" RENAME TO "ServiceSetting"; -CREATE UNIQUE INDEX "ServiceSetting_serviceId_name_key" ON "ServiceSetting"("serviceId", "name"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221021185630_service_template_version/migration.sql b/apps/api/prisma/migrations/20221021185630_service_template_version/migration.sql deleted file mode 100644 index f716a2340..000000000 --- a/apps/api/prisma/migrations/20221021185630_service_template_version/migration.sql +++ /dev/null @@ -1,21 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Service" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "fqdn" TEXT, - "exposePort" INTEGER, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "type" TEXT, - "version" TEXT, - "templateVersion" TEXT NOT NULL DEFAULT '0.0.0', - "destinationDockerId" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Service_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_Service" ("createdAt", "destinationDockerId", "dualCerts", "exposePort", "fqdn", "id", "name", "type", "updatedAt", "version") SELECT "createdAt", "destinationDockerId", "dualCerts", "exposePort", "fqdn", "id", "name", "type", "updatedAt", "version" FROM "Service"; -DROP TABLE "Service"; -ALTER TABLE "new_Service" RENAME TO "Service"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221026115123_service_persistent_unique/migration.sql b/apps/api/prisma/migrations/20221026115123_service_persistent_unique/migration.sql deleted file mode 100644 index dae80a76a..000000000 --- a/apps/api/prisma/migrations/20221026115123_service_persistent_unique/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[serviceId,containerId,path]` on the table `ServicePersistentStorage` will be added. If there are existing duplicate values, this will fail. - -*/ --- DropIndex -DROP INDEX "ServicePersistentStorage_serviceId_path_key"; - --- CreateIndex -CREATE UNIQUE INDEX "ServicePersistentStorage_serviceId_containerId_path_key" ON "ServicePersistentStorage"("serviceId", "containerId", "path"); diff --git a/apps/api/prisma/migrations/20221028074301_wordpress_optional_fields/migration.sql b/apps/api/prisma/migrations/20221028074301_wordpress_optional_fields/migration.sql deleted file mode 100644 index 217039d97..000000000 --- a/apps/api/prisma/migrations/20221028074301_wordpress_optional_fields/migration.sql +++ /dev/null @@ -1,32 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Wordpress" ( - "id" TEXT NOT NULL PRIMARY KEY, - "extraConfig" TEXT, - "tablePrefix" TEXT, - "ownMysql" BOOLEAN NOT NULL DEFAULT false, - "mysqlHost" TEXT, - "mysqlPort" INTEGER, - "mysqlUser" TEXT, - "mysqlPassword" TEXT, - "mysqlRootUser" TEXT, - "mysqlRootUserPassword" TEXT, - "mysqlDatabase" TEXT, - "mysqlPublicPort" INTEGER, - "ftpEnabled" BOOLEAN NOT NULL DEFAULT false, - "ftpUser" TEXT, - "ftpPassword" TEXT, - "ftpPublicPort" INTEGER, - "ftpHostKey" TEXT, - "ftpHostKeyPrivate" TEXT, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "Wordpress_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_Wordpress" ("createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt") SELECT "createdAt", "extraConfig", "ftpEnabled", "ftpHostKey", "ftpHostKeyPrivate", "ftpPassword", "ftpPublicPort", "ftpUser", "id", "mysqlDatabase", "mysqlHost", "mysqlPassword", "mysqlPort", "mysqlPublicPort", "mysqlRootUser", "mysqlRootUserPassword", "mysqlUser", "ownMysql", "serviceId", "tablePrefix", "updatedAt" FROM "Wordpress"; -DROP TABLE "Wordpress"; -ALTER TABLE "new_Wordpress" RENAME TO "Wordpress"; -CREATE UNIQUE INDEX "Wordpress_serviceId_key" ON "Wordpress"("serviceId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221104092223_default_redirect_proxy/migration.sql b/apps/api/prisma/migrations/20221104092223_default_redirect_proxy/migration.sql deleted file mode 100644 index 8584fe25c..000000000 --- a/apps/api/prisma/migrations/20221104092223_default_redirect_proxy/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "proxyDefaultRedirect" TEXT; diff --git a/apps/api/prisma/migrations/20221114093217_application_storage_path_migration/migration.sql b/apps/api/prisma/migrations/20221114093217_application_storage_path_migration/migration.sql deleted file mode 100644 index 6913c8b00..000000000 --- a/apps/api/prisma/migrations/20221114093217_application_storage_path_migration/migration.sql +++ /dev/null @@ -1,45 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "isAPIDebuggingEnabled" BOOLEAN DEFAULT false, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "proxyPassword" TEXT NOT NULL, - "proxyUser" TEXT NOT NULL, - "proxyHash" TEXT, - "proxyDefaultRedirect" TEXT, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "DNSServers" TEXT, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, - "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false -); -INSERT INTO "new_Setting" ("DNSServers", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "proxyHash", "proxyPassword", "proxyUser", "updatedAt") SELECT "DNSServers", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "proxyHash", "proxyPassword", "proxyUser", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -CREATE TABLE "new_ApplicationPersistentStorage" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "path" TEXT NOT NULL, - "oldPath" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationPersistentStorage_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationPersistentStorage" ("applicationId", "createdAt", "id", "path", "updatedAt") SELECT "applicationId", "createdAt", "id", "path", "updatedAt" FROM "ApplicationPersistentStorage"; -DROP TABLE "ApplicationPersistentStorage"; -ALTER TABLE "new_ApplicationPersistentStorage" RENAME TO "ApplicationPersistentStorage"; -CREATE UNIQUE INDEX "ApplicationPersistentStorage_applicationId_path_key" ON "ApplicationPersistentStorage"("applicationId", "path"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221123122143_remote_haproxy_from_db/migration.sql b/apps/api/prisma/migrations/20221123122143_remote_haproxy_from_db/migration.sql deleted file mode 100644 index 1acf261a5..000000000 --- a/apps/api/prisma/migrations/20221123122143_remote_haproxy_from_db/migration.sql +++ /dev/null @@ -1,37 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `proxyHash` on the `Setting` table. All the data in the column will be lost. - - You are about to drop the column `proxyPassword` on the `Setting` table. All the data in the column will be lost. - - You are about to drop the column `proxyUser` on the `Setting` table. All the data in the column will be lost. - -*/ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "DNSServers" TEXT, - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, - "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, - "proxyDefaultRedirect" TEXT, - "isAPIDebuggingEnabled" BOOLEAN DEFAULT false, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221123133429_docker_registries/migration.sql b/apps/api/prisma/migrations/20221123133429_docker_registries/migration.sql deleted file mode 100644 index abebc5514..000000000 --- a/apps/api/prisma/migrations/20221123133429_docker_registries/migration.sql +++ /dev/null @@ -1,59 +0,0 @@ --- CreateTable -CREATE TABLE "DockerRegistry" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "url" TEXT NOT NULL, - "username" TEXT, - "password" TEXT, - "isSystemWide" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "teamId" TEXT, - CONSTRAINT "DockerRegistry_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); - --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Application" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "fqdn" TEXT, - "repository" TEXT, - "configHash" TEXT, - "branch" TEXT, - "buildPack" TEXT, - "projectId" INTEGER, - "port" INTEGER, - "exposePort" INTEGER, - "installCommand" TEXT, - "buildCommand" TEXT, - "startCommand" TEXT, - "baseDirectory" TEXT, - "publishDirectory" TEXT, - "deploymentType" TEXT, - "phpModules" TEXT, - "pythonWSGI" TEXT, - "pythonModule" TEXT, - "pythonVariable" TEXT, - "dockerFileLocation" TEXT, - "denoMainFile" TEXT, - "denoOptions" TEXT, - "dockerComposeFile" TEXT, - "dockerComposeFileLocation" TEXT, - "dockerComposeConfiguration" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "destinationDockerId" TEXT, - "gitSourceId" TEXT, - "baseImage" TEXT, - "baseBuildImage" TEXT, - "dockerRegistryId" TEXT NOT NULL DEFAULT '0', - CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "Application_dockerRegistryId_fkey" FOREIGN KEY ("dockerRegistryId") REFERENCES "DockerRegistry" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_Application" ("baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "exposePort", "fqdn", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt") SELECT "baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "exposePort", "fqdn", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt" FROM "Application"; -DROP TABLE "Application"; -ALTER TABLE "new_Application" RENAME TO "Application"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221128104158_do_not_track/migration.sql b/apps/api/prisma/migrations/20221128104158_do_not_track/migration.sql deleted file mode 100644 index 9cf26d8a8..000000000 --- a/apps/api/prisma/migrations/20221128104158_do_not_track/migration.sql +++ /dev/null @@ -1,30 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "DNSServers" TEXT, - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, - "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, - "proxyDefaultRedirect" TEXT, - "doNotTrack" BOOLEAN NOT NULL DEFAULT false, - "isAPIDebuggingEnabled" BOOLEAN DEFAULT false, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221128104718_fix_defaults/migration.sql b/apps/api/prisma/migrations/20221128104718_fix_defaults/migration.sql deleted file mode 100644 index c1152cdc3..000000000 --- a/apps/api/prisma/migrations/20221128104718_fix_defaults/migration.sql +++ /dev/null @@ -1,60 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "DNSServers" TEXT, - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, - "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, - "proxyDefaultRedirect" TEXT, - "doNotTrack" BOOLEAN NOT NULL DEFAULT false, - "isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT false, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", coalesce("isAPIDebuggingEnabled", false) AS "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -CREATE TABLE "new_GlitchTip" ( - "id" TEXT NOT NULL PRIMARY KEY, - "postgresqlUser" TEXT NOT NULL, - "postgresqlPassword" TEXT NOT NULL, - "postgresqlDatabase" TEXT NOT NULL, - "postgresqlPublicPort" INTEGER, - "secretKeyBase" TEXT, - "defaultEmail" TEXT NOT NULL, - "defaultUsername" TEXT NOT NULL, - "defaultPassword" TEXT NOT NULL, - "defaultEmailFrom" TEXT NOT NULL DEFAULT 'glitchtip@domain.tdl', - "emailSmtpHost" TEXT DEFAULT 'domain.tdl', - "emailSmtpPort" INTEGER DEFAULT 25, - "emailSmtpUser" TEXT, - "emailSmtpPassword" TEXT, - "emailSmtpUseTls" BOOLEAN NOT NULL DEFAULT false, - "emailSmtpUseSsl" BOOLEAN NOT NULL DEFAULT false, - "emailBackend" TEXT, - "mailgunApiKey" TEXT, - "sendgridApiKey" TEXT, - "enableOpenUserRegistration" BOOLEAN NOT NULL DEFAULT true, - "serviceId" TEXT NOT NULL, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "GlitchTip_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_GlitchTip" ("createdAt", "defaultEmail", "defaultEmailFrom", "defaultPassword", "defaultUsername", "emailBackend", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", "emailSmtpUseSsl", "emailSmtpUseTls", "emailSmtpUser", "enableOpenUserRegistration", "id", "mailgunApiKey", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "sendgridApiKey", "serviceId", "updatedAt") SELECT "createdAt", "defaultEmail", "defaultEmailFrom", "defaultPassword", "defaultUsername", "emailBackend", "emailSmtpHost", "emailSmtpPassword", "emailSmtpPort", coalesce("emailSmtpUseSsl", false) AS "emailSmtpUseSsl", coalesce("emailSmtpUseTls", false) AS "emailSmtpUseTls", "emailSmtpUser", "enableOpenUserRegistration", "id", "mailgunApiKey", "postgresqlDatabase", "postgresqlPassword", "postgresqlPublicPort", "postgresqlUser", "secretKeyBase", "sendgridApiKey", "serviceId", "updatedAt" FROM "GlitchTip"; -DROP TABLE "GlitchTip"; -ALTER TABLE "new_GlitchTip" RENAME TO "GlitchTip"; -CREATE UNIQUE INDEX "GlitchTip_serviceId_key" ON "GlitchTip"("serviceId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221128105615_custom_sentry/migration.sql b/apps/api/prisma/migrations/20221128105615_custom_sentry/migration.sql deleted file mode 100644 index 00857eb0d..000000000 --- a/apps/api/prisma/migrations/20221128105615_custom_sentry/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Setting" ADD COLUMN "sentryDSN" TEXT; diff --git a/apps/api/prisma/migrations/20221129081832_fix_defaults/migration.sql b/apps/api/prisma/migrations/20221129081832_fix_defaults/migration.sql deleted file mode 100644 index 96f6de3fb..000000000 --- a/apps/api/prisma/migrations/20221129081832_fix_defaults/migration.sql +++ /dev/null @@ -1,31 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8', - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, - "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, - "proxyDefaultRedirect" TEXT, - "doNotTrack" BOOLEAN NOT NULL DEFAULT false, - "sentryDSN" TEXT, - "isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT coalesce("DNSServers", '1.1.1.1,8.8.8.8') AS "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221129121702_preview_separator/migration.sql b/apps/api/prisma/migrations/20221129121702_preview_separator/migration.sql deleted file mode 100644 index e0d64cdc8..000000000 --- a/apps/api/prisma/migrations/20221129121702_preview_separator/migration.sql +++ /dev/null @@ -1,33 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_Setting" ( - "id" TEXT NOT NULL PRIMARY KEY, - "fqdn" TEXT, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "minPort" INTEGER NOT NULL DEFAULT 9000, - "maxPort" INTEGER NOT NULL DEFAULT 9100, - "DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8', - "ipv4" TEXT, - "ipv6" TEXT, - "arch" TEXT, - "concurrentBuilds" INTEGER NOT NULL DEFAULT 1, - "applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false, - "numberOfDockerImagesKeptLocally" INTEGER NOT NULL DEFAULT 3, - "proxyDefaultRedirect" TEXT, - "doNotTrack" BOOLEAN NOT NULL DEFAULT false, - "sentryDSN" TEXT, - "previewSeparator" TEXT NOT NULL DEFAULT '.', - "isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false, - "isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true, - "isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false, - "isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true, - "isTraefikUsed" BOOLEAN NOT NULL DEFAULT true, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL -); -INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "numberOfDockerImagesKeptLocally", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", 3, "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting"; -DROP TABLE "Setting"; -ALTER TABLE "new_Setting" RENAME TO "Setting"; -CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221129130036_keep_local_docker_images/migration.sql b/apps/api/prisma/migrations/20221129130036_keep_local_docker_images/migration.sql deleted file mode 100644 index aa8136ed8..000000000 --- a/apps/api/prisma/migrations/20221129130036_keep_local_docker_images/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "gitCommitHash" TEXT; diff --git a/apps/api/prisma/migrations/20221130142058_reconfigure_docker_registries/migration.sql b/apps/api/prisma/migrations/20221130142058_reconfigure_docker_registries/migration.sql deleted file mode 100644 index c77afb687..000000000 --- a/apps/api/prisma/migrations/20221130142058_reconfigure_docker_registries/migration.sql +++ /dev/null @@ -1,66 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `isSystemWide` on the `DockerRegistry` table. All the data in the column will be lost. - -*/ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_DockerRegistry" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "url" TEXT NOT NULL, - "username" TEXT, - "password" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "teamId" TEXT, - CONSTRAINT "DockerRegistry_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_DockerRegistry" ("createdAt", "id", "name", "password", "teamId", "updatedAt", "url", "username") SELECT "createdAt", "id", "name", "password", "teamId", "updatedAt", "url", "username" FROM "DockerRegistry"; -DROP TABLE "DockerRegistry"; -ALTER TABLE "new_DockerRegistry" RENAME TO "DockerRegistry"; -CREATE TABLE "new_Application" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "fqdn" TEXT, - "repository" TEXT, - "configHash" TEXT, - "branch" TEXT, - "buildPack" TEXT, - "projectId" INTEGER, - "port" INTEGER, - "exposePort" INTEGER, - "installCommand" TEXT, - "buildCommand" TEXT, - "startCommand" TEXT, - "baseDirectory" TEXT, - "publishDirectory" TEXT, - "deploymentType" TEXT, - "phpModules" TEXT, - "pythonWSGI" TEXT, - "pythonModule" TEXT, - "pythonVariable" TEXT, - "dockerFileLocation" TEXT, - "denoMainFile" TEXT, - "denoOptions" TEXT, - "dockerComposeFile" TEXT, - "dockerComposeFileLocation" TEXT, - "dockerComposeConfiguration" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "destinationDockerId" TEXT, - "gitSourceId" TEXT, - "gitCommitHash" TEXT, - "baseImage" TEXT, - "baseBuildImage" TEXT, - "dockerRegistryId" TEXT, - CONSTRAINT "Application_gitSourceId_fkey" FOREIGN KEY ("gitSourceId") REFERENCES "GitSource" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "Application_destinationDockerId_fkey" FOREIGN KEY ("destinationDockerId") REFERENCES "DestinationDocker" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "Application_dockerRegistryId_fkey" FOREIGN KEY ("dockerRegistryId") REFERENCES "DockerRegistry" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_Application" ("baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "dockerRegistryId", "exposePort", "fqdn", "gitCommitHash", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt") SELECT "baseBuildImage", "baseDirectory", "baseImage", "branch", "buildCommand", "buildPack", "configHash", "createdAt", "denoMainFile", "denoOptions", "deploymentType", "destinationDockerId", "dockerComposeConfiguration", "dockerComposeFile", "dockerComposeFileLocation", "dockerFileLocation", "dockerRegistryId", "exposePort", "fqdn", "gitCommitHash", "gitSourceId", "id", "installCommand", "name", "phpModules", "port", "projectId", "publishDirectory", "pythonModule", "pythonVariable", "pythonWSGI", "repository", "startCommand", "updatedAt" FROM "Application"; -DROP TABLE "Application"; -ALTER TABLE "new_Application" RENAME TO "Application"; -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20221201115801_simple_dockerfile_deployment/migration.sql b/apps/api/prisma/migrations/20221201115801_simple_dockerfile_deployment/migration.sql deleted file mode 100644 index b3406e59e..000000000 --- a/apps/api/prisma/migrations/20221201115801_simple_dockerfile_deployment/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "simpleDockerfile" TEXT; diff --git a/apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql b/apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql deleted file mode 100644 index 9f85d3518..000000000 --- a/apps/api/prisma/migrations/20221201133847_push_image_to_docker_registry/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Application" ADD COLUMN "dockerRegistryImageName" TEXT; diff --git a/apps/api/prisma/migrations/20221221140911_git_source_custom_user/migration.sql b/apps/api/prisma/migrations/20221221140911_git_source_custom_user/migration.sql deleted file mode 100644 index d9d5a7204..000000000 --- a/apps/api/prisma/migrations/20221221140911_git_source_custom_user/migration.sql +++ /dev/null @@ -1,27 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_GitSource" ( - "id" TEXT NOT NULL PRIMARY KEY, - "name" TEXT NOT NULL, - "forPublic" BOOLEAN NOT NULL DEFAULT false, - "type" TEXT, - "apiUrl" TEXT, - "htmlUrl" TEXT, - "customPort" INTEGER NOT NULL DEFAULT 22, - "customUser" TEXT NOT NULL DEFAULT 'git', - "organization" TEXT, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - "githubAppId" TEXT, - "gitlabAppId" TEXT, - "isSystemWide" BOOLEAN NOT NULL DEFAULT false, - CONSTRAINT "GitSource_gitlabAppId_fkey" FOREIGN KEY ("gitlabAppId") REFERENCES "GitlabApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT "GitSource_githubAppId_fkey" FOREIGN KEY ("githubAppId") REFERENCES "GithubApp" ("id") ON DELETE SET NULL ON UPDATE CASCADE -); -INSERT INTO "new_GitSource" ("apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt") SELECT "apiUrl", "createdAt", "customPort", "forPublic", "githubAppId", "gitlabAppId", "htmlUrl", "id", "isSystemWide", "name", "organization", "type", "updatedAt" FROM "GitSource"; -DROP TABLE "GitSource"; -ALTER TABLE "new_GitSource" RENAME TO "GitSource"; -CREATE UNIQUE INDEX "GitSource_githubAppId_key" ON "GitSource"("githubAppId"); -CREATE UNIQUE INDEX "GitSource_gitlabAppId_key" ON "GitSource"("gitlabAppId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20230117092356_http2_protocol/migration.sql b/apps/api/prisma/migrations/20230117092356_http2_protocol/migration.sql deleted file mode 100644 index 7b24ac820..000000000 --- a/apps/api/prisma/migrations/20230117092356_http2_protocol/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_ApplicationSettings" ( - "id" TEXT NOT NULL PRIMARY KEY, - "applicationId" TEXT NOT NULL, - "dualCerts" BOOLEAN NOT NULL DEFAULT false, - "debug" BOOLEAN NOT NULL DEFAULT false, - "previews" BOOLEAN NOT NULL DEFAULT false, - "autodeploy" BOOLEAN NOT NULL DEFAULT true, - "isBot" BOOLEAN NOT NULL DEFAULT false, - "isPublicRepository" BOOLEAN NOT NULL DEFAULT false, - "isDBBranching" BOOLEAN NOT NULL DEFAULT false, - "isCustomSSL" BOOLEAN NOT NULL DEFAULT false, - "isHttp2" BOOLEAN NOT NULL DEFAULT false, - "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" DATETIME NOT NULL, - CONSTRAINT "ApplicationSettings_applicationId_fkey" FOREIGN KEY ("applicationId") REFERENCES "Application" ("id") ON DELETE RESTRICT ON UPDATE CASCADE -); -INSERT INTO "new_ApplicationSettings" ("applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt") SELECT "applicationId", "autodeploy", "createdAt", "debug", "dualCerts", "id", "isBot", "isCustomSSL", "isDBBranching", "isPublicRepository", "previews", "updatedAt" FROM "ApplicationSettings"; -DROP TABLE "ApplicationSettings"; -ALTER TABLE "new_ApplicationSettings" RENAME TO "ApplicationSettings"; -CREATE UNIQUE INDEX "ApplicationSettings_applicationId_key" ON "ApplicationSettings"("applicationId"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/apps/api/prisma/migrations/20230307101148_add_host_volumes/migration.sql b/apps/api/prisma/migrations/20230307101148_add_host_volumes/migration.sql deleted file mode 100644 index 220ad995c..000000000 --- a/apps/api/prisma/migrations/20230307101148_add_host_volumes/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "ApplicationPersistentStorage" ADD COLUMN "hostPath" TEXT; diff --git a/apps/api/prisma/migrations/migration_lock.toml b/apps/api/prisma/migrations/migration_lock.toml deleted file mode 100644 index e5e5c4705..000000000 --- a/apps/api/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "sqlite" \ No newline at end of file diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma deleted file mode 100644 index 7dca8314b..000000000 --- a/apps/api/prisma/schema.prisma +++ /dev/null @@ -1,708 +0,0 @@ -generator client { - provider = "prisma-client-js" - binaryTargets = ["native"] -} - -datasource db { - provider = "sqlite" - url = env("COOLIFY_DATABASE_URL") -} - -model Certificate { - id String @id @default(cuid()) - key String - cert String - team Team? @relation(fields: [teamId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - teamId String? -} - -model Setting { - id String @id @default(cuid()) - fqdn String? @unique - dualCerts Boolean @default(false) - minPort Int @default(9000) - maxPort Int @default(9100) - DNSServers String @default("1.1.1.1,8.8.8.8") - ipv4 String? - ipv6 String? - arch String? - concurrentBuilds Int @default(1) - applicationStoragePathMigrationFinished Boolean @default(false) - numberOfDockerImagesKeptLocally Int @default(3) - proxyDefaultRedirect String? - doNotTrack Boolean @default(false) - sentryDSN String? - previewSeparator String @default(".") - isAPIDebuggingEnabled Boolean @default(false) - isRegistrationEnabled Boolean @default(true) - isAutoUpdateEnabled Boolean @default(false) - isDNSCheckEnabled Boolean @default(true) - isTraefikUsed Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} - -model User { - id String @id @unique @default(cuid()) - email String @unique - type String - password String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - permission Permission[] - teams Team[] -} - -model Permission { - id String @id @default(cuid()) - userId String - teamId String - permission String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - team Team @relation(fields: [teamId], references: [id]) - user User @relation(fields: [userId], references: [id]) -} - -model Team { - id String @id @default(cuid()) - name String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - databaseId String? - serviceId String? - permissions Permission[] - sshKey SshKey[] - applications Application[] - database Database[] - destinationDocker DestinationDocker[] - gitSources GitSource[] - gitHubApps GithubApp[] - gitLabApps GitlabApp[] - service Service[] - users User[] - certificate Certificate[] - dockerRegistry DockerRegistry[] -} - -model TeamInvitation { - id String @id @default(cuid()) - uid String - email String - teamId String - teamName String - permission String - createdAt DateTime @default(now()) -} - -model Application { - id String @id @default(cuid()) - name String - fqdn String? - repository String? - configHash String? - branch String? - buildPack String? - projectId Int? - port Int? - exposePort Int? - installCommand String? - buildCommand String? - startCommand String? - baseDirectory String? - publishDirectory String? - deploymentType String? - phpModules String? - pythonWSGI String? - pythonModule String? - pythonVariable String? - dockerFileLocation String? - denoMainFile String? - denoOptions String? - dockerComposeFile String? - dockerComposeFileLocation String? - dockerComposeConfiguration String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - destinationDockerId String? - gitSourceId String? - gitCommitHash String? - baseImage String? - baseBuildImage String? - settings ApplicationSettings? - dockerRegistryId String? - dockerRegistryImageName String? - simpleDockerfile String? - - persistentStorage ApplicationPersistentStorage[] - secrets Secret[] - teams Team[] - connectedDatabase ApplicationConnectedDatabase? - previewApplication PreviewApplication[] - gitSource GitSource? @relation(fields: [gitSourceId], references: [id]) - destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) - dockerRegistry DockerRegistry? @relation(fields: [dockerRegistryId], references: [id]) -} - -model PreviewApplication { - id String @id @default(cuid()) - pullmergeRequestId String - sourceBranch String - isRandomDomain Boolean @default(false) - customDomain String? - applicationId String - application Application @relation(fields: [applicationId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} - -model ApplicationConnectedDatabase { - id String @id @default(cuid()) - applicationId String @unique - databaseId String? - hostedDatabaseType String? - hostedDatabaseHost String? - hostedDatabasePort Int? - hostedDatabaseName String? - hostedDatabaseUser String? - hostedDatabasePassword String? - hostedDatabaseDBName String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - database Database? @relation(fields: [databaseId], references: [id]) - application Application @relation(fields: [applicationId], references: [id]) -} - -model ApplicationSettings { - id String @id @default(cuid()) - applicationId String @unique - dualCerts Boolean @default(false) - debug Boolean @default(false) - previews Boolean @default(false) - autodeploy Boolean @default(true) - isBot Boolean @default(false) - isPublicRepository Boolean @default(false) - isDBBranching Boolean @default(false) - isCustomSSL Boolean @default(false) - isHttp2 Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - application Application @relation(fields: [applicationId], references: [id]) -} - -model ApplicationPersistentStorage { - id String @id @default(cuid()) - applicationId String - hostPath String? - path String - oldPath Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - application Application @relation(fields: [applicationId], references: [id]) - - @@unique([applicationId, path]) -} - -model ServicePersistentStorage { - id String @id @default(cuid()) - serviceId String - path String - volumeName String? - predefined Boolean @default(false) - containerId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) - - @@unique([serviceId, containerId, path]) -} - -model Secret { - id String @id @default(cuid()) - name String - value String - isPRMRSecret Boolean @default(false) - isBuildSecret Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - applicationId String - application Application @relation(fields: [applicationId], references: [id]) - - @@unique([name, applicationId, isPRMRSecret]) -} - -model ServiceSecret { - id String @id @default(cuid()) - name String - value String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - serviceId String - service Service @relation(fields: [serviceId], references: [id]) - - @@unique([name, serviceId]) -} - -model BuildLog { - id String @id @default(cuid()) - applicationId String? - buildId String - line String - time BigInt -} - -model Build { - id String @id @default(cuid()) - type String - applicationId String? - destinationDockerId String? - gitSourceId String? - githubAppId String? - gitlabAppId String? - commit String? - pullmergeRequestId String? - previewApplicationId String? - forceRebuild Boolean @default(false) - sourceBranch String? - sourceRepository String? - branch String? - status String? @default("queued") - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} - -model DestinationDocker { - id String @id @default(cuid()) - network String @unique - name String - engine String? - remoteEngine Boolean @default(false) - remoteIpAddress String? - remoteUser String? - remotePort Int? - remoteVerified Boolean @default(false) - isCoolifyProxyUsed Boolean? @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - sshKeyId String? - sshKey SshKey? @relation(fields: [sshKeyId], references: [id]) - sshLocalPort Int? - application Application[] - database Database[] - service Service[] - teams Team[] -} - -model SshKey { - id String @id @default(cuid()) - name String - privateKey String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - teamId String? - team Team? @relation(fields: [teamId], references: [id]) - destinationDocker DestinationDocker[] -} - -model DockerRegistry { - id String @id @default(cuid()) - name String - url String - username String? - password String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - teamId String? - team Team? @relation(fields: [teamId], references: [id]) - application Application[] -} - -model GitSource { - id String @id @default(cuid()) - name String - forPublic Boolean @default(false) - type String? - apiUrl String? - htmlUrl String? - customPort Int @default(22) - customUser String @default("git") - organization String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - githubAppId String? @unique - gitlabAppId String? @unique - isSystemWide Boolean @default(false) - gitlabApp GitlabApp? @relation(fields: [gitlabAppId], references: [id]) - githubApp GithubApp? @relation(fields: [githubAppId], references: [id]) - application Application[] - teams Team[] -} - -model GithubApp { - id String @id @default(cuid()) - name String? @unique - appId Int? - installationId Int? - clientId String? - clientSecret String? - webhookSecret String? - privateKey String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - gitSource GitSource? - teams Team[] -} - -model GitlabApp { - id String @id @default(cuid()) - oauthId Int @unique - groupName String? @unique - deployKeyId Int? - privateSshKey String? - publicSshKey String? - webhookToken String? - appId String? - appSecret String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - gitSource GitSource? - teams Team[] -} - -model Database { - id String @id @default(cuid()) - name String - publicPort Int? - defaultDatabase String? - type String? - version String? - dbUser String? - dbUserPassword String? - rootUser String? - rootUserPassword String? - destinationDockerId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) - settings DatabaseSettings? - teams Team[] - applicationConnectedDatabase ApplicationConnectedDatabase[] - databaseSecret DatabaseSecret[] -} - -model DatabaseSecret { - id String @id @default(cuid()) - name String - value String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - databaseId String - database Database @relation(fields: [databaseId], references: [id]) - - @@unique([name, databaseId]) -} - -model DatabaseSettings { - id String @id @default(cuid()) - databaseId String @unique - isPublic Boolean @default(false) - appendOnly Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - database Database @relation(fields: [databaseId], references: [id]) -} - -model Service { - id String @id @default(cuid()) - name String - fqdn String? - exposePort Int? - dualCerts Boolean @default(false) - type String? - version String? - templateVersion String @default("0.0.0") - destinationDockerId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id]) - persistentStorage ServicePersistentStorage[] - serviceSecret ServiceSecret[] - serviceSetting ServiceSetting[] - teams Team[] - - fider Fider? - ghost Ghost? - glitchTip GlitchTip? - hasura Hasura? - meiliSearch MeiliSearch? - minio Minio? - moodle Moodle? - plausibleAnalytics PlausibleAnalytics? - umami Umami? - vscodeserver Vscodeserver? - wordpress Wordpress? - appwrite Appwrite? - searxng Searxng? - weblate Weblate? - taiga Taiga? -} - -model ServiceSetting { - id String @id @default(cuid()) - serviceId String - name String - value String - variableName String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) - - @@unique([serviceId, name]) -} - -model PlausibleAnalytics { - id String @id @default(cuid()) - email String? - username String? - password String - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - secretKeyBase String? - scriptName String @default("plausible.js") - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Minio { - id String @id @default(cuid()) - rootUser String - rootUserPassword String - publicPort Int? - apiFqdn String? - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Vscodeserver { - id String @id @default(cuid()) - password String - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Wordpress { - id String @id @default(cuid()) - extraConfig String? - tablePrefix String? - ownMysql Boolean @default(false) - mysqlHost String? - mysqlPort Int? - mysqlUser String? - mysqlPassword String? - mysqlRootUser String? - mysqlRootUserPassword String? - mysqlDatabase String? - mysqlPublicPort Int? - ftpEnabled Boolean @default(false) - ftpUser String? - ftpPassword String? - ftpPublicPort Int? - ftpHostKey String? - ftpHostKeyPrivate String? - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Ghost { - id String @id @default(cuid()) - defaultEmail String - defaultPassword String - mariadbUser String - mariadbPassword String - mariadbRootUser String - mariadbRootUserPassword String - mariadbDatabase String? - mariadbPublicPort Int? - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model MeiliSearch { - id String @id @default(cuid()) - masterKey String - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Umami { - id String @id @default(cuid()) - serviceId String @unique - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - umamiAdminPassword String - hashSalt String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Hasura { - id String @id @default(cuid()) - serviceId String @unique - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - graphQLAdminPassword String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Fider { - id String @id @default(cuid()) - serviceId String @unique - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - jwtSecret String - emailNoreply String? - emailMailgunApiKey String? - emailMailgunDomain String? - emailMailgunRegion String @default("EU") - emailSmtpHost String? - emailSmtpPort Int? - emailSmtpUser String? - emailSmtpPassword String? - emailSmtpEnableStartTls Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Moodle { - id String @id @default(cuid()) - serviceId String @unique - defaultUsername String - defaultPassword String - defaultEmail String - mariadbUser String - mariadbPassword String - mariadbRootUser String - mariadbRootUserPassword String - mariadbDatabase String - mariadbPublicPort Int? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Appwrite { - id String @id @default(cuid()) - serviceId String @unique - opensslKeyV1 String - executorSecret String - redisPassword String - mariadbHost String? - mariadbPort Int @default(3306) - mariadbUser String - mariadbPassword String - mariadbRootUser String - mariadbRootUserPassword String - mariadbDatabase String - mariadbPublicPort Int? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model GlitchTip { - id String @id @default(cuid()) - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - secretKeyBase String? - defaultEmail String - defaultUsername String - defaultPassword String - defaultEmailFrom String @default("glitchtip@domain.tdl") - emailSmtpHost String? @default("domain.tdl") - emailSmtpPort Int? @default(25) - emailSmtpUser String? - emailSmtpPassword String? - emailSmtpUseTls Boolean @default(false) - emailSmtpUseSsl Boolean @default(false) - emailBackend String? - mailgunApiKey String? - sendgridApiKey String? - enableOpenUserRegistration Boolean @default(true) - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Searxng { - id String @id @default(cuid()) - secretKey String - redisPassword String - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Weblate { - id String @id @default(cuid()) - adminPassword String - postgresqlHost String - postgresqlPort Int - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} - -model Taiga { - id String @id @default(cuid()) - secretKey String - erlangSecret String - djangoAdminPassword String - djangoAdminUser String - rabbitMQUser String - rabbitMQPassword String - postgresqlHost String - postgresqlPort Int - postgresqlUser String - postgresqlPassword String - postgresqlDatabase String - postgresqlPublicPort Int? - serviceId String @unique - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - service Service @relation(fields: [serviceId], references: [id]) -} diff --git a/apps/api/prisma/seed.js b/apps/api/prisma/seed.js deleted file mode 100644 index 3d4b16a28..000000000 --- a/apps/api/prisma/seed.js +++ /dev/null @@ -1,113 +0,0 @@ -const dotEnvExtended = require('dotenv-extended'); -dotEnvExtended.load(); -const crypto = require('crypto'); -const { PrismaClient } = require('@prisma/client'); -const prisma = new PrismaClient(); -const algorithm = 'aes-256-ctr'; - -async function main() { - // Enable registration for the first user - const settingsFound = await prisma.setting.findFirst({}); - if (!settingsFound) { - await prisma.setting.create({ - data: { - id: '0', - arch: process.arch, - } - }); - } else { - await prisma.setting.update({ - where: { - id: settingsFound.id - }, - data: { - id: '0' - } - }); - } - // Create local docker engine - const localDocker = await prisma.destinationDocker.findFirst({ - where: { engine: '/var/run/docker.sock' } - }); - if (!localDocker) { - await prisma.destinationDocker.create({ - data: { - engine: '/var/run/docker.sock', - name: 'Local Docker', - isCoolifyProxyUsed: true, - network: 'coolify' - } - }); - } - - // Set auto-update based on env variable - const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true'; - await prisma.setting.update({ - where: { - id: '0' - }, - data: { - isAutoUpdateEnabled - } - }); - // Create public github source - const github = await prisma.gitSource.findFirst({ - where: { htmlUrl: 'https://github.com', forPublic: true } - }); - if (!github) { - await prisma.gitSource.create({ - data: { - apiUrl: 'https://api.github.com', - htmlUrl: 'https://github.com', - forPublic: true, - name: 'Github Public', - type: 'github' - } - }); - } - // Create public gitlab source - const gitlab = await prisma.gitSource.findFirst({ - where: { htmlUrl: 'https://gitlab.com', forPublic: true } - }); - if (!gitlab) { - await prisma.gitSource.create({ - data: { - apiUrl: 'https://gitlab.com/api/v4', - htmlUrl: 'https://gitlab.com', - forPublic: true, - name: 'Gitlab Public', - type: 'gitlab' - } - }); - } - // Set new preview secrets - const secrets = await prisma.secret.findMany({ where: { isPRMRSecret: false } }) - if (secrets.length > 0) { - for (const secret of secrets) { - const previewSecrets = await prisma.secret.findMany({ where: { applicationId: secret.applicationId, name: secret.name, isPRMRSecret: true } }) - if (previewSecrets.length === 0) { - await prisma.secret.create({ data: { ...secret, id: undefined, isPRMRSecret: true } }) - } - } - } -} -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - }); - -const encrypt = (text) => { - if (text) { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); - const encrypted = Buffer.concat([cipher.update(text), cipher.final()]); - return JSON.stringify({ - iv: iv.toString('hex'), - content: encrypted.toString('hex') - }); - } -}; \ No newline at end of file diff --git a/apps/api/scripts/generateTags.mjs b/apps/api/scripts/generateTags.mjs deleted file mode 100644 index fc2c88540..000000000 --- a/apps/api/scripts/generateTags.mjs +++ /dev/null @@ -1,67 +0,0 @@ -import fs from 'fs/promises'; -import yaml from 'js-yaml'; -import got from 'got'; - -const repositories = []; -const templates = await fs.readFile('./apps/api/devTemplates.yaml', 'utf8'); -const devTemplates = yaml.load(templates); -for (const template of devTemplates) { - let image = template.services['$$id'].image.replaceAll(':$$core_version', ''); - if (!image.includes('/')) { - image = `library/${image}`; - } - repositories.push({ image, name: template.type }); -} -const services = [] -const numberOfTags = 30; -// const semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/g) -for (const repository of repositories) { - console.log('Querying', repository.name, 'at', repository.image); - let semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/g) - if (repository.name.startsWith('wordpress')) { - semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-php(0|[1-9]\d*)$/g) - } - if (repository.name.startsWith('minio')) { - semverRegex = new RegExp(/^RELEASE.*$/g) - } - if (repository.name.startsWith('fider')) { - semverRegex = new RegExp(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-([0-9]+)$/g) - } - if (repository.name.startsWith('searxng')) { - semverRegex = new RegExp(/^\d{4}[\.\-](0?[1-9]|[12][0-9]|3[01])[\.\-](0?[1-9]|1[012]).*$/) - } - if (repository.name.startsWith('umami')) { - semverRegex = new RegExp(/^postgresql-v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-([0-9]+)$/g) - } - if (repository.image.includes('ghcr.io')) { - const { execaCommand } = await import('execa'); - const { stdout } = await execaCommand(`docker run --rm quay.io/skopeo/stable list-tags docker://${repository.image}`); - if (stdout) { - const json = JSON.parse(stdout); - const semverTags = json.Tags.filter((tag) => semverRegex.test(tag)) - let tags = semverTags.length > 10 ? semverTags.sort().reverse().slice(0, numberOfTags) : json.Tags.sort().reverse().slice(0, numberOfTags) - if (!tags.includes('latest')) { - tags.push('latest') - } - services.push({ name: repository.name, image: repository.image, tags }) - } - } else { - const { token } = await got.get(`https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repository.image}:pull`).json() - let data = await got.get(`https://registry-1.docker.io/v2/${repository.image}/tags/list`, { - headers: { - Authorization: `Bearer ${token}` - } - }).json() - const semverTags = data.tags.filter((tag) => semverRegex.test(tag)) - let tags = semverTags.length > 10 ? semverTags.sort().reverse().slice(0, numberOfTags) : data.tags.sort().reverse().slice(0, numberOfTags) - if (!tags.includes('latest')) { - tags.push('latest') - } - services.push({ - name: repository.name, - image: repository.image, - tags - }) - } -} -await fs.writeFile('./apps/api/devTags.json', JSON.stringify(services)); \ No newline at end of file diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts deleted file mode 100644 index d53d02fa6..000000000 --- a/apps/api/src/index.ts +++ /dev/null @@ -1,656 +0,0 @@ -import Fastify from 'fastify'; -import cors from '@fastify/cors'; -import serve from '@fastify/static'; -import env from '@fastify/env'; -import cookie from '@fastify/cookie'; -import multipart from '@fastify/multipart'; -import path, { join } from 'path'; -import autoLoad from '@fastify/autoload'; -import socketIO from 'fastify-socket.io'; -import socketIOServer from './realtime'; - -import { - cleanupDockerStorage, - createRemoteEngineConfiguration, - decrypt, - executeCommand, - generateDatabaseConfiguration, - isDev, - listSettings, - prisma, - sentryDSN, - startTraefikProxy, - startTraefikTCPProxy, - version -} from './lib/common'; -import { scheduler } from './lib/scheduler'; -import { compareVersions } from 'compare-versions'; -import Graceful from '@ladjs/graceful'; -import yaml from 'js-yaml'; -import fs from 'fs/promises'; -import { verifyRemoteDockerEngineFn } from './routes/api/v1/destinations/handlers'; -import { checkContainer } from './lib/docker'; -import { migrateApplicationPersistentStorage, migrateServicesToNewTemplate } from './lib'; -import { refreshTags, refreshTemplates } from './routes/api/v1/handlers'; -import * as Sentry from '@sentry/node'; -declare module 'fastify' { - interface FastifyInstance { - config: { - COOLIFY_APP_ID: string; - COOLIFY_SECRET_KEY: string; - COOLIFY_DATABASE_URL: string; - COOLIFY_IS_ON: string; - COOLIFY_WHITE_LABELED: string; - COOLIFY_WHITE_LABELED_ICON: string | null; - COOLIFY_AUTO_UPDATE: string; - }; - } -} - -const port = isDev ? 3001 : 3000; -const host = '0.0.0.0'; - -(async () => { - const settings = await prisma.setting.findFirst(); - const fastify = Fastify({ - logger: settings?.isAPIDebuggingEnabled || false, - trustProxy: true - }); - - const schema = { - type: 'object', - required: ['COOLIFY_SECRET_KEY', 'COOLIFY_DATABASE_URL', 'COOLIFY_IS_ON'], - properties: { - COOLIFY_APP_ID: { - type: 'string' - }, - COOLIFY_SECRET_KEY: { - type: 'string' - }, - COOLIFY_DATABASE_URL: { - type: 'string', - default: 'file:../db/dev.db' - }, - COOLIFY_IS_ON: { - type: 'string', - default: 'docker' - }, - COOLIFY_WHITE_LABELED: { - type: 'string', - default: 'false' - }, - COOLIFY_WHITE_LABELED_ICON: { - type: 'string', - default: null - }, - COOLIFY_AUTO_UPDATE: { - type: 'string', - default: 'false' - } - } - }; - const options = { - schema, - dotenv: true - }; - fastify.register(env, options); - if (!isDev) { - fastify.register(serve, { - root: path.join(__dirname, './public'), - preCompressed: true - }); - fastify.setNotFoundHandler(async function (request, reply) { - if (request.raw.url && request.raw.url.startsWith('/api')) { - return reply.status(404).send({ - success: false - }); - } - return reply.status(200).sendFile('index.html'); - }); - } - fastify.register(multipart, { limits: { fileSize: 100000 } }); - fastify.register(autoLoad, { - dir: join(__dirname, 'plugins') - }); - fastify.register(autoLoad, { - dir: join(__dirname, 'routes') - }); - fastify.register(cookie); - fastify.register(cors); - fastify.register(socketIO, { - cors: { - origin: isDev ? '*' : '' - } - }); - // To detect allowed origins - // fastify.addHook('onRequest', async (request, reply) => { - // console.log(request.headers.host) - // let allowedList = ['coolify:3000']; - // const { ipv4, ipv6, fqdn } = await prisma.setting.findFirst({}) - - // ipv4 && allowedList.push(`${ipv4}:3000`); - // ipv6 && allowedList.push(ipv6); - // fqdn && allowedList.push(getDomain(fqdn)); - // isDev && allowedList.push('localhost:3000') && allowedList.push('localhost:3001') && allowedList.push('host.docker.internal:3001'); - // const remotes = await prisma.destinationDocker.findMany({ where: { remoteEngine: true, remoteVerified: true } }) - // if (remotes.length > 0) { - // remotes.forEach(remote => { - // allowedList.push(`${remote.remoteIpAddress}:3000`); - // }) - // } - // if (!allowedList.includes(request.headers.host)) { - // // console.log('not allowed', request.headers.host) - // } - // }) - - try { - await fastify.listen({ port, host }); - await socketIOServer(fastify); - console.log(`Coolify's API is listening on ${host}:${port}`); - - migrateServicesToNewTemplate(); - await migrateApplicationPersistentStorage(); - await initServer(); - - const graceful = new Graceful({ brees: [scheduler] }); - graceful.listen(); - - setInterval(async () => { - if (!scheduler.workers.has('deployApplication')) { - scheduler.run('deployApplication'); - } - }, 2000); - - // autoUpdater - setInterval(async () => { - await autoUpdater(); - }, 60000 * 15); - - // cleanupStorage - setInterval(async () => { - await cleanupStorage(); - }, 60000 * 15); - - // Cleanup stucked containers (not defined in Coolify, but still running and managed by Coolify) - setInterval(async () => { - await cleanupStuckedContainers(); - }, 60000); - - // checkProxies, checkFluentBit & refresh templates - setInterval(async () => { - await checkProxies(); - await checkFluentBit(); - }, 60000); - - // Refresh and check templates - setInterval(async () => { - await refreshTemplates(); - }, 60000); - - setInterval(async () => { - await refreshTags(); - }, 60000); - - setInterval( - async () => { - await migrateServicesToNewTemplate(); - }, - isDev ? 10000 : 60000 - ); - - setInterval(async () => { - await copySSLCertificates(); - }, 10000); - - await Promise.all([ - getTagsTemplates(), - getArch(), - getIPAddress(), - configureRemoteDockers() - // cleanupStuckedContainers() - ]); - } catch (error) { - console.error(error); - process.exit(1); - } -})(); - -async function getIPAddress() { - const { publicIpv4, publicIpv6 } = await import('public-ip'); - try { - const settings = await listSettings(); - if (!settings.ipv4) { - const ipv4 = await publicIpv4({ timeout: 2000 }); - console.log(`Getting public IPv4 address...`); - await prisma.setting.update({ where: { id: settings.id }, data: { ipv4 } }); - } - - if (!settings.ipv6) { - const ipv6 = await publicIpv6({ timeout: 2000 }); - console.log(`Getting public IPv6 address...`); - await prisma.setting.update({ where: { id: settings.id }, data: { ipv6 } }); - } - } catch (error) {} -} -async function getTagsTemplates() { - const { default: got } = await import('got'); - try { - if (isDev) { - let templates = await fs.readFile('./devTemplates.yaml', 'utf8'); - let tags = await fs.readFile('./devTags.json', 'utf8'); - try { - if (await fs.stat('./testTemplate.yaml')) { - templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8')); - } - } catch (error) {} - try { - if (await fs.stat('./testTags.json')) { - const testTags = await fs.readFile('./testTags.json', 'utf8'); - if (testTags.length > 0) { - tags = JSON.stringify(JSON.parse(tags).concat(JSON.parse(testTags))); - } - } - } catch (error) {} - - await fs.writeFile('./templates.json', JSON.stringify(yaml.load(templates))); - await fs.writeFile('./tags.json', tags); - console.log('[004] Tags and templates loaded in dev mode...'); - } else { - const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text(); - const response = await got - .get('https://get.coollabs.io/coolify/service-templates.yaml') - .text(); - await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response))); - await fs.writeFile('/app/tags.json', tags); - console.log('[004] Tags and templates loaded...'); - } - } catch (error) { - console.log("Couldn't get latest templates."); - console.log(error); - } -} -async function initServer() { - const appId = process.env['COOLIFY_APP_ID']; - const settings = await prisma.setting.findUnique({ where: { id: '0' } }); - try { - if (settings.doNotTrack === true) { - console.log('[000] Telemetry disabled...'); - } else { - if (settings.sentryDSN !== sentryDSN) { - await prisma.setting.update({ where: { id: '0' }, data: { sentryDSN } }); - } - // Initialize Sentry - // Sentry.init({ - // dsn: sentryDSN, - // environment: isDev ? 'development' : 'production', - // release: version - // }); - // console.log('[000] Sentry initialized...') - } - } catch (error) { - console.error(error); - } - try { - console.log(`[001] Initializing server...`); - await executeCommand({ command: `docker network create --attachable coolify` }); - } catch (error) {} - try { - console.log(`[002] Cleanup stucked builds...`); - const isOlder = compareVersions('3.8.1', version); - if (isOlder === 1) { - await prisma.build.updateMany({ - where: { status: { in: ['running', 'queued'] } }, - data: { status: 'failed' } - }); - } - } catch (error) {} - try { - console.log('[003] Cleaning up old build sources under /tmp/build-sources/...'); - await fs.rm('/tmp/build-sources', { recursive: true, force: true }); - } catch (error) { - console.log(error); - } -} - -async function getArch() { - try { - const settings = await prisma.setting.findFirst({}); - if (settings && !settings.arch) { - console.log(`Getting architecture...`); - await prisma.setting.update({ where: { id: settings.id }, data: { arch: process.arch } }); - } - } catch (error) {} -} - -async function cleanupStuckedContainers() { - try { - const destinationDockers = await prisma.destinationDocker.findMany(); - let enginesDone = new Set(); - for (const destination of destinationDockers) { - if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) - return; - if (destination.engine) { - enginesDone.add(destination.engine); - } - if (destination.remoteIpAddress) { - if (!destination.remoteVerified) continue; - enginesDone.add(destination.remoteIpAddress); - } - const { stdout: containers } = await executeCommand({ - dockerId: destination.id, - command: `docker container ps -a --filter "label=coolify.managed=true" --format '{{ .Names}}'` - }); - if (containers) { - const containersArray = containers.trim().split('\n'); - if (containersArray.length > 0) { - for (const container of containersArray) { - const containerId = container.split('-')[0]; - const application = await prisma.application.findFirst({ - where: { id: { startsWith: containerId } } - }); - const service = await prisma.service.findFirst({ - where: { id: { startsWith: containerId } } - }); - const database = await prisma.database.findFirst({ - where: { id: { startsWith: containerId } } - }); - if (!application && !service && !database) { - await executeCommand({ command: `docker container rm -f ${container}` }); - } - } - } - } - } - } catch (error) { - console.log(error); - } -} -async function configureRemoteDockers() { - try { - const remoteDocker = await prisma.destinationDocker.findMany({ - where: { remoteVerified: true, remoteEngine: true } - }); - if (remoteDocker.length > 0) { - console.log(`Verifying Remote Docker Engines...`); - for (const docker of remoteDocker) { - console.log('Verifying:', docker.remoteIpAddress); - await verifyRemoteDockerEngineFn(docker.id); - } - } - } catch (error) { - console.log(error); - } -} - -async function autoUpdater() { - try { - const { default: got } = await import('got'); - const currentVersion = version; - const { coolify } = await got - .get('https://get.coollabs.io/versions.json', { - searchParams: { - appId: process.env['COOLIFY_APP_ID'] || undefined, - version: currentVersion - } - }) - .json(); - const latestVersion = coolify.main.version; - const isUpdateAvailable = compareVersions(latestVersion, currentVersion); - if (isUpdateAvailable === 1) { - const activeCount = 0; - if (activeCount === 0) { - if (!isDev) { - const { isAutoUpdateEnabled } = await prisma.setting.findFirst(); - if (isAutoUpdateEnabled) { - await executeCommand({ command: `docker pull ghcr.io/coollabsio/coolify:${latestVersion}` }); - await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` }); - await executeCommand({ - command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` - }); - await executeCommand({ - shell: true, - command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db ghcr.io/coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"` - }); - } - } else { - console.log('Updating (not really in dev mode).'); - } - } - } - } catch (error) { - console.log(error); - } -} - -async function checkFluentBit() { - try { - if (!isDev) { - const engine = '/var/run/docker.sock'; - const { id } = await prisma.destinationDocker.findFirst({ - where: { engine, network: 'coolify' } - }); - const { found } = await checkContainer({ - dockerId: id, - container: 'coolify-fluentbit', - remove: true - }); - if (!found) { - await executeCommand({ shell: true, command: `env | grep '^COOLIFY' > .env` }); - await executeCommand({ command: `docker compose up -d fluent-bit` }); - } - } - } catch (error) { - console.log(error); - } -} -async function checkProxies() { - try { - const { default: isReachable } = await import('is-port-reachable'); - let portReachable; - - const { arch, ipv4, ipv6 } = await listSettings(); - - // Coolify Proxy local - const engine = '/var/run/docker.sock'; - const localDocker = await prisma.destinationDocker.findFirst({ - where: { engine, network: 'coolify', isCoolifyProxyUsed: true } - }); - if (localDocker) { - portReachable = await isReachable(80, { host: ipv4 || ipv6 }); - if (!portReachable) { - await startTraefikProxy(localDocker.id); - } - } - // Coolify Proxy remote - const remoteDocker = await prisma.destinationDocker.findMany({ - where: { remoteEngine: true, remoteVerified: true } - }); - if (remoteDocker.length > 0) { - for (const docker of remoteDocker) { - if (docker.isCoolifyProxyUsed) { - portReachable = await isReachable(80, { host: docker.remoteIpAddress }); - if (!portReachable) { - await startTraefikProxy(docker.id); - } - } - try { - await createRemoteEngineConfiguration(docker.id); - } catch (error) {} - } - } - // TCP Proxies - const databasesWithPublicPort = await prisma.database.findMany({ - where: { publicPort: { not: null } }, - include: { settings: true, destinationDocker: true } - }); - for (const database of databasesWithPublicPort) { - const { destinationDockerId, destinationDocker, publicPort, id } = database; - if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) { - const { privatePort } = generateDatabaseConfiguration(database, arch); - await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort); - } - } - const wordpressWithFtp = await prisma.wordpress.findMany({ - where: { ftpPublicPort: { not: null } }, - include: { service: { include: { destinationDocker: true } } } - }); - for (const ftp of wordpressWithFtp) { - const { service, ftpPublicPort } = ftp; - const { destinationDockerId, destinationDocker, id } = service; - if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) { - await startTraefikTCPProxy(destinationDocker, id, ftpPublicPort, 22, 'wordpressftp'); - } - } - - // HTTP Proxies - // const minioInstances = await prisma.minio.findMany({ - // where: { publicPort: { not: null } }, - // include: { service: { include: { destinationDocker: true } } } - // }); - // for (const minio of minioInstances) { - // const { service, publicPort } = minio; - // const { destinationDockerId, destinationDocker, id } = service; - // if (destinationDockerId && destinationDocker.isCoolifyProxyUsed) { - // await startTraefikTCPProxy(destinationDocker, id, publicPort, 9000); - // } - // } - } catch (error) {} -} - -async function copySSLCertificates() { - try { - const pAll = await import('p-all'); - const actions = []; - const certificates = await prisma.certificate.findMany({ include: { team: true } }); - const teamIds = certificates.map((c) => c.teamId); - const destinations = await prisma.destinationDocker.findMany({ - where: { isCoolifyProxyUsed: true, teams: { some: { id: { in: [...teamIds] } } } } - }); - for (const certificate of certificates) { - const { id, key, cert } = certificate; - const decryptedKey = decrypt(key); - await fs.writeFile(`/tmp/${id}-key.pem`, decryptedKey); - await fs.writeFile(`/tmp/${id}-cert.pem`, cert); - for (const destination of destinations) { - if (destination.remoteEngine) { - if (destination.remoteVerified) { - const { id: dockerId, remoteIpAddress } = destination; - actions.push(async () => copyRemoteCertificates(id, dockerId, remoteIpAddress)); - } - } else { - actions.push(async () => copyLocalCertificates(id)); - } - } - } - await pAll.default(actions, { concurrency: 1 }); - } catch (error) { - console.log(error); - } finally { - await executeCommand({ command: `find /tmp/ -maxdepth 1 -type f -name '*-*.pem' -delete` }); - } -} - -async function copyRemoteCertificates(id: string, dockerId: string, remoteIpAddress: string) { - try { - await executeCommand({ - command: `scp /tmp/${id}-cert.pem /tmp/${id}-key.pem ${remoteIpAddress}:/tmp/` - }); - await executeCommand({ - sshCommand: true, - shell: true, - dockerId, - command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'` - }); - await executeCommand({ - sshCommand: true, - dockerId, - command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` - }); - await executeCommand({ - sshCommand: true, - dockerId, - command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` - }); - } catch (error) { - console.log({ error }); - } -} -async function copyLocalCertificates(id: string) { - try { - await executeCommand({ - command: `docker exec coolify-proxy sh -c 'test -d /etc/traefik/acme/custom/ || mkdir -p /etc/traefik/acme/custom/'`, - shell: true - }); - await executeCommand({ - command: `docker cp /tmp/${id}-key.pem coolify-proxy:/etc/traefik/acme/custom/` - }); - await executeCommand({ - command: `docker cp /tmp/${id}-cert.pem coolify-proxy:/etc/traefik/acme/custom/` - }); - } catch (error) { - console.log({ error }); - } -} - -async function cleanupStorage() { - const destinationDockers = await prisma.destinationDocker.findMany(); - let enginesDone = new Set(); - for (const destination of destinationDockers) { - if (enginesDone.has(destination.engine) || enginesDone.has(destination.remoteIpAddress)) return; - if (destination.engine) { - enginesDone.add(destination.engine); - } - if (destination.remoteIpAddress) { - if (!destination.remoteVerified) continue; - enginesDone.add(destination.remoteIpAddress); - } - let lowDiskSpace = false; - try { - let stdout = null; - if (!isDev) { - const output = await executeCommand({ - dockerId: destination.id, - command: `CONTAINER=$(docker ps -lq | head -1) && docker exec $CONTAINER sh -c 'df -kPT /'`, - shell: true - }); - stdout = output.stdout; - } else { - const output = await executeCommand({ - command: `df -kPT /` - }); - stdout = output.stdout; - } - let lines = stdout.trim().split('\n'); - let header = lines[0]; - let regex = - /^Filesystem\s+|Type\s+|1024-blocks|\s+Used|\s+Available|\s+Capacity|\s+Mounted on\s*$/g; - const boundaries = []; - let match; - - while ((match = regex.exec(header))) { - boundaries.push(match[0].length); - } - - boundaries[boundaries.length - 1] = -1; - const data = lines.slice(1).map((line) => { - const cl = boundaries.map((boundary) => { - const column = boundary > 0 ? line.slice(0, boundary) : line; - line = line.slice(boundary); - return column.trim(); - }); - return { - capacity: Number.parseInt(cl[5], 10) / 100 - }; - }); - if (data.length > 0) { - const { capacity } = data[0]; - if (capacity > 0.8) { - lowDiskSpace = true; - } - } - } catch (error) {} - if (lowDiskSpace) { - await cleanupDockerStorage(destination.id); - } - } -} diff --git a/apps/api/src/jobs/deployApplication.ts b/apps/api/src/jobs/deployApplication.ts deleted file mode 100644 index 5c35307b9..000000000 --- a/apps/api/src/jobs/deployApplication.ts +++ /dev/null @@ -1,821 +0,0 @@ -import { parentPort } from 'node:worker_threads'; -import crypto from 'crypto'; -import fs from 'fs/promises'; -import yaml from 'js-yaml'; - -import { - copyBaseConfigurationFiles, - makeLabelForSimpleDockerfile, - makeLabelForStandaloneApplication, - saveBuildLog, - saveDockerRegistryCredentials, - setDefaultConfiguration -} from '../lib/buildPacks/common'; -import { - createDirectories, - decrypt, - defaultComposeConfiguration, - getDomain, - prisma, - decryptApplication, - isDev, - pushToRegistry, - executeCommand, - generateSecrets -} from '../lib/common'; -import * as importers from '../lib/importers'; -import * as buildpacks from '../lib/buildPacks'; - -(async () => { - if (parentPort) { - parentPort.on('message', async (message) => { - if (message === 'error') throw new Error('oops'); - if (message === 'cancel') { - parentPort.postMessage('cancelled'); - await prisma.$disconnect(); - process.exit(0); - } - }); - const pThrottle = await import('p-throttle'); - const throttle = pThrottle.default({ - limit: 1, - interval: 2000 - }); - - const th = throttle(async () => { - try { - const queuedBuilds = await prisma.build.findMany({ - where: { status: { in: ['queued', 'running'] } }, - orderBy: { createdAt: 'asc' } - }); - const { concurrentBuilds } = await prisma.setting.findFirst({}); - if (queuedBuilds.length > 0) { - parentPort.postMessage({ deploying: true }); - const concurrency = concurrentBuilds; - const pAll = await import('p-all'); - const actions = []; - - for (const queueBuild of queuedBuilds) { - actions.push(async () => { - let application = await prisma.application.findUnique({ - where: { id: queueBuild.applicationId }, - include: { - dockerRegistry: true, - destinationDocker: true, - gitSource: { include: { githubApp: true, gitlabApp: true } }, - persistentStorage: true, - secrets: true, - settings: true, - teams: true - } - }); - - let { - id: buildId, - type, - gitSourceId, - sourceBranch = null, - pullmergeRequestId = null, - previewApplicationId = null, - forceRebuild, - sourceRepository = null - } = queueBuild; - application = decryptApplication(application); - - if (!gitSourceId && application.simpleDockerfile) { - const { - id: applicationId, - destinationDocker, - destinationDockerId, - secrets, - port, - persistentStorage, - exposePort, - simpleDockerfile, - dockerRegistry - } = application; - const { workdir } = await createDirectories({ repository: applicationId, buildId }); - try { - if (queueBuild.status === 'running') { - await saveBuildLog({ - line: 'Building halted, restarting...', - buildId, - applicationId: application.id - }); - } - const volumes = - persistentStorage?.map((storage) => { - if (storage.oldPath) { - return `${applicationId}${storage.path - .replace(/\//gi, '-') - .replace('-app', '')}:${storage.path}`; - } - if (storage.hostPath) { - return `${storage.hostPath}:${storage.path}` - } - return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`; - }) || []; - - if (destinationDockerId) { - await prisma.build.update({ - where: { id: buildId }, - data: { status: 'running' } - }); - try { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ - dockerId: destinationDockerId, - command: `docker stop -t 0 ${container}` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker rm --force ${container}` - }); - } - } - } - } catch (error) { - // - } - let envs = []; - if (secrets.length > 0) { - envs = [ - ...envs, - ...generateSecrets(secrets, pullmergeRequestId, false, port) - ]; - } - await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile); - if (dockerRegistry) { - const { url, username, password } = dockerRegistry; - await saveDockerRegistryCredentials({ url, username, password, workdir }); - } - - const labels = makeLabelForSimpleDockerfile({ - applicationId, - type, - port: exposePort ? `${exposePort}:${port}` : port - }); - try { - const composeVolumes = volumes.filter(v => { - if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) { - return v; - } - }).map((volume) => { - return { - [`${volume.split(':')[0]}`]: { - name: volume.split(':')[0] - } - }; - }); - const composeFile = { - version: '3.8', - services: { - [applicationId]: { - build: { - context: workdir - }, - image: `${applicationId}:${buildId}`, - container_name: applicationId, - volumes, - labels, - environment: envs, - depends_on: [], - expose: [port], - ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(destinationDocker.network) - } - }, - networks: { - [destinationDocker.network]: { - external: true - } - }, - volumes: Object.assign({}, ...composeVolumes) - }; - await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); - await executeCommand({ - debug: true, - dockerId: destinationDocker.id, - command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d` - }); - await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId }); - } catch (error) { - await saveBuildLog({ line: error, buildId, applicationId }); - const foundBuild = await prisma.build.findUnique({ where: { id: buildId } }); - if (foundBuild) { - await prisma.build.update({ - where: { id: buildId }, - data: { - status: 'failed' - } - }); - } - throw new Error(error); - } - } - } catch (error) { - const foundBuild = await prisma.build.findUnique({ where: { id: buildId } }); - if (foundBuild) { - await prisma.build.update({ - where: { id: buildId }, - data: { - status: 'failed' - } - }); - } - if (error !== 1) { - await saveBuildLog({ line: error, buildId, applicationId: application.id }); - } - if (error instanceof Error) { - await saveBuildLog({ - line: error.message, - buildId, - applicationId: application.id - }); - } - await fs.rm(workdir, { recursive: true, force: true }); - return; - } - try { - if (application.dockerRegistryImageName) { - const customTag = application.dockerRegistryImageName.split(':')[1] || buildId; - const imageName = application.dockerRegistryImageName.split(':')[0]; - await saveBuildLog({ - line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`, - buildId, - applicationId: application.id - }); - await pushToRegistry(application, workdir, buildId, imageName, customTag); - await saveBuildLog({ line: 'Success', buildId, applicationId: application.id }); - } - } catch (error) { - if (error.stdout) { - await saveBuildLog({ line: error.stdout, buildId, applicationId }); - } - if (error.stderr) { - await saveBuildLog({ line: error.stderr, buildId, applicationId }); - } - } finally { - await fs.rm(workdir, { recursive: true, force: true }); - await prisma.build.update({ - where: { id: buildId }, - data: { status: 'success' } - }); - } - return; - } - - const originalApplicationId = application.id; - const { - id: applicationId, - name, - destinationDocker, - destinationDockerId, - gitSource, - configHash, - fqdn, - projectId, - secrets, - phpModules, - settings, - persistentStorage, - pythonWSGI, - pythonModule, - pythonVariable, - denoOptions, - exposePort, - baseImage, - baseBuildImage, - deploymentType, - gitCommitHash, - dockerRegistry - } = application; - - let { - branch, - repository, - buildPack, - port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory, - dockerFileLocation, - dockerComposeFileLocation, - dockerComposeConfiguration, - denoMainFile - } = application; - - let imageId = applicationId; - let domain = getDomain(fqdn); - - let location = null; - - let tag = null; - let customTag = null; - let imageName = null; - - let imageFoundLocally = false; - let imageFoundRemotely = false; - - if (pullmergeRequestId) { - const previewApplications = await prisma.previewApplication.findMany({ - where: { applicationId: originalApplicationId, pullmergeRequestId } - }); - if (previewApplications.length > 0) { - previewApplicationId = previewApplications[0].id; - } - // Previews, we need to get the source branch and set subdomain - branch = sourceBranch; - domain = `${pullmergeRequestId}.${domain}`; - imageId = `${applicationId}-${pullmergeRequestId}`; - repository = sourceRepository || repository; - } - const { workdir, repodir } = await createDirectories({ repository, buildId }); - try { - if (queueBuild.status === 'running') { - await saveBuildLog({ - line: 'Building halted, restarting...', - buildId, - applicationId: application.id - }); - } - - const currentHash = crypto - .createHash('sha256') - .update( - JSON.stringify({ - pythonWSGI, - pythonModule, - pythonVariable, - deploymentType, - denoOptions, - baseImage, - baseBuildImage, - buildPack, - port, - exposePort, - installCommand, - buildCommand, - startCommand, - secrets, - branch, - repository, - fqdn - }) - ) - .digest('hex'); - const { debug } = settings; - if (!debug) { - await saveBuildLog({ - line: `Debug logging is disabled. Enable it above if necessary!`, - buildId, - applicationId - }); - } - const volumes = - persistentStorage?.map((storage) => { - if (storage.oldPath) { - return `${applicationId}${storage.path - .replace(/\//gi, '-') - .replace('-app', '')}:${storage.path}`; - } - if (storage.hostPath) { - return `${storage.hostPath}:${storage.path}` - } - return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`; - }) || []; - - try { - dockerComposeConfiguration = JSON.parse(dockerComposeConfiguration); - } catch (error) { } - let deployNeeded = true; - let destinationType; - - if (destinationDockerId) { - destinationType = 'docker'; - } - if (destinationType === 'docker') { - await prisma.build.update({ - where: { id: buildId }, - data: { status: 'running' } - }); - - const configuration = await setDefaultConfiguration(application); - - buildPack = configuration.buildPack; - port = configuration.port; - installCommand = configuration.installCommand; - startCommand = configuration.startCommand; - buildCommand = configuration.buildCommand; - publishDirectory = configuration.publishDirectory || ''; - baseDirectory = configuration.baseDirectory || ''; - dockerFileLocation = configuration.dockerFileLocation; - dockerComposeFileLocation = configuration.dockerComposeFileLocation; - denoMainFile = configuration.denoMainFile; - const commit = await importers[gitSource.type]({ - applicationId, - debug, - workdir, - repodir, - githubAppId: gitSource.githubApp?.id, - gitlabAppId: gitSource.gitlabApp?.id, - customPort: gitSource.customPort, - customUser: gitSource.customUser, - gitCommitHash, - configuration, - repository, - branch, - buildId, - apiUrl: gitSource.apiUrl, - htmlUrl: gitSource.htmlUrl, - projectId, - deployKeyId: gitSource.gitlabApp?.deployKeyId || null, - privateSshKey: decrypt(gitSource.gitlabApp?.privateSshKey) || null, - forPublic: gitSource.forPublic - }); - if (!commit) { - throw new Error('No commit found?'); - } - tag = commit.slice(0, 7); - if (pullmergeRequestId) { - tag = `${commit.slice(0, 7)}-${pullmergeRequestId}`; - } - if (application.dockerRegistryImageName) { - imageName = application.dockerRegistryImageName.split(':')[0]; - customTag = application.dockerRegistryImageName.split(':')[1] || tag; - } else { - customTag = tag; - imageName = applicationId; - } - - if (pullmergeRequestId) { - customTag = `${customTag}-${pullmergeRequestId}`; - } - - try { - await prisma.build.update({ where: { id: buildId }, data: { commit } }); - } catch (err) { } - - if (!pullmergeRequestId) { - if (configHash !== currentHash) { - deployNeeded = true; - if (configHash) { - await saveBuildLog({ - line: 'Configuration changed', - buildId, - applicationId - }); - } - } else { - deployNeeded = false; - } - } else { - deployNeeded = true; - } - - try { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker image inspect ${applicationId}:${tag}` - }); - imageFoundLocally = true; - } catch (error) { - // - } - if (dockerRegistry) { - const { url, username, password } = dockerRegistry; - location = await saveDockerRegistryCredentials({ - url, - username, - password, - workdir - }); - } - - try { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker ${location ? `--config ${location}` : '' - } pull ${imageName}:${customTag}` - }); - imageFoundRemotely = true; - } catch (error) { - // - } - let imageFound = `${applicationId}:${tag}`; - if (imageFoundRemotely) { - imageFound = `${imageName}:${customTag}`; - } - await copyBaseConfigurationFiles( - buildPack, - workdir, - buildId, - applicationId, - baseImage - ); - const labels = makeLabelForStandaloneApplication({ - applicationId, - fqdn, - name, - type, - pullmergeRequestId, - buildPack, - repository, - branch, - projectId, - port: exposePort ? `${exposePort}:${port}` : port, - commit, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory - }); - if (forceRebuild) deployNeeded = true; - if ((!imageFoundLocally && !imageFoundRemotely) || deployNeeded) { - if (buildpacks[buildPack]) - await buildpacks[buildPack]({ - dockerId: destinationDocker.id, - network: destinationDocker.network, - buildId, - applicationId, - domain, - name, - type, - volumes, - labels, - pullmergeRequestId, - buildPack, - repository, - branch, - projectId, - publishDirectory, - debug, - commit, - tag, - workdir, - port: exposePort ? `${exposePort}:${port}` : port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - secrets, - phpModules, - pythonWSGI, - pythonModule, - pythonVariable, - dockerFileLocation, - dockerComposeConfiguration, - dockerComposeFileLocation, - denoMainFile, - denoOptions, - baseImage, - baseBuildImage, - deploymentType, - forceRebuild - }); - else { - await saveBuildLog({ - line: `Build pack ${buildPack} not found`, - buildId, - applicationId - }); - throw new Error(`Build pack ${buildPack} not found.`); - } - } else { - if (imageFoundRemotely || deployNeeded) { - await saveBuildLog({ - line: `Container image ${imageFound} found in Docker Registry - reuising it`, - buildId, - applicationId - }); - } else { - if (imageFoundLocally || deployNeeded) { - await saveBuildLog({ - line: `Container image ${imageFound} found locally - reuising it`, - buildId, - applicationId - }); - } - } - } - - if (buildPack === 'compose') { - const fileYaml = `${workdir}${baseDirectory}${dockerComposeFileLocation}`; - try { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=coolify.applicationId=${applicationId}' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ - dockerId: destinationDockerId, - command: `docker stop -t 0 ${container}` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker rm --force ${container}` - }); - } - } - } - } catch (error) { - // - } - try { - await executeCommand({ - debug, - buildId, - applicationId, - dockerId: destinationDocker.id, - command: `docker compose --project-directory ${workdir} -f ${fileYaml} up -d` - }); - await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId }); - await prisma.build.update({ - where: { id: buildId }, - data: { status: 'success' } - }); - await prisma.application.update({ - where: { id: applicationId }, - data: { configHash: currentHash } - }); - } catch (error) { - await saveBuildLog({ line: error, buildId, applicationId }); - const foundBuild = await prisma.build.findUnique({ where: { id: buildId } }); - if (foundBuild) { - await prisma.build.update({ - where: { id: buildId }, - data: { - status: 'failed' - } - }); - } - throw new Error(error); - } - } else { - try { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.service=${pullmergeRequestId ? imageId : applicationId - }' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ - dockerId: destinationDockerId, - command: `docker stop -t 0 ${container}` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker rm --force ${container}` - }); - } - } - } - } catch (error) { - // - } - let envs = []; - if (secrets.length > 0) { - envs = [ - ...envs, - ...generateSecrets(secrets, pullmergeRequestId, false, port) - ]; - } - if (dockerRegistry) { - const { url, username, password } = dockerRegistry; - await saveDockerRegistryCredentials({ url, username, password, workdir }); - } - try { - const composeVolumes = volumes.filter(v => { - if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) { - return v; - } - }).map((volume) => { - return { - [`${volume.split(':')[0]}`]: { - name: volume.split(':')[0] - } - }; - }); - const composeFile = { - version: '3.8', - services: { - [imageId]: { - image: imageFound, - container_name: imageId, - volumes, - environment: envs, - labels, - depends_on: [], - expose: [port], - ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(destinationDocker.network) - } - }, - networks: { - [destinationDocker.network]: { - external: true - } - }, - volumes: Object.assign({}, ...composeVolumes) - }; - await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); - await executeCommand({ - debug, - dockerId: destinationDocker.id, - command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d` - }); - await saveBuildLog({ line: 'Deployed 🎉', buildId, applicationId }); - } catch (error) { - await saveBuildLog({ line: error, buildId, applicationId }); - const foundBuild = await prisma.build.findUnique({ where: { id: buildId } }); - if (foundBuild) { - await prisma.build.update({ - where: { id: buildId }, - data: { - status: 'failed' - } - }); - } - throw new Error(error); - } - - if (!pullmergeRequestId) - await prisma.application.update({ - where: { id: applicationId }, - data: { configHash: currentHash } - }); - } - } - } catch (error) { - const foundBuild = await prisma.build.findUnique({ where: { id: buildId } }); - if (foundBuild) { - await prisma.build.update({ - where: { id: buildId }, - data: { - status: 'failed' - } - }); - } - if (error !== 1) { - await saveBuildLog({ line: error, buildId, applicationId: application.id }); - } - if (error instanceof Error) { - await saveBuildLog({ - line: error.message, - buildId, - applicationId: application.id - }); - } - await fs.rm(workdir, { recursive: true, force: true }); - return; - } - try { - if (application.dockerRegistryImageName && (!imageFoundRemotely || forceRebuild)) { - await saveBuildLog({ - line: `Pushing ${imageName}:${customTag} to Docker Registry... It could take a while...`, - buildId, - applicationId: application.id - }); - await pushToRegistry(application, workdir, tag, imageName, customTag); - await saveBuildLog({ line: 'Success', buildId, applicationId: application.id }); - } - } catch (error) { - if (error.stdout) { - await saveBuildLog({ line: error.stdout, buildId, applicationId }); - } - if (error.stderr) { - await saveBuildLog({ line: error.stderr, buildId, applicationId }); - } - } finally { - await fs.rm(workdir, { recursive: true, force: true }); - await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } }); - } - }); - } - await pAll.default(actions, { concurrency }); - } - } catch (error) { - console.log(error); - } - }); - while (true) { - await th(); - } - } else process.exit(0); -})(); diff --git a/apps/api/src/lib.ts b/apps/api/src/lib.ts deleted file mode 100644 index c4e02cb21..000000000 --- a/apps/api/src/lib.ts +++ /dev/null @@ -1,530 +0,0 @@ -import cuid from "cuid"; -import { decrypt, encrypt, fixType, generatePassword, generateToken, prisma } from "./lib/common"; -import { getTemplates } from "./lib/services"; - -export async function migrateApplicationPersistentStorage() { - const settings = await prisma.setting.findFirst() - if (settings) { - const { id: settingsId, applicationStoragePathMigrationFinished } = settings - try { - if (!applicationStoragePathMigrationFinished) { - const applications = await prisma.application.findMany({ include: { persistentStorage: true } }); - for (const application of applications) { - if (application.persistentStorage && application.persistentStorage.length > 0 && application?.buildPack !== 'docker') { - for (const storage of application.persistentStorage) { - let { id, path } = storage - if (!path.startsWith('/app')) { - path = `/app${path}` - await prisma.applicationPersistentStorage.update({ where: { id }, data: { path, oldPath: true } }) - } - } - } - } - } - } catch (error) { - console.log(error) - } finally { - await prisma.setting.update({ where: { id: settingsId }, data: { applicationStoragePathMigrationFinished: true } }) - } - } -} -export async function migrateServicesToNewTemplate() { - // This function migrates old hardcoded services to the new template based services - try { - let templates = await getTemplates() - const services: any = await prisma.service.findMany({ - include: { - destinationDocker: true, - persistentStorage: true, - serviceSecret: true, - serviceSetting: true, - minio: true, - plausibleAnalytics: true, - vscodeserver: true, - wordpress: true, - ghost: true, - meiliSearch: true, - umami: true, - hasura: true, - fider: true, - moodle: true, - appwrite: true, - glitchTip: true, - searxng: true, - weblate: true, - taiga: true, - } - }) - for (const service of services) { - try { - const { id } = service - if (!service.type) { - continue; - } - let template = templates.find(t => fixType(t.type) === fixType(service.type)); - if (template) { - template = JSON.parse(JSON.stringify(template).replaceAll('$$id', service.id)) - if (service.type === 'plausibleanalytics' && service.plausibleAnalytics) await plausibleAnalytics(service, template) - if (service.type === 'fider' && service.fider) await fider(service, template) - if (service.type === 'minio' && service.minio) await minio(service, template) - if (service.type === 'vscodeserver' && service.vscodeserver) await vscodeserver(service, template) - if (service.type === 'wordpress' && service.wordpress) await wordpress(service, template) - if (service.type === 'ghost' && service.ghost) await ghost(service, template) - if (service.type === 'meilisearch' && service.meiliSearch) await meilisearch(service, template) - if (service.type === 'umami' && service.umami) await umami(service, template) - if (service.type === 'hasura' && service.hasura) await hasura(service, template) - if (service.type === 'glitchTip' && service.glitchTip) await glitchtip(service, template) - if (service.type === 'searxng' && service.searxng) await searxng(service, template) - if (service.type === 'weblate' && service.weblate) await weblate(service, template) - if (service.type === 'appwrite' && service.appwrite) await appwrite(service, template) - - try { - await createVolumes(service, template); - } catch (error) { - console.log(error) - } - if (template.variables) { - if (template.variables.length > 0) { - for (const variable of template.variables) { - const { defaultValue } = variable; - const regex = /^\$\$.*\((\d+)\)$/g; - const length = Number(regex.exec(defaultValue)?.[1]) || undefined - if (variable.defaultValue.startsWith('$$generate_password')) { - variable.value = generatePassword({ length }); - } else if (variable.defaultValue.startsWith('$$generate_hex')) { - variable.value = generatePassword({ length, isHex: true }); - } else if (variable.defaultValue.startsWith('$$generate_username')) { - variable.value = cuid(); - } else if (variable.defaultValue.startsWith('$$generate_token')) { - variable.value = generateToken() - } else { - variable.value = variable.defaultValue || ''; - } - } - } - for (const variable of template.variables) { - if (variable.id.startsWith('$$secret_')) { - const found = await prisma.serviceSecret.findFirst({ where: { name: variable.name, serviceId: id } }) - if (!found) { - await prisma.serviceSecret.create({ - data: { name: variable.name, value: encrypt(variable.value) || '', service: { connect: { id } } } - }) - } - - } - if (variable.id.startsWith('$$config_')) { - const found = await prisma.serviceSetting.findFirst({ where: { name: variable.name, serviceId: id } }) - if (!found) { - await prisma.serviceSetting.create({ - data: { name: variable.name, value: variable.value.toString(), variableName: variable.id, service: { connect: { id } } } - }) - } - } - } - } - for (const s of Object.keys(template.services)) { - if (service.type === 'plausibleanalytics') { - continue; - } - if (template.services[s].volumes) { - for (const volume of template.services[s].volumes) { - const [volumeName, path] = volume.split(':') - if (!volumeName.startsWith('/')) { - const found = await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: id } }) - if (!found) { - await prisma.servicePersistentStorage.create({ - data: { volumeName, path, containerId: s, predefined: true, service: { connect: { id } } } - }); - } - } - } - } - } - await prisma.service.update({ where: { id }, data: { templateVersion: template.templateVersion } }) - } - } catch (error) { - console.log(error) - } - } - } catch (error) { - console.log(error) - - } -} -async function appwrite(service: any, template: any) { - const { opensslKeyV1, executorSecret, mariadbHost, mariadbPort, mariadbUser, mariadbPassword, mariadbRootUserPassword, mariadbDatabase } = service.appwrite - - const secrets = [ - `_APP_EXECUTOR_SECRET@@@${executorSecret}`, - `_APP_OPENSSL_KEY_V1@@@${opensslKeyV1}`, - `_APP_DB_PASS@@@${mariadbPassword}`, - `_APP_DB_ROOT_PASS@@@${mariadbRootUserPassword}`, - ] - - const settings = [ - `_APP_DB_HOST@@@${mariadbHost}`, - `_APP_DB_PORT@@@${mariadbPort}`, - `_APP_DB_USER@@@${mariadbUser}`, - `_APP_DB_SCHEMA@@@${mariadbDatabase}`, - ] - await migrateSecrets(secrets, service); - await migrateSettings(settings, service, template); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { appwrite: { disconnect: true } } }) -} -async function weblate(service: any, template: any) { - const { adminPassword, postgresqlUser, postgresqlPassword, postgresqlDatabase } = service.weblate - - const secrets = [ - `WEBLATE_ADMIN_PASSWORD@@@${adminPassword}`, - `POSTGRES_PASSWORD@@@${postgresqlPassword}`, - ] - - const settings = [ - `WEBLATE_SITE_DOMAIN@@@$$generate_domain`, - `POSTGRES_USER@@@${postgresqlUser}`, - `POSTGRES_DATABASE@@@${postgresqlDatabase}`, - `POSTGRES_DB@@@${postgresqlDatabase}`, - `POSTGRES_HOST@@@$$id-postgres`, - `POSTGRES_PORT@@@5432`, - `REDIS_HOST@@@$$id-redis`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { weblate: { disconnect: true } } }) -} -async function searxng(service: any, template: any) { - const { secretKey, redisPassword } = service.searxng - - const secrets = [ - `SECRET_KEY@@@${secretKey}`, - `REDIS_PASSWORD@@@${redisPassword}`, - ] - - const settings = [ - `SEARXNG_BASE_URL@@@$$generate_fqdn` - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { searxng: { disconnect: true } } }) -} -async function glitchtip(service: any, template: any) { - const { postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, defaultEmail, defaultUsername, defaultPassword, defaultEmailFrom, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpUseTls, emailSmtpUseSsl, emailBackend, mailgunApiKey, sendgridApiKey, enableOpenUserRegistration } = service.glitchTip - const { id } = service - - const secrets = [ - `POSTGRES_PASSWORD@@@${postgresqlPassword}`, - `SECRET_KEY@@@${secretKeyBase}`, - `MAILGUN_API_KEY@@@${mailgunApiKey}`, - `SENDGRID_API_KEY@@@${sendgridApiKey}`, - `DJANGO_SUPERUSER_PASSWORD@@@${defaultPassword}`, - emailSmtpUser && emailSmtpPassword && emailSmtpHost && emailSmtpPort && `EMAIL_URL@@@${encrypt(`smtp://${emailSmtpUser}:${decrypt(emailSmtpPassword)}@${emailSmtpHost}:${emailSmtpPort}`)} || ''`, - `DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`, - `REDIS_URL@@@${encrypt(`redis://${id}-redis:6379`)}` - ] - const settings = [ - `POSTGRES_USER@@@${postgresqlUser}`, - `POSTGRES_DB@@@${postgresqlDatabase}`, - `DEFAULT_FROM_EMAIL@@@${defaultEmailFrom}`, - `EMAIL_USE_TLS@@@${emailSmtpUseTls}`, - `EMAIL_USE_SSL@@@${emailSmtpUseSsl}`, - `EMAIL_BACKEND@@@${emailBackend}`, - `ENABLE_OPEN_USER_REGISTRATION@@@${enableOpenUserRegistration}`, - `DJANGO_SUPERUSER_EMAIL@@@${defaultEmail}`, - `DJANGO_SUPERUSER_USERNAME@@@${defaultUsername}`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - await prisma.service.update({ where: { id: service.id }, data: { type: 'glitchtip' } }) - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { glitchTip: { disconnect: true } } }) -} -async function hasura(service: any, template: any) { - const { postgresqlUser, postgresqlPassword, postgresqlDatabase, graphQLAdminPassword } = service.hasura - const { id } = service - - const secrets = [ - `HASURA_GRAPHQL_ADMIN_SECRET@@@${graphQLAdminPassword}`, - `HASURA_GRAPHQL_METADATA_DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`, - `POSTGRES_PASSWORD@@@${postgresqlPassword}`, - ] - const settings = [ - `POSTGRES_USER@@@${postgresqlUser}`, - `POSTGRES_DB@@@${postgresqlDatabase}`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { hasura: { disconnect: true } } }) -} -async function umami(service: any, template: any) { - const { postgresqlUser, postgresqlPassword, postgresqlDatabase, umamiAdminPassword, hashSalt } = service.umami - const { id } = service - const secrets = [ - `HASH_SALT@@@${hashSalt}`, - `POSTGRES_PASSWORD@@@${postgresqlPassword}`, - `ADMIN_PASSWORD@@@${umamiAdminPassword}`, - `DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`, - ] - const settings = [ - `DATABASE_TYPE@@@postgresql`, - `POSTGRES_USER@@@${postgresqlUser}`, - `POSTGRES_DB@@@${postgresqlDatabase}`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - await prisma.service.update({ where: { id: service.id }, data: { type: "umami-postgresql" } }) - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { umami: { disconnect: true } } }) -} -async function meilisearch(service: any, template: any) { - const { masterKey } = service.meiliSearch - - const secrets = [ - `MEILI_MASTER_KEY@@@${masterKey}`, - ] - - // await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { meiliSearch: { disconnect: true } } }) -} -async function ghost(service: any, template: any) { - const { defaultEmail, defaultPassword, mariadbUser, mariadbPassword, mariadbRootUser, mariadbRootUserPassword, mariadbDatabase } = service.ghost - const { fqdn } = service - - const isHttps = fqdn.startsWith('https://'); - - const secrets = [ - `GHOST_PASSWORD@@@${defaultPassword}`, - `MARIADB_PASSWORD@@@${mariadbPassword}`, - `MARIADB_ROOT_PASSWORD@@@${mariadbRootUserPassword}`, - `GHOST_DATABASE_PASSWORD@@@${mariadbPassword}`, - ] - const settings = [ - `GHOST_EMAIL@@@${defaultEmail}`, - `GHOST_DATABASE_HOST@@@${service.id}-mariadb`, - `GHOST_DATABASE_USER@@@${mariadbUser}`, - `GHOST_DATABASE_NAME@@@${mariadbDatabase}`, - `GHOST_DATABASE_PORT_NUMBER@@@3306`, - `MARIADB_USER@@@${mariadbUser}`, - `MARIADB_DATABASE@@@${mariadbDatabase}`, - `MARIADB_ROOT_USER@@@${mariadbRootUser}`, - `GHOST_HOST@@@$$generate_domain`, - `url@@@$$generate_fqdn`, - `GHOST_ENABLE_HTTPS@@@${isHttps ? 'yes' : 'no'}` - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - await prisma.service.update({ where: { id: service.id }, data: { type: "ghost-mariadb" } }) - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { ghost: { disconnect: true } } }) -} -async function wordpress(service: any, template: any) { - const { extraConfig, tablePrefix, ownMysql, mysqlHost, mysqlPort, mysqlUser, mysqlPassword, mysqlRootUser, mysqlRootUserPassword, mysqlDatabase, ftpEnabled, ftpUser, ftpPassword, ftpPublicPort, ftpHostKey, ftpHostKeyPrivate } = service.wordpress - - let settings = [] - let secrets = [] - if (ownMysql) { - secrets = [ - `WORDPRESS_DB_PASSWORD@@@${mysqlPassword}`, - ftpPassword && `COOLIFY_FTP_PASSWORD@@@${ftpPassword}`, - ftpHostKeyPrivate && `COOLIFY_FTP_HOST_KEY_PRIVATE@@@${ftpHostKeyPrivate}`, - ftpHostKey && `COOLIFY_FTP_HOST_KEY@@@${ftpHostKey}`, - ] - settings = [ - `WORDPRESS_CONFIG_EXTRA@@@${extraConfig}`, - `WORDPRESS_DB_HOST@@@${mysqlHost}`, - `WORDPRESS_DB_PORT@@@${mysqlPort}`, - `WORDPRESS_DB_USER@@@${mysqlUser}`, - `WORDPRESS_DB_NAME@@@${mysqlDatabase}`, - ] - } else { - secrets = [ - `MYSQL_ROOT_PASSWORD@@@${mysqlRootUserPassword}`, - `MYSQL_PASSWORD@@@${mysqlPassword}`, - ftpPassword && `COOLIFY_FTP_PASSWORD@@@${ftpPassword}`, - ftpHostKeyPrivate && `COOLIFY_FTP_HOST_KEY_PRIVATE@@@${ftpHostKeyPrivate}`, - ftpHostKey && `COOLIFY_FTP_HOST_KEY@@@${ftpHostKey}`, - ] - settings = [ - `MYSQL_ROOT_USER@@@${mysqlRootUser}`, - `MYSQL_USER@@@${mysqlUser}`, - `MYSQL_DATABASE@@@${mysqlDatabase}`, - `MYSQL_HOST@@@${service.id}-mysql`, - `MYSQL_PORT@@@${mysqlPort}`, - `WORDPRESS_CONFIG_EXTRA@@@${extraConfig}`, - `WORDPRESS_TABLE_PREFIX@@@${tablePrefix}`, - `WORDPRESS_DB_HOST@@@${service.id}-mysql`, - `COOLIFY_OWN_DB@@@${ownMysql}`, - `COOLIFY_FTP_ENABLED@@@${ftpEnabled}`, - `COOLIFY_FTP_USER@@@${ftpUser}`, - `COOLIFY_FTP_PUBLIC_PORT@@@${ftpPublicPort}`, - ] - } - - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - if (ownMysql) { - await prisma.service.update({ where: { id: service.id }, data: { type: "wordpress-only" } }) - } - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { wordpress: { disconnect: true } } }) -} -async function vscodeserver(service: any, template: any) { - const { password } = service.vscodeserver - - const secrets = [ - `PASSWORD@@@${password}`, - ] - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { vscodeserver: { disconnect: true } } }) -} -async function minio(service: any, template: any) { - const { rootUser, rootUserPassword, apiFqdn } = service.minio - const secrets = [ - `MINIO_ROOT_PASSWORD@@@${rootUserPassword}`, - ] - const settings = [ - `MINIO_ROOT_USER@@@${rootUser}`, - `MINIO_SERVER_URL@@@${apiFqdn}`, - `MINIO_BROWSER_REDIRECT_URL@@@$$generate_fqdn`, - `MINIO_DOMAIN@@@$$generate_domain`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { minio: { disconnect: true } } }) -} -async function fider(service: any, template: any) { - const { postgresqlUser, postgresqlPassword, postgresqlDatabase, jwtSecret, emailNoreply, emailMailgunApiKey, emailMailgunDomain, emailMailgunRegion, emailSmtpHost, emailSmtpPort, emailSmtpUser, emailSmtpPassword, emailSmtpEnableStartTls } = service.fider - const { id } = service - const secrets = [ - `JWT_SECRET@@@${jwtSecret}`, - emailMailgunApiKey && `EMAIL_MAILGUN_API@@@${emailMailgunApiKey}`, - emailSmtpPassword && `EMAIL_SMTP_PASSWORD@@@${emailSmtpPassword}`, - `POSTGRES_PASSWORD@@@${postgresqlPassword}`, - `DATABASE_URL@@@${encrypt(`postgresql://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`)}` - ] - const settings = [ - `BASE_URL@@@$$generate_fqdn`, - `EMAIL_NOREPLY@@@${emailNoreply || 'noreply@example.com'}`, - `EMAIL_MAILGUN_DOMAIN@@@${emailMailgunDomain || ''}`, - `EMAIL_MAILGUN_REGION@@@${emailMailgunRegion || ''}`, - `EMAIL_SMTP_HOST@@@${emailSmtpHost || ''}`, - `EMAIL_SMTP_PORT@@@${emailSmtpPort || 587}`, - `EMAIL_SMTP_USER@@@${emailSmtpUser || ''}`, - `EMAIL_SMTP_PASSWORD@@@${emailSmtpPassword || ''}`, - `EMAIL_SMTP_ENABLE_STARTTLS@@@${emailSmtpEnableStartTls || 'false'}`, - `POSTGRES_USER@@@${postgresqlUser}`, - `POSTGRES_DB@@@${postgresqlDatabase}`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { fider: { disconnect: true } } }) - -} -async function plausibleAnalytics(service: any, template: any) { - const { email, username, password, postgresqlUser, postgresqlPassword, postgresqlDatabase, secretKeyBase, scriptName } = service.plausibleAnalytics; - const { id } = service - - const settings = [ - `BASE_URL@@@$$generate_fqdn`, - `ADMIN_USER_EMAIL@@@${email}`, - `ADMIN_USER_NAME@@@${username}`, - `DISABLE_AUTH@@@false`, - `DISABLE_REGISTRATION@@@true`, - `POSTGRESQL_USERNAME@@@${postgresqlUser}`, - `POSTGRESQL_DATABASE@@@${postgresqlDatabase}`, - `SCRIPT_NAME@@@${scriptName}`, - ] - const secrets = [ - `ADMIN_USER_PWD@@@${password}`, - `SECRET_KEY_BASE@@@${secretKeyBase}`, - `POSTGRESQL_PASSWORD@@@${postgresqlPassword}`, - `DATABASE_URL@@@${encrypt(`postgres://${postgresqlUser}:${decrypt(postgresqlPassword)}@${id}-postgresql:5432/${postgresqlDatabase}`)}`, - ] - await migrateSettings(settings, service, template); - await migrateSecrets(secrets, service); - - // Disconnect old service data - // await prisma.service.update({ where: { id: service.id }, data: { plausibleAnalytics: { disconnect: true } } }) -} -async function migrateSettings(settings: any[], service: any, template: any) { - for (const setting of settings) { - try { - if (!setting) continue; - let [name, value] = setting.split('@@@') - let minio = name - if (name === 'MINIO_SERVER_URL') { - name = 'coolify_fqdn_minio_console' - } - if (!value || value === 'null') { - continue; - } - let variableName = template.variables.find((v: any) => v.name === name)?.id - if (!variableName) { - variableName = `$$config_${name.toLowerCase()}` - } - // console.log('Migrating setting', name, value, 'for service', service.id, ', service name:', service.name, 'variableName: ', variableName) - - await prisma.serviceSetting.findFirst({ where: { name: minio, serviceId: service.id } }) || await prisma.serviceSetting.create({ data: { name: minio, value, variableName, service: { connect: { id: service.id } } } }) - } catch (error) { - console.log(error) - } - } -} -async function migrateSecrets(secrets: any[], service: any) { - for (const secret of secrets) { - try { - if (!secret) continue; - let [name, value] = secret.split('@@@') - if (!value || value === 'null') { - continue - } - // console.log('Migrating secret', name, value, 'for service', service.id, ', service name:', service.name) - await prisma.serviceSecret.findFirst({ where: { name, serviceId: service.id } }) || await prisma.serviceSecret.create({ data: { name, value, service: { connect: { id: service.id } } } }) - } catch (error) { - console.log(error) - } - } -} -async function createVolumes(service: any, template: any) { - const volumes = []; - for (const s of Object.keys(template.services)) { - if (template.services[s].volumes && template.services[s].volumes.length > 0) { - for (const volume of template.services[s].volumes) { - let volumeName = volume.split(':')[0] - const volumePath = volume.split(':')[1] - let volumeService = s - if (service.type === 'plausibleanalytics' && service.plausibleAnalytics?.id) { - let volumeId = volumeName.split('-')[0] - volumeName = volumeName.replace(volumeId, service.plausibleAnalytics.id) - } - volumes.push(`${volumeName}@@@${volumePath}@@@${volumeService}`) - } - } - } - for (const volume of volumes) { - const [volumeName, path, containerId] = volume.split('@@@') - // console.log('Creating volume', volumeName, path, containerId, 'for service', service.id, ', service name:', service.name) - await prisma.servicePersistentStorage.findFirst({ where: { volumeName, serviceId: service.id } }) || await prisma.servicePersistentStorage.create({ data: { volumeName, path, containerId, predefined: true, service: { connect: { id: service.id } } } }) - } -} diff --git a/apps/api/src/lib/buildPacks/common.ts b/apps/api/src/lib/buildPacks/common.ts deleted file mode 100644 index afcfe9207..000000000 --- a/apps/api/src/lib/buildPacks/common.ts +++ /dev/null @@ -1,847 +0,0 @@ -import { - base64Encode, - decrypt, - encrypt, - executeCommand, - generateSecrets, - generateTimestamp, - getDomain, - isARM, - isDev, - prisma, - version -} from '../common'; -import { promises as fs } from 'fs'; -import { day } from '../dayjs'; - -const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy']; -const nodeBased = [ - 'react', - 'preact', - 'vuejs', - 'svelte', - 'gatsby', - 'astro', - 'eleventy', - 'node', - 'nestjs', - 'nuxtjs', - 'nextjs' -]; - -export function setDefaultBaseImage( - buildPack: string | null, - deploymentType: string | null = null -) { - const nodeVersions = [ - { - value: 'node:lts', - label: 'node:lts' - }, - { - value: 'node:18', - label: 'node:18' - }, - { - value: 'node:17', - label: 'node:17' - }, - { - value: 'node:16', - label: 'node:16' - }, - { - value: 'node:14', - label: 'node:14' - }, - { - value: 'node:12', - label: 'node:12' - } - ]; - const staticVersions = [ - { - value: 'webdevops/nginx:alpine', - label: 'webdevops/nginx:alpine' - }, - { - value: 'webdevops/apache:alpine', - label: 'webdevops/apache:alpine' - }, - { - value: 'nginx:alpine', - label: 'nginx:alpine' - }, - { - value: 'httpd:alpine', - label: 'httpd:alpine (Apache)' - } - ]; - const rustVersions = [ - { - value: 'rust:latest', - label: 'rust:latest' - }, - { - value: 'rust:1.60', - label: 'rust:1.60' - }, - { - value: 'rust:1.60-buster', - label: 'rust:1.60-buster' - }, - { - value: 'rust:1.60-bullseye', - label: 'rust:1.60-bullseye' - }, - { - value: 'rust:1.60-slim-buster', - label: 'rust:1.60-slim-buster' - }, - { - value: 'rust:1.60-slim-bullseye', - label: 'rust:1.60-slim-bullseye' - }, - { - value: 'rust:1.60-alpine3.14', - label: 'rust:1.60-alpine3.14' - }, - { - value: 'rust:1.60-alpine3.15', - label: 'rust:1.60-alpine3.15' - } - ]; - const phpVersions = [ - { - value: 'webdevops/php-apache:8.2', - label: 'webdevops/php-apache:8.2' - }, - { - value: 'webdevops/php-nginx:8.2', - label: 'webdevops/php-nginx:8.2' - }, - { - value: 'webdevops/php-apache:8.1', - label: 'webdevops/php-apache:8.1' - }, - { - value: 'webdevops/php-nginx:8.1', - label: 'webdevops/php-nginx:8.1' - }, - { - value: 'webdevops/php-apache:8.0', - label: 'webdevops/php-apache:8.0' - }, - { - value: 'webdevops/php-nginx:8.0', - label: 'webdevops/php-nginx:8.0' - }, - { - value: 'webdevops/php-apache:7.4', - label: 'webdevops/php-apache:7.4' - }, - { - value: 'webdevops/php-nginx:7.4', - label: 'webdevops/php-nginx:7.4' - }, - { - value: 'webdevops/php-apache:7.3', - label: 'webdevops/php-apache:7.3' - }, - { - value: 'webdevops/php-nginx:7.3', - label: 'webdevops/php-nginx:7.3' - }, - { - value: 'webdevops/php-apache:7.2', - label: 'webdevops/php-apache:7.2' - }, - { - value: 'webdevops/php-nginx:7.2', - label: 'webdevops/php-nginx:7.2' - }, - { - value: 'webdevops/php-apache:7.1', - label: 'webdevops/php-apache:7.1' - }, - { - value: 'webdevops/php-nginx:7.1', - label: 'webdevops/php-nginx:7.1' - }, - { - value: 'webdevops/php-apache:7.0', - label: 'webdevops/php-apache:7.0' - }, - { - value: 'webdevops/php-nginx:7.0', - label: 'webdevops/php-nginx:7.0' - }, - { - value: 'webdevops/php-apache:5.6', - label: 'webdevops/php-apache:5.6' - }, - { - value: 'webdevops/php-nginx:5.6', - label: 'webdevops/php-nginx:5.6' - }, - { - value: 'webdevops/php-apache:8.2-alpine', - label: 'webdevops/php-apache:8.2-alpine' - }, - { - value: 'webdevops/php-nginx:8.2-alpine', - label: 'webdevops/php-nginx:8.2-alpine' - }, - { - value: 'webdevops/php-apache:8.1-alpine', - label: 'webdevops/php-apache:8.1-alpine' - }, - { - value: 'webdevops/php-nginx:8.1-alpine', - label: 'webdevops/php-nginx:8.1-alpine' - }, - { - value: 'webdevops/php-apache:8.0-alpine', - label: 'webdevops/php-apache:8.0-alpine' - }, - { - value: 'webdevops/php-nginx:8.0-alpine', - label: 'webdevops/php-nginx:8.0-alpine' - }, - { - value: 'webdevops/php-apache:7.4-alpine', - label: 'webdevops/php-apache:7.4-alpine' - }, - { - value: 'webdevops/php-nginx:7.4-alpine', - label: 'webdevops/php-nginx:7.4-alpine' - }, - { - value: 'webdevops/php-apache:7.3-alpine', - label: 'webdevops/php-apache:7.3-alpine' - }, - { - value: 'webdevops/php-nginx:7.3-alpine', - label: 'webdevops/php-nginx:7.3-alpine' - }, - { - value: 'webdevops/php-apache:7.2-alpine', - label: 'webdevops/php-apache:7.2-alpine' - }, - { - value: 'webdevops/php-nginx:7.2-alpine', - label: 'webdevops/php-nginx:7.2-alpine' - }, - { - value: 'webdevops/php-apache:7.1-alpine', - label: 'webdevops/php-apache:7.1-alpine' - }, - { - value: 'php:8.1-fpm', - label: 'php:8.1-fpm' - }, - { - value: 'php:8.0-fpm', - label: 'php:8.0-fpm' - }, - { - value: 'php:8.1-fpm-alpine', - label: 'php:8.1-fpm-alpine' - }, - { - value: 'php:8.0-fpm-alpine', - label: 'php:8.0-fpm-alpine' - } - ]; - const pythonVersions = [ - { - value: 'python:3.10-alpine', - label: 'python:3.10-alpine' - }, - { - value: 'python:3.10-buster', - label: 'python:3.10-buster' - }, - { - value: 'python:3.10-bullseye', - label: 'python:3.10-bullseye' - }, - { - value: 'python:3.10-slim-bullseye', - label: 'python:3.10-slim-bullseye' - }, - { - value: 'python:3.9-alpine', - label: 'python:3.9-alpine' - }, - { - value: 'python:3.9-buster', - label: 'python:3.9-buster' - }, - { - value: 'python:3.9-bullseye', - label: 'python:3.9-bullseye' - }, - { - value: 'python:3.9-slim-bullseye', - label: 'python:3.9-slim-bullseye' - }, - { - value: 'python:3.8-alpine', - label: 'python:3.8-alpine' - }, - { - value: 'python:3.8-buster', - label: 'python:3.8-buster' - }, - { - value: 'python:3.8-bullseye', - label: 'python:3.8-bullseye' - }, - { - value: 'python:3.8-slim-bullseye', - label: 'python:3.8-slim-bullseye' - }, - { - value: 'python:3.7-alpine', - label: 'python:3.7-alpine' - }, - { - value: 'python:3.7-buster', - label: 'python:3.7-buster' - }, - { - value: 'python:3.7-bullseye', - label: 'python:3.7-bullseye' - }, - { - value: 'python:3.7-slim-bullseye', - label: 'python:3.7-slim-bullseye' - } - ]; - const herokuVersions = [ - { - value: 'heroku/builder:22', - label: 'heroku/builder:22' - }, - { - value: 'heroku/buildpacks:20', - label: 'heroku/buildpacks:20' - }, - { - value: 'heroku/builder-classic:22', - label: 'heroku/builder-classic:22' - } - ]; - let payload: any = { - baseImage: null, - baseBuildImage: null, - baseImages: [], - baseBuildImages: [] - }; - if (nodeBased.includes(buildPack)) { - if (deploymentType === 'static') { - payload.baseImage = isARM() ? 'nginx:alpine' : 'webdevops/nginx:alpine'; - payload.baseImages = isARM() - ? staticVersions.filter((version) => !version.value.includes('webdevops')) - : staticVersions; - payload.baseBuildImage = 'node:lts'; - payload.baseBuildImages = nodeVersions; - } else { - payload.baseImage = 'node:lts'; - payload.baseImages = nodeVersions; - payload.baseBuildImage = 'node:lts'; - payload.baseBuildImages = nodeVersions; - } - } - if (staticApps.includes(buildPack)) { - payload.baseImage = isARM() ? 'nginx:alpine' : 'webdevops/nginx:alpine'; - payload.baseImages = isARM() - ? staticVersions.filter((version) => !version.value.includes('webdevops')) - : staticVersions; - payload.baseBuildImage = 'node:lts'; - payload.baseBuildImages = nodeVersions; - } - if (buildPack === 'python') { - payload.baseImage = 'python:3.10-alpine'; - payload.baseImages = pythonVersions; - } - if (buildPack === 'rust') { - payload.baseImage = 'rust:latest'; - payload.baseBuildImage = 'rust:latest'; - payload.baseImages = rustVersions; - payload.baseBuildImages = rustVersions; - } - if (buildPack === 'deno') { - payload.baseImage = 'denoland/deno:latest'; - } - if (buildPack === 'php') { - payload.baseImage = isARM() - ? 'php:8.1-fpm-alpine' - : 'webdevops/php-apache:8.2-alpine'; - payload.baseImages = isARM() - ? phpVersions.filter((version) => !version.value.includes('webdevops')) - : phpVersions; - } - if (buildPack === 'laravel') { - payload.baseImage = isARM() - ? 'php:8.1-fpm-alpine' - : 'webdevops/php-apache:8.2-alpine'; - payload.baseImages = isARM() - ? phpVersions.filter((version) => !version.value.includes('webdevops')) - : phpVersions; - payload.baseBuildImage = 'node:18'; - payload.baseBuildImages = nodeVersions; - } - if (buildPack === 'heroku') { - payload.baseImage = 'heroku/buildpacks:20'; - payload.baseImages = herokuVersions; - } - return payload; -} - -export const setDefaultConfiguration = async (data: any) => { - let { - buildPack, - port, - installCommand, - startCommand, - buildCommand, - publishDirectory, - baseDirectory, - dockerFileLocation, - dockerComposeFileLocation, - denoMainFile - } = data; - //@ts-ignore - const template = scanningTemplates[buildPack]; - if (!port) { - port = template?.port || 3000; - - if (buildPack === 'static') port = 80; - else if (buildPack === 'node') port = 3000; - else if (buildPack === 'php') port = 80; - else if (buildPack === 'python') port = 8000; - } - if (!installCommand && buildPack !== 'static' && buildPack !== 'laravel') - installCommand = template?.installCommand || 'yarn install'; - if (!startCommand && buildPack !== 'static' && buildPack !== 'laravel') - startCommand = template?.startCommand || 'yarn start'; - if (!buildCommand && buildPack !== 'static' && buildPack !== 'laravel') - buildCommand = template?.buildCommand || null; - if (!publishDirectory) { - publishDirectory = template?.publishDirectory || null; - } else { - if (!publishDirectory.startsWith('/')) publishDirectory = `/${publishDirectory}`; - if (publishDirectory.endsWith('/')) publishDirectory = publishDirectory.slice(0, -1); - } - if (baseDirectory) { - if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`; - if (baseDirectory.endsWith('/') && baseDirectory !== '/') - baseDirectory = baseDirectory.slice(0, -1); - } - if (dockerFileLocation) { - if (!dockerFileLocation.startsWith('/')) dockerFileLocation = `/${dockerFileLocation}`; - if (dockerFileLocation.endsWith('/')) dockerFileLocation = dockerFileLocation.slice(0, -1); - } else { - dockerFileLocation = '/Dockerfile'; - } - if (dockerComposeFileLocation) { - if (!dockerComposeFileLocation.startsWith('/')) - dockerComposeFileLocation = `/${dockerComposeFileLocation}`; - if (dockerComposeFileLocation.endsWith('/')) - dockerComposeFileLocation = dockerComposeFileLocation.slice(0, -1); - } else { - dockerComposeFileLocation = '/Dockerfile'; - } - if (!denoMainFile) { - denoMainFile = 'main.ts'; - } - - return { - buildPack, - port, - installCommand, - startCommand, - buildCommand, - publishDirectory, - baseDirectory, - dockerFileLocation, - dockerComposeFileLocation, - denoMainFile - }; -}; - -export const scanningTemplates = { - '@sveltejs/kit': { - buildPack: 'nodejs' - }, - astro: { - buildPack: 'astro' - }, - '@11ty/eleventy': { - buildPack: 'eleventy' - }, - svelte: { - buildPack: 'svelte' - }, - '@nestjs/core': { - buildPack: 'nestjs' - }, - next: { - buildPack: 'nextjs' - }, - nuxt: { - buildPack: 'nuxtjs' - }, - 'react-scripts': { - buildPack: 'react' - }, - 'parcel-bundler': { - buildPack: 'static' - }, - '@vue/cli-service': { - buildPack: 'vuejs' - }, - vuejs: { - buildPack: 'vuejs' - }, - gatsby: { - buildPack: 'gatsby' - }, - 'preact-cli': { - buildPack: 'react' - } -}; - -export const saveBuildLog = async ({ - line, - buildId, - applicationId -}: { - line: string; - buildId: string; - applicationId: string; -}): Promise => { - if (buildId === 'undefined' || buildId === 'null' || !buildId) return; - if (applicationId === 'undefined' || applicationId === 'null' || !applicationId) return; - const { default: got } = await import('got'); - if (typeof line === 'object' && line) { - if (line.shortMessage) { - line = line.shortMessage + '\n' + line.stderr; - } else { - line = JSON.stringify(line); - } - } - if (line && typeof line === 'string' && line.includes('ghs_')) { - const regex = /ghs_.*@/g; - line = line.replace(regex, '@'); - } - const addTimestamp = `[${generateTimestamp()}] ${line}`; - const fluentBitUrl = isDev - ? process.env.COOLIFY_CONTAINER_DEV === 'true' - ? 'http://coolify-fluentbit:24224' - : 'http://localhost:24224' - : 'http://coolify-fluentbit:24224'; - - if (isDev && !process.env.COOLIFY_CONTAINER_DEV) { - console.debug(`[${applicationId}] ${addTimestamp}`); - } - try { - return await got.post(`${fluentBitUrl}/${applicationId}_buildlog_${buildId}.csv`, { - json: { - line: encrypt(line) - } - }); - } catch (error) { - return await prisma.buildLog.create({ - data: { - line: addTimestamp, - buildId, - time: Number(day().valueOf()), - applicationId - } - }); - } -}; - -export async function copyBaseConfigurationFiles( - buildPack, - workdir, - buildId, - applicationId, - baseImage -) { - try { - if (buildPack === 'php') { - await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`); - await saveBuildLog({ - line: 'Copied default configuration file for PHP.', - buildId, - applicationId - }); - } else if (baseImage?.includes('nginx')) { - await fs.writeFile( - `${workdir}/nginx.conf`, - `user nginx; - worker_processes auto; - - error_log /docker.stdout; - pid /run/nginx.pid; - - events { - worker_connections 1024; - } - - http { - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /docker.stdout main; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - types_hash_max_size 2048; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - server { - listen 80; - server_name localhost; - - location / { - root /app; - index index.html; - try_files $uri $uri/index.html $uri/ /index.html =404; - } - - error_page 404 /50x.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /app; - } - - } - - } - ` - ); - } - // TODO: Add more configuration files for other buildpacks, like apache2, etc. - } catch (error) { - throw new Error(error); - } -} - -export function checkPnpm(installCommand = null, buildCommand = null, startCommand = null) { - return ( - installCommand?.includes('pnpm') || - buildCommand?.includes('pnpm') || - startCommand?.includes('pnpm') - ); -} - -export async function saveDockerRegistryCredentials({ url, username, password, workdir }) { - if (!username || !password) { - return null; - } - - let decryptedPassword = decrypt(password); - const location = `${workdir}/.docker`; - - try { - await fs.mkdir(`${workdir}/.docker`); - } catch (error) { - // console.log(error); - } - const payload = JSON.stringify({ - auths: { - [url]: { - auth: Buffer.from(`${username}:${decryptedPassword}`).toString('base64') - } - } - }); - await fs.writeFile(`${location}/config.json`, payload); - return location; -} -export async function buildImage({ - applicationId, - tag, - workdir, - buildId, - dockerId, - isCache = false, - debug = false, - dockerFileLocation = '/Dockerfile', - commit, - forceRebuild = false -}) { - if (isCache) { - await saveBuildLog({ line: `Building cache image...`, buildId, applicationId }); - } else { - await saveBuildLog({ line: `Building production image...`, buildId, applicationId }); - } - const dockerFile = isCache ? `${dockerFileLocation}-cache` : `${dockerFileLocation}`; - const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`; - let location = null; - - const { dockerRegistry } = await prisma.application.findUnique({ - where: { id: applicationId }, - select: { dockerRegistry: true } - }); - if (dockerRegistry) { - const { url, username, password } = dockerRegistry; - location = await saveDockerRegistryCredentials({ url, username, password, workdir }); - } - - await executeCommand({ - stream: true, - debug, - buildId, - applicationId, - dockerId, - command: `docker ${location ? `--config ${location}` : ''} build ${forceRebuild ? '--no-cache' : '' - } --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}` - }); - - const { status } = await prisma.build.findUnique({ where: { id: buildId } }); - if (status === 'canceled') { - throw new Error('Canceled.'); - } -} -export function makeLabelForSimpleDockerfile({ applicationId, port, type }) { - return [ - 'coolify.managed=true', - `coolify.version=${version}`, - `coolify.applicationId=${applicationId}`, - `coolify.type=standalone-application` - ]; -} -export function makeLabelForStandaloneApplication({ - applicationId, - fqdn, - name, - type, - pullmergeRequestId = null, - buildPack, - repository, - branch, - projectId, - port, - commit, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory -}) { - if (pullmergeRequestId) { - const protocol = fqdn.startsWith('https://') ? 'https' : 'http'; - const domain = getDomain(fqdn); - fqdn = `${protocol}://${pullmergeRequestId}.${domain}`; - } - return [ - 'coolify.managed=true', - `coolify.version=${version}`, - `coolify.applicationId=${applicationId}`, - `coolify.type=standalone-application`, - `coolify.name=${name}`, - `coolify.configuration=${base64Encode( - JSON.stringify({ - applicationId, - fqdn, - name, - type, - pullmergeRequestId, - buildPack, - repository, - branch, - projectId, - port, - commit, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory - }) - )}` - ]; -} - -export async function buildCacheImageWithNode(data, imageForBuild) { - const { - workdir, - buildId, - baseDirectory, - installCommand, - buildCommand, - secrets, - pullmergeRequestId - } = data; - const isPnpm = checkPnpm(installCommand, buildCommand); - const Dockerfile: Array = []; - Dockerfile.push(`FROM ${imageForBuild}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (isPnpm) { - Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7'); - } - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - if (installCommand) { - Dockerfile.push(`RUN ${installCommand}`); - } - Dockerfile.push(`RUN ${buildCommand}`); - await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n')); - await buildImage({ ...data, isCache: true }); -} - -export async function buildCacheImageForLaravel(data, imageForBuild) { - const { workdir, buildId, secrets, pullmergeRequestId } = data; - const Dockerfile: Array = []; - Dockerfile.push(`FROM ${imageForBuild}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - Dockerfile.push(`COPY *.json *.mix.js /app/`); - Dockerfile.push(`COPY resources /app/resources`); - Dockerfile.push(`RUN yarn install && yarn production`); - await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n')); - await buildImage({ ...data, isCache: true }); -} - -export async function buildCacheImageWithCargo(data, imageForBuild) { - const { applicationId, workdir, buildId } = data; - - const Dockerfile: Array = []; - Dockerfile.push(`FROM ${imageForBuild} as planner-${applicationId}`); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push('RUN cargo install cargo-chef'); - Dockerfile.push('COPY . .'); - Dockerfile.push('RUN cargo chef prepare --recipe-path recipe.json'); - Dockerfile.push(`FROM ${imageForBuild}`); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push('RUN cargo install cargo-chef'); - Dockerfile.push(`COPY --from=planner-${applicationId} /app/recipe.json recipe.json`); - Dockerfile.push('RUN cargo chef cook --release --recipe-path recipe.json'); - await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n')); - await buildImage({ ...data, isCache: true }); -} diff --git a/apps/api/src/lib/buildPacks/compose.ts b/apps/api/src/lib/buildPacks/compose.ts deleted file mode 100644 index 839f3e3ae..000000000 --- a/apps/api/src/lib/buildPacks/compose.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { promises as fs } from 'fs'; -import { defaultComposeConfiguration, executeCommand, generateSecrets } from '../common'; -import { saveBuildLog } from './common'; -import yaml from 'js-yaml'; - -export default async function (data) { - let { - applicationId, - debug, - buildId, - dockerId, - network, - volumes, - labels, - workdir, - baseDirectory, - secrets, - pullmergeRequestId, - dockerComposeConfiguration, - dockerComposeFileLocation - } = data; - const fileYaml = `${workdir}${baseDirectory}${dockerComposeFileLocation}`; - const dockerComposeRaw = await fs.readFile(fileYaml, 'utf8'); - const dockerComposeYaml = yaml.load(dockerComposeRaw); - if (!dockerComposeYaml.services) { - throw 'No Services found in docker-compose file.'; - } - let envs = []; - let buildEnvs = []; - if (secrets.length > 0) { - envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, null)]; - buildEnvs = [...buildEnvs, ...generateSecrets(secrets, pullmergeRequestId, true, null, true)]; - } - - const composeVolumes = []; - if (volumes.length > 0) { - for (const volume of volumes) { - let [v, path] = volume.split(':'); - if (!v.startsWith('.') && !v.startsWith('..') && !v.startsWith('/') && !v.startsWith('~')) { - composeVolumes[v] = { - name: v - }; - } - } - } - let networks = {}; - for (let [key, value] of Object.entries(dockerComposeYaml.services)) { - value['container_name'] = `${applicationId}-${key}`; - - let environment = typeof value['environment'] === 'undefined' ? [] : value['environment']; - if (Object.keys(environment).length > 0) { - environment = Object.entries(environment).map(([key, value]) => `${key}=${value}`); - } - value['environment'] = [...environment, ...envs]; - - let build = typeof value['build'] === 'undefined' ? [] : value['build']; - if (typeof build === 'string') { - build = { context: build }; - } - const buildArgs = typeof build['args'] === 'undefined' ? [] : build['args']; - let finalArgs = [...buildEnvs]; - if (Object.keys(buildArgs).length > 0) { - for (const arg of buildArgs) { - const [key, _] = arg.split('='); - if (finalArgs.filter((env) => env.startsWith(key)).length === 0) { - finalArgs.push(arg); - } - } - } - if (build.length > 0 || buildArgs.length > 0) { - value['build'] = { - ...build, - args: finalArgs - }; - } - - value['labels'] = labels; - // TODO: If we support separated volume for each service, we need to add it here - if (value['volumes']?.length > 0) { - value['volumes'] = value['volumes'].map((volume) => { - if (typeof volume === 'string') { - let [v, path, permission] = volume.split(':'); - if ( - v.startsWith('.') || - v.startsWith('..') || - v.startsWith('/') || - v.startsWith('~') || - v.startsWith('$PWD') - ) { - v = v.replace(/^\./, `~`).replace(/^\.\./, '~').replace(/^\$PWD/, '~'); - } else { - if (!path) { - path = v; - v = `${applicationId}${v.replace(/\//gi, '-').replace(/\./gi, '')}`; - } else { - v = `${applicationId}${v.replace(/\//gi, '-').replace(/\./gi, '')}`; - } - composeVolumes[v] = { - name: v - }; - } - return `${v}:${path}${permission ? ':' + permission : ''}`; - } - if (typeof volume === 'object') { - let { source, target, mode } = volume; - if ( - source.startsWith('.') || - source.startsWith('..') || - source.startsWith('/') || - source.startsWith('~') || - source.startsWith('$PWD') - ) { - - source = source.replace(/^\./, `~`).replace(/^\.\./, '~').replace(/^\$PWD/, '~'); - console.log({source}) - - } else { - if (!target) { - target = source; - source = `${applicationId}${source.replace(/\//gi, '-').replace(/\./gi, '')}`; - } else { - source = `${applicationId}${source.replace(/\//gi, '-').replace(/\./gi, '')}`; - } - } - - return `${source}:${target}${mode ? ':' + mode : ''}`; - } - - }); - } - if (volumes.length > 0) { - for (const volume of volumes) { - value['volumes'].push(volume); - } - } - if (dockerComposeConfiguration[key]?.port) { - value['expose'] = [dockerComposeConfiguration[key].port]; - } - if (value['networks']?.length > 0) { - value['networks'].forEach((network) => { - networks[network] = { - name: network - }; - }); - value['networks'] = [...(value['networks'] || ''), network]; - } else { - value['networks'] = [network]; - } - - dockerComposeYaml.services[key] = { - ...dockerComposeYaml.services[key], - restart: defaultComposeConfiguration(network).restart, - deploy: defaultComposeConfiguration(network).deploy - }; - } - if (Object.keys(composeVolumes).length > 0) { - dockerComposeYaml['volumes'] = { ...composeVolumes }; - } - dockerComposeYaml['networks'] = Object.assign({ ...networks }, { [network]: { external: true } }); - - await fs.writeFile(fileYaml, yaml.dump(dockerComposeYaml)); - await executeCommand({ - debug, - buildId, - applicationId, - dockerId, - command: `docker compose --project-directory ${workdir} -f ${fileYaml} pull` - }); - await saveBuildLog({ line: 'Pulling images from Compose file...', buildId, applicationId }); - await executeCommand({ - debug, - buildId, - applicationId, - dockerId, - command: `docker compose --project-directory ${workdir} -f ${fileYaml} build --progress plain` - }); - await saveBuildLog({ line: 'Building images from Compose file...', buildId, applicationId }); -} diff --git a/apps/api/src/lib/buildPacks/deno.ts b/apps/api/src/lib/buildPacks/deno.ts deleted file mode 100644 index 2649e3d0a..000000000 --- a/apps/api/src/lib/buildPacks/deno.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { - workdir, - port, - baseDirectory, - secrets, - pullmergeRequestId, - denoMainFile, - denoOptions, - buildId - } = data; - const Dockerfile: Array = []; - - let depsFound = false; - try { - await fs.readFile(`${workdir}${baseDirectory || ''}/deps.ts`); - depsFound = true; - } catch (error) {} - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (depsFound) { - Dockerfile.push(`COPY .${baseDirectory || ''}/deps.ts /app`); - Dockerfile.push(`RUN deno cache deps.ts`); - } - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - Dockerfile.push(`RUN deno cache ${denoMainFile}`); - Dockerfile.push(`ENV NO_COLOR true`); - Dockerfile.push(`EXPOSE ${port}`); - Dockerfile.push(`CMD deno run ${denoOptions || ''} ${denoMainFile}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/docker.ts b/apps/api/src/lib/buildPacks/docker.ts deleted file mode 100644 index e02103f88..000000000 --- a/apps/api/src/lib/buildPacks/docker.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildImage } from './common'; - -export default async function (data) { - let { workdir, buildId, baseDirectory, secrets, pullmergeRequestId, dockerFileLocation } = data; - const file = `${workdir}${baseDirectory}${dockerFileLocation}`; - data.workdir = `${workdir}${baseDirectory}`; - const DockerfileRaw = await fs.readFile(`${file}`, 'utf8'); - const Dockerfile: Array = DockerfileRaw.toString().trim().split('\n'); - Dockerfile.forEach((line, index) => { - if (line.startsWith('FROM')) { - Dockerfile.splice(index + 1, 0, `LABEL coolify.buildId=${buildId}`); - } - }); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.forEach((line, index) => { - if (line.startsWith('FROM')) { - Dockerfile.splice(index + 1, 0, env); - } - }); - }); - } - await fs.writeFile(`${data.workdir}${dockerFileLocation}`, Dockerfile.join('\n')); - await buildImage(data); -} diff --git a/apps/api/src/lib/buildPacks/gatsby.ts b/apps/api/src/lib/buildPacks/gatsby.ts deleted file mode 100644 index d133fd084..000000000 --- a/apps/api/src/lib/buildPacks/gatsby.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { promises as fs } from 'fs'; -import { buildCacheImageWithNode, buildImage } from './common'; - -const createDockerfile = async (data, imageforBuild): Promise => { - const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data; - const Dockerfile: Array = []; - - Dockerfile.push(`FROM ${imageforBuild}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/heroku.ts b/apps/api/src/lib/buildPacks/heroku.ts deleted file mode 100644 index 7ba131082..000000000 --- a/apps/api/src/lib/buildPacks/heroku.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { executeCommand } from "../common" -import { saveBuildLog } from "./common"; - -export default async function (data: any): Promise { - const { buildId, applicationId, tag, dockerId, debug, workdir, baseDirectory, baseImage } = data - try { - await saveBuildLog({ line: `Building production image...`, buildId, applicationId }); - await executeCommand({ - buildId, - debug, - dockerId, - command: `pack build -p ${workdir}${baseDirectory} ${applicationId}:${tag} --builder ${baseImage}` - }) - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/index.ts b/apps/api/src/lib/buildPacks/index.ts deleted file mode 100644 index 8e82047fe..000000000 --- a/apps/api/src/lib/buildPacks/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import node from './node'; -import staticApp from './static'; -import docker from './docker'; -import gatsby from './gatsby'; -import svelte from './svelte'; -import react from './react'; -import nestjs from './nestjs'; -import nextjs from './nextjs'; -import nuxtjs from './nuxtjs'; -import vuejs from './vuejs'; -import php from './php'; -import rust from './rust'; -import astro from './static'; -import eleventy from './static'; -import python from './python'; -import deno from './deno'; -import laravel from './laravel'; -import heroku from './heroku'; -import compose from './compose' - -export { - node, - staticApp as static, - docker, - gatsby, - svelte, - react, - nestjs, - nextjs, - nuxtjs, - vuejs, - php, - rust, - astro, - eleventy, - python, - deno, - laravel, - heroku, - compose -}; diff --git a/apps/api/src/lib/buildPacks/laravel.ts b/apps/api/src/lib/buildPacks/laravel.ts deleted file mode 100644 index 159e8d9ca..000000000 --- a/apps/api/src/lib/buildPacks/laravel.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildCacheImageForLaravel, buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { workdir, applicationId, tag, buildId, port, secrets, pullmergeRequestId } = data; - const Dockerfile: Array = []; - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`ENV WEB_DOCUMENT_ROOT /app/public`); - Dockerfile.push(`COPY --chown=application:application composer.* ./`); - Dockerfile.push(`COPY --chown=application:application database/ database/`); - Dockerfile.push( - `RUN composer install --ignore-platform-reqs --no-interaction --no-plugins --no-scripts --prefer-dist` - ); - Dockerfile.push( - `COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/public/js/ /app/public/js/` - ); - Dockerfile.push( - `COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/public/css/ /app/public/css/` - ); - Dockerfile.push( - `COPY --chown=application:application --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json` - ); - Dockerfile.push(`COPY --chown=application:application . ./`); - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - const { baseImage, baseBuildImage } = data; - try { - await buildCacheImageForLaravel(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/nestjs.ts b/apps/api/src/lib/buildPacks/nestjs.ts deleted file mode 100644 index ba98b70ef..000000000 --- a/apps/api/src/lib/buildPacks/nestjs.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { promises as fs } from 'fs'; -import { buildCacheImageWithNode, buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { buildId, applicationId, tag, port, startCommand, workdir, publishDirectory } = data; - const Dockerfile: Array = []; - const isPnpm = startCommand.includes('pnpm'); - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (isPnpm) { - Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7'); - } - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - - Dockerfile.push(`EXPOSE ${port}`); - Dockerfile.push(`CMD ${startCommand}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/nextjs.ts b/apps/api/src/lib/buildPacks/nextjs.ts deleted file mode 100644 index af6172c74..000000000 --- a/apps/api/src/lib/buildPacks/nextjs.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildCacheImageWithNode, buildImage, checkPnpm } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { - applicationId, - buildId, - tag, - workdir, - publishDirectory, - port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - secrets, - pullmergeRequestId, - deploymentType, - baseImage - } = data; - const Dockerfile: Array = []; - const isPnpm = checkPnpm(installCommand, buildCommand, startCommand); - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (isPnpm) { - Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7'); - } - if (deploymentType === 'node') { - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - Dockerfile.push(`RUN ${installCommand}`); - Dockerfile.push(`RUN ${buildCommand}`); - Dockerfile.push(`EXPOSE ${port}`); - Dockerfile.push(`CMD ${startCommand}`); - } else if (deploymentType === 'static') { - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - Dockerfile.push(`EXPOSE 80`); - } - - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage, deploymentType, buildCommand } = data; - if (deploymentType === 'node') { - await createDockerfile(data, baseImage); - await buildImage(data); - } else if (deploymentType === 'static') { - if (buildCommand) await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/node.ts b/apps/api/src/lib/buildPacks/node.ts deleted file mode 100644 index 8ccfcc68e..000000000 --- a/apps/api/src/lib/buildPacks/node.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildImage, checkPnpm } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { - workdir, - port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - secrets, - pullmergeRequestId, - buildId - } = data; - const Dockerfile: Array = []; - const isPnpm = checkPnpm(installCommand, buildCommand, startCommand); - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (isPnpm) { - Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7'); - } - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - Dockerfile.push(`RUN ${installCommand}`); - if (buildCommand) { - Dockerfile.push(`RUN ${buildCommand}`); - } - Dockerfile.push(`EXPOSE ${port}`); - Dockerfile.push(`CMD ${startCommand}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage } = data; - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/nuxtjs.ts b/apps/api/src/lib/buildPacks/nuxtjs.ts deleted file mode 100644 index af6172c74..000000000 --- a/apps/api/src/lib/buildPacks/nuxtjs.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildCacheImageWithNode, buildImage, checkPnpm } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { - applicationId, - buildId, - tag, - workdir, - publishDirectory, - port, - installCommand, - buildCommand, - startCommand, - baseDirectory, - secrets, - pullmergeRequestId, - deploymentType, - baseImage - } = data; - const Dockerfile: Array = []; - const isPnpm = checkPnpm(installCommand, buildCommand, startCommand); - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (isPnpm) { - Dockerfile.push('RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7'); - } - if (deploymentType === 'node') { - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - Dockerfile.push(`RUN ${installCommand}`); - Dockerfile.push(`RUN ${buildCommand}`); - Dockerfile.push(`EXPOSE ${port}`); - Dockerfile.push(`CMD ${startCommand}`); - } else if (deploymentType === 'static') { - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - Dockerfile.push(`EXPOSE 80`); - } - - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage, deploymentType, buildCommand } = data; - if (deploymentType === 'node') { - await createDockerfile(data, baseImage); - await buildImage(data); - } else if (deploymentType === 'static') { - if (buildCommand) await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/php.ts b/apps/api/src/lib/buildPacks/php.ts deleted file mode 100644 index abfd7af4f..000000000 --- a/apps/api/src/lib/buildPacks/php.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildImage } from './common'; - -const createDockerfile = async (data, image, htaccessFound): Promise => { - const { workdir, baseDirectory, buildId, port, secrets, pullmergeRequestId } = data; - const Dockerfile: Array = []; - let composerFound = false; - try { - await fs.readFile(`${workdir}${baseDirectory || ''}/composer.json`); - composerFound = true; - } catch (error) {} - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`COPY .${baseDirectory || ''} /app`); - if (htaccessFound) { - Dockerfile.push(`COPY .${baseDirectory || ''}/.htaccess ./`); - } - if (composerFound) { - Dockerfile.push(`RUN composer install`); - } - - Dockerfile.push(`COPY /entrypoint.sh /opt/docker/provision/entrypoint.d/30-entrypoint.sh`); - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - const { workdir, baseDirectory, baseImage } = data; - try { - let htaccessFound = false; - try { - await fs.readFile(`${workdir}${baseDirectory || ''}/.htaccess`); - htaccessFound = true; - } catch (e) { - // - } - await createDockerfile(data, baseImage, htaccessFound); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/python.ts b/apps/api/src/lib/buildPacks/python.ts deleted file mode 100644 index 56294660f..000000000 --- a/apps/api/src/lib/buildPacks/python.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { - workdir, - port, - baseDirectory, - secrets, - pullmergeRequestId, - pythonWSGI, - pythonModule, - pythonVariable, - buildId - } = data; - const Dockerfile: Array = []; - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (pythonWSGI?.toLowerCase() === 'gunicorn') { - Dockerfile.push(`RUN pip install gunicorn`); - } else if (pythonWSGI?.toLowerCase() === 'uvicorn') { - Dockerfile.push(`RUN pip install uvicorn`); - } else if (pythonWSGI?.toLowerCase() === 'uwsgi') { - Dockerfile.push(`RUN apk add --no-cache uwsgi-python3`); - // Dockerfile.push(`RUN pip install --no-cache-dir uwsgi`) - } - - try { - await fs.stat(`${workdir}${baseDirectory || ''}/requirements.txt`); - Dockerfile.push(`COPY .${baseDirectory || ''}/requirements.txt ./`); - Dockerfile.push(`RUN pip install --no-cache-dir -r .${baseDirectory || ''}/requirements.txt`); - } catch (e) { - // - } - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - Dockerfile.push(`EXPOSE ${port}`); - if (pythonWSGI?.toLowerCase() === 'gunicorn') { - Dockerfile.push(`CMD gunicorn -w=4 -b=0.0.0.0:8000 ${pythonModule}:${pythonVariable}`); - } else if (pythonWSGI?.toLowerCase() === 'uvicorn') { - Dockerfile.push(`CMD uvicorn ${pythonModule}:${pythonVariable} --port ${port} --host 0.0.0.0`); - } else if (pythonWSGI?.toLowerCase() === 'uwsgi') { - Dockerfile.push( - `CMD uwsgi --master -p 4 --http-socket 0.0.0.0:8000 --uid uwsgi --plugins python3 --protocol uwsgi --wsgi ${pythonModule}:${pythonVariable}` - ); - } else { - Dockerfile.push(`CMD python ${pythonModule}`); - } - - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/react.ts b/apps/api/src/lib/buildPacks/react.ts deleted file mode 100644 index 27cf8718b..000000000 --- a/apps/api/src/lib/buildPacks/react.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { promises as fs } from 'fs'; -import { buildCacheImageWithNode, buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data; - const Dockerfile: Array = []; - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/rust.ts b/apps/api/src/lib/buildPacks/rust.ts deleted file mode 100644 index 931a4524a..000000000 --- a/apps/api/src/lib/buildPacks/rust.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { promises as fs } from 'fs'; -import TOML from '@iarna/toml'; -import { executeCommand } from '../common'; -import { buildCacheImageWithCargo, buildImage } from './common'; - -const createDockerfile = async (data, image, name): Promise => { - const { workdir, port, applicationId, tag, buildId } = data; - const Dockerfile: Array = []; - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/target target`); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /usr/local/cargo /usr/local/cargo`); - Dockerfile.push(`COPY . .`); - Dockerfile.push(`RUN cargo build --release --bin ${name}`); - Dockerfile.push('FROM debian:buster-slim'); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push( - `RUN apt-get update -y && apt-get install -y --no-install-recommends openssl libcurl4 ca-certificates && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*` - ); - Dockerfile.push(`RUN update-ca-certificates`); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/target/release/${name} ${name}`); - Dockerfile.push(`EXPOSE ${port}`); - Dockerfile.push(`CMD ["/app/${name}"]`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { workdir, baseImage, baseBuildImage } = data; - const { stdout: cargoToml } = await executeCommand({ command: `cat ${workdir}/Cargo.toml` }); - const parsedToml: any = TOML.parse(cargoToml); - const name = parsedToml.package.name; - await buildCacheImageWithCargo(data, baseBuildImage); - await createDockerfile(data, baseImage, name); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/static.ts b/apps/api/src/lib/buildPacks/static.ts deleted file mode 100644 index def989fc9..000000000 --- a/apps/api/src/lib/buildPacks/static.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { promises as fs } from 'fs'; -import { generateSecrets } from '../common'; -import { buildCacheImageWithNode, buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { - applicationId, - tag, - workdir, - buildCommand, - baseDirectory, - publishDirectory, - secrets, - pullmergeRequestId, - baseImage, - buildId, - port - } = data; - const Dockerfile: Array = []; - - Dockerfile.push(`FROM ${image}`); - if (baseImage?.includes('httpd')) { - Dockerfile.push('WORKDIR /usr/local/apache2/htdocs/'); - } else { - Dockerfile.push('WORKDIR /app'); - } - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - if (secrets.length > 0) { - generateSecrets(secrets, pullmergeRequestId, true).forEach((env) => { - Dockerfile.push(env); - }); - } - if (buildCommand) { - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - } else { - Dockerfile.push(`COPY .${baseDirectory || ''} ./`); - } - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - if (data.buildCommand) await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/svelte.ts b/apps/api/src/lib/buildPacks/svelte.ts deleted file mode 100644 index a69eccd97..000000000 --- a/apps/api/src/lib/buildPacks/svelte.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { promises as fs } from 'fs'; -import { buildCacheImageWithNode, buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data; - const Dockerfile: Array = []; - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/buildPacks/vuejs.ts b/apps/api/src/lib/buildPacks/vuejs.ts deleted file mode 100644 index a69eccd97..000000000 --- a/apps/api/src/lib/buildPacks/vuejs.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { promises as fs } from 'fs'; -import { buildCacheImageWithNode, buildImage } from './common'; - -const createDockerfile = async (data, image): Promise => { - const { applicationId, tag, workdir, publishDirectory, baseImage, buildId, port } = data; - const Dockerfile: Array = []; - - Dockerfile.push(`FROM ${image}`); - Dockerfile.push('WORKDIR /app'); - Dockerfile.push(`LABEL coolify.buildId=${buildId}`); - Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app${publishDirectory} ./`); - if (baseImage?.includes('nginx')) { - Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); - } - Dockerfile.push(`EXPOSE ${port}`); - await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); -}; - -export default async function (data) { - try { - const { baseImage, baseBuildImage } = data; - await buildCacheImageWithNode(data, baseBuildImage); - await createDockerfile(data, baseImage); - await buildImage(data); - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/common.ts b/apps/api/src/lib/common.ts deleted file mode 100644 index a8a7b92d0..000000000 --- a/apps/api/src/lib/common.ts +++ /dev/null @@ -1,1928 +0,0 @@ -import { exec } from 'node:child_process'; -import util from 'util'; -import fs from 'fs/promises'; -import yaml from 'js-yaml'; -import forge from 'node-forge'; -import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator'; -import type { Config } from 'unique-names-generator'; -import generator from 'generate-password'; -import crypto from 'crypto'; -import { promises as dns } from 'dns'; -import * as Sentry from '@sentry/node'; -import { PrismaClient } from '@prisma/client'; -import os from 'os'; -import * as SSHConfig from 'ssh-config/src/ssh-config'; -import jsonwebtoken from 'jsonwebtoken'; -import { checkContainer, removeContainer } from './docker'; -import { day } from './dayjs'; -import { saveBuildLog } from './buildPacks/common'; -import { scheduler } from './scheduler'; -import type { ExecaChildProcess } from 'execa'; - -export const version = '3.12.28'; -export const isDev = process.env.NODE_ENV === 'development'; -export const proxyPort = process.env.COOLIFY_PROXY_PORT; -export const proxySecurePort = process.env.COOLIFY_PROXY_SECURE_PORT; -export const sentryDSN = - 'https://409f09bcb7af47928d3e0f46b78987f3@o1082494.ingest.sentry.io/4504236622217216'; -const algorithm = 'aes-256-ctr'; -const customConfig: Config = { - dictionaries: [adjectives, colors, animals], - style: 'capital', - separator: ' ', - length: 3 -}; - -export const defaultTraefikImage = `traefik:v2.8`; -export function getAPIUrl() { - if (process.env.GITPOD_WORKSPACE_URL) { - const { href } = new URL(process.env.GITPOD_WORKSPACE_URL); - const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, ''); - return newURL; - } - if (process.env.CODESANDBOX_HOST) { - return `https://${process.env.CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`; - } - return isDev ? 'http://host.docker.internal:3001' : 'http://localhost:3000'; -} - -export function getUIUrl() { - if (process.env.GITPOD_WORKSPACE_URL) { - const { href } = new URL(process.env.GITPOD_WORKSPACE_URL); - const newURL = href.replace('https://', 'https://3000-').replace(/\/$/, ''); - return newURL; - } - if (process.env.CODESANDBOX_HOST) { - return `https://${process.env.CODESANDBOX_HOST.replace(/\$PORT/, '3000')}`; - } - return 'http://localhost:3000'; -} - -const mainTraefikEndpoint = isDev - ? `${getAPIUrl()}/webhooks/traefik/main.json` - : 'http://coolify:3000/webhooks/traefik/main.json'; - -const otherTraefikEndpoint = isDev - ? `${getAPIUrl()}/webhooks/traefik/other.json` - : 'http://coolify:3000/webhooks/traefik/other.json'; - -export const uniqueName = (): string => uniqueNamesGenerator(customConfig); -export const asyncExecShellStream = async ({ - debug, - buildId, - applicationId, - command, - engine -}: { - debug: boolean; - buildId: string; - applicationId: string; - command: string; - engine: string; -}) => { - return await new Promise(async (resolve, reject) => { - const { execaCommand } = await import('execa'); - const subprocess = execaCommand(command, { - env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } - }); - const logs = []; - subprocess.stdout.on('data', async (data) => { - const stdout = data.toString(); - const array = stdout.split('\n'); - for (const line of array) { - if (line !== '\n' && line !== '') { - const log = { - line: `${line.replace('\n', '')}`, - buildId, - applicationId - }; - logs.push(log); - if (debug) { - await saveBuildLog(log); - } - } - } - }); - subprocess.stderr.on('data', async (data) => { - const stderr = data.toString(); - const array = stderr.split('\n'); - for (const line of array) { - if (line !== '\n' && line !== '') { - const log = { - line: `${line.replace('\n', '')}`, - buildId, - applicationId - }; - logs.push(log); - if (debug) { - await saveBuildLog(log); - } - } - } - }); - subprocess.on('exit', async (code) => { - await asyncSleep(1000); - if (code === 0) { - resolve(code); - } else { - if (!debug) { - for (const log of logs) { - await saveBuildLog(log); - } - } - reject(code); - } - }); - }); -}; - -export const asyncSleep = (delay: number): Promise => - new Promise((resolve) => setTimeout(resolve, delay)); -export const prisma = new PrismaClient({ - errorFormat: 'minimal' - // log: [ - // { - // emit: 'event', - // level: 'query', - // }, - // { - // emit: 'stdout', - // level: 'error', - // }, - // { - // emit: 'stdout', - // level: 'info', - // }, - // { - // emit: 'stdout', - // level: 'warn', - // }, - // ], -}); - -// prisma.$on('query', (e) => { -// console.log({e}) -// console.log('Query: ' + e.query) -// console.log('Params: ' + e.params) -// console.log('Duration: ' + e.duration + 'ms') -// }) -export const base64Encode = (text: string): string => { - return Buffer.from(text).toString('base64'); -}; -export const base64Decode = (text: string): string => { - return Buffer.from(text, 'base64').toString('ascii'); -}; -export const decrypt = (hashString: string) => { - if (hashString) { - try { - const hash = JSON.parse(hashString); - const decipher = crypto.createDecipheriv( - algorithm, - process.env['COOLIFY_SECRET_KEY'], - Buffer.from(hash.iv, 'hex') - ); - const decrpyted = Buffer.concat([ - decipher.update(Buffer.from(hash.content, 'hex')), - decipher.final() - ]); - return decrpyted.toString(); - } catch (error) { - console.log({ decryptionError: error.message }); - return hashString; - } - } -}; -export const encrypt = (text: string) => { - if (text) { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(algorithm, process.env['COOLIFY_SECRET_KEY'], iv); - const encrypted = Buffer.concat([cipher.update(text.trim()), cipher.final()]); - return JSON.stringify({ - iv: iv.toString('hex'), - content: encrypted.toString('hex') - }); - } -}; - -export async function checkDoubleBranch(branch: string, projectId: number): Promise { - const applications = await prisma.application.findMany({ where: { branch, projectId } }); - return applications.length > 1; -} -export async function isDNSValid(hostname: any, domain: string): Promise { - const { isIP } = await import('is-ip'); - const { DNSServers } = await listSettings(); - if (DNSServers) { - dns.setServers([...DNSServers.split(',')]); - } - let resolves = []; - try { - if (isIP(hostname)) { - resolves = [hostname]; - } else { - resolves = await dns.resolve4(hostname); - } - } catch (error) { - throw 'Invalid DNS.'; - } - - try { - let ipDomainFound = false; - const dnsResolve = await dns.resolve4(domain); - if (dnsResolve.length > 0) { - for (const ip of dnsResolve) { - if (resolves.includes(ip)) { - ipDomainFound = true; - } - } - } - if (!ipDomainFound) throw false; - } catch (error) { - throw 'DNS not set'; - } -} - -export function getDomain(domain: string): string { - if (domain) { - return domain?.replace('https://', '').replace('http://', ''); - } else { - return ''; - } -} - -export async function isDomainConfigured({ - id, - fqdn, - checkOwn = false, - remoteIpAddress = undefined -}: { - id: string; - fqdn: string; - checkOwn?: boolean; - remoteIpAddress?: string; -}): Promise { - const domain = getDomain(fqdn); - const nakedDomain = domain.replace('www.', ''); - const foundApp = await prisma.application.findFirst({ - where: { - OR: [ - { fqdn: { endsWith: `//${nakedDomain}` } }, - { fqdn: { endsWith: `//www.${nakedDomain}` } }, - { dockerComposeConfiguration: { contains: `//${nakedDomain}` } }, - { dockerComposeConfiguration: { contains: `//www.${nakedDomain}` } } - ], - id: { not: id }, - destinationDocker: { - remoteIpAddress - } - }, - select: { fqdn: true } - }); - const foundService = await prisma.service.findFirst({ - where: { - OR: [ - { fqdn: { endsWith: `//${nakedDomain}` } }, - { fqdn: { endsWith: `//www.${nakedDomain}` } } - ], - id: { not: checkOwn ? undefined : id }, - destinationDocker: { - remoteIpAddress - } - }, - select: { fqdn: true } - }); - - const coolifyFqdn = await prisma.setting.findFirst({ - where: { - OR: [ - { fqdn: { endsWith: `//${nakedDomain}` } }, - { fqdn: { endsWith: `//www.${nakedDomain}` } } - ], - id: { not: id } - }, - select: { fqdn: true } - }); - return !!(foundApp || foundService || coolifyFqdn); -} - -export async function getContainerUsage(dockerId: string, container: string): Promise { - try { - const { stdout } = await executeCommand({ - dockerId, - command: `docker container stats ${container} --no-stream --no-trunc --format "{{json .}}"` - }); - return JSON.parse(stdout); - } catch (err) { - return { - MemUsage: 0, - CPUPerc: 0, - NetIO: 0 - }; - } -} - -export async function checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }): Promise { - const { isIP } = await import('is-ip'); - const domain = getDomain(fqdn); - const domainDualCert = domain.includes('www.') ? domain.replace('www.', '') : `www.${domain}`; - - const { DNSServers } = await listSettings(); - if (DNSServers) { - dns.setServers([...DNSServers.split(',')]); - } - - let resolves = []; - try { - if (isIP(hostname)) { - resolves = [hostname]; - } else { - resolves = await dns.resolve4(hostname); - } - } catch (error) { - throw { status: 500, message: `Could not determine IP address for ${hostname}.` }; - } - - if (dualCerts) { - try { - const ipDomain = await dns.resolve4(domain); - const ipDomainDualCert = await dns.resolve4(domainDualCert); - - let ipDomainFound = false; - let ipDomainDualCertFound = false; - - for (const ip of ipDomain) { - if (resolves.includes(ip)) { - ipDomainFound = true; - } - } - for (const ip of ipDomainDualCert) { - if (resolves.includes(ip)) { - ipDomainDualCertFound = true; - } - } - if (ipDomainFound && ipDomainDualCertFound) return { status: 200 }; - throw { - status: 500, - message: `DNS not set correctly or propogated.
Please check your DNS settings.` - }; - } catch (error) { - throw { - status: 500, - message: `DNS not set correctly or propogated.
Please check your DNS settings.` - }; - } - } else { - try { - const ipDomain = await dns.resolve4(domain); - let ipDomainFound = false; - for (const ip of ipDomain) { - if (resolves.includes(ip)) { - ipDomainFound = true; - } - } - if (ipDomainFound) return { status: 200 }; - throw { - status: 500, - message: `DNS not set correctly or propogated.
Please check your DNS settings.` - }; - } catch (error) { - throw { - status: 500, - message: `DNS not set correctly or propogated.
Please check your DNS settings.` - }; - } - } -} -export function generateTimestamp(): string { - return `${day().format('HH:mm:ss.SSS')}`; -} - -export const supportedDatabaseTypesAndVersions = [ - { - name: 'mongodb', - fancyName: 'MongoDB', - baseImage: 'bitnami/mongodb', - baseImageARM: 'mongo', - versions: ['5.0', '4.4', '4.2'], - versionsARM: ['5.0', '4.4', '4.2'] - }, - { - name: 'mysql', - fancyName: 'MySQL', - baseImage: 'bitnami/mysql', - baseImageARM: 'mysql', - versions: ['8.0', '5.7'], - versionsARM: ['8.0', '5.7'] - }, - { - name: 'mariadb', - fancyName: 'MariaDB', - baseImage: 'bitnami/mariadb', - baseImageARM: 'mariadb', - versions: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2'], - versionsARM: ['10.8', '10.7', '10.6', '10.5', '10.4', '10.3', '10.2'] - }, - { - name: 'postgresql', - fancyName: 'PostgreSQL', - baseImage: 'bitnami/postgresql', - baseImageARM: 'postgres', - versions: ['14.5.0', '13.8.0', '12.12.0', '11.17.0', '10.22.0'], - versionsARM: ['14.5', '13.8', '12.12', '11.17', '10.22'] - }, - { - name: 'redis', - fancyName: 'Redis', - baseImage: 'bitnami/redis', - baseImageARM: 'redis', - versions: ['7.0', '6.2', '6.0', '5.0'], - versionsARM: ['7.0', '6.2', '6.0', '5.0'] - }, - { - name: 'couchdb', - fancyName: 'CouchDB', - baseImage: 'bitnami/couchdb', - baseImageARM: 'couchdb', - versions: ['3.2.2', '3.1.2', '2.3.1'], - versionsARM: ['3.2.2', '3.1.2', '2.3.1'] - }, - { - name: 'edgedb', - fancyName: 'EdgeDB', - baseImage: 'edgedb/edgedb', - versions: ['latest', '2.1', '2.0', '1.4'] - } -]; - -export async function getFreeSSHLocalPort(id: string): Promise { - const { default: isReachable } = await import('is-port-reachable'); - const { remoteIpAddress, sshLocalPort } = await prisma.destinationDocker.findUnique({ - where: { id } - }); - if (sshLocalPort) { - return Number(sshLocalPort); - } - - const data = await prisma.setting.findFirst(); - const { minPort, maxPort } = data; - - const ports = await prisma.destinationDocker.findMany({ - where: { sshLocalPort: { not: null }, remoteIpAddress: { not: remoteIpAddress } } - }); - - const alreadyConfigured = await prisma.destinationDocker.findFirst({ - where: { - remoteIpAddress, - id: { not: id }, - sshLocalPort: { not: null } - } - }); - if (alreadyConfigured?.sshLocalPort) { - await prisma.destinationDocker.update({ - where: { id }, - data: { sshLocalPort: alreadyConfigured.sshLocalPort } - }); - return Number(alreadyConfigured.sshLocalPort); - } - const range = generateRangeArray(minPort, maxPort); - const availablePorts = range.filter((port) => !ports.map((p) => p.sshLocalPort).includes(port)); - for (const port of availablePorts) { - const found = await isReachable(port, { host: 'localhost' }); - if (!found) { - await prisma.destinationDocker.update({ - where: { id }, - data: { sshLocalPort: Number(port) } - }); - return Number(port); - } - } - return false; -} - -/** - * Update the ssh config file with a host - * - * @param id Destination ID - * @returns - */ -export async function createRemoteEngineConfiguration(id: string) { - const sshKeyFile = `/tmp/id_rsa-${id}`; - const localPort = await getFreeSSHLocalPort(id); - const { - sshKey: { privateKey }, - remoteIpAddress, - remotePort, - remoteUser - } = await prisma.destinationDocker.findFirst({ where: { id }, include: { sshKey: true } }); - - // Write new keyfile - await fs.writeFile(sshKeyFile, decrypt(privateKey) + '\n', { encoding: 'utf8', mode: 400 }); - - const Host = `${remoteIpAddress}-remote`; - - // Removes previous ssh-keys - try { - await executeCommand({ command: `ssh-keygen -R ${Host}` }); - await executeCommand({ command: `ssh-keygen -R ${remoteIpAddress}` }); - await executeCommand({ command: `ssh-keygen -R localhost:${localPort}` }); - } catch (error) { - // - } - - const homedir = os.homedir(); - let currentConfigFileContent = ''; - try { - // Read the current config file - currentConfigFileContent = (await fs.readFile(`${homedir}/.ssh/config`)).toString(); - } catch (error) { - // File doesn't exist, so we do nothing, a new one is going to be created - } - - // Parse the config file - const config = SSHConfig.parse(currentConfigFileContent); - - // Remove current config for the given host - const found = config.find({ Host }); - const foundIp = config.find({ Host: remoteIpAddress }); - - if (found) config.remove({ Host }); - if (foundIp) config.remove({ Host: remoteIpAddress }); - - // Create the new config - config.append({ - Host, - Hostname: remoteIpAddress, - Port: remotePort.toString(), - User: remoteUser, - StrictHostKeyChecking: 'no', - IdentityFile: sshKeyFile, - ControlMaster: 'auto', - ControlPath: `${homedir}/.ssh/coolify-${remoteIpAddress}-%r@%h:%p`, - ControlPersist: '10m' - }); - - // Check if .ssh folder exists, and if not create one - try { - await fs.stat(`${homedir}/.ssh/`); - } catch (error) { - await fs.mkdir(`${homedir}/.ssh/`); - } - - // Write the config - return await fs.writeFile(`${homedir}/.ssh/config`, SSHConfig.stringify(config)); -} - -export async function executeCommand({ - command, - dockerId = null, - sshCommand = false, - shell = false, - stream = false, - buildId, - applicationId, - debug -}: { - command: string; - sshCommand?: boolean; - shell?: boolean; - stream?: boolean; - dockerId?: string; - buildId?: string; - applicationId?: string; - debug?: boolean; -}): Promise> { - const { execa, execaCommand } = await import('execa'); - const { parse } = await import('shell-quote'); - const parsedCommand = parse(command); - const dockerCommand = parsedCommand[0]; - const dockerArgs = parsedCommand.slice(1); - - if (dockerId) { - let { remoteEngine, remoteIpAddress, engine } = await prisma.destinationDocker.findUnique({ - where: { id: dockerId } - }); - if (remoteEngine) { - await createRemoteEngineConfiguration(dockerId); - engine = `ssh://${remoteIpAddress}-remote`; - } else { - engine = 'unix:///var/run/docker.sock'; - } - if (process.env.CODESANDBOX_HOST) { - if (command.startsWith('docker compose')) { - command = command.replace(/docker compose/gi, 'docker-compose'); - } - } - if (sshCommand) { - if (shell) { - return execaCommand(`ssh ${remoteIpAddress}-remote ${command}`); - } - return await execa('ssh', [`${remoteIpAddress}-remote`, dockerCommand, ...dockerArgs]); - } - if (stream) { - return await new Promise(async (resolve, reject) => { - let subprocess = null; - if (shell) { - subprocess = execaCommand(command, { - env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } - }); - } else { - subprocess = execa(dockerCommand, dockerArgs, { - env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } - }); - } - const logs = []; - subprocess.stdout.on('data', async (data) => { - const stdout = data.toString(); - const array = stdout.split('\n'); - for (const line of array) { - if (line !== '\n' && line !== '') { - const log = { - line: `${line.replace('\n', '')}`, - buildId, - applicationId - }; - logs.push(log); - if (debug) { - await saveBuildLog(log); - } - } - } - }); - subprocess.stderr.on('data', async (data) => { - const stderr = data.toString(); - const array = stderr.split('\n'); - for (const line of array) { - if (line !== '\n' && line !== '') { - const log = { - line: `${line.replace('\n', '')}`, - buildId, - applicationId - }; - logs.push(log); - if (debug) { - await saveBuildLog(log); - } - } - } - }); - subprocess.on('exit', async (code) => { - if (code === 0) { - resolve(code); - } else { - if (!debug) { - for (const log of logs) { - await saveBuildLog(log); - } - } - reject(code); - } - }); - }); - } else { - if (shell) { - return await execaCommand(command, { - env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } - }); - } else { - return await execa(dockerCommand, dockerArgs, { - env: { DOCKER_BUILDKIT: '1', DOCKER_HOST: engine } - }); - } - } - } else { - if (shell) { - return execaCommand(command, { shell: true }); - } - return await execa(dockerCommand, dockerArgs); - } -} - -export async function startTraefikProxy(id: string): Promise { - const { engine, network, remoteEngine, remoteIpAddress } = - await prisma.destinationDocker.findUnique({ where: { id } }); - const { found } = await checkContainer({ - dockerId: id, - container: 'coolify-proxy', - remove: true - }); - const { id: settingsId, ipv4, ipv6 } = await listSettings(); - - if (!found) { - const { stdout: coolifyNetwork } = await executeCommand({ - dockerId: id, - command: `docker network ls --filter 'name=coolify-infra' --no-trunc --format "{{json .}}"` - }); - - if (!coolifyNetwork) { - await executeCommand({ - dockerId: id, - command: `docker network create --attachable coolify-infra` - }); - } - const { stdout: Config } = await executeCommand({ - dockerId: id, - command: `docker network inspect ${network} --format '{{json .IPAM.Config }}'` - }); - const ip = JSON.parse(Config)[0].Gateway; - let traefikUrl = mainTraefikEndpoint; - if (remoteEngine) { - let ip = null; - if (isDev) { - ip = getAPIUrl(); - } else { - ip = `http://${ipv4 || ipv6}:3000`; - } - traefikUrl = `${ip}/webhooks/traefik/remote/${id}`; - } - await executeCommand({ - dockerId: id, - command: `docker run --restart always \ - --add-host 'host.docker.internal:host-gateway' \ - ${ip ? `--add-host 'host.docker.internal:${ip}'` : ''} \ - -v coolify-traefik-letsencrypt:/etc/traefik/acme \ - -v /var/run/docker.sock:/var/run/docker.sock \ - --network coolify-infra \ - -p ${proxyPort ? `${proxyPort}:80` : `80:80`} \ - -p ${proxySecurePort ? `${proxySecurePort}:443` : `443:443`} \ - ${isDev ? '-p "8080:8080"' : ''} \ - --name coolify-proxy \ - -d ${defaultTraefikImage} \ - ${isDev ? '--api.insecure=true' : ''} \ - --entrypoints.web.address=:80 \ - --entrypoints.web.forwardedHeaders.insecure=true \ - --entrypoints.websecure.address=:443 \ - --entrypoints.websecure.forwardedHeaders.insecure=true \ - --providers.docker=true \ - --providers.docker.exposedbydefault=false \ - --providers.http.endpoint=${traefikUrl} \ - --providers.http.pollTimeout=5s \ - --certificatesresolvers.letsencrypt.acme.httpchallenge=true \ - --certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json \ - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web \ - --log.level=error` - }); - await prisma.destinationDocker.update({ - where: { id }, - data: { isCoolifyProxyUsed: true } - }); - } - // Configure networks for local docker engine - if (engine) { - const destinations = await prisma.destinationDocker.findMany({ where: { engine } }); - for (const destination of destinations) { - await configureNetworkTraefikProxy(destination); - } - } - // Configure networks for remote docker engine - if (remoteEngine) { - const destinations = await prisma.destinationDocker.findMany({ where: { remoteIpAddress } }); - for (const destination of destinations) { - await configureNetworkTraefikProxy(destination); - } - } -} - -export async function configureNetworkTraefikProxy(destination: any): Promise { - const { id } = destination; - const { stdout: networks } = await executeCommand({ - dockerId: id, - command: `docker ps -a --filter name=coolify-proxy --format '{{json .Networks}}'` - }); - const configuredNetworks = networks.replace(/"/g, '').replace('\n', '').split(','); - if (!configuredNetworks.includes(destination.network)) { - await executeCommand({ - dockerId: destination.id, - command: `docker network connect ${destination.network} coolify-proxy` - }); - } -} - -export async function stopTraefikProxy( - id: string -): Promise<{ stdout: string; stderr: string } | Error> { - const { found } = await checkContainer({ dockerId: id, container: 'coolify-proxy' }); - await prisma.destinationDocker.update({ - where: { id }, - data: { isCoolifyProxyUsed: false } - }); - try { - if (found) { - await executeCommand({ - dockerId: id, - command: `docker stop -t 0 coolify-proxy && docker rm coolify-proxy`, - shell: true - }); - } - } catch (error) { - return error; - } -} - -export async function listSettings(): Promise { - return await prisma.setting.findUnique({ where: { id: '0' } }); -} - -export function generateToken() { - return jsonwebtoken.sign( - { - nbf: Math.floor(Date.now() / 1000) - 30 - }, - process.env['COOLIFY_SECRET_KEY'] - ); -} -export function generatePassword({ - length = 24, - symbols = false, - isHex = false -}: { length?: number; symbols?: boolean; isHex?: boolean } | null): string { - if (isHex) { - return crypto.randomBytes(length).toString('hex'); - } - const password = generator.generate({ - length, - numbers: true, - strict: true, - symbols - }); - - return password; -} - -type DatabaseConfiguration = - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - MYSQL_DATABASE: string; - MYSQL_PASSWORD: string; - MYSQL_ROOT_USER: string; - MYSQL_USER: string; - MYSQL_ROOT_PASSWORD: string; - }; - } - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - MONGO_INITDB_ROOT_USERNAME?: string; - MONGO_INITDB_ROOT_PASSWORD?: string; - MONGODB_ROOT_USER?: string; - MONGODB_ROOT_PASSWORD?: string; - }; - } - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - MARIADB_ROOT_USER: string; - MARIADB_ROOT_PASSWORD: string; - MARIADB_USER: string; - MARIADB_PASSWORD: string; - MARIADB_DATABASE: string; - }; - } - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - POSTGRES_PASSWORD?: string; - POSTGRES_USER?: string; - POSTGRES_DB?: string; - POSTGRESQL_POSTGRES_PASSWORD?: string; - POSTGRESQL_USERNAME?: string; - POSTGRESQL_PASSWORD?: string; - POSTGRESQL_DATABASE?: string; - }; - } - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - REDIS_AOF_ENABLED: string; - REDIS_PASSWORD: string; - }; - } - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - COUCHDB_PASSWORD: string; - COUCHDB_USER: string; - }; - } - | { - volume: string; - image: string; - command?: string; - ulimits: Record; - privatePort: number; - environmentVariables: { - EDGEDB_SERVER_PASSWORD: string; - EDGEDB_SERVER_USER: string; - EDGEDB_SERVER_DATABASE: string; - EDGEDB_SERVER_TLS_CERT_MODE: string; - }; - }; -export function generateDatabaseConfiguration(database: any): DatabaseConfiguration { - const { id, dbUser, dbUserPassword, rootUser, rootUserPassword, defaultDatabase, version, type } = - database; - const baseImage = getDatabaseImage(type); - if (type === 'mysql') { - const configuration = { - privatePort: 3306, - environmentVariables: { - MYSQL_USER: dbUser, - MYSQL_PASSWORD: dbUserPassword, - MYSQL_ROOT_PASSWORD: rootUserPassword, - MYSQL_ROOT_USER: rootUser, - MYSQL_DATABASE: defaultDatabase - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/bitnami/mysql/data`, - ulimits: {} - }; - if (isARM()) { - configuration.volume = `${id}-${type}-data:/var/lib/mysql`; - } - return configuration; - } else if (type === 'mariadb') { - const configuration: DatabaseConfiguration = { - privatePort: 3306, - environmentVariables: { - MARIADB_ROOT_USER: rootUser, - MARIADB_ROOT_PASSWORD: rootUserPassword, - MARIADB_USER: dbUser, - MARIADB_PASSWORD: dbUserPassword, - MARIADB_DATABASE: defaultDatabase - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/bitnami/mariadb`, - ulimits: {} - }; - if (isARM()) { - configuration.volume = `${id}-${type}-data:/var/lib/mysql`; - } - return configuration; - } else if (type === 'mongodb') { - const configuration: DatabaseConfiguration = { - privatePort: 27017, - environmentVariables: { - MONGODB_ROOT_USER: rootUser, - MONGODB_ROOT_PASSWORD: rootUserPassword - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/bitnami/mongodb`, - ulimits: {} - }; - if (isARM()) { - configuration.environmentVariables = { - MONGO_INITDB_ROOT_USERNAME: rootUser, - MONGO_INITDB_ROOT_PASSWORD: rootUserPassword - }; - configuration.volume = `${id}-${type}-data:/data/db`; - } - return configuration; - } else if (type === 'postgresql') { - const configuration: DatabaseConfiguration = { - privatePort: 5432, - environmentVariables: { - POSTGRESQL_POSTGRES_PASSWORD: rootUserPassword, - POSTGRESQL_PASSWORD: dbUserPassword, - POSTGRESQL_USERNAME: dbUser, - POSTGRESQL_DATABASE: defaultDatabase - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/bitnami/postgresql`, - ulimits: {} - }; - if (isARM()) { - configuration.volume = `${id}-${type}-data:/var/lib/postgresql`; - configuration.environmentVariables = { - POSTGRES_PASSWORD: dbUserPassword, - POSTGRES_USER: dbUser, - POSTGRES_DB: defaultDatabase - }; - } - return configuration; - } else if (type === 'redis') { - const { - settings: { appendOnly } - } = database; - const configuration: DatabaseConfiguration = { - privatePort: 6379, - command: undefined, - environmentVariables: { - REDIS_PASSWORD: dbUserPassword, - REDIS_AOF_ENABLED: appendOnly ? 'yes' : 'no' - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/bitnami/redis/data`, - ulimits: {} - }; - if (isARM()) { - configuration.volume = `${id}-${type}-data:/data`; - configuration.command = `/usr/local/bin/redis-server --appendonly ${appendOnly ? 'yes' : 'no' - } --requirepass ${dbUserPassword}`; - } - return configuration; - } else if (type === 'couchdb') { - const configuration: DatabaseConfiguration = { - privatePort: 5984, - environmentVariables: { - COUCHDB_PASSWORD: dbUserPassword, - COUCHDB_USER: dbUser - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/bitnami/couchdb`, - ulimits: {} - }; - if (isARM()) { - configuration.volume = `${id}-${type}-data:/opt/couchdb/data`; - } - return configuration; - } else if (type === 'edgedb') { - const configuration: DatabaseConfiguration = { - privatePort: 5656, - environmentVariables: { - EDGEDB_SERVER_PASSWORD: rootUserPassword, - EDGEDB_SERVER_USER: rootUser, - EDGEDB_SERVER_DATABASE: defaultDatabase, - EDGEDB_SERVER_TLS_CERT_MODE: 'generate_self_signed' - }, - image: `${baseImage}:${version}`, - volume: `${id}-${type}-data:/var/lib/edgedb/data`, - ulimits: {} - }; - return configuration; - } -} -export function isARM() { - const arch = process.arch; - if (arch === 'arm' || arch === 'arm64' || arch === 'aarch' || arch === 'aarch64') { - return true; - } - return false; -} -export function getDatabaseImage(type: string): string { - const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type); - if (found) { - if (isARM()) { - return found.baseImageARM || found.baseImage; - } - return found.baseImage; - } - return ''; -} - -export function getDatabaseVersions(type: string): string[] { - const found = supportedDatabaseTypesAndVersions.find((t) => t.name === type); - if (found) { - if (isARM()) { - return found.versionsARM || found.versions; - } - return found.versions; - } - return []; -} - -export type ComposeFile = { - version: ComposerFileVersion; - services: Record; - networks: Record; - volumes?: Record; -}; - -export type ComposeFileService = { - container_name: string; - image?: string; - networks: string[]; - environment?: Record; - volumes?: string[]; - ulimits?: unknown; - labels?: string[]; - env_file?: string[]; - extra_hosts?: string[]; - restart: ComposeFileRestartOption; - depends_on?: string[]; - command?: string; - ports?: string[]; - build?: - | { - context: string; - dockerfile: string; - args?: Record; - } - | string; - deploy?: { - restart_policy?: { - condition?: string; - delay?: string; - max_attempts?: number; - window?: string; - }; - }; -}; - -export type ComposerFileVersion = - | '3.8' - | '3.7' - | '3.6' - | '3.5' - | '3.4' - | '3.3' - | '3.2' - | '3.1' - | '3.0' - | '2.4' - | '2.3' - | '2.2' - | '2.1' - | '2.0'; - -export type ComposeFileRestartOption = 'no' | 'always' | 'on-failure' | 'unless-stopped'; - -export type ComposeFileNetwork = { - external: boolean; -}; - -export type ComposeFileVolume = { - external?: boolean; - name?: string; -}; - -export async function makeLabelForStandaloneDatabase({ id, image, volume }) { - const database = await prisma.database.findFirst({ where: { id } }); - delete database.destinationDockerId; - delete database.createdAt; - delete database.updatedAt; - return [ - 'coolify.managed=true', - `coolify.version=${version}`, - `coolify.type=standalone-database`, - `coolify.name=${database.name}`, - `coolify.configuration=${base64Encode( - JSON.stringify({ - version, - image, - volume, - ...database - }) - )}` - ]; -} -export const createDirectories = async ({ - repository, - buildId -}: { - repository: string; - buildId: string; -}): Promise<{ workdir: string; repodir: string }> => { - if (repository) repository = repository.replaceAll(' ', ''); - const repodir = `/tmp/build-sources/${repository}/`; - const workdir = `/tmp/build-sources/${repository}/${buildId}`; - let workdirFound = false; - try { - workdirFound = !!(await fs.stat(workdir)); - } catch (error) { } - if (workdirFound) { - await executeCommand({ command: `rm -fr ${workdir}` }); - } - await executeCommand({ command: `mkdir -p ${workdir}` }); - return { - workdir, - repodir - }; -}; - -export async function stopDatabaseContainer(database: any): Promise { - let everStarted = false; - const { - id, - destinationDockerId, - destinationDocker: { engine, id: dockerId } - } = database; - if (destinationDockerId) { - try { - const { stdout } = await executeCommand({ - dockerId, - command: `docker inspect --format '{{json .State}}' ${id}` - }); - - if (stdout) { - everStarted = true; - await removeContainer({ id, dockerId }); - } - } catch (error) { - // - } - } - return everStarted; -} - -export async function stopTcpHttpProxy( - id: string, - destinationDocker: any, - publicPort: number, - forceName: string = null -): Promise<{ stdout: string; stderr: string } | Error> { - const { id: dockerId } = destinationDocker; - let container = `${id}-${publicPort}`; - if (forceName) container = forceName; - const { found } = await checkContainer({ dockerId, container }); - try { - if (found) { - return await executeCommand({ - dockerId, - command: `docker stop -t 0 ${container} && docker rm ${container}`, - shell: true - }); - } - } catch (error) { - return error; - } -} - -export async function updatePasswordInDb(database, user, newPassword, isRoot) { - const { - id, - type, - rootUser, - rootUserPassword, - dbUser, - dbUserPassword, - defaultDatabase, - destinationDockerId, - destinationDocker: { id: dockerId } - } = database; - if (destinationDockerId) { - if (type === 'mysql') { - await executeCommand({ - dockerId, - command: `docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"ALTER USER '${user}'@'%' IDENTIFIED WITH caching_sha2_password BY '${newPassword}';\"` - }); - } else if (type === 'mariadb') { - await executeCommand({ - dockerId, - command: `docker exec ${id} mysql -u ${rootUser} -p${rootUserPassword} -e \"SET PASSWORD FOR '${user}'@'%' = PASSWORD('${newPassword}');\"` - }); - } else if (type === 'postgresql') { - if (isRoot) { - await executeCommand({ - dockerId, - command: `docker exec ${id} psql postgresql://postgres:${rootUserPassword}@${id}:5432/${defaultDatabase} -c "ALTER role postgres WITH PASSWORD '${newPassword}'"` - }); - } else { - await executeCommand({ - dockerId, - command: `docker exec ${id} psql postgresql://${dbUser}:${dbUserPassword}@${id}:5432/${defaultDatabase} -c "ALTER role ${user} WITH PASSWORD '${newPassword}'"` - }); - } - } else if (type === 'mongodb') { - await executeCommand({ - dockerId, - command: `docker exec ${id} mongo 'mongodb://${rootUser}:${rootUserPassword}@${id}:27017/admin?readPreference=primary&ssl=false' --eval "db.changeUserPassword('${user}','${newPassword}')"` - }); - } else if (type === 'redis') { - await executeCommand({ - dockerId, - command: `docker exec ${id} redis-cli -u redis://${dbUserPassword}@${id}:6379 --raw CONFIG SET requirepass ${newPassword}` - }); - } - } -} -export async function checkExposedPort({ - id, - configuredPort, - exposePort, - engine, - remoteEngine, - remoteIpAddress -}: { - id: string; - configuredPort?: number; - exposePort: number; - engine: string; - remoteEngine: boolean; - remoteIpAddress?: string; -}) { - if (exposePort < 1024 || exposePort > 65535) { - throw { status: 500, message: `Exposed Port needs to be between 1024 and 65535.` }; - } - if (configuredPort) { - if (configuredPort !== exposePort) { - const availablePort = await getFreeExposedPort( - id, - exposePort, - engine, - remoteEngine, - remoteIpAddress - ); - if (availablePort.toString() !== exposePort.toString()) { - throw { status: 500, message: `Port ${exposePort} is already in use.` }; - } - } - } else { - const availablePort = await getFreeExposedPort( - id, - exposePort, - engine, - remoteEngine, - remoteIpAddress - ); - if (availablePort.toString() !== exposePort.toString()) { - throw { status: 500, message: `Port ${exposePort} is already in use.` }; - } - } -} -export async function getFreeExposedPort(id, exposePort, engine, remoteEngine, remoteIpAddress) { - const { default: checkPort } = await import('is-port-reachable'); - if (remoteEngine) { - const applicationUsed = await ( - await prisma.application.findMany({ - where: { - exposePort: { not: null }, - id: { not: id }, - destinationDocker: { remoteIpAddress } - }, - select: { exposePort: true } - }) - ).map((a) => a.exposePort); - const serviceUsed = await ( - await prisma.service.findMany({ - where: { - exposePort: { not: null }, - id: { not: id }, - destinationDocker: { remoteIpAddress } - }, - select: { exposePort: true } - }) - ).map((a) => a.exposePort); - const usedPorts = [...applicationUsed, ...serviceUsed]; - if (usedPorts.includes(exposePort)) { - return false; - } - const found = await checkPort(exposePort, { host: remoteIpAddress }); - if (!found) { - return exposePort; - } - return false; - } else { - const applicationUsed = await ( - await prisma.application.findMany({ - where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { engine } }, - select: { exposePort: true } - }) - ).map((a) => a.exposePort); - const serviceUsed = await ( - await prisma.service.findMany({ - where: { exposePort: { not: null }, id: { not: id }, destinationDocker: { engine } }, - select: { exposePort: true } - }) - ).map((a) => a.exposePort); - const usedPorts = [...applicationUsed, ...serviceUsed]; - if (usedPorts.includes(exposePort)) { - return false; - } - const found = await checkPort(exposePort, { host: 'localhost' }); - if (!found) { - return exposePort; - } - return false; - } -} -export function generateRangeArray(start, end) { - return Array.from({ length: end - start }, (v, k) => k + start); -} -export async function getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress }) { - const { default: isReachable } = await import('is-port-reachable'); - const data = await prisma.setting.findFirst(); - const { minPort, maxPort } = data; - if (remoteEngine) { - const dbUsed = await ( - await prisma.database.findMany({ - where: { - publicPort: { not: null }, - id: { not: id }, - destinationDocker: { remoteIpAddress } - }, - select: { publicPort: true } - }) - ).map((a) => a.publicPort); - const wpFtpUsed = await ( - await prisma.wordpress.findMany({ - where: { - ftpPublicPort: { not: null }, - id: { not: id }, - service: { destinationDocker: { remoteIpAddress } } - }, - select: { ftpPublicPort: true } - }) - ).map((a) => a.ftpPublicPort); - const wpUsed = await ( - await prisma.wordpress.findMany({ - where: { - mysqlPublicPort: { not: null }, - id: { not: id }, - service: { destinationDocker: { remoteIpAddress } } - }, - select: { mysqlPublicPort: true } - }) - ).map((a) => a.mysqlPublicPort); - const minioUsed = await ( - await prisma.minio.findMany({ - where: { - publicPort: { not: null }, - id: { not: id }, - service: { destinationDocker: { remoteIpAddress } } - }, - select: { publicPort: true } - }) - ).map((a) => a.publicPort); - const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed]; - const range = generateRangeArray(minPort, maxPort); - const availablePorts = range.filter((port) => !usedPorts.includes(port)); - for (const port of availablePorts) { - const found = await isReachable(port, { host: remoteIpAddress }); - if (!found) { - return port; - } - } - return false; - } else { - const dbUsed = await ( - await prisma.database.findMany({ - where: { publicPort: { not: null }, id: { not: id }, destinationDocker: { engine } }, - select: { publicPort: true } - }) - ).map((a) => a.publicPort); - const wpFtpUsed = await ( - await prisma.wordpress.findMany({ - where: { - ftpPublicPort: { not: null }, - id: { not: id }, - service: { destinationDocker: { engine } } - }, - select: { ftpPublicPort: true } - }) - ).map((a) => a.ftpPublicPort); - const wpUsed = await ( - await prisma.wordpress.findMany({ - where: { - mysqlPublicPort: { not: null }, - id: { not: id }, - service: { destinationDocker: { engine } } - }, - select: { mysqlPublicPort: true } - }) - ).map((a) => a.mysqlPublicPort); - const minioUsed = await ( - await prisma.minio.findMany({ - where: { - publicPort: { not: null }, - id: { not: id }, - service: { destinationDocker: { engine } } - }, - select: { publicPort: true } - }) - ).map((a) => a.publicPort); - const usedPorts = [...dbUsed, ...wpFtpUsed, ...wpUsed, ...minioUsed]; - const range = generateRangeArray(minPort, maxPort); - const availablePorts = range.filter((port) => !usedPorts.includes(port)); - for (const port of availablePorts) { - const found = await isReachable(port, { host: 'localhost' }); - if (!found) { - return port; - } - } - return false; - } -} - -export async function startTraefikTCPProxy( - destinationDocker: any, - id: string, - publicPort: number, - privatePort: number, - type?: string -): Promise<{ stdout: string; stderr: string } | Error> { - const { network, id: dockerId, remoteEngine } = destinationDocker; - const container = `${id}-${publicPort}`; - const { found } = await checkContainer({ dockerId, container, remove: true }); - const { ipv4, ipv6 } = await listSettings(); - - let dependentId = id; - if (type === 'wordpressftp') dependentId = `${id}-ftp`; - const { found: foundDependentContainer } = await checkContainer({ - dockerId, - container: dependentId, - remove: true - }); - try { - if (foundDependentContainer && !found) { - const { stdout: Config } = await executeCommand({ - dockerId, - command: `docker network inspect ${network} --format '{{json .IPAM.Config }}'` - }); - - const ip = JSON.parse(Config)[0].Gateway; - let traefikUrl = otherTraefikEndpoint; - if (remoteEngine) { - let ip = null; - if (isDev) { - ip = getAPIUrl(); - } else { - ip = `http://${ipv4 || ipv6}:3000`; - } - traefikUrl = `${ip}/webhooks/traefik/other.json`; - } - const tcpProxy = { - version: '3.8', - services: { - [`${id}-${publicPort}`]: { - container_name: container, - image: defaultTraefikImage, - command: [ - `--entrypoints.tcp.address=:${publicPort}`, - `--entryPoints.tcp.forwardedHeaders.insecure=true`, - `--providers.http.endpoint=${traefikUrl}?id=${id}&privatePort=${privatePort}&publicPort=${publicPort}&type=tcp&address=${dependentId}`, - '--providers.http.pollTimeout=10s', - '--log.level=error' - ], - ports: [`${publicPort}:${publicPort}`], - extra_hosts: ['host.docker.internal:host-gateway', `host.docker.internal: ${ip}`], - volumes: ['/var/run/docker.sock:/var/run/docker.sock'], - networks: ['coolify-infra', network] - } - }, - networks: { - [network]: { - external: false, - name: network - }, - 'coolify-infra': { - external: false, - name: 'coolify-infra' - } - } - }; - await fs.writeFile(`/tmp/docker-compose-${id}.yaml`, yaml.dump(tcpProxy)); - await executeCommand({ - dockerId, - command: `docker compose -f /tmp/docker-compose-${id}.yaml up -d` - }); - await fs.rm(`/tmp/docker-compose-${id}.yaml`); - } - if (!foundDependentContainer && found) { - await executeCommand({ - dockerId, - command: `docker stop -t 0 ${container} && docker rm ${container}`, - shell: true - }); - } - } catch (error) { - return error; - } -} - -export async function getServiceFromDB({ - id, - teamId -}: { - id: string; - teamId: string; -}): Promise { - const settings = await prisma.setting.findFirst(); - const body = await prisma.service.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { - destinationDocker: true, - persistentStorage: true, - serviceSecret: true, - serviceSetting: true, - wordpress: true, - plausibleAnalytics: true - } - }); - if (!body) { - return null; - } - // body.type = fixType(body.type); - - if (body?.serviceSecret.length > 0) { - body.serviceSecret = body.serviceSecret.map((s) => { - s.value = decrypt(s.value); - return s; - }); - } - if (body.wordpress) { - body.wordpress.ftpPassword = decrypt(body.wordpress.ftpPassword); - } - - return { ...body, settings }; -} - -export function fixType(type) { - return type?.replaceAll(' ', '').toLowerCase() || null; -} - -export function makeLabelForServices(type) { - return [ - 'coolify.managed=true', - `coolify.version=${version}`, - `coolify.type=service`, - `coolify.service.type=${type}` - ]; -} -export function errorHandler({ - status = 500, - message = 'Unknown error.', - type = 'normal' -}: { - status: number; - message: string | any; - type?: string | null; -}) { - if (message.message) message = message.message; - if (message.includes('Unique constraint failed')) { - message = 'This data is unique and already exists. Please try again with a different value.'; - } - if (type === 'normal') { - Sentry.captureException(message); - } - throw { status, message }; -} -export async function generateSshKeyPair(): Promise<{ publicKey: string; privateKey: string }> { - return await new Promise((resolve, reject) => { - forge.pki.rsa.generateKeyPair({ bits: 4096, workers: -1 }, function (err, keys) { - if (keys) { - resolve({ - publicKey: forge.ssh.publicKeyToOpenSSH(keys.publicKey), - privateKey: forge.ssh.privateKeyToOpenSSH(keys.privateKey) - }); - } else { - reject(keys); - } - }); - }); -} - -export async function stopBuild(buildId, applicationId) { - let count = 0; - await new Promise(async (resolve, reject) => { - const { destinationDockerId, status } = await prisma.build.findFirst({ - where: { id: buildId } - }); - const { id: dockerId } = await prisma.destinationDocker.findFirst({ - where: { id: destinationDockerId } - }); - const interval = setInterval(async () => { - try { - if (status === 'failed' || status === 'canceled') { - clearInterval(interval); - return resolve(); - } - if (count > 15) { - clearInterval(interval); - if (scheduler.workers.has('deployApplication')) { - scheduler.workers.get('deployApplication').postMessage('cancel'); - } - await cleanupDB(buildId, applicationId); - return reject(new Error('Canceled.')); - } - const { stdout: buildContainers } = await executeCommand({ - dockerId, - command: `docker container ls --filter "label=coolify.buildId=${buildId}" --format '{{json .}}'` - }); - if (buildContainers) { - const containersArray = buildContainers.trim().split('\n'); - for (const container of containersArray) { - const containerObj = JSON.parse(container); - const id = containerObj.ID; - if (!containerObj.Names.startsWith(`${applicationId} `)) { - await removeContainer({ id, dockerId }); - clearInterval(interval); - if (scheduler.workers.has('deployApplication')) { - scheduler.workers.get('deployApplication').postMessage('cancel'); - } - await cleanupDB(buildId, applicationId); - return resolve(); - } - } - } - count++; - } catch (error) { } - }, 100); - }); -} - -async function cleanupDB(buildId: string, applicationId: string) { - const data = await prisma.build.findUnique({ where: { id: buildId } }); - if (data?.status === 'queued' || data?.status === 'running') { - await prisma.build.update({ where: { id: buildId }, data: { status: 'canceled' } }); - } - await saveBuildLog({ line: 'Canceled.', buildId, applicationId }); -} - -export function convertTolOldVolumeNames(type) { - if (type === 'nocodb') { - return 'nc'; - } -} - -export async function cleanupDockerStorage(dockerId) { - // Cleanup images that are not used by any container - try { - await executeCommand({ dockerId, command: `docker image prune -af` }); - } catch (error) { } - - // Prune coolify managed containers - try { - await executeCommand({ - dockerId, - command: `docker container prune -f --filter "label=coolify.managed=true"` - }); - } catch (error) { } - - // Cleanup build caches - try { - await executeCommand({ dockerId, command: `docker builder prune -af` }); - } catch (error) { } -} - -export function persistentVolumes(id, persistentStorage, config) { - let volumeSet = new Set(); - if (Object.keys(config).length > 0) { - for (const [key, value] of Object.entries(config)) { - if (value.volumes) { - for (const volume of value.volumes) { - if (!volume.startsWith('/')) { - volumeSet.add(volume); - } - } - } - } - } - const volumesArray = Array.from(volumeSet); - const persistentVolume = - persistentStorage?.map((storage) => { - return `${id}${storage.path.replace(/\//gi, '-')}:${storage.path}`; - }) || []; - - let volumes = [...persistentVolume]; - if (volumesArray) volumes = [...volumesArray, ...volumes]; - const composeVolumes = - (volumes.length > 0 && - volumes.map((volume) => { - return { - [`${volume.split(':')[0]}`]: { - name: volume.split(':')[0] - } - }; - })) || - []; - - const volumeMounts = Object.assign({}, ...composeVolumes) || {}; - return { volumeMounts }; -} -export function defaultComposeConfiguration(network: string): any { - return { - networks: [network], - restart: 'on-failure', - deploy: { - restart_policy: { - condition: 'on-failure', - delay: '5s', - max_attempts: 10, - window: '120s' - } - } - }; -} -export function decryptApplication(application: any) { - if (application) { - if (application?.gitSource?.githubApp?.clientSecret) { - application.gitSource.githubApp.clientSecret = - decrypt(application.gitSource.githubApp.clientSecret) || null; - } - if (application?.gitSource?.githubApp?.webhookSecret) { - application.gitSource.githubApp.webhookSecret = - decrypt(application.gitSource.githubApp.webhookSecret) || null; - } - if (application?.gitSource?.githubApp?.privateKey) { - application.gitSource.githubApp.privateKey = - decrypt(application.gitSource.githubApp.privateKey) || null; - } - if (application?.gitSource?.gitlabApp?.appSecret) { - application.gitSource.gitlabApp.appSecret = - decrypt(application.gitSource.gitlabApp.appSecret) || null; - } - if (application?.secrets.length > 0) { - application.secrets = application.secrets.map((s: any) => { - s.value = decrypt(s.value) || null; - return s; - }); - } - - return application; - } -} - -export async function pushToRegistry( - application: any, - workdir: string, - tag: string, - imageName: string, - customTag: string -) { - const location = `${workdir}/.docker`; - const tagCommand = `docker tag ${application.id}:${tag} ${imageName}:${customTag}`; - const pushCommand = `docker --config ${location} push ${imageName}:${customTag}`; - await executeCommand({ - dockerId: application.destinationDockerId, - command: tagCommand - }); - await executeCommand({ - dockerId: application.destinationDockerId, - command: pushCommand - }); -} - -function parseSecret(secret, isBuild) { - if (secret.value.includes('$')) { - secret.value = secret.value.replaceAll('$', '$$$$'); - } - if (secret.value.includes('\\n')) { - if (isBuild) { - return `ARG ${secret.name}=${secret.value}`; - } else { - return `${secret.name}=${secret.value}`; - } - } else if (secret.value.includes(' ')) { - if (isBuild) { - return `ARG ${secret.name}='${secret.value}'`; - } else { - return `${secret.name}='${secret.value}'`; - } - } else { - if (isBuild) { - return `ARG ${secret.name}=${secret.value}`; - } else { - return `${secret.name}=${secret.value}`; - } - } -} -export function generateSecrets( - secrets: Array, - pullmergeRequestId: string, - isBuild = false, - port = null, - compose = false -): Array { - const envs = []; - const isPRMRSecret = secrets.filter((s) => s.isPRMRSecret); - const normalSecrets = secrets.filter((s) => !s.isPRMRSecret); - if (pullmergeRequestId && isPRMRSecret.length > 0) { - isPRMRSecret.forEach((secret) => { - if (isBuild && !secret.isBuildSecret) { - return; - } - const build = isBuild && secret.isBuildSecret; - envs.push(parseSecret(secret, compose ? false : build)); - }); - } - if (!pullmergeRequestId && normalSecrets.length > 0) { - normalSecrets.forEach((secret) => { - if (isBuild && !secret.isBuildSecret) { - return; - } - const build = isBuild && secret.isBuildSecret; - envs.push(parseSecret(secret, compose ? false : build)); - }); - } - const portFound = envs.filter((env) => env.startsWith('PORT')); - if (portFound.length === 0 && port && !isBuild) { - envs.push(`PORT=${port}`); - } - const nodeEnv = envs.filter((env) => env.startsWith('NODE_ENV')); - if (nodeEnv.length === 0 && !isBuild) { - envs.push(`NODE_ENV=production`); - } - return envs; -} diff --git a/apps/api/src/lib/dayjs.ts b/apps/api/src/lib/dayjs.ts deleted file mode 100644 index 9ff5b0a1a..000000000 --- a/apps/api/src/lib/dayjs.ts +++ /dev/null @@ -1,7 +0,0 @@ -import dayjs from 'dayjs'; -import utc from 'dayjs/plugin/utc.js'; -import relativeTime from 'dayjs/plugin/relativeTime.js'; -dayjs.extend(utc); -dayjs.extend(relativeTime); - -export { dayjs as day }; diff --git a/apps/api/src/lib/docker.ts b/apps/api/src/lib/docker.ts deleted file mode 100644 index 0e2822d59..000000000 --- a/apps/api/src/lib/docker.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { executeCommand } from './common'; - -export function formatLabelsOnDocker(data) { - return data.trim().split('\n').map(a => JSON.parse(a)).map((container) => { - const labels = container.Labels.split(',') - let jsonLabels = {} - labels.forEach(l => { - const name = l.split('=')[0] - const value = l.split('=')[1] - jsonLabels = { ...jsonLabels, ...{ [name]: value } } - }) - container.Labels = jsonLabels; - return container - }) -} -export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<{ found: boolean, status?: { isExited: boolean, isRunning: boolean, isRestarting: boolean } }> { - let containerFound = false; - try { - const { stdout } = await executeCommand({ - dockerId, - command: - `docker inspect --format '{{json .State}}' ${container}` - }); - containerFound = true - const parsedStdout = JSON.parse(stdout); - const status = parsedStdout.Status; - const isRunning = status === 'running'; - const isRestarting = status === 'restarting' - const isExited = status === 'exited' - if (status === 'created') { - await executeCommand({ - dockerId, - command: - `docker rm ${container}` - }); - } - if (remove && status === 'exited') { - await executeCommand({ - dockerId, - command: - `docker rm ${container}` - }); - } - - return { - found: containerFound, - status: { - isRunning, - isRestarting, - isExited - } - }; - } catch (err) { - // Container not found - } - return { - found: false - }; - -} - -export async function isContainerExited(dockerId: string, containerName: string): Promise { - let isExited = false; - try { - const { stdout } = await executeCommand({ dockerId, command: `docker inspect -f '{{.State.Status}}' ${containerName}` }) - if (stdout.trim() === 'exited') { - isExited = true; - } - } catch (error) { - // - } - - return isExited; -} - -export async function removeContainer({ - id, - dockerId -}: { - id: string; - dockerId: string; -}): Promise { - try { - const { stdout } = await executeCommand({ dockerId, command: `docker inspect --format '{{json .State}}' ${id}` }) - if (JSON.parse(stdout).Running) { - await executeCommand({ dockerId, command: `docker stop -t 0 ${id}` }) - await executeCommand({ dockerId, command: `docker rm ${id}` }) - } - if (JSON.parse(stdout).Status === 'exited') { - await executeCommand({ dockerId, command: `docker rm ${id}` }) - } - } catch (error) { - throw error; - } -} diff --git a/apps/api/src/lib/importers/github.ts b/apps/api/src/lib/importers/github.ts deleted file mode 100644 index 1313b77c5..000000000 --- a/apps/api/src/lib/importers/github.ts +++ /dev/null @@ -1,94 +0,0 @@ - -import jsonwebtoken from 'jsonwebtoken'; -import { saveBuildLog } from '../buildPacks/common'; -import { decrypt, executeCommand, prisma } from '../common'; - -export default async function ({ - applicationId, - workdir, - githubAppId, - repository, - apiUrl, - gitCommitHash, - htmlUrl, - branch, - buildId, - customPort, - forPublic -}: { - applicationId: string; - workdir: string; - githubAppId: string; - repository: string; - apiUrl: string; - gitCommitHash?: string; - htmlUrl: string; - branch: string; - buildId: string; - customPort: number; - forPublic?: boolean; -}): Promise { - const { default: got } = await import('got') - const url = htmlUrl.replace('https://', '').replace('http://', ''); - if (forPublic) { - await saveBuildLog({ - line: `Cloning ${repository}:${branch}...`, - buildId, - applicationId - }); - if (gitCommitHash) { - await saveBuildLog({ - line: `Checking out ${gitCommitHash} commit...`, - buildId, - applicationId - }); - } - await executeCommand({ - command: - `git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir} && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, - shell: true - }); - - } else { - const body = await prisma.githubApp.findUnique({ where: { id: githubAppId } }); - if (body.privateKey) body.privateKey = decrypt(body.privateKey); - const { privateKey, appId, installationId } = body - const githubPrivateKey = privateKey.replace(/\\n/g, '\n').replace(/"/g, ''); - - const payload = { - iat: Math.round(new Date().getTime() / 1000), - exp: Math.round(new Date().getTime() / 1000 + 60), - iss: appId - }; - const jwtToken = jsonwebtoken.sign(payload, githubPrivateKey, { - algorithm: 'RS256' - }); - const { token } = await got - .post(`${apiUrl}/app/installations/${installationId}/access_tokens`, { - headers: { - Authorization: `Bearer ${jwtToken}`, - Accept: 'application/vnd.github.machine-man-preview+json' - } - }) - .json(); - await saveBuildLog({ - line: `Cloning ${repository}:${branch}...`, - buildId, - applicationId - }); - if (gitCommitHash) { - await saveBuildLog({ - line: `Checking out ${gitCommitHash} commit...`, - buildId, - applicationId - }); - } - await executeCommand({ - command: - `git clone -q -b ${branch} https://x-access-token:${token}@${url}/${repository}.git --config core.sshCommand="ssh -p ${customPort}" ${workdir}/ && cd ${workdir} && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, - shell: true - }); - } - const { stdout: commit } = await executeCommand({ command: `cd ${workdir}/ && git rev-parse HEAD`, shell: true }); - return commit.replace('\n', ''); -} diff --git a/apps/api/src/lib/importers/gitlab.ts b/apps/api/src/lib/importers/gitlab.ts deleted file mode 100644 index d75c3bbef..000000000 --- a/apps/api/src/lib/importers/gitlab.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { saveBuildLog } from "../buildPacks/common"; -import { executeCommand } from "../common"; - -export default async function ({ - applicationId, - workdir, - repodir, - htmlUrl, - gitCommitHash, - repository, - branch, - buildId, - privateSshKey, - customPort, - forPublic, - customUser, -}: { - applicationId: string; - workdir: string; - repository: string; - htmlUrl: string; - branch: string; - buildId: string; - repodir: string; - gitCommitHash: string; - privateSshKey: string; - customPort: number; - forPublic: boolean; - customUser: string; -}): Promise { - const url = htmlUrl.replace('https://', '').replace('http://', '').replace(/\/$/, ''); - if (!forPublic) { - await executeCommand({ command: `echo '${privateSshKey}' > ${repodir}/id.rsa`, shell: true }); - await executeCommand({ command: `chmod 600 ${repodir}/id.rsa` }); - } - - await saveBuildLog({ - line: `Cloning ${repository}:${branch}...`, - buildId, - applicationId - }); - if (gitCommitHash) { - await saveBuildLog({ - line: `Checking out ${gitCommitHash} commit...`, - buildId, - applicationId - }); - } - if (forPublic) { - await executeCommand({ - command: - `git clone -q -b ${branch} https://${url}/${repository}.git ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true - } - ); - } else { - await executeCommand({ - command: - `git clone -q -b ${branch} ${customUser}@${url}:${repository}.git --config core.sshCommand="ssh -p ${customPort} -q -i ${repodir}id.rsa -o StrictHostKeyChecking=no" ${workdir}/ && cd ${workdir}/ && git checkout ${gitCommitHash || ""} && git submodule update --init --recursive && git lfs pull && cd .. `, shell: true - } - ); - } - - const { stdout: commit } = await executeCommand({ command: `cd ${workdir}/ && git rev-parse HEAD`, shell: true }); - return commit.replace('\n', ''); -} diff --git a/apps/api/src/lib/importers/index.ts b/apps/api/src/lib/importers/index.ts deleted file mode 100644 index 193443890..000000000 --- a/apps/api/src/lib/importers/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import github from './github'; -import gitlab from './gitlab'; - -export { github, gitlab }; diff --git a/apps/api/src/lib/scheduler.ts b/apps/api/src/lib/scheduler.ts deleted file mode 100644 index b1d173120..000000000 --- a/apps/api/src/lib/scheduler.ts +++ /dev/null @@ -1,29 +0,0 @@ -import Bree from 'bree'; -import path from 'path'; -import Cabin from 'cabin'; -import TSBree from '@breejs/ts-worker'; - -export const isDev = process.env.NODE_ENV === 'development'; - -Bree.extend(TSBree); - -const options: any = { - defaultExtension: 'js', - logger: false, - // logger: false, - // workerMessageHandler: async ({ name, message }) => { - // if (name === 'deployApplication' && message?.deploying) { - // if (scheduler.workers.has('autoUpdater') || scheduler.workers.has('cleanupStorage')) { - // scheduler.workers.get('deployApplication').postMessage('cancel') - // } - // } - // }, - jobs: [ - { name: 'deployApplication' }, - ], -}; -if (isDev) options.root = path.join(__dirname, '../jobs'); - -export const scheduler = new Bree(options); - - diff --git a/apps/api/src/lib/services.ts b/apps/api/src/lib/services.ts deleted file mode 100644 index 23e472d6e..000000000 --- a/apps/api/src/lib/services.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { isARM, isDev } from './common'; -import fs from 'fs/promises'; -export async function getTemplates() { - const templatePath = isDev ? './templates.json' : '/app/templates.json'; - const open = await fs.open(templatePath, 'r'); - try { - let data = await open.readFile({ encoding: 'utf-8' }); - let jsonData = JSON.parse(data); - if (isARM()) { - jsonData = jsonData.filter((d) => d.arch !== 'amd64'); - } - return jsonData; - } catch (error) { - return []; - } finally { - await open?.close(); - } -} -const compareSemanticVersions = (a: string, b: string) => { - const a1 = a.split('.'); - const b1 = b.split('.'); - const len = Math.min(a1.length, b1.length); - for (let i = 0; i < len; i++) { - const a2 = +a1[i] || 0; - const b2 = +b1[i] || 0; - if (a2 !== b2) { - return a2 > b2 ? 1 : -1; - } - } - return b1.length - a1.length; -}; -export async function getTags(type: string) { - try { - if (type) { - const tagsPath = isDev ? './tags.json' : '/app/tags.json'; - const data = await fs.readFile(tagsPath, 'utf8'); - let tags = JSON.parse(data); - if (tags) { - tags = tags.find((tag: any) => tag.name.includes(type)); - tags.tags = tags.tags.sort(compareSemanticVersions).reverse(); - return tags; - } - } - } catch (error) { - return []; - } -} diff --git a/apps/api/src/lib/services/common.ts b/apps/api/src/lib/services/common.ts deleted file mode 100644 index 0d7cec42e..000000000 --- a/apps/api/src/lib/services/common.ts +++ /dev/null @@ -1,39 +0,0 @@ - -import { decrypt, prisma } from '../common'; - -export async function removeService({ id }: { id: string }): Promise { - await prisma.serviceSecret.deleteMany({ where: { serviceId: id } }); - await prisma.serviceSetting.deleteMany({ where: { serviceId: id } }); - await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } }); - await prisma.meiliSearch.deleteMany({ where: { serviceId: id } }); - await prisma.fider.deleteMany({ where: { serviceId: id } }); - await prisma.ghost.deleteMany({ where: { serviceId: id } }); - await prisma.umami.deleteMany({ where: { serviceId: id } }); - await prisma.hasura.deleteMany({ where: { serviceId: id } }); - await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } }); - await prisma.minio.deleteMany({ where: { serviceId: id } }); - await prisma.vscodeserver.deleteMany({ where: { serviceId: id } }); - await prisma.wordpress.deleteMany({ where: { serviceId: id } }); - await prisma.glitchTip.deleteMany({ where: { serviceId: id } }); - await prisma.moodle.deleteMany({ where: { serviceId: id } }); - await prisma.appwrite.deleteMany({ where: { serviceId: id } }); - await prisma.searxng.deleteMany({ where: { serviceId: id } }); - await prisma.weblate.deleteMany({ where: { serviceId: id } }); - await prisma.taiga.deleteMany({ where: { serviceId: id } }); - - await prisma.service.delete({ where: { id } }); -} -export async function verifyAndDecryptServiceSecrets(id: string) { - const secrets = await prisma.serviceSecret.findMany({ where: { serviceId: id } }) - let decryptedSecrets = secrets.map(secret => { - const { name, value } = secret - if (value) { - let rawValue = decrypt(value) - rawValue = rawValue.replaceAll(/\$/gi, '$$$') - return { name, value: rawValue } - } - return { name, value } - - }) - return decryptedSecrets -} \ No newline at end of file diff --git a/apps/api/src/lib/services/handlers.ts b/apps/api/src/lib/services/handlers.ts deleted file mode 100644 index c0f33bd02..000000000 --- a/apps/api/src/lib/services/handlers.ts +++ /dev/null @@ -1,250 +0,0 @@ -import type { FastifyReply, FastifyRequest } from 'fastify'; -import fs from 'fs/promises'; -import yaml from 'js-yaml'; -import path from 'path'; -import { asyncSleep, ComposeFile, createDirectories, decrypt, defaultComposeConfiguration, errorHandler, executeCommand, getServiceFromDB, isARM, makeLabelForServices, persistentVolumes, prisma, stopTcpHttpProxy } from '../common'; -import { parseAndFindServiceTemplates } from '../../routes/api/v1/services/handlers'; - -import { ServiceStartStop } from '../../routes/api/v1/services/types'; -import { OnlyId } from '../../types'; -import { verifyAndDecryptServiceSecrets } from './common'; - -export async function stopService(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const { destinationDockerId } = await getServiceFromDB({ id, teamId }); - if (destinationDockerId) { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.project=${id}' --format {{.ID}}` - }) - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ dockerId: destinationDockerId, command: `docker stop -t 0 ${container}` }) - await executeCommand({ dockerId: destinationDockerId, command: `docker rm --force ${container}` }) - } - } - } - return {} - } - throw { status: 500, message: 'Could not stop containers.' } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function startService(request: FastifyRequest, fastify: any) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const service = await getServiceFromDB({ id, teamId }); - const arm = isARM(); - const { type, destinationDockerId, destinationDocker, persistentStorage, exposePort } = - service; - - const { workdir } = await createDirectories({ repository: type, buildId: id }); - const template: any = await parseAndFindServiceTemplates(service, workdir, true) - const network = destinationDockerId && destinationDocker.network; - const config = {}; - for (const s in template.services) { - let newEnvironments = [] - if (template.services[s]?.environment?.length > 0) { - for (const environment of template.services[s].environment) { - let [env, ...value] = environment.split("="); - value = value.join("=") - if (!value.startsWith('$$secret') && value !== '') { - newEnvironments.push(`${env}=${value}`) - } - } - } - const secrets = await verifyAndDecryptServiceSecrets(id) - for (const secret of secrets) { - const { name, value } = secret - if (value) { - const foundEnv = !!template.services[s].environment?.find(env => env.startsWith(`${name}=`)) - const foundNewEnv = !!newEnvironments?.find(env => env.startsWith(`${name}=`)) - if (foundEnv && !foundNewEnv) { - newEnvironments.push(`${name}=${value}`) - } - if (!foundEnv && !foundNewEnv && s === id) { - newEnvironments.push(`${name}=${value}`) - } - } - } - const customVolumes = await prisma.servicePersistentStorage.findMany({ where: { serviceId: id } }) - let volumes = new Set() - if (arm && template.services[s]?.volumesArm?.length > 0) { - template.services[s].volumesArm.forEach(v => volumes.add(v)) - } else { - if (template.services[s]?.volumes?.length > 0) { - template.services[s].volumes.forEach(v => volumes.add(v)) - } - } - // Workaround: old plausible analytics service wrong volume id name - if (service.type === 'plausibleanalytics' && service.plausibleAnalytics?.id) { - let temp = Array.from(volumes) - temp.forEach(a => { - const t = a.replace(service.id, service.plausibleAnalytics.id) - volumes.delete(a) - volumes.add(t) - }) - } - - if (customVolumes.length > 0) { - for (const customVolume of customVolumes) { - const { volumeName, path, containerId } = customVolume - if (volumes && volumes.size > 0 && !volumes.has(`${volumeName}:${path}`) && containerId === service) { - volumes.add(`${volumeName}:${path}`) - } - } - } - let ports = [] - if (template.services[s].proxy?.length > 0) { - for (const proxy of template.services[s].proxy) { - if (proxy.hostPort) { - ports.push(`${proxy.hostPort}:${proxy.port}`) - } - } - } else { - if (template.services[s].ports?.length === 1) { - for (const port of template.services[s].ports) { - if (exposePort) { - ports.push(`${exposePort}:${port}`) - } - } - } - } - let image = template.services[s].image - if (arm && template.services[s].imageArm) { - image = template.services[s].imageArm - } - config[s] = { - container_name: s, - build: template.services[s].build || undefined, - command: template.services[s].command, - entrypoint: template.services[s]?.entrypoint, - image, - expose: template.services[s].ports, - ports: ports.length > 0 ? ports : undefined, - volumes: Array.from(volumes), - environment: newEnvironments, - depends_on: template.services[s]?.depends_on, - ulimits: template.services[s]?.ulimits, - cap_drop: template.services[s]?.cap_drop, - cap_add: template.services[s]?.cap_add, - labels: makeLabelForServices(type), - ...defaultComposeConfiguration(network), - } - // Generate files for builds - if (template.services[s]?.files?.length > 0) { - if (!config[s].build) { - config[s].build = { - context: workdir, - dockerfile: `Dockerfile.${s}` - } - } - let Dockerfile = ` - FROM ${template.services[s].image}` - for (const file of template.services[s].files) { - const { location, content } = file; - const source = path.join(workdir, location); - await fs.mkdir(path.dirname(source), { recursive: true }); - await fs.writeFile(source, content); - Dockerfile += ` - COPY .${location} ${location}` - } - await fs.writeFile(`${workdir}/Dockerfile.${s}`, Dockerfile); - } - } - const { volumeMounts } = persistentVolumes(id, persistentStorage, config) - const composeFile: ComposeFile = { - version: '3.8', - services: config, - networks: { - [network]: { - external: true - } - }, - volumes: volumeMounts - } - const composeFileDestination = `${workdir}/docker-compose.yaml`; - await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); - await startServiceContainers(fastify, id, teamId, destinationDocker.id, composeFileDestination) - - // Workaround: Stop old minio proxies - if (service.type === 'minio') { - try { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDocker.id, - command: - `docker container ls -a --filter 'name=${id}-' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ dockerId: destinationDockerId, command: `docker stop -t 0 ${container}` }) - await executeCommand({ dockerId: destinationDockerId, command: `docker rm --force ${container}` }) - } - } - } - } catch (error) { } - try { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDocker.id, - command: - `docker container ls -a --filter 'name=${id}-' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ dockerId: destinationDockerId, command: `docker stop -t 0 ${container}` }) - await executeCommand({ dockerId: destinationDockerId, command: `docker rm --force ${container}` }) - } - } - } - } catch (error) { } - } - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -async function startServiceContainers(fastify, id, teamId, dockerId, composeFileDestination) { - try { - fastify.io.to(teamId).emit(`start-service`, { serviceId: id, state: 'Pulling images...' }) - await executeCommand({ dockerId, command: `docker compose -f ${composeFileDestination} pull` }) - } catch (error) { } - fastify.io.to(teamId).emit(`start-service`, { serviceId: id, state: 'Building images...' }) - await executeCommand({ dockerId, command: `docker compose -f ${composeFileDestination} build --no-cache` }) - fastify.io.to(teamId).emit(`start-service`, { serviceId: id, state: 'Creating containers...' }) - await executeCommand({ dockerId, command: `docker compose -f ${composeFileDestination} create` }) - fastify.io.to(teamId).emit(`start-service`, { serviceId: id, state: 'Starting containers...' }) - await executeCommand({ dockerId, command: `docker compose -f ${composeFileDestination} start` }) - await asyncSleep(1000); - await executeCommand({ dockerId, command: `docker compose -f ${composeFileDestination} up -d` }) - fastify.io.to(teamId).emit(`start-service`, { serviceId: id, state: 0 }) -} -export async function migrateAppwriteDB(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params - const teamId = request.user.teamId; - const { - destinationDockerId, - destinationDocker, - } = await getServiceFromDB({ id, teamId }); - if (destinationDockerId) { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker exec ${id} migrate` - }) - return await reply.code(201).send() - } - throw { status: 500, message: 'Could cleanup logs.' } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} diff --git a/apps/api/src/lib/services/serviceFields.ts b/apps/api/src/lib/services/serviceFields.ts deleted file mode 100644 index 22549e9a5..000000000 --- a/apps/api/src/lib/services/serviceFields.ts +++ /dev/null @@ -1,867 +0,0 @@ -// Example: -// export const nocodb = [{ -// name: 'postgreslUser', -// isEditable: false, -// isLowerCase: false, -// isNumber: false, -// isBoolean: false, -// isEncrypted: false -// }] - -export const plausibleAnalytics = [{ - name: 'email', - isEditable: true, - isLowerCase: true, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, { - name: 'username', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'password', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlDatabase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPublicPort', - isEditable: false, - isLowerCase: false, - isNumber: true, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'secretKeyBase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'scriptName', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] -export const minio = [{ - name: 'apiFqdn', - isEditable: true, - isLowerCase: true, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, { - name: 'rootUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'rootUserPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] -export const vscodeserver = [{ - name: 'password', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] -export const wordpress = [{ - name: 'extraConfig', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mysqlHost', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mysqlPort', - isEditable: true, - isLowerCase: false, - isNumber: true, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mysqlUser', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mysqlPassword', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mysqlRootUser', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mysqlRootUserPassword', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mysqlDatabase', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'ftpPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] -export const ghost = [{ - name: 'defaultEmail', - isEditable: false, - isLowerCase: true, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'defaultPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbRootUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbRootUserPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbDatabase', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] -export const meiliSearch = [{ - name: 'masterKey', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] -export const umami = [{ - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlDatabase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'umamiAdminPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'hashSalt', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] -export const hasura = [{ - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlDatabase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'graphQLAdminPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] -export const fider = [{ - name: 'jwtSecret', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, { - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'emailNoreply', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailSmtpHost', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailSmtpPassword', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'emailSmtpPort', - isEditable: true, - isLowerCase: false, - isNumber: true, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailSmtpUser', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailSmtpEnableStartTls', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: true, - isEncrypted: false -}, -{ - name: 'emailMailgunApiKey', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'emailMailgunDomain', - isEditable: true, - isLowerCase: true, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailMailgunRegion', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] -export const moodle = [{ - name: 'defaultEmail', - isEditable: true, - isLowerCase: true, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'defaultUsername', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'defaultPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbRootUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbRootUserPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbDatabase', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] - -export const appwrite = [{ - name: 'opensslKeyV1', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'executorSecret', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'redisPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbHost', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbPort', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbRootUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mariadbRootUserPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'mariadbDatabase', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] - -export const glitchTip = [{ - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlDatabase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPublicPort', - isEditable: false, - isLowerCase: false, - isNumber: true, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'secretKeyBase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'emailSmtpHost', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailSmtpPassword', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'emailSmtpUseSsl', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: true, - isEncrypted: false -}, -{ - name: 'emailSmtpUseSsl', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: true, - isEncrypted: false -}, -{ - name: 'emailSmtpPort', - isEditable: true, - isLowerCase: false, - isNumber: true, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailSmtpUser', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'defaultEmail', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'defaultUsername', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'defaultPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'defaultEmailFrom', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailUrl', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'emailBackend', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'mailgunApiKey', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'sendgridApiKey', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'enableOpenUserRegistration', - isEditable: true, - isLowerCase: false, - isNumber: false, - isBoolean: true, - isEncrypted: false -}] - -export const searxng = [{ - name: 'secretKey', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'redisPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}] - -export const weblate = [{ - name: 'adminPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlHost', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPort', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlDatabase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] -export const taiga = [{ - name: 'secretKey', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'djangoAdminUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'djangoAdminPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'rabbitMQUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'rabbitMQPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlHost', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPort', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlUser', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}, -{ - name: 'postgresqlPassword', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: true -}, -{ - name: 'postgresqlDatabase', - isEditable: false, - isLowerCase: false, - isNumber: false, - isBoolean: false, - isEncrypted: false -}] \ No newline at end of file diff --git a/apps/api/src/plugins/jwt.ts b/apps/api/src/plugins/jwt.ts deleted file mode 100644 index 029aecd94..000000000 --- a/apps/api/src/plugins/jwt.ts +++ /dev/null @@ -1,33 +0,0 @@ -import fp from 'fastify-plugin' -import fastifyJwt, { FastifyJWTOptions } from '@fastify/jwt' - -declare module "@fastify/jwt" { - interface FastifyJWT { - user: { - userId: string, - teamId: string, - permission: string, - isAdmin: boolean - } - } -} - -export default fp(async (fastify, opts) => { - fastify.register(fastifyJwt, { - secret: fastify.config.COOLIFY_SECRET_KEY - }) - - fastify.decorate("authenticate", async function (request, reply) { - try { - await request.jwtVerify() - } catch (err) { - reply.send(err) - } - }) -}) - -declare module 'fastify' { - export interface FastifyInstance { - authenticate(): Promise - } -} diff --git a/apps/api/src/realtime/index.ts b/apps/api/src/realtime/index.ts deleted file mode 100644 index 288c17bea..000000000 --- a/apps/api/src/realtime/index.ts +++ /dev/null @@ -1,29 +0,0 @@ - -export default async (fastify) => { - fastify.io.use((socket, next) => { - const { token } = socket.handshake.auth; - if (token && fastify.jwt.verify(token)) { - next(); - } else { - return next(new Error("unauthorized event")); - } - }); - fastify.io.on('connection', (socket: any) => { - const { token } = socket.handshake.auth; - const { teamId } = fastify.jwt.decode(token); - socket.join(teamId); - // console.info('Socket connected!', socket.id) - // console.info('Socket joined team!', teamId) - // socket.on('message', (message) => { - // console.log(message) - // }) - // socket.on('error', (err) => { - // console.log(err) - // }) - }) - // fastify.io.on("error", (err) => { - // if (err && err.message === "unauthorized event") { - // fastify.io.disconnect(); - // } - // }); -} diff --git a/apps/api/src/routes/api/v1/applications/handlers.ts b/apps/api/src/routes/api/v1/applications/handlers.ts deleted file mode 100644 index 8621a93ca..000000000 --- a/apps/api/src/routes/api/v1/applications/handlers.ts +++ /dev/null @@ -1,1856 +0,0 @@ -import cuid from 'cuid'; -import crypto from 'node:crypto'; -import jsonwebtoken from 'jsonwebtoken'; -import { FastifyReply } from 'fastify'; -import fs from 'fs/promises'; -import yaml from 'js-yaml'; -import csv from 'csvtojson'; - -import { day } from '../../../../lib/dayjs'; -import { - saveDockerRegistryCredentials, - setDefaultBaseImage, - setDefaultConfiguration -} from '../../../../lib/buildPacks/common'; -import { - checkDomainsIsValidInDNS, - checkExposedPort, - createDirectories, - decrypt, - defaultComposeConfiguration, - encrypt, - errorHandler, - executeCommand, - generateSecrets, - generateSshKeyPair, - getContainerUsage, - getDomain, - isDev, - isDomainConfigured, - listSettings, - prisma, - stopBuild, - uniqueName -} from '../../../../lib/common'; -import { checkContainer, formatLabelsOnDocker, removeContainer } from '../../../../lib/docker'; - -import type { FastifyRequest } from 'fastify'; -import type { - GetImages, - CancelDeployment, - CheckDNS, - CheckRepository, - DeleteApplication, - DeleteSecret, - DeleteStorage, - GetApplicationLogs, - GetBuildIdLogs, - SaveApplication, - SaveApplicationSettings, - SaveApplicationSource, - SaveDeployKey, - SaveDestination, - SaveSecret, - SaveStorage, - DeployApplication, - CheckDomain, - StopPreviewApplication, - RestartPreviewApplication, - GetBuilds, - RestartApplication -} from './types'; -import { OnlyId } from '../../../../types'; - -function filterObject(obj, callback) { - return Object.fromEntries(Object.entries(obj).filter(([key, val]) => callback(val, key))); -} - -export async function listApplications(request: FastifyRequest) { - try { - const { teamId } = request.user; - const applications = await prisma.application.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { teams: true, destinationDocker: true, settings: true } - }); - const settings = await prisma.setting.findFirst(); - return { - applications, - settings - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getImages(request: FastifyRequest) { - try { - const { buildPack, deploymentType } = request.body; - let publishDirectory = undefined; - let port = undefined; - const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage( - buildPack, - deploymentType - ); - if (buildPack === 'nextjs') { - if (deploymentType === 'static') { - publishDirectory = 'out'; - port = '80'; - } else { - publishDirectory = ''; - port = '3000'; - } - } - if (buildPack === 'nuxtjs') { - if (deploymentType === 'static') { - publishDirectory = 'dist'; - port = '80'; - } else { - publishDirectory = ''; - port = '3000'; - } - } - - return { baseImage, baseImages, baseBuildImage, baseBuildImages, publishDirectory, port }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function cleanupUnconfiguredApplications(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const applications = await prisma.application.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { settings: true, destinationDocker: true, teams: true } - }); - for (const application of applications) { - if (application?.buildPack === 'compose') { - continue; - } - if ( - !application.buildPack || - !application.destinationDockerId || - !application.branch || - (!application.settings?.isBot && !application?.fqdn) - ) { - if (application?.destinationDockerId && application.destinationDocker?.network) { - const { stdout: containers } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${application.id} --format '{{json .}}'` - }); - if (containers) { - const containersArray = containers.trim().split('\n'); - for (const container of containersArray) { - const containerObj = JSON.parse(container); - const id = containerObj.ID; - await removeContainer({ id, dockerId: application.destinationDocker.id }); - } - } - } - await prisma.applicationSettings.deleteMany({ where: { applicationId: application.id } }); - await prisma.buildLog.deleteMany({ where: { applicationId: application.id } }); - await prisma.build.deleteMany({ where: { applicationId: application.id } }); - await prisma.secret.deleteMany({ where: { applicationId: application.id } }); - await prisma.applicationPersistentStorage.deleteMany({ - where: { applicationId: application.id } - }); - await prisma.applicationConnectedDatabase.deleteMany({ - where: { applicationId: application.id } - }); - await prisma.application.deleteMany({ where: { id: application.id } }); - } - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getApplicationStatus(request: FastifyRequest) { - try { - const { id } = request.params; - const { teamId } = request.user; - const payload = []; - const application: any = await getApplicationFromDB(id, teamId); - if (application?.destinationDockerId) { - if (application.buildPack === 'compose') { - const { stdout: containers } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker ps -a --filter "label=coolify.applicationId=${id}" --format '{{json .}}'` - }); - const containersArray = containers.trim().split('\n'); - if (containersArray.length > 0 && containersArray[0] !== '') { - for (const container of containersArray) { - let isRunning = false; - let isExited = false; - let isRestarting = false; - const containerObj = JSON.parse(container); - const status = containerObj.State; - if (status === 'running') { - isRunning = true; - } - if (status === 'exited') { - isExited = true; - } - if (status === 'restarting') { - isRestarting = true; - } - payload.push({ - name: containerObj.Names, - status: { - isRunning, - isExited, - isRestarting - } - }); - } - } - } else { - let isRunning = false; - let isExited = false; - let isRestarting = false; - const status = await checkContainer({ - dockerId: application.destinationDocker.id, - container: id - }); - if (status?.found) { - isRunning = status.status.isRunning; - isExited = status.status.isExited; - isRestarting = status.status.isRestarting; - payload.push({ - name: id, - status: { - isRunning, - isExited, - isRestarting - } - }); - } - } - } - return payload; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getApplication(request: FastifyRequest) { - try { - const { id } = request.params; - const { teamId } = request.user; - const appId = process.env['COOLIFY_APP_ID']; - const application: any = await getApplicationFromDB(id, teamId); - const settings = await listSettings(); - return { - application, - appId, - settings - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function newApplication(request: FastifyRequest, reply: FastifyReply) { - try { - const name = uniqueName(); - const { teamId } = request.user; - const { id } = await prisma.application.create({ - data: { - name, - teams: { connect: { id: teamId } }, - settings: { create: { debug: false, previews: false } } - } - }); - return reply.code(201).send({ id }); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -function decryptApplication(application: any) { - if (application) { - if (application?.gitSource?.githubApp?.clientSecret) { - application.gitSource.githubApp.clientSecret = - decrypt(application.gitSource.githubApp.clientSecret) || null; - } - if (application?.gitSource?.githubApp?.webhookSecret) { - application.gitSource.githubApp.webhookSecret = - decrypt(application.gitSource.githubApp.webhookSecret) || null; - } - if (application?.gitSource?.githubApp?.privateKey) { - application.gitSource.githubApp.privateKey = - decrypt(application.gitSource.githubApp.privateKey) || null; - } - if (application?.gitSource?.gitlabApp?.appSecret) { - application.gitSource.gitlabApp.appSecret = - decrypt(application.gitSource.gitlabApp.appSecret) || null; - } - if (application?.secrets.length > 0) { - application.secrets = application.secrets.map((s: any) => { - s.value = decrypt(s.value) || null; - return s; - }); - } - - return application; - } -} -export async function getApplicationFromDB(id: string, teamId: string) { - try { - let application = await prisma.application.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { - destinationDocker: true, - settings: true, - gitSource: { include: { githubApp: true, gitlabApp: true } }, - secrets: true, - persistentStorage: true, - connectedDatabase: true, - previewApplication: true, - dockerRegistry: true - } - }); - if (!application) { - throw { status: 404, message: 'Application not found.' }; - } - application = decryptApplication(application); - const buildPack = application?.buildPack || null; - const { baseImage, baseBuildImage, baseBuildImages, baseImages } = - setDefaultBaseImage(buildPack); - - // Set default build images - if (!application.baseImage) { - application.baseImage = baseImage; - } - if (!application.baseBuildImage) { - application.baseBuildImage = baseBuildImage; - } - return { ...application, baseBuildImages, baseImages }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getApplicationFromDBWebhook(projectId: number, branch: string) { - try { - let applications = await prisma.application.findMany({ - where: { projectId, branch, settings: { autodeploy: true } }, - include: { - destinationDocker: true, - settings: true, - gitSource: { include: { githubApp: true, gitlabApp: true } }, - secrets: true, - persistentStorage: true, - connectedDatabase: true - } - }); - if (applications.length === 0) { - throw { status: 500, message: 'Application not configured.', type: 'webhook' }; - } - applications = applications.map((application: any) => { - application = decryptApplication(application); - const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage( - application.buildPack - ); - - // Set default build images - if (!application.baseImage) { - application.baseImage = baseImage; - } - if (!application.baseBuildImage) { - application.baseBuildImage = baseBuildImage; - } - application.baseBuildImages = baseBuildImages; - application.baseImages = baseImages; - return application; - }); - - return applications; - } catch ({ status, message, type }) { - return errorHandler({ status, message, type }); - } -} -export async function saveApplication( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - let { - name, - buildPack, - fqdn, - port, - exposePort, - installCommand, - buildCommand, - startCommand, - baseDirectory, - publishDirectory, - pythonWSGI, - pythonModule, - pythonVariable, - dockerFileLocation, - denoMainFile, - denoOptions, - gitCommitHash, - baseImage, - baseBuildImage, - deploymentType, - baseDatabaseBranch, - dockerComposeFile, - dockerComposeFileLocation, - dockerComposeConfiguration, - simpleDockerfile, - dockerRegistryImageName - } = request.body; - if (port) port = Number(port); - if (exposePort) { - exposePort = Number(exposePort); - } - const { - destinationDocker: { engine, remoteEngine, remoteIpAddress }, - exposePort: configuredPort - } = await prisma.application.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - if (exposePort) - await checkExposedPort({ - id, - configuredPort, - exposePort, - engine, - remoteEngine, - remoteIpAddress - }); - if (denoOptions) denoOptions = denoOptions.trim(); - const defaultConfiguration = await setDefaultConfiguration({ - buildPack, - port, - installCommand, - startCommand, - buildCommand, - publishDirectory, - baseDirectory, - dockerFileLocation, - dockerComposeFileLocation, - denoMainFile - }); - if (baseDatabaseBranch) { - await prisma.application.update({ - where: { id }, - data: { - name, - fqdn, - exposePort, - pythonWSGI, - pythonModule, - pythonVariable, - denoOptions, - baseImage, - gitCommitHash, - baseBuildImage, - deploymentType, - dockerComposeFile, - dockerComposeFileLocation, - dockerComposeConfiguration, - simpleDockerfile, - dockerRegistryImageName, - ...defaultConfiguration, - connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } } - } - }); - } else { - await prisma.application.update({ - where: { id }, - data: { - name, - fqdn, - exposePort, - pythonWSGI, - pythonModule, - gitCommitHash, - pythonVariable, - denoOptions, - baseImage, - baseBuildImage, - deploymentType, - dockerComposeFile, - dockerComposeFileLocation, - dockerComposeConfiguration, - simpleDockerfile, - dockerRegistryImageName, - ...defaultConfiguration - } - }); - } - - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveApplicationSettings( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { - debug, - previews, - dualCerts, - autodeploy, - branch, - projectId, - isBot, - isDBBranching, - isCustomSSL, - isHttp2 - } = request.body; - await prisma.application.update({ - where: { id }, - data: { - fqdn: isBot ? null : undefined, - settings: { - update: { - debug, - previews, - dualCerts, - autodeploy, - isBot, - isDBBranching, - isCustomSSL, - isHttp2 - } - } - }, - include: { destinationDocker: true } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function stopPreviewApplication( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { pullmergeRequestId } = request.body; - const { teamId } = request.user; - const application: any = await getApplicationFromDB(id, teamId); - if (application?.destinationDockerId) { - const container = `${id}-${pullmergeRequestId}`; - const { id: dockerId } = application.destinationDocker; - const { found } = await checkContainer({ dockerId, container }); - if (found) { - await removeContainer({ id: container, dockerId: application.destinationDocker.id }); - } - await prisma.previewApplication.deleteMany({ - where: { applicationId: application.id, pullmergeRequestId } - }); - } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function restartApplication( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { imageId = null } = request.body; - const { teamId } = request.user; - const application: any = await getApplicationFromDB(id, teamId); - if (application?.destinationDockerId) { - const buildId = cuid(); - const { id: dockerId, network } = application.destinationDocker; - const { - dockerRegistry, - secrets, - pullmergeRequestId, - port, - repository, - persistentStorage, - id: applicationId, - buildPack, - exposePort - } = application; - let location = null; - - let envs = []; - if (secrets.length > 0) { - envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, port)]; - } - - const { workdir } = await createDirectories({ repository, buildId }); - const labels = []; - let image = null; - if (imageId) { - image = imageId; - } else { - const { stdout: container } = await executeCommand({ - dockerId, - command: `docker container ls --filter 'label=com.docker.compose.service=${id}' --format '{{json .}}'` - }); - const containersArray = container.trim().split('\n'); - for (const container of containersArray) { - const containerObj = formatLabelsOnDocker(container); - image = containerObj[0].Image; - Object.keys(containerObj[0].Labels).forEach(function (key) { - if (key.startsWith('coolify')) { - labels.push(`${key}=${containerObj[0].Labels[key]}`); - } - }); - } - } - if (dockerRegistry) { - const { url, username, password } = dockerRegistry; - location = await saveDockerRegistryCredentials({ url, username, password, workdir }); - } - - let imageFoundLocally = false; - try { - await executeCommand({ - dockerId, - command: `docker image inspect ${image}` - }); - imageFoundLocally = true; - } catch (error) { - // - } - let imageFoundRemotely = false; - try { - await executeCommand({ - dockerId, - command: `docker ${location ? `--config ${location}` : ''} pull ${image}` - }); - imageFoundRemotely = true; - } catch (error) { - // - } - - if (!imageFoundLocally && !imageFoundRemotely) { - throw { status: 500, message: 'Image not found, cannot restart application.' }; - } - - const volumes = - persistentStorage?.map((storage) => { - return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : '' - }${storage.path}`; - }) || []; - const composeVolumes = volumes.map((volume) => { - return { - [`${volume.split(':')[0]}`]: { - name: volume.split(':')[0] - } - }; - }); - const composeFile = { - version: '3.8', - services: { - [applicationId]: { - image, - container_name: applicationId, - volumes, - environment: envs, - labels, - depends_on: [], - expose: [port], - ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(network) - } - }, - networks: { - [network]: { - external: true - } - }, - volumes: Object.assign({}, ...composeVolumes) - }; - await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); - try { - await executeCommand({ dockerId, command: `docker stop -t 0 ${id}` }); - await executeCommand({ dockerId, command: `docker rm ${id}` }); - } catch (error) { - // - } - - await executeCommand({ - dockerId, - command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d` - }); - return reply.code(201).send(); - } - throw { status: 500, message: 'Application cannot be restarted.' }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function stopApplication(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const { teamId } = request.user; - const application: any = await getApplicationFromDB(id, teamId); - if (application?.destinationDockerId) { - const { id: dockerId } = application.destinationDocker; - if (application.buildPack === 'compose') { - const { stdout: containers } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker ps -a --filter "label=coolify.applicationId=${id}" --format '{{json .}}'` - }); - const containersArray = containers.trim().split('\n'); - if (containersArray.length > 0 && containersArray[0] !== '') { - for (const container of containersArray) { - const containerObj = JSON.parse(container); - await removeContainer({ - id: containerObj.ID, - dockerId: application.destinationDocker.id - }); - } - } - return; - } - const { found } = await checkContainer({ dockerId, container: id }); - if (found) { - await removeContainer({ id, dockerId: application.destinationDocker.id }); - } - } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deleteApplication( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { teamId } = request.user; - const application = await prisma.application.findUnique({ - where: { id }, - include: { destinationDocker: true, teams: true } - }); - if (teamId !== '0' && !application.teams.some((team) => team.id === teamId)) { - throw { status: 403, message: 'You are not allowed to delete this application.' }; - } - if (application?.destinationDocker?.id && application.destinationDocker?.network) { - const { stdout: containers } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${id} --format '{{json .}}'` - }); - if (containers) { - const containersArray = containers.trim().split('\n'); - for (const container of containersArray) { - const containerObj = JSON.parse(container); - const id = containerObj.ID; - await removeContainer({ id, dockerId: application.destinationDocker.id }); - } - } - } - await prisma.applicationSettings.deleteMany({ where: { application: { id } } }); - await prisma.buildLog.deleteMany({ where: { applicationId: id } }); - await prisma.build.deleteMany({ where: { applicationId: id } }); - await prisma.secret.deleteMany({ where: { applicationId: id } }); - await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } }); - await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: id } }); - await prisma.previewApplication.deleteMany({ where: { applicationId: id } }); - if (teamId === '0') { - await prisma.application.deleteMany({ where: { id } }); - } else { - await prisma.application.deleteMany({ where: { id, teams: { some: { id: teamId } } } }); - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function checkDomain(request: FastifyRequest) { - try { - const { id } = request.params; - const { domain } = request.query; - const { - fqdn, - settings: { dualCerts } - } = await prisma.application.findUnique({ where: { id }, include: { settings: true } }); - // TODO: Disabled this because it is having problems with remote docker engines. - // return await checkDomainsIsValidInDNS({ hostname: domain, fqdn, dualCerts }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function checkDNS(request: FastifyRequest) { - try { - const { id } = request.params; - let { exposePort, fqdn, forceSave, dualCerts } = request.body; - if (!fqdn) { - return {}; - } else { - fqdn = fqdn.toLowerCase(); - } - if (exposePort) exposePort = Number(exposePort); - - const { - destinationDocker: { engine, remoteIpAddress, remoteEngine }, - exposePort: configuredPort - } = await prisma.application.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - const { isDNSCheckEnabled } = await prisma.setting.findFirst({}); - - const found = await isDomainConfigured({ id, fqdn, remoteIpAddress }); - if (found) { - throw { - status: 500, - message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` - }; - } - if (exposePort) - await checkExposedPort({ - id, - configuredPort, - exposePort, - engine, - remoteEngine, - remoteIpAddress - }); - // TODO: Disabled this because it is having problems with remote docker engines. - // if (isDNSCheckEnabled && !isDev && !forceSave) { - // let hostname = request.hostname.split(':')[0]; - // if (remoteEngine) hostname = remoteIpAddress; - // return await checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }); - // } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getUsage(request) { - try { - const { id } = request.params; - const teamId = request.user?.teamId; - let usage = {}; - - const application: any = await getApplicationFromDB(id, teamId); - if (application.destinationDockerId) { - [usage] = await Promise.all([getContainerUsage(application.destinationDocker.id, id)]); - } - return { - usage - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getDockerImages(request) { - try { - const { id } = request.params; - const teamId = request.user?.teamId; - const application: any = await getApplicationFromDB(id, teamId); - let imagesAvailables = []; - try { - const { stdout } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker images --format '{{.Repository}}#{{.Tag}}#{{.CreatedAt}}'` - }); - const { stdout: runningImage } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker ps -a --filter 'label=com.docker.compose.service=${id}' --format {{.Image}}` - }); - const images = stdout - .trim() - .split('\n') - .filter((image) => image.includes(id) && !image.includes('-cache')); - for (const image of images) { - const [repository, tag, createdAt] = image.split('#'); - if (tag.includes('-')) { - continue; - } - const [year, time] = createdAt.split(' '); - imagesAvailables.push({ - repository, - tag, - createdAt: day(year + time).unix() - }); - } - - imagesAvailables = imagesAvailables.sort((a, b) => b.tag - a.tag); - - return { - imagesAvailables, - runningImage - }; - } catch (error) { - console.log(error); - return { - imagesAvailables - }; - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getUsageByContainer(request) { - try { - const { id, containerId } = request.params; - const teamId = request.user?.teamId; - let usage = {}; - - const application: any = await getApplicationFromDB(id, teamId); - if (application.destinationDockerId) { - [usage] = await Promise.all([ - getContainerUsage(application.destinationDocker.id, containerId) - ]); - } - return { - usage - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deployApplication(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user?.teamId; - const { pullmergeRequestId = null, branch, forceRebuild } = request.body; - const buildId = cuid(); - const application = await getApplicationFromDB(id, teamId); - if (application) { - if (!application?.configHash) { - const configHash = crypto - .createHash('sha256') - .update( - JSON.stringify({ - buildPack: application.buildPack, - port: application.port, - exposePort: application.exposePort, - installCommand: application.installCommand, - buildCommand: application.buildCommand, - startCommand: application.startCommand - }) - ) - .digest('hex'); - await prisma.application.update({ where: { id }, data: { configHash } }); - } - await prisma.application.update({ where: { id }, data: { updatedAt: new Date() } }); - if (application.gitSourceId) { - await prisma.build.create({ - data: { - id: buildId, - applicationId: id, - sourceBranch: branch, - branch: application.branch, - pullmergeRequestId: pullmergeRequestId?.toString(), - forceRebuild, - destinationDockerId: application.destinationDocker?.id, - gitSourceId: application.gitSource?.id, - githubAppId: application.gitSource?.githubApp?.id, - gitlabAppId: application.gitSource?.gitlabApp?.id, - status: 'queued', - type: pullmergeRequestId - ? application.gitSource?.githubApp?.id - ? 'manual_pr' - : 'manual_mr' - : 'manual' - } - }); - } else { - await prisma.build.create({ - data: { - id: buildId, - applicationId: id, - branch: 'latest', - forceRebuild, - destinationDockerId: application.destinationDocker?.id, - status: 'queued', - type: 'manual' - } - }); - } - - return { - buildId - }; - } - throw { status: 500, message: 'Application not found!' }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveApplicationSource( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { gitSourceId, forPublic, type, simpleDockerfile } = request.body; - if (forPublic) { - const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } }); - await prisma.application.update({ - where: { id }, - data: { gitSource: { connect: { id: publicGit.id } } } - }); - } - if (simpleDockerfile) { - await prisma.application.update({ - where: { id }, - data: { simpleDockerfile, settings: { update: { autodeploy: false } } } - }); - } - if (gitSourceId) { - await prisma.application.update({ - where: { id }, - data: { gitSource: { connect: { id: gitSourceId } } } - }); - } - - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getGitHubToken(request: FastifyRequest, reply: FastifyReply) { - try { - const { default: got } = await import('got'); - const { id } = request.params; - const { teamId } = request.user; - const application: any = await getApplicationFromDB(id, teamId); - const payload = { - iat: Math.round(new Date().getTime() / 1000), - exp: Math.round(new Date().getTime() / 1000 + 60), - iss: application.gitSource.githubApp.appId - }; - const githubToken = jsonwebtoken.sign(payload, application.gitSource.githubApp.privateKey, { - algorithm: 'RS256' - }); - const { token } = await got - .post( - `${application.gitSource.apiUrl}/app/installations/${application.gitSource.githubApp.installationId}/access_tokens`, - { - headers: { - Authorization: `Bearer ${githubToken}` - } - } - ) - .json(); - return reply.code(201).send({ - token - }); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function checkRepository(request: FastifyRequest) { - try { - const { id } = request.params; - const { repository, branch } = request.query; - const application = await prisma.application.findUnique({ - where: { id }, - include: { gitSource: true } - }); - const found = await prisma.application.findFirst({ - where: { - branch, - repository, - gitSource: { type: application.gitSource.type }, - id: { not: id } - } - }); - return { - used: found ? true : false - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveRepository(request, reply) { - try { - const { id } = request.params; - let { - repository, - branch, - projectId, - autodeploy, - webhookToken, - isPublicRepository = false - } = request.body; - - repository = repository.toLowerCase(); - - projectId = Number(projectId); - if (webhookToken) { - await prisma.application.update({ - where: { id }, - data: { - repository, - branch, - projectId, - gitSource: { - update: { - gitlabApp: { update: { webhookToken: webhookToken ? webhookToken : undefined } } - } - }, - settings: { update: { autodeploy, isPublicRepository } } - } - }); - } else { - await prisma.application.update({ - where: { id }, - data: { - repository, - branch, - projectId, - settings: { update: { autodeploy, isPublicRepository } } - } - }); - } - // if (!isPublicRepository) { - // const isDouble = await checkDoubleBranch(branch, projectId); - // if (isDouble) { - // await prisma.applicationSettings.updateMany({ where: { application: { branch, projectId } }, data: { autodeploy: false, isPublicRepository } }) - // } - // } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveDestination( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { destinationId } = request.body; - await prisma.application.update({ - where: { id }, - data: { destinationDocker: { connect: { id: destinationId } } } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getBuildPack(request) { - try { - const { id } = request.params; - const teamId = request.user?.teamId; - const application: any = await getApplicationFromDB(id, teamId); - return { - type: application.gitSource?.type || 'dockerRegistry', - projectId: application.projectId, - repository: application.repository, - branch: application.branch, - apiUrl: application.gitSource?.apiUrl || null, - isPublicRepository: application.settings.isPublicRepository - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveRegistry(request, reply) { - try { - const { id } = request.params; - const { registryId } = request.body; - await prisma.application.update({ - where: { id }, - data: { dockerRegistry: { connect: { id: registryId } } } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveBuildPack(request, reply) { - try { - const { id } = request.params; - const { buildPack } = request.body; - const { baseImage, baseBuildImage } = setDefaultBaseImage(buildPack); - await prisma.application.update({ - where: { id }, - data: { buildPack, baseImage, baseBuildImage } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveConnectedDatabase(request, reply) { - try { - const { id } = request.params; - const { databaseId, type } = request.body; - await prisma.application.update({ - where: { id }, - data: { - connectedDatabase: { - upsert: { - create: { database: { connect: { id: databaseId } }, hostedDatabaseType: type }, - update: { database: { connect: { id: databaseId } }, hostedDatabaseType: type } - } - } - } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getSecrets(request: FastifyRequest) { - try { - const { id } = request.params; - - let secrets = await prisma.secret.findMany({ - where: { applicationId: id, isPRMRSecret: false }, - orderBy: { createdAt: 'asc' } - }); - let previewSecrets = await prisma.secret.findMany({ - where: { applicationId: id, isPRMRSecret: true }, - orderBy: { createdAt: 'asc' } - }); - - secrets = secrets.map((secret) => { - secret.value = decrypt(secret.value); - return secret; - }); - previewSecrets = previewSecrets.map((secret) => { - secret.value = decrypt(secret.value); - return secret; - }); - - return { - previewSecrets: previewSecrets.sort((a, b) => { - return ('' + a.name).localeCompare(b.name); - }), - secrets: secrets.sort((a, b) => { - return ('' + a.name).localeCompare(b.name); - }) - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function updatePreviewSecret( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - let { name, value } = request.body; - if (value) { - value = encrypt(value.trim()); - } else { - value = ''; - } - await prisma.secret.updateMany({ - where: { applicationId: id, name, isPRMRSecret: true }, - data: { value } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function updateSecret(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const { name, value, isBuildSecret = undefined } = request.body; - await prisma.secret.updateMany({ - where: { applicationId: id, name }, - data: { value: encrypt(value.trim()), isBuildSecret } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveSecret(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const { name, value, isBuildSecret = false } = request.body; - const found = await prisma.secret.findMany({ where: { applicationId: id, name } }); - if (found.length > 0) { - throw { message: 'Secret already exists.' }; - } - await prisma.secret.create({ - data: { - name, - value: encrypt(value.trim()), - isBuildSecret, - isPRMRSecret: false, - application: { connect: { id } } - } - }); - await prisma.secret.create({ - data: { - name, - value: encrypt(value.trim()), - isBuildSecret, - isPRMRSecret: true, - application: { connect: { id } } - } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deleteSecret(request: FastifyRequest) { - try { - const { id } = request.params; - const { name } = request.body; - await prisma.secret.deleteMany({ where: { applicationId: id, name } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getStorages(request: FastifyRequest) { - try { - const { id } = request.params; - const persistentStorages = await prisma.applicationPersistentStorage.findMany({ - where: { applicationId: id } - }); - return { - persistentStorages - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveStorage(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const { hostPath, path, newStorage, storageId } = request.body; - - if (newStorage) { - await prisma.applicationPersistentStorage.create({ - data: { hostPath, path, application: { connect: { id } } } - }); - } else { - await prisma.applicationPersistentStorage.update({ - where: { id: storageId }, - data: { hostPath, path } - }); - } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function deleteStorage(request: FastifyRequest) { - try { - const { id } = request.params; - const { path } = request.body; - await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id, path } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function restartPreview( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id, pullmergeRequestId } = request.params; - const { teamId } = request.user; - const application: any = await getApplicationFromDB(id, teamId); - if (application?.destinationDockerId) { - const buildId = cuid(); - const { id: dockerId, network } = application.destinationDocker; - const { - secrets, - port, - repository, - persistentStorage, - id: applicationId, - buildPack, - exposePort - } = application; - - let envs = []; - if (secrets.length > 0) { - envs = [...envs, ...generateSecrets(secrets, pullmergeRequestId, false, port)]; - } - const { workdir } = await createDirectories({ repository, buildId }); - const labels = []; - let image = null; - const { stdout: container } = await executeCommand({ - dockerId, - command: `docker container ls --filter 'label=com.docker.compose.service=${id}-${pullmergeRequestId}' --format '{{json .}}'` - }); - const containersArray = container.trim().split('\n'); - for (const container of containersArray) { - const containerObj = formatLabelsOnDocker(container); - image = containerObj[0].Image; - Object.keys(containerObj[0].Labels).forEach(function (key) { - if (key.startsWith('coolify')) { - labels.push(`${key}=${containerObj[0].Labels[key]}`); - } - }); - } - let imageFound = false; - try { - await executeCommand({ - dockerId, - command: `docker image inspect ${image}` - }); - imageFound = true; - } catch (error) { - // - } - if (!imageFound) { - throw { status: 500, message: 'Image not found, cannot restart application.' }; - } - - const volumes = - persistentStorage?.map((storage) => { - return `${applicationId}${storage.path.replace(/\//gi, '-')}:${buildPack !== 'docker' ? '/app' : '' - }${storage.path}`; - }) || []; - const composeVolumes = volumes.map((volume) => { - return { - [`${volume.split(':')[0]}`]: { - name: volume.split(':')[0] - } - }; - }); - const composeFile = { - version: '3.8', - services: { - [`${applicationId}-${pullmergeRequestId}`]: { - image, - container_name: `${applicationId}-${pullmergeRequestId}`, - volumes, - environment: envs, - labels, - depends_on: [], - expose: [port], - ...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}), - ...defaultComposeConfiguration(network) - } - }, - networks: { - [network]: { - external: true - } - }, - volumes: Object.assign({}, ...composeVolumes) - }; - await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile)); - await executeCommand({ dockerId, command: `docker stop -t 0 ${id}-${pullmergeRequestId}` }); - await executeCommand({ dockerId, command: `docker rm ${id}-${pullmergeRequestId}` }); - await executeCommand({ - dockerId, - command: `docker compose --project-directory ${workdir} -f ${workdir}/docker-compose.yml up -d` - }); - return reply.code(201).send(); - } - throw { status: 500, message: 'Application cannot be restarted.' }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getPreviewStatus(request: FastifyRequest) { - try { - const { id, pullmergeRequestId } = request.params; - const { teamId } = request.user; - let isRunning = false; - let isExited = false; - let isRestarting = false; - let isBuilding = false; - const application: any = await getApplicationFromDB(id, teamId); - if (application?.destinationDockerId) { - const status = await checkContainer({ - dockerId: application.destinationDocker.id, - container: `${id}-${pullmergeRequestId}` - }); - if (status?.found) { - isRunning = status.status.isRunning; - isExited = status.status.isExited; - isRestarting = status.status.isRestarting; - } - const building = await prisma.build.findMany({ - where: { applicationId: id, pullmergeRequestId, status: { in: ['queued', 'running'] } } - }); - isBuilding = building.length > 0; - } - return { - isBuilding, - isRunning, - isRestarting, - isExited - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function loadPreviews(request: FastifyRequest) { - try { - const { id } = request.params; - const application = await prisma.application.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - const { stdout } = await executeCommand({ - dockerId: application.destinationDocker.id, - command: `docker container ls --filter 'name=${id}-' --format "{{json .}}"` - }); - if (stdout === '') { - throw { status: 500, message: 'No previews found.' }; - } - const containers = formatLabelsOnDocker(stdout).filter( - (container) => - container.Labels['coolify.configuration'] && - container.Labels['coolify.type'] === 'standalone-application' - ); - - const jsonContainers = containers - .map((container) => - JSON.parse(Buffer.from(container.Labels['coolify.configuration'], 'base64').toString()) - ) - .filter((container) => { - return container.pullmergeRequestId && container.applicationId === id; - }); - for (const container of jsonContainers) { - const found = await prisma.previewApplication.findMany({ - where: { - applicationId: container.applicationId, - pullmergeRequestId: container.pullmergeRequestId - } - }); - if (found.length === 0) { - await prisma.previewApplication.create({ - data: { - pullmergeRequestId: container.pullmergeRequestId, - sourceBranch: container.branch, - customDomain: container.fqdn, - application: { connect: { id: container.applicationId } } - } - }); - } - } - return { - previews: await prisma.previewApplication.findMany({ where: { applicationId: id } }) - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getPreviews(request: FastifyRequest) { - try { - const { id } = request.params; - const { teamId } = request.user; - let secrets = await prisma.secret.findMany({ - where: { applicationId: id }, - orderBy: { createdAt: 'desc' } - }); - secrets = secrets.map((secret) => { - secret.value = decrypt(secret.value); - return secret; - }); - - const applicationSecrets = secrets.filter((secret) => !secret.isPRMRSecret); - const PRMRSecrets = secrets.filter((secret) => secret.isPRMRSecret); - return { - applicationSecrets: applicationSecrets.sort((a, b) => { - return ('' + a.name).localeCompare(b.name); - }), - PRMRSecrets: PRMRSecrets.sort((a, b) => { - return ('' + a.name).localeCompare(b.name); - }) - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getApplicationLogs(request: FastifyRequest) { - try { - const { id, containerId } = request.params; - let { since = 0 } = request.query; - if (since !== 0) { - since = day(since).unix(); - } - const { - destinationDockerId, - destinationDocker: { id: dockerId } - } = await prisma.application.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - if (destinationDockerId) { - try { - const { default: ansi } = await import('strip-ansi'); - const { stdout, stderr } = await executeCommand({ - dockerId, - command: `docker logs --since ${since} --tail 5000 --timestamps ${containerId}` - }); - const stripLogsStdout = stdout - .toString() - .split('\n') - .map((l) => ansi(l)) - .filter((a) => a); - const stripLogsStderr = stderr - .toString() - .split('\n') - .map((l) => ansi(l)) - .filter((a) => a); - return { logs: stripLogsStderr.concat(stripLogsStdout) }; - } catch (error) { - const { statusCode, stderr } = error; - if (stderr.startsWith('Error: No such container')) { - return { logs: [], noContainer: true }; - } - if (statusCode === 404) { - return { - logs: [] - }; - } - } - } - return { - message: 'No logs found.' - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getBuilds(request: FastifyRequest) { - try { - const { id } = request.params; - let { buildId, skip = 0 } = request.query; - if (typeof skip !== 'number') { - skip = Number(skip); - } - - let builds = []; - - const buildCount = await prisma.build.count({ where: { applicationId: id } }); - if (buildId) { - builds = await prisma.build.findMany({ where: { applicationId: id, id: buildId } }); - } else { - builds = await prisma.build.findMany({ - where: { applicationId: id }, - orderBy: { createdAt: 'desc' }, - take: 5 + skip - }); - } - builds = builds.map((build) => { - if (build.status === 'running') { - build.elapsed = (day().utc().diff(day(build.createdAt)) / 1000).toFixed(0); - } - return build; - }); - return { - builds, - buildCount - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getBuildIdLogs(request: FastifyRequest) { - try { - // TODO: Fluentbit could still hold the logs, so we need to check if the logs are done - const { buildId, id } = request.params; - let { sequence = 0 } = request.query; - if (typeof sequence !== 'number') { - sequence = Number(sequence); - } - let file = `/app/logs/${id}_buildlog_${buildId}.csv`; - if (isDev) { - file = `${process.cwd()}/../../logs/${id}_buildlog_${buildId}.csv`; - } - const data = await prisma.build.findFirst({ where: { id: buildId } }); - const createdAt = day(data.createdAt).utc(); - try { - await fs.stat(file); - } catch (error) { - const logs = await prisma.buildLog.findMany({ - where: { buildId, time: { gt: sequence } }, - orderBy: { time: 'asc' } - }); - const data = await prisma.build.findFirst({ where: { id: buildId } }); - const createdAt = day(data.createdAt).utc(); - return { - logs: logs.map((log) => { - log.time = Number(log.time); - return log; - }), - fromDb: true, - took: day().diff(createdAt) / 1000, - status: data?.status || 'queued' - }; - } - const fileLogs = (await fs.readFile(file)).toString(); - const decryptedLogs = await csv({ noheader: true }).fromString(fileLogs); - const logs = decryptedLogs - .map((log) => { - const parsed = { - time: log['field1'], - line: decrypt(log['field2'] + '","' + log['field3']) - }; - return parsed; - }) - .filter((log) => log.time > sequence); - return { - logs, - fromDb: false, - took: day().diff(createdAt) / 1000, - status: data?.status || 'queued' - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getGitLabSSHKey(request: FastifyRequest) { - try { - const { id } = request.params; - const application = await prisma.application.findUnique({ - where: { id }, - include: { gitSource: { include: { gitlabApp: true } } } - }); - return { publicKey: application.gitSource.gitlabApp.publicSshKey }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveGitLabSSHKey(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const application = await prisma.application.findUnique({ - where: { id }, - include: { gitSource: { include: { gitlabApp: true } } } - }); - if (!application.gitSource?.gitlabApp?.privateSshKey) { - const keys = await generateSshKeyPair(); - const encryptedPrivateKey = encrypt(keys.privateKey); - await prisma.gitlabApp.update({ - where: { id: application.gitSource.gitlabApp.id }, - data: { privateSshKey: encryptedPrivateKey, publicSshKey: keys.publicKey } - }); - return reply.code(201).send({ publicKey: keys.publicKey }); - } - return { message: 'SSH key already exists' }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveDeployKey(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - let { deployKeyId } = request.body; - - deployKeyId = Number(deployKeyId); - const application = await prisma.application.findUnique({ - where: { id }, - include: { gitSource: { include: { gitlabApp: true } } } - }); - await prisma.gitlabApp.update({ - where: { id: application.gitSource.gitlabApp.id }, - data: { deployKeyId } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function cancelDeployment( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { buildId, applicationId } = request.body; - if (!buildId) { - throw { status: 500, message: 'buildId is required' }; - } - await stopBuild(buildId, applicationId); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function createdBranchDatabase( - database: any, - baseDatabaseBranch: string, - pullmergeRequestId: string -) { - try { - if (!baseDatabaseBranch) return; - const { id, type, destinationDockerId, rootUser, rootUserPassword, dbUser } = database; - if (destinationDockerId) { - if (type === 'postgresql') { - const decryptedRootUserPassword = decrypt(rootUserPassword); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker exec ${id} pg_dump -d "postgresql://postgres:${decryptedRootUserPassword}@${id}:5432/${baseDatabaseBranch}" --encoding=UTF8 --schema-only -f /tmp/${baseDatabaseBranch}.dump` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "CREATE DATABASE branch_${pullmergeRequestId}"` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker exec ${id} psql -d "postgresql://postgres:${decryptedRootUserPassword}@${id}:5432/branch_${pullmergeRequestId}" -f /tmp/${baseDatabaseBranch}.dump` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "ALTER DATABASE branch_${pullmergeRequestId} OWNER TO ${dbUser}"` - }); - } - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function removeBranchDatabase(database: any, pullmergeRequestId: string) { - try { - const { id, type, destinationDockerId, rootUser, rootUserPassword } = database; - if (destinationDockerId) { - if (type === 'postgresql') { - const decryptedRootUserPassword = decrypt(rootUserPassword); - // Terminate all connections to the database - await executeCommand({ - dockerId: destinationDockerId, - command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'branch_${pullmergeRequestId}' AND pid <> pg_backend_pid();"` - }); - - await executeCommand({ - dockerId: destinationDockerId, - command: `docker exec ${id} psql postgresql://postgres:${decryptedRootUserPassword}@${id}:5432 -c "DROP DATABASE branch_${pullmergeRequestId}"` - }); - } - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} diff --git a/apps/api/src/routes/api/v1/applications/index.ts b/apps/api/src/routes/api/v1/applications/index.ts deleted file mode 100644 index 9943dcee6..000000000 --- a/apps/api/src/routes/api/v1/applications/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { OnlyId } from '../../../../types'; -import { cancelDeployment, checkDNS, checkDomain, checkRepository, cleanupUnconfiguredApplications, deleteApplication, deleteSecret, deleteStorage, deployApplication, getApplication, getApplicationLogs, getApplicationStatus, getBuildIdLogs, getBuildPack, getBuilds, getDockerImages, getGitHubToken, getGitLabSSHKey, getImages, getPreviews, getPreviewStatus, getSecrets, getStorages, getUsage, getUsageByContainer, listApplications, loadPreviews, newApplication, restartApplication, restartPreview, saveApplication, saveApplicationSettings, saveApplicationSource, saveBuildPack, saveConnectedDatabase, saveDeployKey, saveDestination, saveGitLabSSHKey, saveRegistry, saveRepository, saveSecret, saveStorage, stopApplication, stopPreviewApplication, updatePreviewSecret, updateSecret } from './handlers'; - -import type { CancelDeployment, CheckDNS, CheckDomain, CheckRepository, DeleteApplication, DeleteSecret, DeleteStorage, DeployApplication, GetApplicationLogs, GetBuildIdLogs, GetBuilds, GetImages, RestartApplication, RestartPreviewApplication, SaveApplication, SaveApplicationSettings, SaveApplicationSource, SaveDeployKey, SaveDestination, SaveSecret, SaveStorage, StopPreviewApplication } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listApplications(request)); - fastify.post('/images', async (request) => await getImages(request)); - - fastify.post('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredApplications(request)); - - fastify.post('/new', async (request, reply) => await newApplication(request, reply)); - - fastify.get('/:id', async (request) => await getApplication(request)); - fastify.post('/:id', async (request, reply) => await saveApplication(request, reply)); - fastify.delete('/:id', async (request, reply) => await deleteApplication(request, reply)); - - fastify.get('/:id/status', async (request) => await getApplicationStatus(request)); - - fastify.post('/:id/restart', async (request, reply) => await restartApplication(request, reply)); - fastify.post('/:id/stop', async (request, reply) => await stopApplication(request, reply)); - fastify.post('/:id/stop/preview', async (request, reply) => await stopPreviewApplication(request, reply)); - - fastify.post('/:id/settings', async (request, reply) => await saveApplicationSettings(request, reply)); - - fastify.get('/:id/check', async (request) => await checkDomain(request)); - fastify.post('/:id/check', async (request) => await checkDNS(request)); - - fastify.get('/:id/secrets', async (request) => await getSecrets(request)); - fastify.post('/:id/secrets', async (request, reply) => await saveSecret(request, reply)); - fastify.put('/:id/secrets', async (request, reply) => await updateSecret(request, reply)); - fastify.put('/:id/secrets/preview', async (request, reply) => await updatePreviewSecret(request, reply)); - fastify.delete('/:id/secrets', async (request) => await deleteSecret(request)); - - fastify.get('/:id/storages', async (request) => await getStorages(request)); - fastify.post('/:id/storages', async (request, reply) => await saveStorage(request, reply)); - fastify.delete('/:id/storages', async (request) => await deleteStorage(request)); - - fastify.get('/:id/previews', async (request) => await getPreviews(request)); - fastify.post('/:id/previews/load', async (request) => await loadPreviews(request)); - fastify.get('/:id/previews/:pullmergeRequestId/status', async (request) => await getPreviewStatus(request)); - fastify.post('/:id/previews/:pullmergeRequestId/restart', async (request, reply) => await restartPreview(request, reply)); - - fastify.get('/:id/logs/:containerId', async (request) => await getApplicationLogs(request)); - fastify.get('/:id/logs/build', async (request) => await getBuilds(request)); - fastify.get('/:id/logs/build/:buildId', async (request) => await getBuildIdLogs(request)); - - fastify.get('/:id/usage', async (request) => await getUsage(request)) - fastify.get('/:id/usage/:containerId', async (request) => await getUsageByContainer(request)) - - fastify.get('/:id/images', async (request) => await getDockerImages(request)) - - fastify.post('/:id/deploy', async (request) => await deployApplication(request)) - fastify.post('/:id/cancel', async (request, reply) => await cancelDeployment(request, reply)); - - fastify.post('/:id/configuration/source', async (request, reply) => await saveApplicationSource(request, reply)); - - fastify.get('/:id/configuration/repository', async (request) => await checkRepository(request)); - fastify.post('/:id/configuration/repository', async (request, reply) => await saveRepository(request, reply)); - fastify.post('/:id/configuration/destination', async (request, reply) => await saveDestination(request, reply)); - fastify.get('/:id/configuration/buildpack', async (request) => await getBuildPack(request)); - fastify.post('/:id/configuration/buildpack', async (request, reply) => await saveBuildPack(request, reply)); - - fastify.post('/:id/configuration/registry', async (request, reply) => await saveRegistry(request, reply)); - - fastify.post('/:id/configuration/database', async (request, reply) => await saveConnectedDatabase(request, reply)); - - fastify.get('/:id/configuration/sshkey', async (request) => await getGitLabSSHKey(request)); - fastify.post('/:id/configuration/sshkey', async (request, reply) => await saveGitLabSSHKey(request, reply)); - - fastify.post('/:id/configuration/deploykey', async (request, reply) => await saveDeployKey(request, reply)); - - fastify.get('/:id/configuration/githubToken', async (request, reply) => await getGitHubToken(request, reply)); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/applications/types.ts b/apps/api/src/routes/api/v1/applications/types.ts deleted file mode 100644 index 1c42f468a..000000000 --- a/apps/api/src/routes/api/v1/applications/types.ts +++ /dev/null @@ -1,171 +0,0 @@ -import type { OnlyId } from '../../../../types'; - -export interface SaveApplication extends OnlyId { - Body: { - name: string; - buildPack: string; - fqdn: string; - port: number; - exposePort: number; - installCommand: string; - buildCommand: string; - startCommand: string; - baseDirectory: string; - publishDirectory: string; - pythonWSGI: string; - pythonModule: string; - pythonVariable: string; - dockerFileLocation: string; - denoMainFile: string; - denoOptions: string; - baseImage: string; - gitCommitHash: string; - baseBuildImage: string; - deploymentType: string; - baseDatabaseBranch: string; - dockerComposeFile: string; - dockerComposeFileLocation: string; - dockerComposeConfiguration: string; - simpleDockerfile: string; - dockerRegistryImageName: string; - }; -} -export interface SaveApplicationSettings extends OnlyId { - Querystring: { domain: string }; - Body: { - debug: boolean; - previews: boolean; - dualCerts: boolean; - autodeploy: boolean; - branch: string; - projectId: number; - isBot: boolean; - isDBBranching: boolean; - isCustomSSL: boolean; - isHttp2: boolean; - }; -} -export interface DeleteApplication extends OnlyId { - Querystring: { domain: string }; - Body: { force: boolean }; -} -export interface CheckDomain extends OnlyId { - Querystring: { domain: string }; -} -export interface CheckDNS extends OnlyId { - Querystring: { domain: string }; - Body: { - exposePort: number; - fqdn: string; - forceSave: boolean; - dualCerts: boolean; - }; -} -export interface DeployApplication { - Querystring: { domain: string }; - Body: { pullmergeRequestId: string | null; branch: string; forceRebuild?: boolean }; -} -export interface GetImages { - Body: { buildPack: string; deploymentType: string }; -} -export interface SaveApplicationSource extends OnlyId { - Body: { - gitSourceId?: string | null; - forPublic?: boolean; - type?: string; - simpleDockerfile?: string; - }; -} -export interface CheckRepository extends OnlyId { - Querystring: { repository: string; branch: string }; -} -export interface SaveDestination extends OnlyId { - Body: { destinationId: string }; -} -export interface SaveSecret extends OnlyId { - Body: { - name: string; - value: string; - isBuildSecret: boolean; - previewSecret: boolean; - isNew: boolean; - }; -} -export interface DeleteSecret extends OnlyId { - Body: { name: string }; -} -export interface SaveStorage extends OnlyId { - Body: { - hostPath?: string; - path: string; - newStorage: boolean; - storageId: string; - }; -} -export interface DeleteStorage extends OnlyId { - Body: { - path: string; - }; -} -export interface GetApplicationLogs { - Params: { - id: string; - containerId: string; - }; - Querystring: { - since: number; - }; -} -export interface GetBuilds extends OnlyId { - Querystring: { - buildId: string; - skip: number; - }; -} -export interface GetBuildIdLogs { - Params: { - id: string; - buildId: string; - }; - Querystring: { - sequence: number; - }; -} -export interface SaveDeployKey extends OnlyId { - Body: { - deployKeyId: number; - }; -} -export interface CancelDeployment { - Body: { - buildId: string; - applicationId: string; - }; -} -export interface DeployApplication extends OnlyId { - Body: { - pullmergeRequestId: string | null; - branch: string; - forceRebuild?: boolean; - }; -} - -export interface StopPreviewApplication extends OnlyId { - Body: { - pullmergeRequestId: string | null; - }; -} -export interface RestartPreviewApplication { - Params: { - id: string; - pullmergeRequestId: string | null; - }; -} -export interface RestartApplication { - Params: { - id: string; - }; - Body: { - imageId: string | null; - }; -} diff --git a/apps/api/src/routes/api/v1/base/index.ts b/apps/api/src/routes/api/v1/base/index.ts deleted file mode 100644 index b03aa197f..000000000 --- a/apps/api/src/routes/api/v1/base/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { errorHandler, isARM, listSettings, version } from '../../../../lib/common'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - try { - await request.jwtVerify(); - } catch (error) { - return; - } - }); - fastify.get('/', async (request) => { - const teamId = request.user?.teamId; - const settings = await listSettings(); - try { - return { - ipv4: teamId ? settings.ipv4 : null, - ipv6: teamId ? settings.ipv6 : null, - version, - whiteLabeled: process.env.COOLIFY_WHITE_LABELED === 'true', - whiteLabeledIcon: process.env.COOLIFY_WHITE_LABELED_ICON, - isRegistrationEnabled: settings.isRegistrationEnabled, - isARM: isARM() - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } - }); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/databases/handlers.ts b/apps/api/src/routes/api/v1/databases/handlers.ts deleted file mode 100644 index 982699fde..000000000 --- a/apps/api/src/routes/api/v1/databases/handlers.ts +++ /dev/null @@ -1,607 +0,0 @@ -import cuid from 'cuid'; -import type { FastifyRequest } from 'fastify'; -import { FastifyReply } from 'fastify'; -import yaml from 'js-yaml'; -import fs from 'fs/promises'; -import { - ComposeFile, - createDirectories, - decrypt, - defaultComposeConfiguration, - encrypt, - errorHandler, - executeCommand, - generateDatabaseConfiguration, - generatePassword, - getContainerUsage, - getDatabaseImage, - getDatabaseVersions, - getFreePublicPort, - listSettings, - makeLabelForStandaloneDatabase, - prisma, - startTraefikTCPProxy, - stopDatabaseContainer, - stopTcpHttpProxy, - supportedDatabaseTypesAndVersions, - uniqueName, - updatePasswordInDb -} from '../../../../lib/common'; -import { day } from '../../../../lib/dayjs'; - -import type { OnlyId } from '../../../../types'; -import type { - DeleteDatabase, - DeleteDatabaseSecret, - GetDatabaseLogs, - SaveDatabase, - SaveDatabaseDestination, - SaveDatabaseSecret, - SaveDatabaseSettings, - SaveDatabaseType, - SaveVersion -} from './types'; - -export async function listDatabases(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const databases = await prisma.database.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { teams: true, destinationDocker: true } - }); - return { - databases - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function newDatabase(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - - const name = uniqueName(); - const dbUser = cuid(); - const dbUserPassword = encrypt(generatePassword({})); - const rootUser = cuid(); - const rootUserPassword = encrypt(generatePassword({})); - const defaultDatabase = cuid(); - - const { id } = await prisma.database.create({ - data: { - name, - defaultDatabase, - dbUser, - dbUserPassword, - rootUser, - rootUserPassword, - teams: { connect: { id: teamId } }, - settings: { create: { isPublic: false } } - } - }); - return reply.code(201).send({ id }); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function cleanupUnconfiguredDatabases(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - let databases = await prisma.database.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { settings: true, destinationDocker: true, teams: true } - }); - for (const database of databases) { - if (!database?.version) { - const { id } = database; - if (database.destinationDockerId) { - const everStarted = await stopDatabaseContainer(database); - if (everStarted) - await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); - } - await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); - await prisma.databaseSecret.deleteMany({ where: { databaseId: id } }); - await prisma.database.delete({ where: { id } }); - } - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getDatabaseStatus(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - let isRunning = false; - - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - if (database) { - const { destinationDockerId, destinationDocker } = database; - if (destinationDockerId) { - try { - const { stdout } = await executeCommand({ - dockerId: destinationDocker.id, - command: `docker inspect --format '{{json .State}}' ${id}` - }); - - if (JSON.parse(stdout).Running) { - isRunning = true; - } - } catch (error) { - // - } - } - } - return { - isRunning - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getDatabase(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - if (!database) { - throw { status: 404, message: 'Database not found.' }; - } - const settings = await listSettings(); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - const configuration = generateDatabaseConfiguration(database, settings.arch); - return { - privatePort: configuration?.privatePort, - database, - versions: await getDatabaseVersions(database.type, settings.arch), - settings - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getDatabaseTypes(request: FastifyRequest) { - try { - return { - types: supportedDatabaseTypesAndVersions - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveDatabaseType( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { type } = request.body; - await prisma.database.update({ - where: { id }, - data: { type } - }); - return reply.code(201).send({}); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getVersions(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const { type } = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - const { arch } = await listSettings(); - const versions = getDatabaseVersions(type, arch); - return { - versions - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveVersion(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const { version } = request.body; - - await prisma.database.update({ - where: { id }, - data: { - version - } - }); - return reply.code(201).send({}); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveDatabaseDestination( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { destinationId } = request.body; - - const { arch } = await listSettings(); - await prisma.database.update({ - where: { id }, - data: { destinationDocker: { connect: { id: destinationId } } } - }); - - const { - destinationDockerId, - destinationDocker: { engine, id: dockerId }, - version, - type - } = await prisma.database.findUnique({ where: { id }, include: { destinationDocker: true } }); - - if (destinationDockerId) { - if (type && version) { - const baseImage = getDatabaseImage(type, arch); - executeCommand({ dockerId, command: `docker pull ${baseImage}:${version}` }); - } - } - return reply.code(201).send({}); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getDatabaseUsage(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - let usage = {}; - - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - if (database.destinationDockerId) { - [usage] = await Promise.all([getContainerUsage(database.destinationDocker.id, id)]); - } - return { - usage - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function startDatabase(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true, databaseSecret: true } - }); - const { arch } = await listSettings(); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - const { - type, - destinationDockerId, - destinationDocker, - publicPort, - settings: { isPublic }, - databaseSecret - } = database; - const { privatePort, command, environmentVariables, image, volume, ulimits } = - generateDatabaseConfiguration(database, arch); - - const network = destinationDockerId && destinationDocker.network; - const volumeName = volume.split(':')[0]; - const labels = await makeLabelForStandaloneDatabase({ id, image, volume }); - - const { workdir } = await createDirectories({ repository: type, buildId: id }); - if (databaseSecret.length > 0) { - databaseSecret.forEach((secret) => { - environmentVariables[secret.name] = decrypt(secret.value); - }); - } - const composeFile: ComposeFile = { - version: '3.8', - services: { - [id]: { - container_name: id, - image, - command, - environment: environmentVariables, - volumes: [volume], - ulimits, - labels, - ...defaultComposeConfiguration(network) - } - }, - networks: { - [network]: { - external: true - } - }, - volumes: { - [volumeName]: { - name: volumeName - } - } - }; - const composeFileDestination = `${workdir}/docker-compose.yaml`; - await fs.writeFile(composeFileDestination, yaml.dump(composeFile)); - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker compose -f ${composeFileDestination} up -d` - }); - if (isPublic) await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function stopDatabase(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - const everStarted = await stopDatabaseContainer(database); - if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); - await prisma.database.update({ - where: { id }, - data: { - settings: { upsert: { update: { isPublic: false }, create: { isPublic: false } } } - } - }); - await prisma.database.update({ where: { id }, data: { publicPort: null } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getDatabaseLogs(request: FastifyRequest) { - try { - const { id } = request.params; - let { since = 0 } = request.query; - if (since !== 0) { - since = day(since).unix(); - } - const { - destinationDockerId, - destinationDocker: { id: dockerId } - } = await prisma.database.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - if (destinationDockerId) { - try { - const { default: ansi } = await import('strip-ansi'); - const { stdout, stderr } = await executeCommand({ - dockerId, - command: `docker logs --since ${since} --tail 5000 --timestamps ${id}` - }); - const stripLogsStdout = stdout - .toString() - .split('\n') - .map((l) => ansi(l)) - .filter((a) => a); - const stripLogsStderr = stderr - .toString() - .split('\n') - .map((l) => ansi(l)) - .filter((a) => a); - return { logs: stripLogsStderr.concat(stripLogsStdout) }; - } catch (error) { - const { statusCode } = error; - if (statusCode === 404) { - return { - logs: [] - }; - } - } - } - return { - message: 'No logs found.' - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deleteDatabase(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - if (database.destinationDockerId) { - const everStarted = await stopDatabaseContainer(database); - if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); - } - await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); - await prisma.databaseSecret.deleteMany({ where: { databaseId: id } }); - await prisma.database.delete({ where: { id } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveDatabase(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const { - name, - defaultDatabase, - dbUser, - dbUserPassword, - rootUser, - rootUserPassword, - version, - isRunning - } = request.body; - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - if (isRunning) { - if (database.dbUserPassword !== dbUserPassword) { - await updatePasswordInDb(database, dbUser, dbUserPassword, false); - } else if (database.rootUserPassword !== rootUserPassword) { - await updatePasswordInDb(database, rootUser, rootUserPassword, true); - } - } - const encryptedDbUserPassword = dbUserPassword && encrypt(dbUserPassword); - const encryptedRootUserPassword = rootUserPassword && encrypt(rootUserPassword); - await prisma.database.update({ - where: { id }, - data: { - name, - defaultDatabase, - dbUser, - dbUserPassword: encryptedDbUserPassword, - rootUser, - rootUserPassword: encryptedRootUserPassword, - version - } - }); - return reply.code(201).send({}); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveDatabaseSettings(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const { isPublic, appendOnly = true } = request.body; - - let publicPort = null; - - const { - destinationDocker: { remoteEngine, engine, remoteIpAddress } - } = await prisma.database.findUnique({ where: { id }, include: { destinationDocker: true } }); - - if (isPublic) { - publicPort = await getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress }); - } - await prisma.database.update({ - where: { id }, - data: { - settings: { upsert: { update: { isPublic, appendOnly }, create: { isPublic, appendOnly } } } - } - }); - const database = await prisma.database.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, settings: true } - }); - const { arch } = await listSettings(); - if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); - if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); - - const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database; - const { privatePort } = generateDatabaseConfiguration(database, arch); - - if (destinationDockerId) { - if (isPublic) { - await prisma.database.update({ where: { id }, data: { publicPort } }); - await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort); - } else { - await prisma.database.update({ where: { id }, data: { publicPort: null } }); - await stopTcpHttpProxy(id, destinationDocker, oldPublicPort); - } - } - return { publicPort }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getDatabaseSecrets(request: FastifyRequest) { - try { - const { id } = request.params; - let secrets = await prisma.databaseSecret.findMany({ - where: { databaseId: id }, - orderBy: { createdAt: 'desc' } - }); - secrets = secrets.map((secret) => { - secret.value = decrypt(secret.value); - return secret; - }); - - return { - secrets - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveDatabaseSecret( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - let { name, value, isNew } = request.body; - - if (isNew) { - const found = await prisma.databaseSecret.findFirst({ where: { name, databaseId: id } }); - if (found) { - throw `Secret ${name} already exists.`; - } else { - value = encrypt(value.trim()); - await prisma.databaseSecret.create({ - data: { name, value, database: { connect: { id } } } - }); - } - } else { - value = encrypt(value.trim()); - const found = await prisma.databaseSecret.findFirst({ where: { databaseId: id, name } }); - - if (found) { - await prisma.databaseSecret.updateMany({ - where: { databaseId: id, name }, - data: { value } - }); - } else { - await prisma.databaseSecret.create({ - data: { name, value, database: { connect: { id } } } - }); - } - } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deleteDatabaseSecret(request: FastifyRequest) { - try { - const { id } = request.params; - const { name } = request.body; - await prisma.databaseSecret.deleteMany({ where: { databaseId: id, name } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} diff --git a/apps/api/src/routes/api/v1/databases/index.ts b/apps/api/src/routes/api/v1/databases/index.ts deleted file mode 100644 index 65f0d58f4..000000000 --- a/apps/api/src/routes/api/v1/databases/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { cleanupUnconfiguredDatabases, deleteDatabase, deleteDatabaseSecret, getDatabase, getDatabaseLogs, getDatabaseSecrets, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSecret, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers'; - -import type { OnlyId } from '../../../../types'; - -import type { DeleteDatabase, SaveDatabaseType, DeleteDatabaseSecret, GetDatabaseLogs, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSecret, SaveDatabaseSettings, SaveVersion } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listDatabases(request)); - fastify.post('/new', async (request, reply) => await newDatabase(request, reply)); - - fastify.post('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredDatabases(request)); - - fastify.get('/:id', async (request) => await getDatabase(request)); - fastify.post('/:id', async (request, reply) => await saveDatabase(request, reply)); - fastify.delete('/:id', async (request) => await deleteDatabase(request)); - - fastify.get('/:id/status', async (request) => await getDatabaseStatus(request)); - - fastify.post('/:id/settings', async (request) => await saveDatabaseSettings(request)); - - fastify.get('/:id/secrets', async (request) => await getDatabaseSecrets(request)); - fastify.post('/:id/secrets', async (request, reply) => await saveDatabaseSecret(request, reply)); - fastify.delete('/:id/secrets', async (request) => await deleteDatabaseSecret(request)); - - fastify.get('/:id/configuration/type', async (request) => await getDatabaseTypes(request)); - fastify.post('/:id/configuration/type', async (request, reply) => await saveDatabaseType(request, reply)); - - fastify.get('/:id/configuration/version', async (request) => await getVersions(request)); - fastify.post('/:id/configuration/version', async (request, reply) => await saveVersion(request, reply)); - - fastify.post('/:id/configuration/destination', async (request, reply) => await saveDatabaseDestination(request, reply)); - - fastify.get('/:id/usage', async (request) => await getDatabaseUsage(request)); - fastify.get('/:id/logs', async (request) => await getDatabaseLogs(request)); - - fastify.post('/:id/start', async (request) => await startDatabase(request)); - fastify.post('/:id/stop', async (request) => await stopDatabase(request)); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/databases/types.ts b/apps/api/src/routes/api/v1/databases/types.ts deleted file mode 100644 index 554521e21..000000000 --- a/apps/api/src/routes/api/v1/databases/types.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { OnlyId } from "../../../../types"; - -export interface SaveDatabaseType extends OnlyId { - Body: { type: string } -} -export interface DeleteDatabase extends OnlyId { - Body: { } -} -export interface SaveVersion extends OnlyId { - Body: { - version: string - } -} -export interface SaveDatabaseDestination extends OnlyId { - Body: { - destinationId: string - } -} -export interface GetDatabaseLogs extends OnlyId { - Querystring: { - since: number - } -} -export interface SaveDatabase extends OnlyId { - Body: { - name: string, - defaultDatabase: string, - dbUser: string, - dbUserPassword: string, - rootUser: string, - rootUserPassword: string, - version: string, - isRunning: boolean - } -} -export interface SaveDatabaseSettings extends OnlyId { - Body: { - isPublic: boolean, - appendOnly: boolean - } -} - -export interface SaveDatabaseSecret extends OnlyId { - Body: { - name: string, - value: string, - isNew: string, - } -} -export interface DeleteDatabaseSecret extends OnlyId { - Body: { - name: string, - } -} - diff --git a/apps/api/src/routes/api/v1/destinations/handlers.ts b/apps/api/src/routes/api/v1/destinations/handlers.ts deleted file mode 100644 index f5ccb2614..000000000 --- a/apps/api/src/routes/api/v1/destinations/handlers.ts +++ /dev/null @@ -1,279 +0,0 @@ -import type { FastifyRequest } from 'fastify'; -import { FastifyReply } from 'fastify'; -import sshConfig from 'ssh-config' -import fs from 'fs/promises' -import os from 'os'; - -import { createRemoteEngineConfiguration, decrypt, errorHandler, executeCommand, listSettings, prisma, startTraefikProxy, stopTraefikProxy } from '../../../../lib/common'; -import { checkContainer } from '../../../../lib/docker'; - -import type { OnlyId } from '../../../../types'; -import type { CheckDestination, ListDestinations, NewDestination, Proxy, SaveDestinationSettings } from './types'; - -export async function listDestinations(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { onlyVerified = false } = request.query - let destinations = [] - if (teamId === '0') { - destinations = await prisma.destinationDocker.findMany({ include: { teams: true } }); - } else { - destinations = await prisma.destinationDocker.findMany({ - where: { teams: { some: { id: teamId } } }, - include: { teams: true } - }); - } - if (onlyVerified) { - destinations = destinations.filter(destination => destination.engine || (destination.remoteEngine && destination.remoteVerified)) - } - return { - destinations - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function checkDestination(request: FastifyRequest) { - try { - const { network } = request.body; - const found = await prisma.destinationDocker.findFirst({ where: { network } }); - if (found) { - throw { - message: `Network already exists: ${network}` - }; - } - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function getDestination(request: FastifyRequest) { - try { - const { id } = request.params - const teamId = request.user?.teamId; - const destination = await prisma.destinationDocker.findFirst({ - where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { sshKey: true, application: true, service: true, database: true } - }); - if (!destination && id !== 'new') { - throw { status: 404, message: `Destination not found.` }; - } - const settings = await listSettings(); - const payload = { - destination, - settings - }; - return { - ...payload - }; - - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function newDestination(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.params - - let { name, network, engine, isCoolifyProxyUsed, remoteIpAddress, remoteUser, remotePort } = request.body - if (id === 'new') { - if (engine) { - const { stdout } = await await executeCommand({ command: `docker network ls --filter 'name=^${network}$' --format '{{json .}}'` }); - if (stdout === '') { - await await executeCommand({ command: `docker network create --attachable ${network}` }); - } - await prisma.destinationDocker.create({ - data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed } - }); - const destinations = await prisma.destinationDocker.findMany({ where: { engine } }); - const destination = destinations.find((destination) => destination.network === network); - if (destinations.length > 0) { - const proxyConfigured = destinations.find( - (destination) => destination.network !== network && destination.isCoolifyProxyUsed === true - ); - if (proxyConfigured) { - isCoolifyProxyUsed = !!proxyConfigured.isCoolifyProxyUsed; - } - await prisma.destinationDocker.updateMany({ where: { engine }, data: { isCoolifyProxyUsed } }); - } - if (isCoolifyProxyUsed) { - await startTraefikProxy(destination.id); - } - return reply.code(201).send({ id: destination.id }); - } else { - const destination = await prisma.destinationDocker.create({ - data: { name, teams: { connect: { id: teamId } }, engine, network, isCoolifyProxyUsed, remoteEngine: true, remoteIpAddress, remoteUser, remotePort: Number(remotePort) } - }); - return reply.code(201).send({ id: destination.id }) - } - } else { - await prisma.destinationDocker.update({ where: { id }, data: { name, engine, network } }); - return reply.code(201).send(); - } - - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function deleteDestination(request: FastifyRequest) { - try { - const { id } = request.params - const { network, remoteVerified, engine, isCoolifyProxyUsed } = await prisma.destinationDocker.findUnique({ where: { id } }); - if (isCoolifyProxyUsed) { - if (engine || remoteVerified) { - const { stdout: found } = await executeCommand({ - dockerId: id, - command: `docker ps -a --filter network=${network} --filter name=coolify-proxy --format '{{.}}'` - }) - if (found) { - await executeCommand({ dockerId: id, command: `docker network disconnect ${network} coolify-proxy` }) - await executeCommand({ dockerId: id, command: `docker network rm ${network}` }) - } - } - } - await prisma.destinationDocker.delete({ where: { id } }); - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function saveDestinationSettings(request: FastifyRequest) { - try { - const { engine, isCoolifyProxyUsed } = request.body; - await prisma.destinationDocker.updateMany({ - where: { engine }, - data: { isCoolifyProxyUsed } - }); - - return { - status: 202 - } - // return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function startProxy(request: FastifyRequest) { - const { id } = request.params - try { - await startTraefikProxy(id); - return {} - } catch ({ status, message }) { - await stopTraefikProxy(id); - return errorHandler({ status, message }) - } -} -export async function stopProxy(request: FastifyRequest) { - const { id } = request.params - try { - await stopTraefikProxy(id); - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function restartProxy(request: FastifyRequest) { - const { id } = request.params - try { - await stopTraefikProxy(id); - await startTraefikProxy(id); - await prisma.destinationDocker.update({ - where: { id }, - data: { isCoolifyProxyUsed: true } - }); - return {} - } catch ({ status, message }) { - await prisma.destinationDocker.update({ - where: { id }, - data: { isCoolifyProxyUsed: false } - }); - return errorHandler({ status, message }) - } -} - -export async function assignSSHKey(request: FastifyRequest) { - try { - const { id: sshKeyId } = request.body; - const { id } = request.params; - await prisma.destinationDocker.update({ where: { id }, data: { sshKey: { connect: { id: sshKeyId } } } }) - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function verifyRemoteDockerEngineFn(id: string) { - const { remoteIpAddress, network, isCoolifyProxyUsed } = await prisma.destinationDocker.findFirst({ where: { id } }) - const daemonJson = `daemon-${id}.json` - try { - await executeCommand({ sshCommand: true, command: `docker network inspect ${network}`, dockerId: id }); - } catch (error) { - await executeCommand({ command: `docker network create --attachable ${network}`, dockerId: id }); - } - - try { - await executeCommand({ sshCommand: true, command: `docker network inspect coolify-infra`, dockerId: id }); - } catch (error) { - await executeCommand({ command: `docker network create --attachable coolify-infra`, dockerId: id }); - } - - if (isCoolifyProxyUsed) await startTraefikProxy(id); - let isUpdated = false; - let daemonJsonParsed = { - "live-restore": true, - "features": { - "buildkit": true - } - }; - try { - const { stdout: daemonJson } = await executeCommand({ sshCommand: true, dockerId: id, command: `cat /etc/docker/daemon.json` }); - daemonJsonParsed = JSON.parse(daemonJson); - if (!daemonJsonParsed['live-restore'] || daemonJsonParsed['live-restore'] !== true) { - isUpdated = true; - daemonJsonParsed['live-restore'] = true - - } - if (!daemonJsonParsed?.features?.buildkit) { - isUpdated = true; - daemonJsonParsed.features = { - buildkit: true - } - } - } catch (error) { - isUpdated = true; - } - try { - if (isUpdated) { - await executeCommand({ shell: true, command: `echo '${JSON.stringify(daemonJsonParsed, null, 2)}' > /tmp/${daemonJson}` }) - await executeCommand({ dockerId: id, command: `scp /tmp/${daemonJson} ${remoteIpAddress}-remote:/etc/docker/daemon.json` }); - await executeCommand({ command: `rm /tmp/${daemonJson}` }) - await executeCommand({ sshCommand: true, dockerId: id, command: `systemctl restart docker` }); - } - await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: true } }) - } catch (error) { - throw new Error('Error while verifying remote docker engine') - } -} -export async function verifyRemoteDockerEngine(request: FastifyRequest, reply: FastifyReply) { - const { id } = request.params; - try { - await verifyRemoteDockerEngineFn(id); - return reply.code(201).send() - } catch ({ status, message }) { - await prisma.destinationDocker.update({ where: { id }, data: { remoteVerified: false } }) - return errorHandler({ status, message }) - } -} - -export async function getDestinationStatus(request: FastifyRequest) { - try { - const { id } = request.params - const destination = await prisma.destinationDocker.findUnique({ where: { id } }) - const { found: isRunning } = await checkContainer({ dockerId: destination.id, container: 'coolify-proxy', remove: true }) - return { - isRunning - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} diff --git a/apps/api/src/routes/api/v1/destinations/index.ts b/apps/api/src/routes/api/v1/destinations/index.ts deleted file mode 100644 index 774afa285..000000000 --- a/apps/api/src/routes/api/v1/destinations/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { assignSSHKey, checkDestination, deleteDestination, getDestination, getDestinationStatus, listDestinations, newDestination, restartProxy, saveDestinationSettings, startProxy, stopProxy, verifyRemoteDockerEngine } from './handlers'; - -import type { OnlyId } from '../../../../types'; -import type { CheckDestination, ListDestinations, NewDestination, Proxy, SaveDestinationSettings } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listDestinations(request)); - fastify.post('/check', async (request) => await checkDestination(request)); - - fastify.get('/:id', async (request) => await getDestination(request)); - fastify.post('/:id', async (request, reply) => await newDestination(request, reply)); - fastify.delete('/:id', async (request) => await deleteDestination(request)); - fastify.get('/:id/status', async (request) => await getDestinationStatus(request)); - - fastify.post('/:id/settings', async (request) => await saveDestinationSettings(request)); - fastify.post('/:id/start', async (request,) => await startProxy(request)); - fastify.post('/:id/stop', async (request) => await stopProxy(request)); - fastify.post('/:id/restart', async (request) => await restartProxy(request)); - - fastify.post('/:id/configuration/sshKey', async (request) => await assignSSHKey(request)); - - fastify.post('/:id/verify', async (request, reply) => await verifyRemoteDockerEngine(request, reply)); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/destinations/types.ts b/apps/api/src/routes/api/v1/destinations/types.ts deleted file mode 100644 index fe2742218..000000000 --- a/apps/api/src/routes/api/v1/destinations/types.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { OnlyId } from "../../../../types" - -export interface ListDestinations { - Querystring: { - onlyVerified: string - } -} -export interface CheckDestination { - Body: { - network: string - } -} -export interface NewDestination extends OnlyId { - Body: { - name: string - network: string - engine: string - isCoolifyProxyUsed: boolean - } -} -export interface SaveDestinationSettings extends OnlyId { - Body: { - engine: string - isCoolifyProxyUsed: boolean - } -} -export interface Proxy extends OnlyId { - -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/handlers.ts b/apps/api/src/routes/api/v1/handlers.ts deleted file mode 100644 index 5cb283137..000000000 --- a/apps/api/src/routes/api/v1/handlers.ts +++ /dev/null @@ -1,460 +0,0 @@ -import { compareVersions } from 'compare-versions'; -import cuid from 'cuid'; -import bcrypt from 'bcryptjs'; -import fs from 'fs/promises'; -import yaml from 'js-yaml'; -import { - asyncSleep, - cleanupDockerStorage, - errorHandler, - isDev, - listSettings, - prisma, - uniqueName, - version, - sentryDSN, - executeCommand -} from '../../../lib/common'; -import { scheduler } from '../../../lib/scheduler'; -import type { FastifyReply, FastifyRequest } from 'fastify'; -import type { Login, Update } from '.'; -import type { GetCurrentUser } from './types'; - -export async function hashPassword(password: string): Promise { - const saltRounds = 15; - return bcrypt.hash(password, saltRounds); -} - -export async function backup(request: FastifyRequest) { - try { - const { backupData } = request.params; - let std = null; - const [id, backupType, type, zipped, storage] = backupData.split(':'); - console.log(id, backupType, type, zipped, storage); - const database = await prisma.database.findUnique({ where: { id } }); - if (database) { - // await executeDockerCmd({ - // dockerId: database.destinationDockerId, - // command: `docker pull coollabsio/backup:latest`, - // }) - std = await executeCommand({ - dockerId: database.destinationDockerId, - command: `docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v coolify-local-backup:/app/backups -e CONTAINERS_TO_BACKUP="${backupData}" coollabsio/backup` - }); - } - if (std.stdout) { - return std.stdout; - } - if (std.stderr) { - return std.stderr; - } - return 'nope'; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function cleanupManually(request: FastifyRequest) { - try { - const { serverId } = request.body; - const destination = await prisma.destinationDocker.findUnique({ - where: { id: serverId } - }); - await cleanupDockerStorage(destination.id); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function refreshTags() { - try { - const { default: got } = await import('got'); - try { - if (isDev) { - let tags = await fs.readFile('./devTags.json', 'utf8'); - try { - if (await fs.stat('./testTags.json')) { - const testTags = await fs.readFile('./testTags.json', 'utf8'); - if (testTags.length > 0) { - tags = JSON.parse(tags).concat(JSON.parse(testTags)); - } - } - } catch (error) {} - await fs.writeFile('./tags.json', tags); - } else { - const tags = await got.get('https://get.coollabs.io/coolify/service-tags.json').text(); - await fs.writeFile('/app/tags.json', tags); - } - } catch (error) { - console.log(error); - } - - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function refreshTemplates() { - try { - const { default: got } = await import('got'); - try { - if (isDev) { - let templates = await fs.readFile('./devTemplates.yaml', 'utf8'); - try { - if (await fs.stat('./testTemplate.yaml')) { - templates = templates + (await fs.readFile('./testTemplate.yaml', 'utf8')); - } - } catch (error) {} - const response = await fs.readFile('./devTemplates.yaml', 'utf8'); - await fs.writeFile('./templates.json', JSON.stringify(yaml.load(response))); - } else { - const response = await got - .get('https://get.coollabs.io/coolify/service-templates.yaml') - .text(); - await fs.writeFile('/app/templates.json', JSON.stringify(yaml.load(response))); - } - } catch (error) { - console.log(error); - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function checkUpdate(request: FastifyRequest) { - try { - const { default: got } = await import('got'); - const isStaging = - request.hostname === 'staging.coolify.io' || request.hostname === 'arm.coolify.io'; - const currentVersion = version; - const { coolify } = await got - .get('https://get.coollabs.io/versions.json', { - searchParams: { - appId: process.env['COOLIFY_APP_ID'] || undefined, - version: currentVersion - } - }) - .json(); - const latestVersion = coolify.main.version; - const isUpdateAvailable = compareVersions(latestVersion, currentVersion); - if (isStaging) { - return { - isUpdateAvailable: true, - latestVersion: 'next' - }; - } - return { - isUpdateAvailable: isStaging ? true : isUpdateAvailable === 1, - latestVersion - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function update(request: FastifyRequest) { - const { latestVersion } = request.body; - try { - if (!isDev) { - const { isAutoUpdateEnabled } = await prisma.setting.findFirst(); - let image = `ghcr.io/coollabsio/coolify:${latestVersion}`; - try { - await executeCommand({ command: `docker pull ${image}` }); - } catch (error) { - image = `coollabsio/coolify:${latestVersion}`; - await executeCommand({ command: `docker pull ${image}` }); - } - - await executeCommand({ shell: true, command: `env | grep COOLIFY > .env` }); - await executeCommand({ - command: `sed -i '/COOLIFY_AUTO_UPDATE=/cCOOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env` - }); - await executeCommand({ - shell: true, - command: `docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db ${image} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-fluentbit && docker rm coolify coolify-fluentbit && docker compose pull && docker compose up -d --force-recreate"` - }); - return {}; - } else { - await asyncSleep(2000); - return {}; - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function resetQueue(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - if (teamId === '0') { - await prisma.build.updateMany({ - where: { status: { in: ['queued', 'running'] } }, - data: { status: 'canceled' } - }); - scheduler.workers.get('deployApplication').postMessage('cancel'); - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function restartCoolify(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - if (teamId === '0') { - if (!isDev) { - await executeCommand({ command: `docker restart coolify` }); - return {}; - } else { - return {}; - } - } - throw { - status: 500, - message: 'You are not authorized to restart Coolify.' - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function showDashboard(request: FastifyRequest) { - try { - const userId = request.user.userId; - const teamId = request.user.teamId; - let applications = await prisma.application.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { settings: true, destinationDocker: true, teams: true } - }); - const databases = await prisma.database.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { settings: true, destinationDocker: true, teams: true } - }); - const services = await prisma.service.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, teams: true } - }); - const gitSources = await prisma.gitSource.findMany({ - where: { - OR: [ - { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - { isSystemWide: true } - ] - }, - include: { teams: true } - }); - const destinations = await prisma.destinationDocker.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { teams: true } - }); - const settings = await listSettings(); - - let foundUnconfiguredApplication = false; - for (const application of applications) { - if ( - ((!application.buildPack || !application.branch) && !application.simpleDockerfile) || - !application.destinationDockerId || - (!application.settings?.isBot && !application?.fqdn && application.buildPack !== 'compose') - ) { - foundUnconfiguredApplication = true; - } - } - let foundUnconfiguredService = false; - for (const service of services) { - if (!service.fqdn) { - foundUnconfiguredService = true; - } - } - let foundUnconfiguredDatabase = false; - for (const database of databases) { - if (!database.version) { - foundUnconfiguredDatabase = true; - } - } - return { - foundUnconfiguredApplication, - foundUnconfiguredDatabase, - foundUnconfiguredService, - applications, - databases, - services, - gitSources, - destinations, - settings - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function login(request: FastifyRequest, reply: FastifyReply) { - if (request.user) { - return reply.redirect('/dashboard'); - } else { - const { email, password, isLogin } = request.body || {}; - if (!email || !password) { - throw { status: 500, message: 'Email and password are required.' }; - } - const users = await prisma.user.count(); - const userFound = await prisma.user.findUnique({ - where: { email }, - include: { teams: true, permission: true }, - rejectOnNotFound: false - }); - if (!userFound && isLogin) { - throw { status: 500, message: 'User not found.' }; - } - const { isRegistrationEnabled, id } = await prisma.setting.findFirst(); - let uid = cuid(); - let permission = 'read'; - let isAdmin = false; - - if (users === 0) { - await prisma.setting.update({ - where: { id }, - data: { isRegistrationEnabled: false } - }); - uid = '0'; - } - if (userFound) { - if (userFound.type === 'email') { - if (userFound.password === 'RESETME') { - const hashedPassword = await hashPassword(password); - if (userFound.updatedAt < new Date(Date.now() - 1000 * 60 * 10)) { - if (userFound.id === '0') { - await prisma.user.update({ - where: { email: userFound.email }, - data: { password: 'RESETME' } - }); - } else { - await prisma.user.update({ - where: { email: userFound.email }, - data: { password: 'RESETTIMEOUT' } - }); - } - - throw { - status: 500, - message: 'Password reset link has expired. Please request a new one.' - }; - } else { - await prisma.user.update({ - where: { email: userFound.email }, - data: { password: hashedPassword } - }); - return { - userId: userFound.id, - teamId: userFound.id, - permission: userFound.permission, - isAdmin: true - }; - } - } - - const passwordMatch = await bcrypt.compare(password, userFound.password); - if (!passwordMatch) { - throw { - status: 500, - message: 'Wrong password or email address.' - }; - } - uid = userFound.id; - isAdmin = true; - } - } else { - permission = 'owner'; - isAdmin = true; - if (!isRegistrationEnabled) { - throw { - status: 404, - message: 'Registration disabled by administrator.' - }; - } - const hashedPassword = await hashPassword(password); - if (users === 0) { - await prisma.user.create({ - data: { - id: uid, - email, - password: hashedPassword, - type: 'email', - teams: { - create: { - id: uid, - name: uniqueName(), - destinationDocker: { connect: { network: 'coolify' } } - } - }, - permission: { create: { teamId: uid, permission: 'owner' } } - }, - include: { teams: true } - }); - } else { - await prisma.user.create({ - data: { - id: uid, - email, - password: hashedPassword, - type: 'email', - teams: { - create: { - id: uid, - name: uniqueName() - } - }, - permission: { create: { teamId: uid, permission: 'owner' } } - }, - include: { teams: true } - }); - } - } - return { - userId: uid, - teamId: uid, - permission, - isAdmin - }; - } -} - -export async function getCurrentUser(request: FastifyRequest, fastify) { - let token = null; - const { teamId } = request.query; - try { - const user = await prisma.user.findUnique({ - where: { id: request.user.userId } - }); - if (!user) { - throw 'User not found'; - } - } catch (error) { - throw { status: 401, message: error }; - } - if (teamId) { - try { - const user = await prisma.user.findFirst({ - where: { id: request.user.userId, teams: { some: { id: teamId } } }, - include: { teams: true, permission: true } - }); - if (user) { - const permission = user.permission.find((p) => p.teamId === teamId).permission; - const payload = { - ...request.user, - teamId, - permission: permission || null, - isAdmin: permission === 'owner' || permission === 'admin' - }; - token = fastify.jwt.sign(payload); - } - } catch (error) { - // No new token -> not switching teams - } - } - const pendingInvitations = await prisma.teamInvitation.findMany({ - where: { uid: request.user.userId } - }); - return { - settings: await prisma.setting.findUnique({ where: { id: '0' } }), - sentryDSN, - pendingInvitations, - token, - ...request.user - }; -} diff --git a/apps/api/src/routes/api/v1/iam/handlers.ts b/apps/api/src/routes/api/v1/iam/handlers.ts deleted file mode 100644 index d61b173f2..000000000 --- a/apps/api/src/routes/api/v1/iam/handlers.ts +++ /dev/null @@ -1,491 +0,0 @@ -import cuid from 'cuid'; -import type { FastifyRequest } from 'fastify'; -import { FastifyReply } from 'fastify'; -import { decrypt, errorHandler, prisma, uniqueName } from '../../../../lib/common'; -import { day } from '../../../../lib/dayjs'; - -import type { OnlyId } from '../../../../types'; -import type { BodyId, DeleteUserFromTeam, InviteToTeam, SaveTeam, SetPermission } from './types'; - - -export async function listAccounts(request: FastifyRequest) { - try { - const userId = request.user.userId; - const teamId = request.user.teamId; - const account = await prisma.user.findUnique({ - where: { id: userId }, - select: { id: true, email: true, teams: true } - }); - let accounts = await prisma.user.findMany({ where: { teams: { some: { id: teamId } } }, select: { id: true, email: true, teams: true } }); - if (teamId === '0') { - accounts = await prisma.user.findMany({ select: { id: true, email: true, teams: true } }); - } - return { - account, - accounts - }; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function listTeams(request: FastifyRequest) { - try { - const userId = request.user.userId; - const teamId = request.user.teamId; - let allTeams = []; - if (teamId === '0') { - allTeams = await prisma.team.findMany({ - where: { users: { none: { id: userId } } }, - include: { permissions: true } - }); - } - const ownTeams = await prisma.team.findMany({ - where: { users: { some: { id: userId } } }, - include: { permissions: true } - }); - return { - ownTeams, - allTeams, - }; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function removeUserFromTeam(request: FastifyRequest, reply: FastifyReply) { - try { - const { uid } = request.body; - const { id } = request.params; - const userId = request.user.userId; - const foundUser = await prisma.team.findMany({ where: { id, users: { some: { id: userId } } } }); - if (foundUser.length === 0) { - return errorHandler({ status: 404, message: 'Team not found' }); - } - await prisma.team.update({ where: { id }, data: { users: { disconnect: { id: uid } } } }); - await prisma.permission.deleteMany({ where: { teamId: id, userId: uid } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function deleteTeam(request: FastifyRequest, reply: FastifyReply) { - try { - const userId = request.user.userId; - const { id } = request.params; - - const aloneInTeams = await prisma.team.findMany({ where: { users: { every: { id: userId } }, id } }); - if (aloneInTeams.length > 0) { - for (const team of aloneInTeams) { - const applications = await prisma.application.findMany({ - where: { teams: { every: { id: team.id } } } - }); - if (applications.length > 0) { - for (const application of applications) { - await prisma.application.update({ - where: { id: application.id }, - data: { teams: { connect: { id: '0' } } } - }); - } - } - const services = await prisma.service.findMany({ - where: { teams: { every: { id: team.id } } } - }); - if (services.length > 0) { - for (const service of services) { - await prisma.service.update({ - where: { id: service.id }, - data: { teams: { connect: { id: '0' } } } - }); - } - } - const databases = await prisma.database.findMany({ - where: { teams: { every: { id: team.id } } } - }); - if (databases.length > 0) { - for (const database of databases) { - await prisma.database.update({ - where: { id: database.id }, - data: { teams: { connect: { id: '0' } } } - }); - } - } - const sources = await prisma.gitSource.findMany({ - where: { teams: { every: { id: team.id } } } - }); - if (sources.length > 0) { - for (const source of sources) { - await prisma.gitSource.update({ - where: { id: source.id }, - data: { teams: { connect: { id: '0' } } } - }); - } - } - const destinations = await prisma.destinationDocker.findMany({ - where: { teams: { every: { id: team.id } } } - }); - if (destinations.length > 0) { - for (const destination of destinations) { - await prisma.destinationDocker.update({ - where: { id: destination.id }, - data: { teams: { connect: { id: '0' } } } - }); - } - } - await prisma.teamInvitation.deleteMany({ where: { teamId: team.id } }); - await prisma.permission.deleteMany({ where: { teamId: team.id } }); - // await prisma.user.delete({ where: { id } }); - await prisma.team.delete({ where: { id: team.id } }); - } - } - - const notAloneInTeams = await prisma.team.findMany({ where: { users: { some: { id: userId } } } }); - if (notAloneInTeams.length > 0) { - for (const team of notAloneInTeams) { - await prisma.team.update({ - where: { id: team.id }, - data: { users: { disconnect: { id } } } - }); - } - } - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function newTeam(request: FastifyRequest, reply: FastifyReply) { - try { - const userId = request.user?.userId; - const name = uniqueName(); - const { id } = await prisma.team.create({ - data: { - name, - permissions: { create: { user: { connect: { id: userId } }, permission: 'owner' } }, - users: { connect: { id: userId } } - } - }); - return reply.code(201).send({ id }) - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function getTeam(request: FastifyRequest, reply: FastifyReply) { - try { - const userId = request.user.userId; - const teamId = request.user.teamId; - const { id } = request.params; - - const user = await prisma.user.findFirst({ - where: { id: userId, teams: teamId === '0' ? undefined : { some: { id } } }, - include: { permission: true } - }); - if (!user) return reply.code(401).send() - - const permissions = await prisma.permission.findMany({ - where: { teamId: id }, - include: { user: { select: { id: true, email: true } } } - }); - const team = await prisma.team.findUnique({ where: { id }, include: { permissions: true } }); - const invitations = await prisma.teamInvitation.findMany({ where: { teamId: team.id } }); - const { teams } = await prisma.user.findUnique({ where: { id: userId }, include: { teams: true } }) - return { - currentTeam: teamId, - team, - teams, - permissions, - invitations - }; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function saveTeam(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const { name } = request.body; - - await prisma.team.update({ where: { id }, data: { name: { set: name } } }); - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -// export async function deleteUser(request: FastifyRequest, reply: FastifyReply) { -// try { -// const userId = request.user.userId; -// const { id } = request.params; - -// const aloneInTeams = await prisma.team.findMany({ where: { users: { every: { id: userId } }, id } }); -// if (aloneInTeams.length > 0) { -// for (const team of aloneInTeams) { -// const applications = await prisma.application.findMany({ -// where: { teams: { every: { id: team.id } } } -// }); -// if (applications.length > 0) { -// for (const application of applications) { -// await prisma.application.update({ -// where: { id: application.id }, -// data: { teams: { connect: { id: '0' } } } -// }); -// } -// } -// const services = await prisma.service.findMany({ -// where: { teams: { every: { id: team.id } } } -// }); -// if (services.length > 0) { -// for (const service of services) { -// await prisma.service.update({ -// where: { id: service.id }, -// data: { teams: { connect: { id: '0' } } } -// }); -// } -// } -// const databases = await prisma.database.findMany({ -// where: { teams: { every: { id: team.id } } } -// }); -// if (databases.length > 0) { -// for (const database of databases) { -// await prisma.database.update({ -// where: { id: database.id }, -// data: { teams: { connect: { id: '0' } } } -// }); -// } -// } -// const sources = await prisma.gitSource.findMany({ -// where: { teams: { every: { id: team.id } } } -// }); -// if (sources.length > 0) { -// for (const source of sources) { -// await prisma.gitSource.update({ -// where: { id: source.id }, -// data: { teams: { connect: { id: '0' } } } -// }); -// } -// } -// const destinations = await prisma.destinationDocker.findMany({ -// where: { teams: { every: { id: team.id } } } -// }); -// if (destinations.length > 0) { -// for (const destination of destinations) { -// await prisma.destinationDocker.update({ -// where: { id: destination.id }, -// data: { teams: { connect: { id: '0' } } } -// }); -// } -// } -// await prisma.teamInvitation.deleteMany({ where: { teamId: team.id } }); -// await prisma.permission.deleteMany({ where: { teamId: team.id } }); -// await prisma.user.delete({ where: { id: userId } }); -// await prisma.team.delete({ where: { id: team.id } }); - -// } -// } - -// const notAloneInTeams = await prisma.team.findMany({ where: { users: { some: { id: userId } } } }); -// if (notAloneInTeams.length > 0) { -// for (const team of notAloneInTeams) { -// await prisma.team.update({ -// where: { id: team.id }, -// data: { users: { disconnect: { id } } } -// }); -// } -// } - -// return reply.code(201).send() -// } catch (error) { -// console.log(error) -// throw { status: 500, message: error } -// } -// } - -export async function inviteToTeam(request: FastifyRequest, reply: FastifyReply) { - try { - const userId = request.user.userId; - const { email, permission, teamId, teamName } = request.body; - const userFound = await prisma.user.findUnique({ where: { email } }); - if (!userFound) { - throw { - message: `No user found with '${email}' email address.` - }; - } - const uid = userFound.id; - if (uid === userId) { - throw { - message: `Invitation to yourself? Whaaaaat?` - }; - } - const alreadyInTeam = await prisma.team.findFirst({ - where: { id: teamId, users: { some: { id: uid } } } - }); - if (alreadyInTeam) { - throw { - message: `Already in the team.` - }; - } - const invitationFound = await prisma.teamInvitation.findFirst({ where: { uid, teamId } }); - if (invitationFound) { - if (day().toDate() < day(invitationFound.createdAt).add(1, 'day').toDate()) { - throw 'Invitiation already pending on user confirmation.' - } else { - await prisma.teamInvitation.delete({ where: { id: invitationFound.id } }); - await prisma.teamInvitation.create({ - data: { email, uid, teamId, teamName, permission } - }); - return reply.code(201).send({ message: 'Invitiation sent.' }) - } - } else { - await prisma.teamInvitation.create({ - data: { email, uid, teamId, teamName, permission } - }); - return reply.code(201).send({ message: 'Invitiation sent.' }) - - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function acceptInvitation(request: FastifyRequest) { - try { - const userId = request.user.userId; - const { id } = request.body; - const invitation = await prisma.teamInvitation.findFirst({ - where: { uid: userId }, - rejectOnNotFound: true - }); - await prisma.team.update({ - where: { id: invitation.teamId }, - data: { users: { connect: { id: userId } } } - }); - await prisma.permission.create({ - data: { - user: { connect: { id: userId } }, - permission: invitation.permission, - team: { connect: { id: invitation.teamId } } - } - }); - await prisma.teamInvitation.delete({ where: { id } }); - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function revokeInvitation(request: FastifyRequest) { - try { - const { id } = request.body - await prisma.teamInvitation.delete({ where: { id } }); - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function removeUser(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.body; - const user = await prisma.user.findUnique({ where: { id }, include: { teams: true, permission: true } }); - if (user) { - const permissions = user.permission; - if (permissions.length > 0) { - for (const permission of permissions) { - await prisma.permission.deleteMany({ where: { id: permission.id, userId: id } }); - } - } - const teams = user.teams; - if (teams.length > 0) { - for (const team of teams) { - const newTeam = await prisma.team.update({ - where: { id: team.id }, - data: { users: { disconnect: { id } } }, - include: { applications: true, database: true, gitHubApps: true, gitLabApps: true, gitSources: true, destinationDocker: true, service: true, users: true } - }); - if (newTeam.users.length === 0) { - if (newTeam.applications.length > 0) { - for (const application of newTeam.applications) { - await prisma.application.update({ - where: { id: application.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - if (newTeam.database.length > 0) { - for (const database of newTeam.database) { - await prisma.database.update({ - where: { id: database.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - if (newTeam.service.length > 0) { - for (const service of newTeam.service) { - await prisma.service.update({ - where: { id: service.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - if (newTeam.gitHubApps.length > 0) { - for (const gitHubApp of newTeam.gitHubApps) { - await prisma.githubApp.update({ - where: { id: gitHubApp.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - if (newTeam.gitLabApps.length > 0) { - for (const gitLabApp of newTeam.gitLabApps) { - await prisma.gitlabApp.update({ - where: { id: gitLabApp.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - if (newTeam.gitSources.length > 0) { - for (const gitSource of newTeam.gitSources) { - await prisma.gitSource.update({ - where: { id: gitSource.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - if (newTeam.destinationDocker.length > 0) { - for (const destinationDocker of newTeam.destinationDocker) { - await prisma.destinationDocker.update({ - where: { id: destinationDocker.id }, - data: { teams: { disconnect: { id: team.id }, connect: { id: '0' } } } - }); - } - } - await prisma.team.delete({ where: { id: team.id } }); - } - } - } - } - await prisma.user.delete({ where: { id } }); - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function setPermission(request: FastifyRequest, reply: FastifyReply) { - try { - const { userId, newPermission, permissionId } = request.body; - await prisma.permission.updateMany({ - where: { id: permissionId, userId }, - data: { permission: { set: newPermission } } - }); - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function changePassword(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.body; - await prisma.user.update({ where: { id }, data: { password: 'RESETME' } }); - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/iam/index.ts b/apps/api/src/routes/api/v1/iam/index.ts deleted file mode 100644 index e54d2b9fd..000000000 --- a/apps/api/src/routes/api/v1/iam/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { acceptInvitation, changePassword, deleteTeam, getTeam, inviteToTeam, listAccounts, listTeams, newTeam, removeUser, removeUserFromTeam, revokeInvitation, saveTeam, setPermission } from './handlers'; - -import type { OnlyId } from '../../../../types'; -import type { BodyId, DeleteUserFromTeam, InviteToTeam, SaveTeam, SetPermission } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - - fastify.get('/', async (request) => await listAccounts(request)); - fastify.post('/new', async (request, reply) => await newTeam(request, reply)); - fastify.get('/teams', async (request) => await listTeams(request)); - - fastify.get('/team/:id', async (request, reply) => await getTeam(request, reply)); - fastify.post('/team/:id', async (request, reply) => await saveTeam(request, reply)); - fastify.delete('/team/:id', async (request, reply) => await deleteTeam(request, reply)); - fastify.post('/team/:id/user/remove', async (request, reply) => await removeUserFromTeam(request, reply)); - - fastify.post('/team/:id/invitation/invite', async (request, reply) => await inviteToTeam(request, reply)) - fastify.post('/team/:id/invitation/accept', async (request) => await acceptInvitation(request)); - fastify.post('/team/:id/invitation/revoke', async (request) => await revokeInvitation(request)); - - fastify.post('/team/:id/permission', async (request, reply) => await setPermission(request, reply)); - - fastify.delete('/user/remove', async (request, reply) => await removeUser(request, reply)); - fastify.post('/user/password', async (request, reply) => await changePassword(request, reply)); - -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/iam/types.ts b/apps/api/src/routes/api/v1/iam/types.ts deleted file mode 100644 index 959d3dac5..000000000 --- a/apps/api/src/routes/api/v1/iam/types.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { OnlyId } from "../../../../types" - -export interface SaveTeam extends OnlyId { - Body: { - name: string - } -} -export interface DeleteUserFromTeam { - Body: { - uid: string - }, - Params: { - id: string - } -} -export interface InviteToTeam { - Body: { - email: string, - permission: string, - teamId: string, - teamName: string - } -} -export interface BodyId { - Body: { - id: string - } -} -export interface SetPermission { - Body: { - userId: string, - newPermission: string, - permissionId: string - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/index.ts b/apps/api/src/routes/api/v1/index.ts deleted file mode 100644 index 243683a59..000000000 --- a/apps/api/src/routes/api/v1/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { checkUpdate, login, showDashboard, update, resetQueue, getCurrentUser, cleanupManually, restartCoolify, backup } from './handlers'; -import { GetCurrentUser } from './types'; - -export interface Update { - Body: { latestVersion: string } -} -export interface Login { - Body: { email: string, password: string, isLogin: boolean } -} - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.get('/', async function (_request, reply) { - return reply.redirect(302, '/'); - }); - fastify.post('/login', async (request, reply) => { - const payload = await login(request, reply) - const token = fastify.jwt.sign(payload) - return { token, payload } - }); - - fastify.get('/user', { - onRequest: [fastify.authenticate] - }, async (request) => await getCurrentUser(request, fastify)); - - fastify.get('/undead', async function () { - return { message: 'nope' }; - }); - - fastify.get('/update', { - onRequest: [fastify.authenticate] - }, async (request) => await checkUpdate(request)); - - fastify.post( - '/update', { - onRequest: [fastify.authenticate] - }, - async (request) => await update(request) - ); - fastify.get('/resources', { - onRequest: [fastify.authenticate] - }, async (request) => await showDashboard(request)); - - fastify.post('/internal/restart', { - onRequest: [fastify.authenticate] - }, async (request) => await restartCoolify(request)); - - fastify.post('/internal/resetQueue', { - onRequest: [fastify.authenticate] - }, async (request) => await resetQueue(request)); - - fastify.post('/internal/cleanup', { - onRequest: [fastify.authenticate] - }, async (request) => await cleanupManually(request)); - - // fastify.get('/internal/backup/:backupData', { - // onRequest: [fastify.authenticate] - // }, async (request) => await backup(request)); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/servers/handlers.ts b/apps/api/src/routes/api/v1/servers/handlers.ts deleted file mode 100644 index 9b163ab7b..000000000 --- a/apps/api/src/routes/api/v1/servers/handlers.ts +++ /dev/null @@ -1,125 +0,0 @@ -import type { FastifyRequest } from 'fastify'; -import { errorHandler, prisma, executeCommand } from '../../../../lib/common'; -import os from 'node:os'; -import osu from 'node-os-utils'; - - -export async function listServers(request: FastifyRequest) { - try { - const userId = request.user.userId; - const teamId = request.user.teamId; - let servers = await prisma.destinationDocker.findMany({ where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, distinct: ['remoteIpAddress', 'engine'] }) - servers = servers.filter((server) => { - if (server.remoteEngine) { - if (server.remoteVerified) { - return server - } - } else { - return server - } - }) - return { - servers - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -const mappingTable = [ - ['K total memory', 'totalMemoryKB'], - ['K used memory', 'usedMemoryKB'], - ['K active memory', 'activeMemoryKB'], - ['K inactive memory', 'inactiveMemoryKB'], - ['K free memory', 'freeMemoryKB'], - ['K buffer memory', 'bufferMemoryKB'], - ['K swap cache', 'swapCacheKB'], - ['K total swap', 'totalSwapKB'], - ['K used swap', 'usedSwapKB'], - ['K free swap', 'freeSwapKB'], - ['non-nice user cpu ticks', 'nonNiceUserCpuTicks'], - ['nice user cpu ticks', 'niceUserCpuTicks'], - ['system cpu ticks', 'systemCpuTicks'], - ['idle cpu ticks', 'idleCpuTicks'], - ['IO-wait cpu ticks', 'ioWaitCpuTicks'], - ['IRQ cpu ticks', 'irqCpuTicks'], - ['softirq cpu ticks', 'softIrqCpuTicks'], - ['stolen cpu ticks', 'stolenCpuTicks'], - ['pages paged in', 'pagesPagedIn'], - ['pages paged out', 'pagesPagedOut'], - ['pages swapped in', 'pagesSwappedIn'], - ['pages swapped out', 'pagesSwappedOut'], - ['interrupts', 'interrupts'], - ['CPU context switches', 'cpuContextSwitches'], - ['boot time', 'bootTime'], - ['forks', 'forks'] -]; -function parseFromText(text) { - var data = {}; - var lines = text.split(/\r?\n/); - for (const line of lines) { - for (const [key, value] of mappingTable) { - if (line.indexOf(key) >= 0) { - const values = line.match(/[0-9]+/)[0]; - data[value] = parseInt(values, 10); - } - } - } - return data; -} -export async function showUsage(request: FastifyRequest) { - const { id } = request.params; - let { remoteEngine } = request.query - remoteEngine = remoteEngine === 'true' ? true : false - if (remoteEngine) { - const { stdout: stats } = await executeCommand({ sshCommand: true, dockerId: id, command: `vmstat -s` }) - const { stdout: disks } = await executeCommand({ sshCommand: true, shell: true, dockerId: id, command: `df -m / --output=size,used,pcent|grep -v 'Used'| xargs` }) - const { stdout: cpus } = await executeCommand({ sshCommand: true, dockerId: id, command: `nproc --all` }) - const { stdout: cpuUsage } = await executeCommand({ sshCommand: true, shell: true, dockerId: id, command: `echo $[100-$(vmstat 1 2|tail -1|awk '{print $15}')]` }) - const parsed: any = parseFromText(stats) - return { - usage: { - uptime: parsed.bootTime / 1024, - memory: { - totalMemMb: parsed.totalMemoryKB / 1024, - usedMemMb: parsed.usedMemoryKB / 1024, - freeMemMb: parsed.freeMemoryKB / 1024, - usedMemPercentage: (parsed.usedMemoryKB / parsed.totalMemoryKB) * 100, - freeMemPercentage: (parsed.totalMemoryKB - parsed.usedMemoryKB) / parsed.totalMemoryKB * 100 - }, - cpu: { - load: [0, 0, 0], - usage: cpuUsage, - count: cpus - }, - disk: { - totalGb: (disks.split(' ')[0] / 1024).toFixed(1), - usedGb: (disks.split(' ')[1] / 1024).toFixed(1), - freeGb: (disks.split(' ')[0] - disks.split(' ')[1]).toFixed(1), - usedPercentage: disks.split(' ')[2].replace('%', ''), - freePercentage: 100 - disks.split(' ')[2].replace('%', '') - } - - } - } - } else { - try { - return { - usage: { - uptime: os.uptime(), - memory: await osu.mem.info(), - cpu: { - load: os.loadavg(), - usage: await osu.cpu.usage(), - count: os.cpus().length - }, - disk: await osu.drive.info('/') - } - - }; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } - } - - -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/servers/index.ts b/apps/api/src/routes/api/v1/servers/index.ts deleted file mode 100644 index 7373d8cb4..000000000 --- a/apps/api/src/routes/api/v1/servers/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { listServers, showUsage } from './handlers'; - - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listServers(request)); - fastify.get('/usage/:id', async (request) => await showUsage(request)); - -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/servers/types.ts b/apps/api/src/routes/api/v1/servers/types.ts deleted file mode 100644 index 26a896b8e..000000000 --- a/apps/api/src/routes/api/v1/servers/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { OnlyId } from "../../../../types" - -export interface SaveTeam extends OnlyId { - Body: { - name: string - } -} -export interface InviteToTeam { - Body: { - email: string, - permission: string, - teamId: string, - teamName: string - } -} -export interface BodyId { - Body: { - id: string - } -} -export interface SetPermission { - Body: { - userId: string, - newPermission: string, - permissionId: string - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/services/handlers.ts b/apps/api/src/routes/api/v1/services/handlers.ts deleted file mode 100644 index 1c6cc79fe..000000000 --- a/apps/api/src/routes/api/v1/services/handlers.ts +++ /dev/null @@ -1,1183 +0,0 @@ -import type { FastifyReply, FastifyRequest } from 'fastify'; -import fs from 'fs/promises'; -import yaml from 'js-yaml'; -import bcrypt from 'bcryptjs'; -import cuid from 'cuid'; - -import { - prisma, - uniqueName, - getServiceFromDB, - getContainerUsage, - isDomainConfigured, - fixType, - decrypt, - encrypt, - ComposeFile, - getFreePublicPort, - getDomain, - errorHandler, - generatePassword, - isDev, - stopTcpHttpProxy, - checkDomainsIsValidInDNS, - checkExposedPort, - listSettings, - generateToken, - executeCommand -} from '../../../../lib/common'; -import { day } from '../../../../lib/dayjs'; -import { checkContainer } from '../../../../lib/docker'; -import { removeService } from '../../../../lib/services/common'; -import { getTags, getTemplates } from '../../../../lib/services'; - -import type { - ActivateWordpressFtp, - CheckService, - CheckServiceDomain, - DeleteServiceSecret, - DeleteServiceStorage, - GetServiceLogs, - SaveService, - SaveServiceDestination, - SaveServiceSecret, - SaveServiceSettings, - SaveServiceStorage, - SaveServiceType, - SaveServiceVersion, - ServiceStartStop, - SetGlitchTipSettings, - SetWordpressSettings -} from './types'; -import type { OnlyId } from '../../../../types'; - -export async function listServices(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const services = await prisma.service.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { teams: true, destinationDocker: true } - }); - return { - services - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function newService(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const name = uniqueName(); - - const { id } = await prisma.service.create({ - data: { name, teams: { connect: { id: teamId } } } - }); - return reply.status(201).send({ id }); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function cleanupUnconfiguredServices(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - let services = await prisma.service.findMany({ - where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } }, - include: { destinationDocker: true, teams: true } - }); - for (const service of services) { - if (!service.fqdn) { - if (service.destinationDockerId) { - const { stdout: containers } = await executeCommand({ - dockerId: service.destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.project=${service.id}' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ - dockerId: service.destinationDockerId, - command: `docker stop -t 0 ${container}` - }); - await executeCommand({ - dockerId: service.destinationDockerId, - command: `docker rm --force ${container}` - }); - } - } - } - } - await removeService({ id: service.id }); - } - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getServiceStatus(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const service = await getServiceFromDB({ id, teamId }); - const { destinationDockerId, settings } = service; - let payload = {}; - if (destinationDockerId) { - const { stdout: containers } = await executeCommand({ - dockerId: service.destinationDocker.id, - command: `docker ps -a --filter "label=com.docker.compose.project=${id}" --format '{{json .}}'` - }); - if (containers) { - const containersArray = containers.trim().split('\n'); - if (containersArray.length > 0 && containersArray[0] !== '') { - const templates = await getTemplates(); - let template = templates.find((t) => t.type === service.type); - const templateStr = JSON.stringify(template); - if (templateStr) { - template = JSON.parse(templateStr.replaceAll('$$id', service.id)); - } - for (const container of containersArray) { - let isRunning = false; - let isExited = false; - let isRestarting = false; - let isExcluded = false; - const containerObj = JSON.parse(container); - const exclude = template?.services[containerObj.Names]?.exclude; - if (exclude) { - payload[containerObj.Names] = { - status: { - isExcluded: true, - isRunning: false, - isExited: false, - isRestarting: false - } - }; - continue; - } - - const status = containerObj.State; - if (status === 'running') { - isRunning = true; - } - if (status === 'exited') { - isExited = true; - } - if (status === 'restarting') { - isRestarting = true; - } - payload[containerObj.Names] = { - status: { - isExcluded, - isRunning, - isExited, - isRestarting - } - }; - } - } - } - } - return payload; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function parseAndFindServiceTemplates( - service: any, - workdir?: string, - isDeploy: boolean = false -) { - const templates = await getTemplates(); - const foundTemplate = templates.find((t) => fixType(t.type) === service.type); - let parsedTemplate = {}; - if (foundTemplate) { - if (!isDeploy) { - for (const [key, value] of Object.entries(foundTemplate.services)) { - const realKey = key.replace('$$id', service.id); - let name = value.name; - if (!name) { - if (Object.keys(foundTemplate.services).length === 1) { - name = foundTemplate.name || service.name.toLowerCase(); - } else { - if (key === '$$id') { - name = - foundTemplate.name || key.replaceAll('$$id-', '') || service.name.toLowerCase(); - } else { - name = key.replaceAll('$$id-', '') || service.name.toLowerCase(); - } - } - } - parsedTemplate[realKey] = { - value, - name, - documentation: - value.documentation || foundTemplate.documentation || 'https://docs.coollabs.io', - image: value.image, - files: value?.files, - environment: [], - fqdns: [], - hostPorts: [], - proxy: {} - }; - if (value.environment?.length > 0) { - for (const env of value.environment) { - let [envKey, ...envValue] = env.split('='); - envValue = envValue.join('='); - let variable = null; - if (foundTemplate?.variables) { - variable = - foundTemplate?.variables.find((v) => v.name === envKey) || - foundTemplate?.variables.find((v) => v.id === envValue); - } - if (variable) { - const id = variable.id.replaceAll('$$', ''); - const label = variable?.label; - const description = variable?.description; - const defaultValue = variable?.defaultValue; - const main = variable?.main || '$$id'; - const type = variable?.type || 'input'; - const placeholder = variable?.placeholder || ''; - const readOnly = variable?.readOnly || false; - const required = variable?.required || false; - if (envValue.startsWith('$$config') || variable?.showOnConfiguration) { - if (envValue.startsWith('$$config_coolify')) { - continue; - } - parsedTemplate[realKey].environment.push({ - id, - name: envKey, - value: envValue, - main, - label, - description, - defaultValue, - type, - placeholder, - required, - readOnly - }); - } - } - } - } - if (value?.proxy && value.proxy.length > 0) { - for (const proxyValue of value.proxy) { - if (proxyValue.domain) { - const variable = foundTemplate?.variables.find((v) => v.id === proxyValue.domain); - if (variable) { - const { id, name, label, description, defaultValue, required = false } = variable; - const found = await prisma.serviceSetting.findFirst({ - where: { serviceId: service.id, variableName: proxyValue.domain } - }); - parsedTemplate[realKey].fqdns.push({ - id, - name, - value: found?.value || '', - label, - description, - defaultValue, - required - }); - } - } - if (proxyValue.hostPort) { - const variable = foundTemplate?.variables.find((v) => v.id === proxyValue.hostPort); - if (variable) { - const { id, name, label, description, defaultValue, required = false } = variable; - const found = await prisma.serviceSetting.findFirst({ - where: { serviceId: service.id, variableName: proxyValue.hostPort } - }); - parsedTemplate[realKey].hostPorts.push({ - id, - name, - value: found?.value || '', - label, - description, - defaultValue, - required - }); - } - } - } - } - } - } else { - parsedTemplate = foundTemplate; - } - let strParsedTemplate = JSON.stringify(parsedTemplate); - - // replace $$id and $$workdir - strParsedTemplate = strParsedTemplate.replaceAll('$$id', service.id); - strParsedTemplate = strParsedTemplate.replaceAll( - '$$core_version', - service.version || foundTemplate.defaultVersion - ); - - // replace $$workdir - if (workdir) { - strParsedTemplate = strParsedTemplate.replaceAll('$$workdir', workdir); - } - - // replace $$config - if (service.serviceSetting.length > 0) { - for (const setting of service.serviceSetting) { - const { value, variableName } = setting; - const regex = new RegExp(`\\$\\$config_${variableName.replace('$$config_', '')}\"`, 'gi'); - if (value === '$$generate_fqdn') { - strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn + '"' || '' + '"'); - } else if (value === '$$generate_fqdn_slash') { - strParsedTemplate = strParsedTemplate.replaceAll(regex, service.fqdn + '/' + '"'); - } else if (value === '$$generate_domain') { - strParsedTemplate = strParsedTemplate.replaceAll(regex, getDomain(service.fqdn) + '"'); - } else if (service.destinationDocker?.network && value === '$$generate_network') { - strParsedTemplate = strParsedTemplate.replaceAll( - regex, - service.destinationDocker.network + '"' - ); - } else { - strParsedTemplate = strParsedTemplate.replaceAll(regex, value + '"'); - } - } - } - - // replace $$secret - if (service.serviceSecret.length > 0) { - for (const secret of service.serviceSecret) { - let { name, value } = secret; - name = name.toLowerCase(); - const regexHashed = new RegExp(`\\$\\$hashed\\$\\$secret_${name}`, 'gi'); - const regex = new RegExp(`\\$\\$secret_${name}`, 'gi'); - if (value) { - strParsedTemplate = strParsedTemplate.replaceAll( - regexHashed, - bcrypt.hashSync(value.replaceAll('"', '\\"'), 10) - ); - strParsedTemplate = strParsedTemplate.replaceAll(regex, value.replaceAll('"', '\\"')); - } else { - strParsedTemplate = strParsedTemplate.replaceAll(regexHashed, ''); - strParsedTemplate = strParsedTemplate.replaceAll(regex, ''); - } - } - } - parsedTemplate = JSON.parse(strParsedTemplate); - } - return parsedTemplate; -} - -export async function getService(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - const service = await getServiceFromDB({ id, teamId }); - if (!service) { - throw { status: 404, message: 'Service not found.' }; - } - let template = {}; - let tags = []; - if (service.type) { - template = await parseAndFindServiceTemplates(service); - tags = await getTags(service.type); - } - return { - settings: await listSettings(), - service, - template, - tags - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getServiceType(request: FastifyRequest) { - try { - return { - services: await getTemplates() - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveServiceType( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { type } = request.body; - const templates = await getTemplates(); - let foundTemplate = templates.find((t) => fixType(t.type) === fixType(type)); - if (foundTemplate) { - foundTemplate = JSON.parse(JSON.stringify(foundTemplate).replaceAll('$$id', id)); - if (foundTemplate.variables) { - if (foundTemplate.variables.length > 0) { - for (const variable of foundTemplate.variables) { - const { defaultValue } = variable; - const regex = /^\$\$.*\((\d+)\)$/g; - const length = Number(regex.exec(defaultValue)?.[1]) || undefined; - if (variable.defaultValue.startsWith('$$generate_password')) { - variable.value = generatePassword({ length }); - } else if (variable.defaultValue.startsWith('$$generate_hex')) { - variable.value = generatePassword({ length, isHex: true }); - } else if (variable.defaultValue.startsWith('$$generate_username')) { - variable.value = cuid(); - } else if (variable.defaultValue.startsWith('$$generate_token')) { - variable.value = generateToken(); - } else { - variable.value = variable.defaultValue || ''; - } - const foundVariableSomewhereElse = foundTemplate.variables.find((v) => - v.defaultValue.includes(variable.id) - ); - if (foundVariableSomewhereElse) { - foundVariableSomewhereElse.value = foundVariableSomewhereElse.value.replaceAll( - variable.id, - variable.value - ); - } - } - } - for (const variable of foundTemplate.variables) { - if (variable.id.startsWith('$$secret_')) { - const found = await prisma.serviceSecret.findFirst({ - where: { name: variable.name, serviceId: id } - }); - if (!found) { - await prisma.serviceSecret.create({ - data: { - name: variable.name, - value: encrypt(variable.value) || '', - service: { connect: { id } } - } - }); - } - } - if (variable.id.startsWith('$$config_')) { - const found = await prisma.serviceSetting.findFirst({ - where: { name: variable.name, serviceId: id } - }); - if (!found) { - await prisma.serviceSetting.create({ - data: { - name: variable.name, - value: variable.value.toString(), - variableName: variable.id, - service: { connect: { id } } - } - }); - } - } - } - } - for (const service of Object.keys(foundTemplate.services)) { - if (foundTemplate.services[service].volumes) { - for (const volume of foundTemplate.services[service].volumes) { - const [volumeName, path] = volume.split(':'); - if (!volumeName.startsWith('/')) { - const found = await prisma.servicePersistentStorage.findFirst({ - where: { volumeName, serviceId: id } - }); - if (!found) { - await prisma.servicePersistentStorage.create({ - data: { - volumeName, - path, - containerId: service, - predefined: true, - service: { connect: { id } } - } - }); - } - } - } - } - } - await prisma.service.update({ - where: { id }, - data: { - type, - version: foundTemplate.defaultVersion, - templateVersion: foundTemplate.templateVersion - } - }); - - if (type.startsWith('wordpress')) { - await prisma.service.update({ where: { id }, data: { wordpress: { create: {} } } }); - } - return reply.code(201).send(); - } else { - throw { status: 404, message: 'Service type not found.' }; - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveServiceVersion( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { version } = request.body; - await prisma.service.update({ - where: { id }, - data: { version } - }); - return reply.code(201).send({}); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveServiceDestination( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { destinationId } = request.body; - await prisma.service.update({ - where: { id }, - data: { destinationDocker: { connect: { id: destinationId } } } - }); - return reply.code(201).send({}); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getServiceUsage(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const { id } = request.params; - let usage = {}; - - const service = await getServiceFromDB({ id, teamId }); - if (service.destinationDockerId) { - [usage] = await Promise.all([getContainerUsage(service.destinationDocker.id, id)]); - } - return { - usage - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function getServiceLogs(request: FastifyRequest) { - try { - const { id, containerId } = request.params; - let { since = 0 } = request.query; - if (since !== 0) { - since = day(since).unix(); - } - const { - destinationDockerId, - destinationDocker: { id: dockerId } - } = await prisma.service.findUnique({ - where: { id }, - include: { destinationDocker: true } - }); - if (destinationDockerId) { - try { - const { default: ansi } = await import('strip-ansi'); - const { stdout, stderr } = await executeCommand({ - dockerId, - command: `docker logs --since ${since} --tail 5000 --timestamps ${containerId}` - }); - const stripLogsStdout = stdout - .toString() - .split('\n') - .map((l) => ansi(l)) - .filter((a) => a); - const stripLogsStderr = stderr - .toString() - .split('\n') - .map((l) => ansi(l)) - .filter((a) => a); - return { logs: stripLogsStderr.concat(stripLogsStdout) }; - // } - } catch (error) { - const { statusCode, stderr } = error; - if (stderr.startsWith('Error: No such container')) { - return { logs: [], noContainer: true }; - } - if (statusCode === 404) { - return { - logs: [] - }; - } - } - } - return { - message: 'No logs found.' - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deleteService(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const { destinationDockerId } = await getServiceFromDB({ id, teamId }); - if (destinationDockerId) { - const { stdout: containers } = await executeCommand({ - dockerId: destinationDockerId, - command: `docker ps -a --filter 'label=com.docker.compose.project=${id}' --format {{.ID}}` - }); - if (containers) { - const containerArray = containers.split('\n'); - if (containerArray.length > 0) { - for (const container of containerArray) { - await executeCommand({ - dockerId: destinationDockerId, - command: `docker stop -t 0 ${container}` - }); - await executeCommand({ - dockerId: destinationDockerId, - command: `docker rm --force ${container}` - }); - } - } - } - } - await removeService({ id }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveServiceSettings( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { dualCerts } = request.body; - await prisma.service.update({ - where: { id }, - data: { dualCerts } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function checkServiceDomain(request: FastifyRequest) { - try { - const { id } = request.params; - const { domain } = request.query; - const { fqdn, dualCerts } = await prisma.service.findUnique({ where: { id } }); - // TODO: Disabled this because it is having problems with remote docker engines. - // return await checkDomainsIsValidInDNS({ hostname: domain, fqdn, dualCerts }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function checkService(request: FastifyRequest) { - try { - const { id } = request.params; - let { fqdn, exposePort, forceSave, dualCerts, otherFqdn = false } = request.body; - - const domainsList = await prisma.serviceSetting.findMany({ - where: { variableName: { startsWith: '$$config_coolify_fqdn' } } - }); - - if (fqdn) fqdn = fqdn.toLowerCase(); - if (exposePort) exposePort = Number(exposePort); - - const { - destinationDocker: { remoteIpAddress, remoteEngine, engine }, - exposePort: configuredPort - } = await prisma.service.findUnique({ where: { id }, include: { destinationDocker: true } }); - const { isDNSCheckEnabled } = await prisma.setting.findFirst({}); - - let found = await isDomainConfigured({ id, fqdn, remoteIpAddress, checkOwn: otherFqdn }); - if (found) { - throw { - status: 500, - message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` - }; - } - if (domainsList.find((d) => getDomain(d.value) === getDomain(fqdn))) { - throw { - status: 500, - message: `Domain ${getDomain(fqdn).replace('www.', '')} is already in use!` - }; - } - if (exposePort) - await checkExposedPort({ - id, - configuredPort, - exposePort, - engine, - remoteEngine, - remoteIpAddress - }); - // TODO: Disabled this because it is having problems with remote docker engines. - // if (isDNSCheckEnabled && !isDev && !forceSave) { - // let hostname = request.hostname.split(':')[0]; - // if (remoteEngine) hostname = remoteIpAddress; - // return await checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }); - // } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function saveService(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - let { name, fqdn, exposePort, type, serviceSetting, version } = request.body; - if (fqdn) fqdn = fqdn.toLowerCase(); - if (exposePort) exposePort = Number(exposePort); - type = fixType(type); - - const data = { - fqdn, - name, - exposePort, - version - }; - const templates = await getTemplates(); - const service = await prisma.service.findUnique({ where: { id } }); - const foundTemplate = templates.find((t) => fixType(t.type) === fixType(service.type)); - for (const setting of serviceSetting) { - let { id: settingId, name, value, changed = false, isNew = false, variableName } = setting; - if (value) { - if (changed) { - await prisma.serviceSetting.update({ where: { id: settingId }, data: { value: value.replace(/\n/, "\\n") } }); - } - if (isNew) { - if (!variableName) { - variableName = foundTemplate?.variables.find((v) => v.name === name).id; - } - await prisma.serviceSetting.create({ - data: { name, value, variableName, service: { connect: { id } } } - }); - } - } - } - await prisma.service.update({ - where: { id }, - data - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getServiceSecrets(request: FastifyRequest) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const service = await getServiceFromDB({ id, teamId }); - let secrets = await prisma.serviceSecret.findMany({ - where: { serviceId: id }, - orderBy: { createdAt: 'desc' } - }); - const templates = await getTemplates(); - const foundTemplate = templates.find((t) => fixType(t.type) === service.type); - secrets = secrets.map((secret) => { - const foundVariable = foundTemplate?.variables.find((v) => v.name === secret.name) || null; - if (foundVariable) { - secret.readOnly = foundVariable.readOnly; - } - secret.value = decrypt(secret.value); - return secret; - }); - - return { - secrets - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveServiceSecret( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - let { name, value, isNew } = request.body; - if (isNew) { - const found = await prisma.serviceSecret.findFirst({ where: { name, serviceId: id } }); - if (found) { - throw `Secret ${name} already exists.`; - } else { - value = encrypt(value.trim()); - await prisma.serviceSecret.create({ - data: { name, value, service: { connect: { id } } } - }); - } - } else { - value = encrypt(value.trim()); - const found = await prisma.serviceSecret.findFirst({ where: { serviceId: id, name } }); - - if (found) { - await prisma.serviceSecret.updateMany({ - where: { serviceId: id, name }, - data: { value } - }); - } else { - await prisma.serviceSecret.create({ - data: { name, value, service: { connect: { id } } } - }); - } - } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function deleteServiceSecret(request: FastifyRequest) { - try { - const { id } = request.params; - const { name } = request.body; - await prisma.serviceSecret.deleteMany({ where: { serviceId: id, name } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function getServiceStorages(request: FastifyRequest) { - try { - const { id } = request.params; - const persistentStorages = await prisma.servicePersistentStorage.findMany({ - where: { serviceId: id } - }); - return { - persistentStorages - }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function saveServiceStorage( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { path, isNewStorage, storageId, containerId } = request.body; - - if (isNewStorage) { - const volumeName = `${id}-custom${path.replace(/\//gi, '-')}`; - const found = await prisma.servicePersistentStorage.findFirst({ - where: { path, containerId } - }); - if (found) { - throw { - status: 500, - message: 'Persistent storage already exists for this container and path.' - }; - } - await prisma.servicePersistentStorage.create({ - data: { path, volumeName, containerId, service: { connect: { id } } } - }); - } else { - await prisma.servicePersistentStorage.update({ - where: { id: storageId }, - data: { path, containerId } - }); - } - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function deleteServiceStorage(request: FastifyRequest) { - try { - const { storageId } = request.body; - await prisma.servicePersistentStorage.deleteMany({ where: { id: storageId } }); - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function setSettingsService( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { type } = request.params; - if (type === 'wordpress') { - return await setWordpressSettings(request, reply); - } - if (type === 'glitchtip') { - return await setGlitchTipSettings(request, reply); - } - throw `Service type ${type} not supported.`; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -async function setGlitchTipSettings( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { enableOpenUserRegistration, emailSmtpUseSsl, emailSmtpUseTls } = request.body; - await prisma.glitchTip.update({ - where: { serviceId: id }, - data: { enableOpenUserRegistration, emailSmtpUseSsl, emailSmtpUseTls } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -async function setWordpressSettings( - request: FastifyRequest, - reply: FastifyReply -) { - try { - const { id } = request.params; - const { ownMysql } = request.body; - await prisma.wordpress.update({ - where: { serviceId: id }, - data: { ownMysql } - }); - return reply.code(201).send(); - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} - -export async function activatePlausibleUsers(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const { destinationDockerId, destinationDocker, serviceSecret } = await getServiceFromDB({ - id, - teamId - }); - if (destinationDockerId) { - const databaseUrl = serviceSecret.find((secret) => secret.name === 'DATABASE_URL'); - if (databaseUrl) { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker exec ${id}-postgresql psql -H ${databaseUrl.value} -c "UPDATE users SET email_verified = true;"` - }); - return await reply.code(201).send(); - } - } - throw { status: 500, message: 'Could not activate users.' }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function cleanupPlausibleLogs(request: FastifyRequest, reply: FastifyReply) { - try { - const { id } = request.params; - const teamId = request.user.teamId; - const { destinationDockerId, destinationDocker } = await getServiceFromDB({ id, teamId }); - if (destinationDockerId) { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker exec ${id}-clickhouse /usr/bin/clickhouse-client -q \\"SELECT name FROM system.tables WHERE name LIKE '%log%';\\"| xargs -I{} /usr/bin/clickhouse-client -q \"TRUNCATE TABLE system.{};\"`, - shell: true - }); - return await reply.code(201).send(); - } - throw { status: 500, message: 'Could cleanup logs.' }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} -export async function activateWordpressFtp( - request: FastifyRequest, - reply: FastifyReply -) { - const { id } = request.params; - const { ftpEnabled } = request.body; - - const { - service: { - destinationDocker: { engine, remoteEngine, remoteIpAddress } - } - } = await prisma.wordpress.findUnique({ - where: { serviceId: id }, - include: { service: { include: { destinationDocker: true } } } - }); - - const publicPort = await getFreePublicPort({ id, remoteEngine, engine, remoteIpAddress }); - - let ftpUser = cuid(); - let ftpPassword = generatePassword({}); - - const hostkeyDir = isDev ? '/tmp/hostkeys' : '/app/ssl/hostkeys'; - try { - const data = await prisma.wordpress.update({ - where: { serviceId: id }, - data: { ftpEnabled }, - include: { service: { include: { destinationDocker: true } } } - }); - const { - service: { destinationDockerId, destinationDocker }, - ftpPublicPort, - ftpUser: user, - ftpPassword: savedPassword, - ftpHostKey, - ftpHostKeyPrivate - } = data; - const { network, engine } = destinationDocker; - if (ftpEnabled) { - if (user) ftpUser = user; - if (savedPassword) ftpPassword = decrypt(savedPassword); - - // TODO: rewrite these to usable without shell - const { stdout: password } = await executeCommand({ - command: `echo ${ftpPassword} | openssl passwd -1 -stdin`, - shell: true - }); - if (destinationDockerId) { - try { - await fs.stat(hostkeyDir); - } catch (error) { - await executeCommand({ command: `mkdir -p ${hostkeyDir}` }); - } - if (!ftpHostKey) { - await executeCommand({ - command: `ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -q -f ${hostkeyDir}/${id}.ed25519` - }); - const { stdout: ftpHostKey } = await executeCommand({ - command: `cat ${hostkeyDir}/${id}.ed25519` - }); - await prisma.wordpress.update({ - where: { serviceId: id }, - data: { ftpHostKey: encrypt(ftpHostKey) } - }); - } else { - await executeCommand({ - command: `echo "${decrypt(ftpHostKey)}" > ${hostkeyDir}/${id}.ed25519`, - shell: true - }); - } - if (!ftpHostKeyPrivate) { - await executeCommand({ - command: `ssh-keygen -t rsa -b 4096 -N "" -f ${hostkeyDir}/${id}.rsa` - }); - const { stdout: ftpHostKeyPrivate } = await executeCommand({ - command: `cat ${hostkeyDir}/${id}.rsa` - }); - await prisma.wordpress.update({ - where: { serviceId: id }, - data: { ftpHostKeyPrivate: encrypt(ftpHostKeyPrivate) } - }); - } else { - await executeCommand({ - command: `echo "${decrypt(ftpHostKeyPrivate)}" > ${hostkeyDir}/${id}.rsa`, - shell: true - }); - } - - await prisma.wordpress.update({ - where: { serviceId: id }, - data: { - ftpPublicPort: publicPort, - ftpUser: user ? undefined : ftpUser, - ftpPassword: savedPassword ? undefined : encrypt(ftpPassword) - } - }); - - try { - const { found: isRunning } = await checkContainer({ - dockerId: destinationDocker.id, - container: `${id}-ftp` - }); - if (isRunning) { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`, - shell: true - }); - } - } catch (error) { } - const volumes = [ - `${id}-wordpress-data:/home/${ftpUser}/wordpress`, - `${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys' - }/${id}.ed25519:/etc/ssh/ssh_host_ed25519_key`, - `${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys' - }/${id}.rsa:/etc/ssh/ssh_host_rsa_key`, - `${isDev ? hostkeyDir : '/var/lib/docker/volumes/coolify-ssl-certs/_data/hostkeys' - }/${id}.sh:/etc/sftp.d/chmod.sh` - ]; - - const compose: ComposeFile = { - version: '3.8', - services: { - [`${id}-ftp`]: { - image: `atmoz/sftp:alpine`, - command: `'${ftpUser}:${password.replace('\n', '').replace(/\$/g, '$$$')}:e:33'`, - extra_hosts: ['host.docker.internal:host-gateway'], - container_name: `${id}-ftp`, - volumes, - networks: [network], - depends_on: [], - restart: 'always' - } - }, - networks: { - [network]: { - external: true - } - }, - volumes: { - [`${id}-wordpress-data`]: { - external: true, - name: `${id}-wordpress-data` - } - } - }; - await fs.writeFile( - `${hostkeyDir}/${id}.sh`, - `#!/bin/bash\nchmod 600 /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key\nuserdel -f xfs\nchown -R 33:33 /home/${ftpUser}/wordpress/` - ); - await executeCommand({ command: `chmod +x ${hostkeyDir}/${id}.sh` }); - await fs.writeFile(`${hostkeyDir}/${id}-docker-compose.yml`, yaml.dump(compose)); - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker compose -f ${hostkeyDir}/${id}-docker-compose.yml up -d` - }); - } - return reply.code(201).send({ - publicPort, - ftpUser, - ftpPassword - }); - } else { - await prisma.wordpress.update({ - where: { serviceId: id }, - data: { ftpPublicPort: null } - }); - try { - await executeCommand({ - dockerId: destinationDocker.id, - command: `docker stop -t 0 ${id}-ftp && docker rm ${id}-ftp`, - shell: true - }); - } catch (error) { - // - } - await stopTcpHttpProxy(id, destinationDocker, ftpPublicPort); - return {}; - } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } finally { - try { - await executeCommand({ - command: `rm -fr ${hostkeyDir}/${id}-docker-compose.yml ${hostkeyDir}/${id}.ed25519 ${hostkeyDir}/${id}.ed25519.pub ${hostkeyDir}/${id}.rsa ${hostkeyDir}/${id}.rsa.pub ${hostkeyDir}/${id}.sh` - }); - } catch (error) { } - } -} diff --git a/apps/api/src/routes/api/v1/services/index.ts b/apps/api/src/routes/api/v1/services/index.ts deleted file mode 100644 index dc23abe05..000000000 --- a/apps/api/src/routes/api/v1/services/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { - activatePlausibleUsers, - activateWordpressFtp, - checkService, - checkServiceDomain, - cleanupPlausibleLogs, - cleanupUnconfiguredServices, - deleteService, - deleteServiceSecret, - deleteServiceStorage, - getService, - getServiceLogs, - getServiceSecrets, - getServiceStatus, - getServiceStorages, - getServiceType, - getServiceUsage, - listServices, - newService, - saveService, - saveServiceDestination, - saveServiceSecret, - saveServiceSettings, - saveServiceStorage, - saveServiceType, - saveServiceVersion, - setSettingsService, -} from './handlers'; - -import type { OnlyId } from '../../../../types'; -import type { ActivateWordpressFtp, CheckService, CheckServiceDomain, DeleteServiceSecret, DeleteServiceStorage, GetServiceLogs, SaveService, SaveServiceDestination, SaveServiceSecret, SaveServiceSettings, SaveServiceStorage, SaveServiceType, SaveServiceVersion, ServiceStartStop, SetGlitchTipSettings, SetWordpressSettings } from './types'; -import { migrateAppwriteDB, startService, stopService } from '../../../../lib/services/handlers'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listServices(request)); - fastify.post('/new', async (request, reply) => await newService(request, reply)); - - fastify.post('/cleanup/unconfigured', async (request) => await cleanupUnconfiguredServices(request)); - - fastify.get('/:id', async (request) => await getService(request)); - fastify.post('/:id', async (request, reply) => await saveService(request, reply)); - fastify.delete('/:id', async (request) => await deleteService(request)); - - fastify.get('/:id/status', async (request) => await getServiceStatus(request)); - - fastify.get('/:id/check', async (request) => await checkServiceDomain(request)); - fastify.post('/:id/check', async (request) => await checkService(request)); - - fastify.post('/:id/settings', async (request, reply) => await saveServiceSettings(request, reply)); - - fastify.get('/:id/secrets', async (request) => await getServiceSecrets(request)); - fastify.post('/:id/secrets', async (request, reply) => await saveServiceSecret(request, reply)); - fastify.delete('/:id/secrets', async (request) => await deleteServiceSecret(request)); - - fastify.get('/:id/storages', async (request) => await getServiceStorages(request)); - fastify.post('/:id/storages', async (request, reply) => await saveServiceStorage(request, reply)); - fastify.delete('/:id/storages', async (request) => await deleteServiceStorage(request)); - - fastify.get('/:id/configuration/type', async (request) => await getServiceType(request)); - fastify.post('/:id/configuration/type', async (request, reply) => await saveServiceType(request, reply)); - - fastify.post('/:id/configuration/version', async (request, reply) => await saveServiceVersion(request, reply)); - - fastify.post('/:id/configuration/destination', async (request, reply) => await saveServiceDestination(request, reply)); - - fastify.get('/:id/usage', async (request) => await getServiceUsage(request)); - fastify.get('/:id/logs/:containerId', async (request) => await getServiceLogs(request)); - - fastify.post('/:id/start', async (request) => await startService(request, fastify)); - fastify.post('/:id/stop', async (request) => await stopService(request)); - fastify.post('/:id/:type/settings', async (request, reply) => await setSettingsService(request, reply)); - - fastify.post('/:id/plausibleanalytics/activate', async (request, reply) => await activatePlausibleUsers(request, reply)); - fastify.post('/:id/plausibleanalytics/cleanup', async (request, reply) => await cleanupPlausibleLogs(request, reply)); - fastify.post('/:id/wordpress/ftp', async (request, reply) => await activateWordpressFtp(request, reply)); - - fastify.post('/:id/appwrite/migrate', async (request, reply) => await migrateAppwriteDB(request, reply)); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/services/types.ts b/apps/api/src/routes/api/v1/services/types.ts deleted file mode 100644 index 253c5bcab..000000000 --- a/apps/api/src/routes/api/v1/services/types.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { OnlyId } from "../../../../types"; - -export interface SaveServiceType extends OnlyId { - Body: { - type: string - } -} -export interface SaveServiceVersion extends OnlyId { - Body: { - version: string - } -} -export interface SaveServiceDestination extends OnlyId { - Body: { - destinationId: string - } -} -export interface GetServiceLogs{ - Params: { - id: string, - containerId: string - }, - Querystring: { - since: number, - } -} -export interface SaveServiceSettings extends OnlyId { - Body: { - dualCerts: boolean - } -} -export interface CheckServiceDomain extends OnlyId { - Querystring: { - domain: string - } -} -export interface CheckService extends OnlyId { - Body: { - fqdn: string, - forceSave: boolean, - dualCerts: boolean, - exposePort: number, - otherFqdn: boolean - } -} -export interface SaveService extends OnlyId { - Body: { - name: string, - fqdn: string, - exposePort: number, - version: string, - serviceSetting: any - type: string - } -} -export interface SaveServiceSecret extends OnlyId { - Body: { - name: string, - value: string, - isNew: string, - } -} -export interface DeleteServiceSecret extends OnlyId { - Body: { - name: string, - } -} -export interface SaveServiceStorage extends OnlyId { - Body: { - path: string, - containerId: string, - storageId: string, - isNewStorage: boolean, - } -} - -export interface DeleteServiceStorage extends OnlyId { - Body: { - storageId: string, - } -} -export interface ServiceStartStop { - Params: { - id?: string, - type: string, - } -} -export interface SetWordpressSettings extends OnlyId { - Body: { - ownMysql: boolean - } -} -export interface ActivateWordpressFtp extends OnlyId { - Body: { - ftpEnabled: boolean - } -} - -export interface SetGlitchTipSettings extends OnlyId { - Body: { - enableOpenUserRegistration: boolean, - emailSmtpUseSsl: boolean, - emailSmtpUseTls: boolean - } -} diff --git a/apps/api/src/routes/api/v1/settings/handlers.ts b/apps/api/src/routes/api/v1/settings/handlers.ts deleted file mode 100644 index 1f0ecb006..000000000 --- a/apps/api/src/routes/api/v1/settings/handlers.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { promises as dns } from 'dns'; -import { X509Certificate } from 'node:crypto'; -import * as Sentry from '@sentry/node'; -import type { FastifyReply, FastifyRequest } from 'fastify'; -import { checkDomainsIsValidInDNS, decrypt, encrypt, errorHandler, executeCommand, getDomain, isDev, isDNSValid, isDomainConfigured, listSettings, prisma, sentryDSN, version } from '../../../../lib/common'; -import { AddDefaultRegistry, CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; - - -export async function listAllSettings(request: FastifyRequest) { - try { - const teamId = request.user.teamId; - const settings = await listSettings(); - const sshKeys = await prisma.sshKey.findMany({ where: { team: { id: teamId } } }) - let registries = await prisma.dockerRegistry.findMany({ where: { team: { id: teamId } } }) - registries = registries.map((registry) => { - if (registry.password) { - registry.password = decrypt(registry.password) - } - return registry - }) - const unencryptedKeys = [] - if (sshKeys.length > 0) { - for (const key of sshKeys) { - unencryptedKeys.push({ id: key.id, name: key.name, privateKey: decrypt(key.privateKey), createdAt: key.createdAt }) - } - } - const certificates = await prisma.certificate.findMany({ where: { team: { id: teamId } } }) - let cns = []; - for (const certificate of certificates) { - const x509 = new X509Certificate(certificate.cert); - cns.push({ commonName: x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', ''), id: certificate.id, createdAt: certificate.createdAt }) - } - - return { - settings, - certificates: cns, - sshKeys: unencryptedKeys, - registries - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function saveSettings(request: FastifyRequest, reply: FastifyReply) { - try { - let { - previewSeparator, - numberOfDockerImagesKeptLocally, - doNotTrack, - fqdn, - isAPIDebuggingEnabled, - isRegistrationEnabled, - dualCerts, - minPort, - maxPort, - isAutoUpdateEnabled, - isDNSCheckEnabled, - DNSServers, - proxyDefaultRedirect - } = request.body - const { id, previewSeparator: SetPreviewSeparator } = await listSettings(); - if (numberOfDockerImagesKeptLocally) { - numberOfDockerImagesKeptLocally = Number(numberOfDockerImagesKeptLocally) - } - if (previewSeparator == '') { - previewSeparator = '.' - } - if (SetPreviewSeparator != previewSeparator) { - const applications = await prisma.application.findMany({ where: { previewApplication: { some: { id: { not: undefined } } } }, include: { previewApplication: true } }) - for (const application of applications) { - for (const preview of application.previewApplication) { - const { protocol } = new URL(preview.customDomain) - const { pullmergeRequestId } = preview - const { fqdn } = application - const newPreviewDomain = `${protocol}//${pullmergeRequestId}${previewSeparator}${getDomain(fqdn)}` - await prisma.previewApplication.update({ where: { id: preview.id }, data: { customDomain: newPreviewDomain } }) - } - } - } - - await prisma.setting.update({ - where: { id }, - data: { previewSeparator, numberOfDockerImagesKeptLocally, doNotTrack, isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled } - }); - if (fqdn) { - await prisma.setting.update({ where: { id }, data: { fqdn } }); - } - await prisma.setting.update({ where: { id }, data: { proxyDefaultRedirect } }); - if (minPort && maxPort) { - await prisma.setting.update({ where: { id }, data: { minPort, maxPort } }); - } - if (doNotTrack === false) { - // Sentry.init({ - // dsn: sentryDSN, - // environment: isDev ? 'development' : 'production', - // release: version - // }); - // console.log('Sentry initialized') - } - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function deleteDomain(request: FastifyRequest, reply: FastifyReply) { - try { - const { fqdn } = request.body - const { DNSServers } = await listSettings(); - if (DNSServers) { - dns.setServers([...DNSServers.split(',')]); - } - let ip; - try { - ip = await dns.resolve(fqdn); - } catch (error) { - // Do not care. - } - await prisma.setting.update({ where: { fqdn }, data: { fqdn: null } }); - return reply.redirect(302, ip ? `http://${ip[0]}:3000/settings` : undefined) - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function checkDomain(request: FastifyRequest) { - try { - const { id } = request.params; - let { fqdn, forceSave, dualCerts, isDNSCheckEnabled } = request.body - if (fqdn) fqdn = fqdn.toLowerCase(); - const found = await isDomainConfigured({ id, fqdn }); - if (found) { - throw { message: "Domain already configured" }; - } - if (isDNSCheckEnabled && !forceSave && !isDev) { - const hostname = request.hostname.split(':')[0] - return await checkDomainsIsValidInDNS({ hostname, fqdn, dualCerts }); - } - return {}; - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function checkDNS(request: FastifyRequest) { - try { - const { domain } = request.params; - await isDNSValid(request.hostname, domain); - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function saveSSHKey(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { privateKey, name } = request.body; - const found = await prisma.sshKey.findMany({ where: { name } }) - if (found.length > 0) { - throw { - message: "Name already used. Choose another one please." - } - } - const encryptedSSHKey = encrypt(privateKey) - await prisma.sshKey.create({ data: { name, privateKey: encryptedSSHKey, team: { connect: { id: teamId } } } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function deleteSSHKey(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.body; - await prisma.sshKey.deleteMany({ where: { id, teamId } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function deleteCertificates(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.body; - await executeCommand({ command: `docker exec coolify-proxy sh -c 'rm -f /etc/traefik/acme/custom/${id}-key.pem /etc/traefik/acme/custom/${id}-cert.pem'`, shell: true }) - await prisma.certificate.deleteMany({ where: { id, teamId } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function setDockerRegistry(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id, username, password } = request.body; - - let encryptedPassword = '' - if (password) encryptedPassword = encrypt(password) - - if (teamId === '0') { - await prisma.dockerRegistry.update({ where: { id }, data: { username, password: encryptedPassword } }) - } else { - await prisma.dockerRegistry.updateMany({ where: { id, teamId }, data: { username, password: encryptedPassword } }) - } - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function addDockerRegistry(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { name, url, username, password } = request.body; - - let encryptedPassword = '' - if (password) encryptedPassword = encrypt(password) - await prisma.dockerRegistry.create({ data: { name, url, username, password: encryptedPassword, team: { connect: { id: teamId } } } }) - - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function deleteDockerRegistry(request: FastifyRequest, reply: FastifyReply) { - try { - const teamId = request.user.teamId; - const { id } = request.body; - await prisma.application.updateMany({ where: { dockerRegistryId: id }, data: { dockerRegistryId: null } }) - await prisma.dockerRegistry.deleteMany({ where: { id, teamId } }) - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/settings/index.ts b/apps/api/src/routes/api/v1/settings/index.ts deleted file mode 100644 index 2ba8b178a..000000000 --- a/apps/api/src/routes/api/v1/settings/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { X509Certificate } from 'node:crypto'; - -import { encrypt, errorHandler, prisma } from '../../../../lib/common'; -import { addDockerRegistry, checkDNS, checkDomain, deleteCertificates, deleteDockerRegistry, deleteDomain, deleteSSHKey, listAllSettings, saveSettings, saveSSHKey, setDockerRegistry } from './handlers'; -import { AddDefaultRegistry, CheckDNS, CheckDomain, DeleteDomain, OnlyIdInBody, SaveSettings, SaveSSHKey, SetDefaultRegistry } from './types'; - - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listAllSettings(request)); - fastify.post('/', async (request, reply) => await saveSettings(request, reply)); - fastify.delete('/', async (request, reply) => await deleteDomain(request, reply)); - - fastify.get('/check', async (request) => await checkDNS(request)); - fastify.post('/check', async (request) => await checkDomain(request)); - - fastify.post('/sshKey', async (request, reply) => await saveSSHKey(request, reply)); - fastify.delete('/sshKey', async (request, reply) => await deleteSSHKey(request, reply)); - - fastify.post('/registry', async (request, reply) => await setDockerRegistry(request, reply)); - fastify.post('/registry/new', async (request, reply) => await addDockerRegistry(request, reply)); - fastify.delete('/registry', async (request, reply) => await deleteDockerRegistry(request, reply)); - - fastify.post('/upload', async (request) => { - try { - const teamId = request.user.teamId; - const certificates = await prisma.certificate.findMany({}) - let cns = []; - for (const certificate of certificates) { - const x509 = new X509Certificate(certificate.cert); - cns.push(x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', '')) - } - const parts = await request.files() - let key = null - let cert = null - for await (const part of parts) { - const name = part.fieldname - if (name === 'key') key = (await part.toBuffer()).toString() - if (name === 'cert') cert = (await part.toBuffer()).toString() - } - const x509 = new X509Certificate(cert); - const cn = x509.subject.split('\n').find((s) => s.startsWith('CN=')).replace('CN=', '') - if (cns.includes(cn)) { - throw { - message: `A certificate with ${cn} common name already exists.` - } - } - await prisma.certificate.create({ data: { cert, key: encrypt(key), team: { connect: { id: teamId } } } }) - await prisma.applicationSettings.updateMany({ where: { application: { AND: [{ fqdn: { endsWith: cn } }, { fqdn: { startsWith: 'https' } }] } }, data: { isCustomSSL: true } }) - return { message: 'Certificated uploaded' } - } catch ({ status, message }) { - return errorHandler({ status, message }); - } - - }); - fastify.delete('/certificate', async (request, reply) => await deleteCertificates(request, reply)) -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/settings/types.ts b/apps/api/src/routes/api/v1/settings/types.ts deleted file mode 100644 index 418f2661f..000000000 --- a/apps/api/src/routes/api/v1/settings/types.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { OnlyId } from "../../../../types" - -export interface SaveSettings { - Body: { - previewSeparator: string, - numberOfDockerImagesKeptLocally: number, - doNotTrack: boolean, - fqdn: string, - isAPIDebuggingEnabled: boolean, - isRegistrationEnabled: boolean, - dualCerts: boolean, - minPort: number, - maxPort: number, - isAutoUpdateEnabled: boolean, - isDNSCheckEnabled: boolean, - DNSServers: string, - proxyDefaultRedirect: string - } -} -export interface DeleteDomain { - Body: { - fqdn: string - } -} -export interface CheckDomain extends OnlyId { - Body: { - fqdn: string, - forceSave: boolean, - dualCerts: boolean, - isDNSCheckEnabled: boolean, - } -} -export interface CheckDNS { - Params: { - domain: string, - } -} -export interface SaveSSHKey { - Body: { - privateKey: string, - name: string - } -} -export interface DeleteSSHKey { - Body: { - id: string - } -} -export interface OnlyIdInBody { - Body: { - id: string - } -} - -export interface SetDefaultRegistry { - Body: { - id: string - username: string - password: string - } -} -export interface AddDefaultRegistry { - Body: { - url: string - name: string - username: string - password: string - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/sources/handlers.ts b/apps/api/src/routes/api/v1/sources/handlers.ts deleted file mode 100644 index 61849526a..000000000 --- a/apps/api/src/routes/api/v1/sources/handlers.ts +++ /dev/null @@ -1,191 +0,0 @@ -import cuid from 'cuid'; -import type { FastifyRequest } from 'fastify'; -import { FastifyReply } from 'fastify'; -import { decrypt, encrypt, errorHandler, prisma } from '../../../../lib/common'; -import { OnlyId } from '../../../../types'; -import { CheckGitLabOAuthId, SaveGitHubSource, SaveGitLabSource } from './types'; - -export async function listSources(request: FastifyRequest) { - try { - const teamId = request.user?.teamId; - const sources = await prisma.gitSource.findMany({ - where: { OR: [{ teams: { some: { id: teamId === "0" ? undefined : teamId } } }, { isSystemWide: true }] }, - include: { teams: true, githubApp: true, gitlabApp: true } - }); - return { - sources - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function saveSource(request, reply) { - try { - const { id } = request.params - let { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } = request.body - if (customPort) customPort = Number(customPort) - await prisma.gitSource.update({ - where: { id }, - data: { name, htmlUrl, apiUrl, customPort, customUser, isSystemWide } - }); - return reply.code(201).send() - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function getSource(request: FastifyRequest) { - try { - const { id } = request.params - const { teamId } = request.user - const settings = await prisma.setting.findFirst({}); - - if (id === 'new') { - return { - source: { - name: null, - type: null, - htmlUrl: null, - apiUrl: null, - organization: null, - customPort: 22, - customUser: 'git', - }, - settings - } - } - - const source = await prisma.gitSource.findFirst({ - where: { id, OR: [{ teams: { some: { id: teamId === "0" ? undefined : teamId } } }, { isSystemWide: true }] }, - include: { githubApp: true, gitlabApp: true } - }); - if (!source) { - throw { status: 404, message: 'Source not found.' } - } - - if (source?.githubApp?.clientSecret) - source.githubApp.clientSecret = decrypt(source.githubApp.clientSecret); - if (source?.githubApp?.webhookSecret) - source.githubApp.webhookSecret = decrypt(source.githubApp.webhookSecret); - if (source?.githubApp?.privateKey) source.githubApp.privateKey = decrypt(source.githubApp.privateKey); - if (source?.gitlabApp?.appSecret) source.gitlabApp.appSecret = decrypt(source.gitlabApp.appSecret); - - return { - source, - settings - }; - - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function deleteSource(request) { - try { - const { id } = request.params - const source = await prisma.gitSource.delete({ - where: { id }, - include: { githubApp: true, gitlabApp: true } - }); - if (source.githubAppId) { - await prisma.githubApp.delete({ where: { id: source.githubAppId } }); - } - if (source.gitlabAppId) { - await prisma.gitlabApp.delete({ where: { id: source.gitlabAppId } }); - } - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } - -} -export async function saveGitHubSource(request: FastifyRequest) { - try { - const { teamId } = request.user - - const { id } = request.params - let { name, htmlUrl, apiUrl, organization, customPort, isSystemWide } = request.body - - if (customPort) customPort = Number(customPort) - if (id === 'new') { - const newId = cuid() - await prisma.gitSource.create({ - data: { - id: newId, - name, - htmlUrl, - apiUrl, - organization, - customPort, - isSystemWide, - type: 'github', - teams: { connect: { id: teamId } } - } - }); - return { - id: newId - } - } - throw { status: 500, message: 'Wrong request.' } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function saveGitLabSource(request: FastifyRequest) { - try { - const { id } = request.params - const { teamId } = request.user - let { type, name, htmlUrl, apiUrl, oauthId, appId, appSecret, groupName, customPort, customUser } = - request.body - - if (oauthId) oauthId = Number(oauthId); - if (customPort) customPort = Number(customPort) - const encryptedAppSecret = encrypt(appSecret); - - if (id === 'new') { - const newId = cuid() - await prisma.gitSource.create({ data: { id: newId, type, apiUrl, htmlUrl, name, customPort, customUser, teams: { connect: { id: teamId } } } }); - await prisma.gitlabApp.create({ - data: { - teams: { connect: { id: teamId } }, - appId, - oauthId, - groupName, - appSecret: encryptedAppSecret, - gitSource: { connect: { id: newId } } - } - }); - return { - status: 201, - id: newId - } - } else { - await prisma.gitSource.update({ where: { id }, data: { type, apiUrl, htmlUrl, name, customPort, customUser } }); - await prisma.gitlabApp.update({ - where: { id }, - data: { - appId, - oauthId, - groupName, - appSecret: encryptedAppSecret, - } - }); - } - return { status: 201 }; - - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} - -export async function checkGitLabOAuthID(request: FastifyRequest) { - try { - const { oauthId } = request.body - const found = await prisma.gitlabApp.findFirst({ where: { oauthId: Number(oauthId) } }); - if (found) { - throw { status: 500, message: 'OAuthID already configured in Coolify.' } - } - return {} - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/sources/index.ts b/apps/api/src/routes/api/v1/sources/index.ts deleted file mode 100644 index 3d904162f..000000000 --- a/apps/api/src/routes/api/v1/sources/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { checkGitLabOAuthID, deleteSource, getSource, listSources, saveGitHubSource, saveGitLabSource, saveSource } from './handlers'; - -import type { OnlyId } from '../../../../types'; -import type { CheckGitLabOAuthId, SaveGitHubSource, SaveGitLabSource } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.addHook('onRequest', async (request) => { - return await request.jwtVerify() - }) - fastify.get('/', async (request) => await listSources(request)); - - fastify.get('/:id', async (request) => await getSource(request)); - fastify.post('/:id', async (request, reply) => await saveSource(request, reply)); - fastify.delete('/:id', async (request) => await deleteSource(request)); - - fastify.post('/:id/check', async (request) => await checkGitLabOAuthID(request)); - fastify.post('/:id/github', async (request) => await saveGitHubSource(request)); - fastify.post('/:id/gitlab', async (request) => await saveGitLabSource(request)); -}; - -export default root; diff --git a/apps/api/src/routes/api/v1/sources/types.ts b/apps/api/src/routes/api/v1/sources/types.ts deleted file mode 100644 index bbccd2e7b..000000000 --- a/apps/api/src/routes/api/v1/sources/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { OnlyId } from "../../../../types"; - -export interface SaveGitHubSource extends OnlyId { - Body: { - name: string, - htmlUrl: string, - apiUrl: string, - organization: string, - customPort: number, - isSystemWide: boolean - } -} -export interface SaveGitLabSource extends OnlyId { - Body: { - type: string, - name: string, - htmlUrl: string, - apiUrl: string, - oauthId: number, - appId: string, - appSecret: string, - groupName: string, - customPort: number, - customUser: string, - } -} -export interface CheckGitLabOAuthId extends OnlyId { - Body: { - oauthId: number, - } -} \ No newline at end of file diff --git a/apps/api/src/routes/api/v1/types.ts b/apps/api/src/routes/api/v1/types.ts deleted file mode 100644 index 945355df7..000000000 --- a/apps/api/src/routes/api/v1/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface GetCurrentUser { - Querystring: { teamId: string } -} \ No newline at end of file diff --git a/apps/api/src/routes/webhooks/github/handlers.ts b/apps/api/src/routes/webhooks/github/handlers.ts deleted file mode 100644 index 5e0f2bbba..000000000 --- a/apps/api/src/routes/webhooks/github/handlers.ts +++ /dev/null @@ -1,265 +0,0 @@ -import cuid from "cuid"; -import crypto from "crypto"; -import { encrypt, errorHandler, getDomain, getUIUrl, isDev, prisma } from "../../../lib/common"; -import { checkContainer, removeContainer } from "../../../lib/docker"; -import { createdBranchDatabase, getApplicationFromDBWebhook, removeBranchDatabase } from "../../api/v1/applications/handlers"; - -import type { FastifyReply, FastifyRequest } from "fastify"; -import type { GitHubEvents, InstallGithub } from "./types"; - -export async function installGithub(request: FastifyRequest, reply: FastifyReply): Promise { - try { - const { gitSourceId, installation_id } = request.query; - const source = await prisma.gitSource.findUnique({ - where: { id: gitSourceId }, - include: { githubApp: true } - }); - await prisma.githubApp.update({ - where: { id: source.githubAppId }, - data: { installationId: Number(installation_id) } - }); - if (isDev) { - return reply.redirect(`${getUIUrl()}/sources/${gitSourceId}`) - } else { - return reply.redirect(`/sources/${gitSourceId}`) - } - - } catch ({ status, message }) { - return errorHandler({ status, message }) - } - -} -export async function configureGitHubApp(request, reply) { - try { - const { default: got } = await import('got') - const { code, state } = request.query; - const { apiUrl } = await prisma.gitSource.findFirst({ - where: { id: state }, - include: { githubApp: true, gitlabApp: true } - }); - - const data: any = await got.post(`${apiUrl}/app-manifests/${code}/conversions`).json() - const { id, client_id, slug, client_secret, pem, webhook_secret } = data - - const encryptedClientSecret = encrypt(client_secret); - const encryptedWebhookSecret = encrypt(webhook_secret); - const encryptedPem = encrypt(pem); - await prisma.githubApp.create({ - data: { - appId: id, - name: slug, - clientId: client_id, - clientSecret: encryptedClientSecret, - webhookSecret: encryptedWebhookSecret, - privateKey: encryptedPem, - gitSource: { connect: { id: state } } - } - }); - if (isDev) { - return reply.redirect(`${getUIUrl()}/sources/${state}`) - } else { - return reply.redirect(`/sources/${state}`) - } - } catch ({ status, message }) { - return errorHandler({ status, message }) - } -} -export async function gitHubEvents(request: FastifyRequest): Promise { - try { - const allowedGithubEvents = ['push', 'pull_request', 'ping', 'installation']; - const allowedActions = ['opened', 'reopened', 'synchronize', 'closed']; - const githubEvent = request.headers['x-github-event']?.toString().toLowerCase(); - const githubSignature = request.headers['x-hub-signature-256']?.toString().toLowerCase(); - if (!allowedGithubEvents.includes(githubEvent)) { - throw { status: 500, message: 'Event not allowed.', type: 'webhook' } - } - if (githubEvent === 'ping') { - return { pong: 'cool' } - } - if (githubEvent === 'installation') { - return { status: 'cool' } - } - let projectId, branch; - const body = request.body - if (githubEvent === 'push') { - projectId = body.repository.id; - branch = body.ref.includes('/') ? body.ref.split('/')[2] : body.ref; - } else if (githubEvent === 'pull_request') { - projectId = body.pull_request.base.repo.id; - branch = body.pull_request.base.ref - } - if (!projectId || !branch) { - throw { status: 500, message: 'Cannot parse projectId or branch from the webhook?!', type: 'webhook' } - } - const applicationsFound = await getApplicationFromDBWebhook(projectId, branch); - const settings = await prisma.setting.findUnique({ where: { id: '0' } }); - if (applicationsFound && applicationsFound.length > 0) { - for (const application of applicationsFound) { - const buildId = cuid(); - const webhookSecret = application.gitSource.githubApp.webhookSecret || null; - //@ts-ignore - const hmac = crypto.createHmac('sha256', webhookSecret); - const digest = Buffer.from( - 'sha256=' + hmac.update(JSON.stringify(body)).digest('hex'), - 'utf8' - ); - if (!isDev) { - const checksum = Buffer.from(githubSignature, 'utf8'); - //@ts-ignore - if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) { - throw { status: 500, message: 'SHA256 checksum failed. Are you doing something fishy?', type: 'webhook' } - }; - } - - if (githubEvent === 'push') { - if (!application.configHash) { - const configHash = crypto - //@ts-ignore - .createHash('sha256') - .update( - JSON.stringify({ - buildPack: application.buildPack, - port: application.port, - exposePort: application.exposePort, - installCommand: application.installCommand, - buildCommand: application.buildCommand, - startCommand: application.startCommand - }) - ) - .digest('hex'); - await prisma.application.update({ - where: { id: application.id }, - data: { configHash } - }); - } - - await prisma.application.update({ - where: { id: application.id }, - data: { updatedAt: new Date() } - }); - await prisma.build.create({ - data: { - id: buildId, - applicationId: application.id, - destinationDockerId: application.destinationDocker.id, - gitSourceId: application.gitSource.id, - githubAppId: application.gitSource.githubApp?.id, - gitlabAppId: application.gitSource.gitlabApp?.id, - status: 'queued', - type: 'webhook_commit' - } - }); - console.log(`Webhook for ${application.name} queued.`) - - } else if (githubEvent === 'pull_request') { - const pullmergeRequestId = body.number.toString(); - const pullmergeRequestAction = body.action; - const sourceBranch = body.pull_request.head.ref - const sourceRepository = body.pull_request.head.repo.full_name - if (!allowedActions.includes(pullmergeRequestAction)) { - throw { status: 500, message: 'Action not allowed.', type: 'webhook' } - } - - if (application.settings.previews) { - if (application.destinationDockerId) { - const { found: isRunning } = await checkContainer( - { - dockerId: application.destinationDocker.id, - container: application.id - } - ); - if (!isRunning) { - throw { status: 500, message: 'Application not running.', type: 'webhook' } - } - } - if ( - pullmergeRequestAction === 'opened' || - pullmergeRequestAction === 'reopened' || - pullmergeRequestAction === 'synchronize' - ) { - - await prisma.application.update({ - where: { id: application.id }, - data: { updatedAt: new Date() } - }); - let previewApplicationId = undefined - if (pullmergeRequestId) { - const foundPreviewApplications = await prisma.previewApplication.findMany({ where: { applicationId: application.id, pullmergeRequestId } }) - if (foundPreviewApplications.length > 0) { - previewApplicationId = foundPreviewApplications[0].id - } else { - const protocol = application.fqdn.includes('https://') ? 'https://' : 'http://' - const previewApplication = await prisma.previewApplication.create({ - data: { - pullmergeRequestId, - sourceBranch, - customDomain: `${protocol}${pullmergeRequestId}${settings.previewSeparator}${getDomain(application.fqdn)}`, - application: { connect: { id: application.id } } - } - }) - previewApplicationId = previewApplication.id - } - } - // if (application.connectedDatabase && pullmergeRequestAction === 'opened' || pullmergeRequestAction === 'reopened') { - // // Coolify hosted database - // if (application.connectedDatabase.databaseId) { - // const databaseId = application.connectedDatabase.databaseId; - // const database = await prisma.database.findUnique({ where: { id: databaseId } }); - // if (database) { - // await createdBranchDatabase(database, application.connectedDatabase.hostedDatabaseDBName, pullmergeRequestId); - // } - // } - // } - await prisma.build.create({ - data: { - id: buildId, - sourceRepository, - pullmergeRequestId, - previewApplicationId, - sourceBranch, - applicationId: application.id, - destinationDockerId: application.destinationDocker.id, - gitSourceId: application.gitSource.id, - githubAppId: application.gitSource.githubApp?.id, - gitlabAppId: application.gitSource.gitlabApp?.id, - status: 'queued', - type: 'webhook_pr' - } - }); - - return { - message: 'Queued. Thank you!' - }; - } else if (pullmergeRequestAction === 'closed') { - if (application.destinationDockerId) { - const id = `${application.id}-${pullmergeRequestId}`; - try { - await removeContainer({ id, dockerId: application.destinationDocker.id }); - } catch (error) { } - } - const foundPreviewApplications = await prisma.previewApplication.findMany({ where: { applicationId: application.id, pullmergeRequestId } }) - if (foundPreviewApplications.length > 0) { - for (const preview of foundPreviewApplications) { - await prisma.previewApplication.delete({ where: { id: preview.id } }) - } - } - return { - message: 'PR closed. Thank you!' - }; - // if (application?.connectedDatabase?.databaseId) { - // const databaseId = application.connectedDatabase.databaseId; - // const database = await prisma.database.findUnique({ where: { id: databaseId } }); - // if (database) { - // await removeBranchDatabase(database, pullmergeRequestId); - // } - // } - } - } - } - } - } - } catch ({ status, message, type }) { - return errorHandler({ status, message, type }) - } - -} \ No newline at end of file diff --git a/apps/api/src/routes/webhooks/github/index.ts b/apps/api/src/routes/webhooks/github/index.ts deleted file mode 100644 index c4a3afbb9..000000000 --- a/apps/api/src/routes/webhooks/github/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { configureGitHubApp, gitHubEvents, installGithub } from './handlers'; - -import type { GitHubEvents, InstallGithub } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.get('/', async (request, reply) => configureGitHubApp(request, reply)); - fastify.get('/install', async (request, reply) => installGithub(request, reply)); - fastify.post('/events', async (request, reply) => gitHubEvents(request)); -}; - -export default root; diff --git a/apps/api/src/routes/webhooks/github/types.ts b/apps/api/src/routes/webhooks/github/types.ts deleted file mode 100644 index 359125608..000000000 --- a/apps/api/src/routes/webhooks/github/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -export interface InstallGithub { - Querystring: { - gitSourceId: string, - installation_id: string - } -} -export interface GitHubEvents { - Body: { - number: string, - action: string, - repository: { - id: string, - }, - ref: string, - pull_request: { - base: { - ref: string, - repo: { - id: string, - } - }, - head: { - ref: string, - repo: { - id: string, - full_name: string, - } - } - } - } -} \ No newline at end of file diff --git a/apps/api/src/routes/webhooks/gitlab/handlers.ts b/apps/api/src/routes/webhooks/gitlab/handlers.ts deleted file mode 100644 index a3ac97705..000000000 --- a/apps/api/src/routes/webhooks/gitlab/handlers.ts +++ /dev/null @@ -1,195 +0,0 @@ -import cuid from "cuid"; -import crypto from "crypto"; -import type { FastifyReply, FastifyRequest } from "fastify"; -import { errorHandler, getAPIUrl, getDomain, getUIUrl, isDev, listSettings, prisma } from "../../../lib/common"; -import { checkContainer, removeContainer } from "../../../lib/docker"; -import { getApplicationFromDB, getApplicationFromDBWebhook } from "../../api/v1/applications/handlers"; - -import type { ConfigureGitLabApp, GitLabEvents } from "./types"; - -export async function configureGitLabApp(request: FastifyRequest, reply: FastifyReply) { - try { - const { default: got } = await import('got') - const { code, state } = request.query; - const { fqdn } = await listSettings(); - const { gitSource: { gitlabApp: { appId, appSecret }, htmlUrl } }: any = await getApplicationFromDB(state, undefined); - - let domain = `http://${request.hostname}`; - if (fqdn) domain = fqdn; - if (isDev) { - domain = getAPIUrl(); - } - - const { access_token } = await got.post(`${htmlUrl}/oauth/token`, { - searchParams: { - client_id: appId, - client_secret: appSecret, - code, - state, - grant_type: 'authorization_code', - redirect_uri: `${domain}/webhooks/gitlab` - } - }).json() - if (isDev) { - return reply.redirect(`${getUIUrl()}/webhooks/success?token=${access_token}`) - } - return reply.redirect(`/webhooks/success?token=${access_token}`) - } catch ({ status, message, ...other }) { - return errorHandler({ status, message }) - } -} -export async function gitLabEvents(request: FastifyRequest) { - const { object_kind: objectKind, ref, project_id } = request.body - try { - const allowedActions = ['opened', 'reopen', 'close', 'open', 'update']; - const webhookToken = request.headers['x-gitlab-token']; - if (!webhookToken && !isDev) { - throw { status: 500, message: 'Invalid webhookToken.', type: 'webhook' } - } - const settings = await prisma.setting.findUnique({ where: { id: '0' } }); - if (objectKind === 'push') { - const projectId = Number(project_id); - const branch = ref.split('/')[2]; - const applicationsFound = await getApplicationFromDBWebhook(projectId, branch); - if (applicationsFound && applicationsFound.length > 0) { - for (const application of applicationsFound) { - const buildId = cuid(); - if (!application.configHash) { - const configHash = crypto - .createHash('sha256') - .update( - JSON.stringify({ - buildPack: application.buildPack, - port: application.port, - exposePort: application.exposePort, - installCommand: application.installCommand, - buildCommand: application.buildCommand, - startCommand: application.startCommand - }) - ) - .digest('hex'); - await prisma.application.update({ - where: { id: application.id }, - data: { configHash } - }); - } - await prisma.application.update({ - where: { id: application.id }, - data: { updatedAt: new Date() } - }); - await prisma.build.create({ - data: { - id: buildId, - applicationId: application.id, - destinationDockerId: application.destinationDocker.id, - gitSourceId: application.gitSource.id, - githubAppId: application.gitSource.githubApp?.id, - gitlabAppId: application.gitSource.gitlabApp?.id, - status: 'queued', - type: 'webhook_commit' - } - }); - } - } - } else if (objectKind === 'merge_request') { - const { object_attributes: { work_in_progress: isDraft, action, source_branch: sourceBranch, target_branch: targetBranch, source: { path_with_namespace: sourceRepository } }, project: { id } } = request.body - const pullmergeRequestId = request.body.object_attributes.iid.toString(); - const projectId = Number(id); - if (!allowedActions.includes(action)) { - throw { status: 500, message: 'Action not allowed.', type: 'webhook' } - } - if (isDraft) { - throw { status: 500, message: 'Draft MR, do nothing.', type: 'webhook' } - } - const applicationsFound = await getApplicationFromDBWebhook(projectId, targetBranch); - if (applicationsFound && applicationsFound.length > 0) { - for (const application of applicationsFound) { - const buildId = cuid(); - if (application.settings.previews) { - if (application.destinationDockerId) { - const { found: isRunning } = await checkContainer( - { - dockerId: application.destinationDocker.id, - container: application.id - } - ); - if (!isRunning) { - throw { status: 500, message: 'Application not running.', type: 'webhook' } - } - } - if (!isDev && application.gitSource.gitlabApp.webhookToken !== webhookToken) { - throw { status: 500, message: 'Invalid webhookToken. Are you doing something nasty?!', type: 'webhook' } - } - if ( - action === 'opened' || - action === 'reopen' || - action === 'open' || - action === 'update' - ) { - await prisma.application.update({ - where: { id: application.id }, - data: { updatedAt: new Date() } - }); - let previewApplicationId = undefined - if (pullmergeRequestId) { - const foundPreviewApplications = await prisma.previewApplication.findMany({ where: { applicationId: application.id, pullmergeRequestId } }) - if (foundPreviewApplications.length > 0) { - previewApplicationId = foundPreviewApplications[0].id - } else { - const protocol = application.fqdn.includes('https://') ? 'https://' : 'http://' - const previewApplication = await prisma.previewApplication.create({ - data: { - pullmergeRequestId, - sourceBranch, - customDomain: `${protocol}${pullmergeRequestId}${settings.previewSeparator}${getDomain(application.fqdn)}`, - application: { connect: { id: application.id } } - } - }) - previewApplicationId = previewApplication.id - } - } - await prisma.build.create({ - data: { - id: buildId, - pullmergeRequestId, - previewApplicationId, - sourceRepository, - sourceBranch, - applicationId: application.id, - destinationDockerId: application.destinationDocker.id, - gitSourceId: application.gitSource.id, - githubAppId: application.gitSource.githubApp?.id, - gitlabAppId: application.gitSource.gitlabApp?.id, - status: 'queued', - type: 'webhook_mr' - } - }); - return { - message: 'Queued. Thank you!' - }; - } else if (action === 'close') { - if (application.destinationDockerId) { - const id = `${application.id}-${pullmergeRequestId}`; - try { - await removeContainer({ id, dockerId: application.destinationDocker.id }); - } catch (error) { } - } - const foundPreviewApplications = await prisma.previewApplication.findMany({ where: { applicationId: application.id, pullmergeRequestId } }) - if (foundPreviewApplications.length > 0) { - for (const preview of foundPreviewApplications) { - await prisma.previewApplication.delete({ where: { id: preview.id } }) - } - } - return { - message: 'MR closed. Thank you!' - }; - - } - } - } - } - } - } catch ({ status, message, type }) { - return errorHandler({ status, message, type }) - } -} \ No newline at end of file diff --git a/apps/api/src/routes/webhooks/gitlab/index.ts b/apps/api/src/routes/webhooks/gitlab/index.ts deleted file mode 100644 index 77f3067e4..000000000 --- a/apps/api/src/routes/webhooks/gitlab/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { configureGitLabApp, gitLabEvents } from './handlers'; - -import type { ConfigureGitLabApp, GitLabEvents } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.get('/', async (request, reply) => configureGitLabApp(request, reply)); - fastify.post('/events', async (request) => gitLabEvents(request)); -}; - -export default root; diff --git a/apps/api/src/routes/webhooks/gitlab/types.ts b/apps/api/src/routes/webhooks/gitlab/types.ts deleted file mode 100644 index f2f1e1e3a..000000000 --- a/apps/api/src/routes/webhooks/gitlab/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface ConfigureGitLabApp { - Querystring: { - code: string, - state: string - } -} -export interface GitLabEvents { - Body: { - object_attributes: { - work_in_progress: string - source: { - path_with_namespace: string - } - isDraft: string - action: string - source_branch: string - target_branch: string - iid: string - }, - project: { - id: string - }, - object_kind: string, - ref: string, - project_id: string - } -} \ No newline at end of file diff --git a/apps/api/src/routes/webhooks/traefik/handlers.ts b/apps/api/src/routes/webhooks/traefik/handlers.ts deleted file mode 100644 index 2667c54ca..000000000 --- a/apps/api/src/routes/webhooks/traefik/handlers.ts +++ /dev/null @@ -1,768 +0,0 @@ -import { FastifyRequest } from 'fastify'; -import { errorHandler, getDomain, isDev, prisma, executeCommand } from '../../../lib/common'; -import { getTemplates } from '../../../lib/services'; -import { OnlyId } from '../../../types'; -import { parseAndFindServiceTemplates } from '../../api/v1/services/handlers'; - -function generateServices(serviceId, containerId, port, isHttp2 = false, isHttps = false) { - if (isHttp2) { - return { - [serviceId]: { - loadbalancer: { - servers: [ - { - url: `${isHttps ? 'https' : 'http'}://${containerId}:${port}` - } - ] - } - }, - [`${serviceId}-http2`]: { - loadbalancer: { - servers: [ - { - url: `h2c://${containerId}:${port}` - } - ] - } - } - }; - } - return { - [serviceId]: { - loadbalancer: { - servers: [ - { - url: `http://${containerId}:${port}` - } - ] - } - } - }; -} -function generateRouters( - serviceId, - domain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - isDualCerts, - isCustomSSL, - isHttp2 = false -) { - let rule = `Host(\`${nakedDomain}\`)${pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : ''}`; - let ruleWWW = `Host(\`www.${nakedDomain}\`)${ - pathPrefix ? ` && PathPrefix(\`${pathPrefix}\`)` : '' - }`; - let http: any = { - entrypoints: ['web'], - rule, - service: `${serviceId}`, - priority: 2, - middlewares: [] - }; - let https: any = { - entrypoints: ['websecure'], - rule, - service: `${serviceId}`, - priority: 2, - tls: { - certresolver: 'letsencrypt' - }, - middlewares: [] - }; - let httpWWW: any = { - entrypoints: ['web'], - rule: ruleWWW, - service: `${serviceId}`, - priority: 2, - middlewares: [] - }; - let httpsWWW: any = { - entrypoints: ['websecure'], - rule: ruleWWW, - service: `${serviceId}`, - priority: 2, - tls: { - certresolver: 'letsencrypt' - }, - middlewares: [] - }; - // 2. http + non-www only - if (!isHttps && !isWWW) { - https.middlewares.push('redirect-to-http'); - httpsWWW.middlewares.push('redirect-to-http'); - - httpWWW.middlewares.push('redirect-to-non-www'); - httpsWWW.middlewares.push('redirect-to-non-www'); - delete https.tls; - delete httpsWWW.tls; - } - - // 3. http + www only - if (!isHttps && isWWW) { - https.middlewares.push('redirect-to-http'); - httpsWWW.middlewares.push('redirect-to-http'); - - http.middlewares.push('redirect-to-www'); - https.middlewares.push('redirect-to-www'); - delete https.tls; - delete httpsWWW.tls; - } - // 5. https + non-www only - if (isHttps && !isWWW) { - http.middlewares.push('redirect-to-https'); - httpWWW.middlewares.push('redirect-to-https'); - if (!isDualCerts) { - httpWWW.middlewares.push('redirect-to-non-www'); - httpsWWW.middlewares.push('redirect-to-non-www'); - } - if (isCustomSSL) { - if (isDualCerts) { - https.tls = true; - httpsWWW.tls = true; - } else { - https.tls = true; - delete httpsWWW.tls.certresolver; - httpsWWW.tls.domains = { - main: domain - }; - } - } else { - if (!isDualCerts) { - delete httpsWWW.tls.certresolver; - httpsWWW.tls.domains = { - main: domain - }; - } - } - } - // 6. https + www only - if (isHttps && isWWW) { - http.middlewares.push('redirect-to-https'); - httpWWW.middlewares.push('redirect-to-https'); - if (!isDualCerts) { - http.middlewares.push('redirect-to-www'); - https.middlewares.push('redirect-to-www'); - } - if (isCustomSSL) { - if (isDualCerts) { - https.tls = true; - httpsWWW.tls = true; - } else { - httpsWWW.tls = true; - delete https.tls.certresolver; - https.tls.domains = { - main: domain - }; - } - } else { - if (!isDualCerts) { - delete https.tls.certresolver; - https.tls.domains = { - main: domain - }; - } - } - } - if (isHttp2) { - let http2 = { - ...http, - service: `${serviceId}-http2`, - rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)` - }; - let http2WWW = { - ...httpWWW, - service: `${serviceId}-http2`, - rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)` - }; - let https2 = { - ...https, - service: `${serviceId}-http2`, - rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)` - }; - - let https2WWW = { - ...httpsWWW, - service: `${serviceId}-http2`, - rule: `${rule} && HeadersRegexp(\`Content-Type\`, \`application/grpc*\`)` - }; - return { - [`${serviceId}-${pathPrefix}`]: { ...http }, - [`${serviceId}-${pathPrefix}-http2`]: { ...http2 }, - [`${serviceId}-${pathPrefix}-secure`]: { ...https }, - [`${serviceId}-${pathPrefix}-secure-http2`]: { ...https2 }, - [`${serviceId}-${pathPrefix}-www`]: { ...httpWWW }, - [`${serviceId}-${pathPrefix}-www-http2`]: { ...http2WWW }, - [`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW }, - [`${serviceId}-${pathPrefix}-secure-www-http2`]: { ...https2WWW } - }; - } - return { - [`${serviceId}-${pathPrefix}`]: { ...http }, - [`${serviceId}-${pathPrefix}-secure`]: { ...https }, - [`${serviceId}-${pathPrefix}-www`]: { ...httpWWW }, - [`${serviceId}-${pathPrefix}-secure-www`]: { ...httpsWWW } - }; -} -export async function proxyConfiguration(request: FastifyRequest, remote: boolean = false) { - const traefik = { - tls: { - certificates: [] - }, - http: { - routers: {}, - services: {}, - middlewares: { - 'redirect-to-https': { - redirectscheme: { - scheme: 'https' - } - }, - 'redirect-to-http': { - redirectscheme: { - scheme: 'http' - } - }, - 'redirect-to-non-www': { - redirectregex: { - regex: '^https?://www\\.(.+)', - replacement: 'http://${1}' - } - }, - 'redirect-to-www': { - redirectregex: { - regex: '^https?://(?:www\\.)?(.+)', - replacement: 'http://www.${1}' - } - } - } - } - }; - try { - const { id = null } = request.params; - const coolifySettings = await prisma.setting.findFirst(); - if (coolifySettings.isTraefikUsed && coolifySettings.proxyDefaultRedirect) { - traefik.http.routers['catchall-http'] = { - entrypoints: ['web'], - rule: 'HostRegexp(`{catchall:.*}`)', - service: 'noop', - priority: 1, - middlewares: ['redirect-regexp'] - }; - traefik.http.routers['catchall-https'] = { - entrypoints: ['websecure'], - rule: 'HostRegexp(`{catchall:.*}`)', - service: 'noop', - priority: 1, - middlewares: ['redirect-regexp'] - }; - traefik.http.middlewares['redirect-regexp'] = { - redirectregex: { - regex: '(.*)', - replacement: coolifySettings.proxyDefaultRedirect, - permanent: false - } - }; - traefik.http.services['noop'] = { - loadBalancer: { - servers: [ - { - url: '' - } - ] - } - }; - } - const sslpath = '/etc/traefik/acme/custom'; - - let certificates = await prisma.certificate.findMany({ - where: { - team: { - applications: { some: { settings: { isCustomSSL: true } } }, - destinationDocker: { some: { remoteEngine: false, isCoolifyProxyUsed: true } } - } - } - }); - - if (remote) { - certificates = await prisma.certificate.findMany({ - where: { - team: { - applications: { some: { settings: { isCustomSSL: true } } }, - destinationDocker: { - some: { id, remoteEngine: true, isCoolifyProxyUsed: true, remoteVerified: true } - } - } - } - }); - } - - let parsedCertificates = []; - for (const certificate of certificates) { - parsedCertificates.push({ - certFile: `${sslpath}/${certificate.id}-cert.pem`, - keyFile: `${sslpath}/${certificate.id}-key.pem` - }); - } - if (parsedCertificates.length > 0) { - traefik.tls.certificates = parsedCertificates; - } - - let applications = []; - let services = []; - if (id) { - applications = await prisma.application.findMany({ - where: { destinationDocker: { id } }, - include: { destinationDocker: true, settings: true } - }); - services = await prisma.service.findMany({ - where: { destinationDocker: { id } }, - include: { - destinationDocker: true, - persistentStorage: true, - serviceSecret: true, - serviceSetting: true - }, - orderBy: { createdAt: 'desc' } - }); - } else { - applications = await prisma.application.findMany({ - where: { destinationDocker: { remoteEngine: false } }, - include: { destinationDocker: true, settings: true } - }); - services = await prisma.service.findMany({ - where: { destinationDocker: { remoteEngine: false } }, - include: { - destinationDocker: true, - persistentStorage: true, - serviceSecret: true, - serviceSetting: true - }, - orderBy: { createdAt: 'desc' } - }); - } - - if (applications.length > 0) { - const dockerIds = new Set(); - const runningContainers = {}; - applications.forEach((app) => dockerIds.add(app.destinationDocker.id)); - for (const dockerId of dockerIds) { - const { stdout: container } = await executeCommand({ - dockerId, - command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'` - }); - if (container) { - const containersArray = container.trim().split('\n'); - if (containersArray.length > 0) { - runningContainers[dockerId] = containersArray; - } - } - } - for (const application of applications) { - try { - const { - fqdn, - id, - port, - buildPack, - dockerComposeConfiguration, - destinationDocker, - destinationDockerId, - settings - } = application; - if (!destinationDockerId) { - continue; - } - if ( - !runningContainers[destinationDockerId] || - runningContainers[destinationDockerId].length === 0 || - runningContainers[destinationDockerId].filter((container) => container.startsWith(id)) - .length === 0 - ) { - continue; - } - if (buildPack === 'compose') { - const services = Object.entries(JSON.parse(dockerComposeConfiguration)); - if (services.length > 0) { - for (const service of services) { - const [key, value] = service; - if (key && value) { - if (!value.fqdn || !value.port) { - continue; - } - const { fqdn, port } = value; - const containerId = `${id}-${key}`; - const domain = getDomain(fqdn); - const nakedDomain = domain.replace(/^www\./, ''); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const pathPrefix = '/'; - const isCustomSSL = false; - const dualCerts = false; - const serviceId = `${id}-${port || 'default'}`; - - traefik.http.routers = { - ...traefik.http.routers, - ...generateRouters( - serviceId, - domain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - dualCerts, - isCustomSSL - ) - }; - traefik.http.services = { - ...traefik.http.services, - ...generateServices(serviceId, containerId, port) - }; - } - } - } - continue; - } - const { previews, dualCerts, isCustomSSL, isHttp2 } = settings; - const { network, id: dockerId } = destinationDocker; - if (!fqdn) { - continue; - } - const domain = getDomain(fqdn); - const nakedDomain = domain.replace(/^www\./, ''); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const pathPrefix = '/'; - const serviceId = `${id}-${port || 'default'}`; - traefik.http.routers = { - ...traefik.http.routers, - ...generateRouters( - serviceId, - domain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - dualCerts, - isCustomSSL, - isHttp2 - ) - }; - traefik.http.services = { - ...traefik.http.services, - ...generateServices(serviceId, id, port, isHttp2, isHttps) - }; - if (previews) { - const { stdout } = await executeCommand({ - dockerId, - command: `docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"` - }); - if (stdout) { - const containers = stdout - .trim() - .split('\n') - .filter((a) => a) - .map((c) => c.replace(/"/g, '')); - if (containers.length > 0) { - for (const container of containers) { - const previewDomain = `${container.split('-')[1]}${ - coolifySettings.previewSeparator - }${domain}`; - const nakedDomain = previewDomain.replace(/^www\./, ''); - const pathPrefix = '/'; - const serviceId = `${container}-${port || 'default'}`; - traefik.http.routers = { - ...traefik.http.routers, - ...generateRouters( - serviceId, - previewDomain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - dualCerts, - isCustomSSL - ) - }; - traefik.http.services = { - ...traefik.http.services, - ...generateServices(serviceId, container, port, isHttp2) - }; - } - } - } - } - } catch (error) { - console.log(error); - } - } - } - if (services.length > 0) { - const dockerIds = new Set(); - const runningContainers = {}; - services.forEach((app) => dockerIds.add(app.destinationDocker.id)); - for (const dockerId of dockerIds) { - const { stdout: container } = await executeCommand({ - dockerId, - command: `docker container ls --filter 'label=coolify.managed=true' --format '{{ .Names}}'` - }); - if (container) { - const containersArray = container.trim().split('\n'); - if (containersArray.length > 0) { - runningContainers[dockerId] = containersArray; - } - } - } - for (const service of services) { - try { - let { fqdn, id, type, destinationDockerId, dualCerts, serviceSetting } = service; - if (!fqdn) { - continue; - } - if (!destinationDockerId) { - continue; - } - if ( - !runningContainers[destinationDockerId] || - runningContainers[destinationDockerId].length === 0 || - !runningContainers[destinationDockerId].includes(id) - ) { - continue; - } - const templates = await getTemplates(); - let found = templates.find((a) => a.type === type); - if (!found) { - continue; - } - found = JSON.parse(JSON.stringify(found).replaceAll('$$id', id)); - for (const oneService of Object.keys(found.services)) { - const isDomainAndProxyConfiguration = - found?.services[oneService]?.proxy?.filter((p) => p.port) ?? []; - if (isDomainAndProxyConfiguration.length > 0) { - const template: any = await parseAndFindServiceTemplates(service, null, true); - const { proxy } = template.services[oneService] || found.services[oneService]; - for (let configuration of proxy) { - if (configuration.domain) { - const setting = serviceSetting.find( - (a) => a.variableName === configuration.domain - ); - if (setting) { - configuration.domain = configuration.domain.replace( - configuration.domain, - setting.value - ); - } - } - const foundPortVariable = serviceSetting.find( - (a) => a.name.toLowerCase() === 'port' - ); - if (foundPortVariable) { - configuration.port = foundPortVariable.value; - } - let port, pathPrefix, customDomain; - if (configuration) { - port = configuration?.port; - pathPrefix = configuration?.pathPrefix || '/'; - customDomain = configuration?.domain; - } - if (customDomain) { - fqdn = customDomain; - } else { - fqdn = service.fqdn; - } - const domain = getDomain(fqdn); - const nakedDomain = domain.replace(/^www\./, ''); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const isCustomSSL = false; - const serviceId = `${oneService}-${port || 'default'}`; - traefik.http.routers = { - ...traefik.http.routers, - ...generateRouters( - serviceId, - domain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - dualCerts, - isCustomSSL - ) - }; - traefik.http.services = { - ...traefik.http.services, - ...generateServices(serviceId, oneService, port) - }; - } - } else { - if (found.services[oneService].ports && found.services[oneService].ports.length > 0) { - for (let [index, port] of found.services[oneService].ports.entries()) { - if (port == 22) continue; - if (index === 0) { - const foundPortVariable = serviceSetting.find( - (a) => a.name.toLowerCase() === 'port' - ); - if (foundPortVariable) { - port = foundPortVariable.value; - } - } - const domain = getDomain(fqdn); - const nakedDomain = domain.replace(/^www\./, ''); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const pathPrefix = '/'; - const isCustomSSL = false; - const serviceId = `${oneService}-${port || 'default'}`; - traefik.http.routers = { - ...traefik.http.routers, - ...generateRouters( - serviceId, - domain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - dualCerts, - isCustomSSL - ) - }; - traefik.http.services = { - ...traefik.http.services, - ...generateServices(serviceId, id, port) - }; - } - } - } - } - } catch (error) { - console.log(error); - } - } - } - if (!remote) { - const { fqdn, dualCerts } = await prisma.setting.findFirst(); - if (!fqdn) { - return; - } - const domain = getDomain(fqdn); - const nakedDomain = domain.replace(/^www\./, ''); - const isHttps = fqdn.startsWith('https://'); - const isWWW = fqdn.includes('www.'); - const id = isDev ? 'host.docker.internal' : 'coolify'; - const container = isDev ? 'host.docker.internal' : 'coolify'; - const port = 3000; - const pathPrefix = '/'; - const isCustomSSL = false; - const serviceId = `${id}-${port || 'default'}`; - traefik.http.routers = { - ...traefik.http.routers, - ...generateRouters( - serviceId, - domain, - nakedDomain, - pathPrefix, - isHttps, - isWWW, - dualCerts, - isCustomSSL - ) - }; - traefik.http.services = { - ...traefik.http.services, - ...generateServices(serviceId, container, port) - }; - } - } catch (error) { - console.log(error); - } finally { - if (Object.keys(traefik.http.routers).length === 0) { - traefik.http.routers = null; - } - if (Object.keys(traefik.http.services).length === 0) { - traefik.http.services = null; - } - return traefik; - } -} - -export async function otherProxyConfiguration(request: FastifyRequest) { - try { - const { id } = request.query; - if (id) { - const { privatePort, publicPort, type, address = id } = request.query; - let traefik = {}; - if (publicPort && type && privatePort) { - if (type === 'tcp') { - traefik = { - [type]: { - routers: { - [id]: { - entrypoints: [type], - rule: `HostSNI(\`*\`)`, - service: id - } - }, - services: { - [id]: { - loadbalancer: { - servers: [{ address: `${address}:${privatePort}` }] - } - } - } - } - }; - } else if (type === 'http') { - const service = await prisma.service.findFirst({ - where: { id } - }); - if (service && service?.fqdn) { - const domain = getDomain(service.fqdn); - const isHttps = service.fqdn.startsWith('https://'); - traefik = { - [type]: { - routers: { - [id]: { - entrypoints: [type], - rule: `Host(\`${domain}:${privatePort}\`)`, - service: id - } - }, - services: { - [id]: { - loadbalancer: { - servers: [{ url: `http://${id}:${privatePort}` }] - } - } - } - } - }; - if (isHttps) { - if (isDev) { - traefik[type].routers[id].tls = { - domains: { - main: `${domain}` - } - }; - } else { - traefik[type].routers[id].tls = { - certresolver: 'letsencrypt' - }; - } - } - } else { - throw { status: 500 }; - } - } - } else { - throw { status: 500 }; - } - return { - ...traefik - }; - } - throw { status: 500 }; - } catch ({ status, message }) { - return errorHandler({ status, message }); - } -} diff --git a/apps/api/src/routes/webhooks/traefik/index.ts b/apps/api/src/routes/webhooks/traefik/index.ts deleted file mode 100644 index ed7298ef9..000000000 --- a/apps/api/src/routes/webhooks/traefik/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { OnlyId } from '../../../types'; -import { proxyConfiguration, otherProxyConfiguration } from './handlers'; -import { OtherProxyConfiguration } from './types'; - -const root: FastifyPluginAsync = async (fastify): Promise => { - fastify.get('/main.json', async (request, reply) => proxyConfiguration(request, false)); - fastify.get('/remote/:id', async (request) => proxyConfiguration(request, true)); - fastify.get('/other.json', async (request, reply) => - otherProxyConfiguration(request) - ); -}; - -export default root; diff --git a/apps/api/src/routes/webhooks/traefik/types.ts b/apps/api/src/routes/webhooks/traefik/types.ts deleted file mode 100644 index 141066111..000000000 --- a/apps/api/src/routes/webhooks/traefik/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface OtherProxyConfiguration { - Querystring: { - id: string, - privatePort: number, - publicPort: number, - type: string, - address: string - } -} \ No newline at end of file diff --git a/apps/api/src/types.ts b/apps/api/src/types.ts deleted file mode 100644 index d4ab01953..000000000 --- a/apps/api/src/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface OnlyId { - Params: { id?: string }, -} - diff --git a/apps/api/tags.json b/apps/api/tags.json deleted file mode 100644 index a788801e2..000000000 --- a/apps/api/tags.json +++ /dev/null @@ -1,983 +0,0 @@ -[ - { - "name": "appsmith", - "image": "appsmith/appsmith-ce", - "tags": [ - "v1.9.3", - "v1.9.1", - "v1.8.15", - "v1.8.12", - "v1.8.10", - "v1.8.9", - "v1.8.7", - "v1.8.5", - "v1.8.3", - "v1.8.0", - "v1.7.13", - "v1.7.11", - "v1.7.8", - "v1.7.6", - "v1.7.4", - "v1.7.2", - "v1.7.1", - "v1.6.22", - "v1.6.20", - "v1.6.19", - "v1.6.17", - "v1.6.15", - "v1.6.13", - "v1.6.11", - "v1.6.9", - "v1.6.7", - "v1.6.5", - "v1.6.3", - "v1.6.1", - "v1.5.30" - ] - }, - { - "name": "appwrite", - "image": "appwrite/appwrite", - "tags": [ - "1.2.0", - "1.1.2", - "1.1.0", - "1.0.3", - "1.0.1", - "1.0.0", - "0.15.2", - "0.15.0", - "0.14.2", - "0.14.0", - "0.13.4", - "0.13.2", - "0.13.0", - "0.12.3", - "0.12.1", - "0.12.0", - "0.11.2", - "0.11.0", - "0.10.4", - "0.10.2", - "0.10.0", - "0.9.3", - "0.9.1", - "0.8.0", - "0.7.1", - "0.6.2", - "0.6.0", - "0.5.2", - "0.5.0", - "0.3.1" - ] - }, - { - "name": "directus-postgresql", - "image": "directus/directus", - "tags": [ - "9.22.3", - "9.22.0", - "9.21.0", - "9.20.4", - "9.20.2", - "9.20.0", - "9.19.2", - "9.18.0", - "9.17.4", - "9.17.2", - "9.17.0", - "9.16.0", - "9.15.0", - "9.14.5", - "9.14.3", - "9.14.0", - "9.13.0", - "9.12.2", - "9.12.0", - "9.11.0", - "9.10.0", - "9.9.0", - "9.8.0", - "9.7.0", - "9.6.0", - "9.5.2", - "9.5.0", - "9.4.2", - "9.4.0", - "9.3.0" - ] - }, - { - "name": "fider", - "image": "getfider/fider", - "tags": [ - "stable", - "master", - "main", - "dev", - "SHA_ee6e83cfaadadaa56ab76e089e01f5631af3506f", - "SHA_deb4f9b4f561d890d8a80e6872fea9a98a265cc6", - "SHA_d5cc307909d43447200483d76b5db74d8ed8349e", - "SHA_d1674476577a7fd3c88fc29f91c3f35f5bd6a260", - "SHA_d107cbb157abca6576110080736213efe0955cff", - "SHA_c9c55b2f5b33a76015241b97e03cfac1254b42a7", - "SHA_bcf451a3cb02d5c8a489fd30309249296057b084", - "SHA_bbfe419639514f949a042807addf0fde7d4de225", - "SHA_adc3afc4c7bcf96931a5f90cab65c282d860dbfd", - "SHA_ab5283ae95334f10b5041402dce79e333c472015", - "SHA_a3f4cb5ed0a4ee2d726705fc426636364aac17a1", - "SHA_a18224142bf51bc6463c3d22f45f62287902e9a6", - "SHA_8e5cff30d95963eaee2587488d351e0d658c8195", - "SHA_8cabe2817ce7ccaf2f0a9fdbb1b5d3411de87f81", - "SHA_7851f9da566132d87fa2a63004e78c3bc9c09c6c", - "SHA_6c0f2bed1754e9d579eb9575129a6e3dbc529c32", - "SHA_603508c8790d6a6fb1e852df1a58ead8e5b3ea6c", - "SHA_55efacf164a4749b50ee68ae8925e7dc9dfa3a0c", - "SHA_4bdd291ce61e5f5dfc063fa1b2d9be8c9ff1d4c4", - "SHA_3fba9cb6a9ceab0c78c6cff3220610f591f657cb", - "SHA_3d635b57606a9885babe91fe975b11429e0f2c38", - "SHA_3b794edbd9789a8aa38ecd3714bc536a675d3058", - "SHA_3570c454ad3252b690608f7bf8051737d8519f8a", - "SHA_263e2709fd145f3ea511e5557e170102899995b0", - "SHA_255c30ed012fc4c39ffc97efc1d3b00425b17c72", - "SHA_17f92b16ef790003338f0926fc8d791a9a61333c" - ] - }, - { - "name": "ghost-mariadb", - "image": "bitnami/ghost", - "tags": [ - "5.30.1", - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.4", - "5.26.3", - "5.26.2", - "5.26.1", - "5.26.0", - "5.25.5", - "5.25.4", - "5.25.3", - "5.25.2", - "5.25.1", - "5.25.0", - "5.24.2", - "5.24.1", - "5.24.0", - "5.23.0", - "5.22.11", - "5.22.10", - "5.22.9", - "5.22.8", - "5.22.7", - "5.22.6", - "5.22.5", - "5.22.4", - "5.22.3", - "5.22.2", - "4.48.8" - ] - }, - { - "name": "ghost-mysql", - "image": "library/ghost", - "tags": [ - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.4", - "5.26.3", - "5.26.2", - "5.26.1", - "5.25.5", - "5.25.3", - "5.25.2", - "5.25.1", - "5.25.0", - "5.24.2", - "5.24.1", - "5.23.0", - "5.22.11", - "5.22.10", - "5.22.9", - "5.22.8", - "5.22.4", - "5.22.1", - "5.20.0", - "5.19.3", - "5.19.0", - "5.18.0", - "5.17.2", - "5.17.1", - "5.17.0", - "5.16.2" - ] - }, - { - "name": "ghost-only", - "image": "library/ghost", - "tags": [ - "5.30.0", - "5.29.0", - "5.28.0", - "5.27.0", - "5.26.4", - "5.26.3", - "5.26.2", - "5.26.1", - "5.25.5", - "5.25.3", - "5.25.2", - "5.25.1", - "5.25.0", - "5.24.2", - "5.24.1", - "5.23.0", - "5.22.11", - "5.22.10", - "5.22.9", - "5.22.8", - "5.22.4", - "5.22.1", - "5.20.0", - "5.19.3", - "5.19.0", - "5.18.0", - "5.17.2", - "5.17.1", - "5.17.0", - "5.16.2" - ] - }, - { - "name": "gitea", - "image": "gitea/gitea", - "tags": [ - "1.18.0", - "1.17.4", - "1.17.3", - "1.17.2", - "1.17.1", - "1.17.0", - "1.16.9", - "1.16.8", - "1.16.7", - "1.16.6", - "1.16.5", - "1.16.4", - "1.16.3", - "1.16.2", - "1.16.1", - "1.16.0", - "1.15.11", - "1.15.10", - "1.15.9", - "1.15.8", - "1.15.7", - "1.15.6", - "1.15.5", - "1.15.4", - "1.15.3", - "1.15.2", - "1.15.1", - "1.15.0", - "1.14.7", - "1.14.6" - ] - }, - { - "name": "glitchtip", - "image": "glitchtip/glitchtip", - "tags": [ - "v3.0.2", - "v3.0.0", - "v2.0.7", - "v2.0.5", - "v2.0.2", - "v2.0.0", - "v1.12.4", - "v1.12.2", - "v1.12.0", - "v1.10.3", - "v1.10.1", - "v1.9.2", - "v1.9.0", - "v1.8.4", - "v1.8.2", - "v1.8.0", - "v1.7.1", - "v1.6.4", - "v1.6.2", - "v1.6.0", - "v1.5.3", - "v1.5.1", - "v1.4.1", - "v1.3.3", - "v1.3.1", - "v1.2.6", - "v1.2.4", - "v1.2.2", - "v1.2.0", - "v1.1.2" - ] - }, - { - "name": "grafana", - "image": "grafana/grafana", - "tags": [ - "9.3.2", - "9.3.1", - "9.3.0", - "9.2.8", - "9.2.7", - "9.2.6", - "9.2.5", - "9.2.4", - "9.2.3", - "9.2.2", - "9.2.1", - "9.2.0", - "9.1.8", - "9.1.7", - "9.1.6", - "9.1.5", - "9.1.4", - "9.1.3", - "9.1.2", - "9.1.1", - "9.1.0", - "9.0.9", - "9.0.8", - "9.0.7", - "9.0.6", - "9.0.5", - "9.0.4", - "9.0.3", - "9.0.2", - "9.0.1" - ] - }, - { - "name": "hasura", - "image": "hasura/graphql-engine", - "tags": [ - "v2.16.1", - "v2.16.0", - "v2.15.2", - "v2.14.1", - "v2.13.2", - "v2.12.1", - "v2.11.3", - "v2.10.2", - "v2.9.0", - "v2.8.4", - "v2.8.3", - "v2.8.2", - "v2.8.1", - "v2.8.0", - "v2.7.0", - "v2.6.2", - "v2.6.1", - "v2.6.0", - "v2.5.2", - "v2.5.1", - "v2.5.0", - "v2.4.0", - "v2.3.1", - "v2.3.0", - "v2.2.2", - "v2.2.1", - "v2.2.0", - "v2.1.1", - "v2.1.0", - "v2.0.10" - ] - }, - { - "name": "keycloak", - "image": "quay.io/keycloak/keycloak", - "tags": [ - "9.0.3", - "9.0.0", - "8.0.1", - "7.0.0", - "6.0.1", - "6.0.0", - "20.0.3", - "20.0.2", - "20.0.1", - "20.0.0", - "19.0.3", - "19.0.1", - "19.0.0", - "18.0.1", - "18.0.0", - "17.0.1", - "17.0.0", - "16.1.0", - "15.1.1", - "15.0.2", - "15.0.0", - "13.0.1", - "12.0.4", - "12.0.2", - "12.0.0", - "11.0.2", - "11.0.0", - "10.0.1" - ] - }, - { - "name": "languagetool", - "image": "silviof/docker-languagetool", - "tags": ["latest", "6.0", "5.8", "5.7", "5.6", "5.5", "5.4", "5.3"] - }, - { - "name": "lavalink", - "image": "fredboat/lavalink", - "tags": ["3.7.0", "3.6.1", "3.5.1", "v2.0.1"] - }, - { - "name": "libretranslate", - "image": "libretranslate/libretranslate", - "tags": ["v1.3.8", "v1.3.6", "v1.3.4", "v1.3.2", "v1.3.0", "v1.2.8"] - }, - { - "name": "meilisearch", - "image": "getmeili/meilisearch", - "tags": [ - "v0.30.5", - "v0.30.3", - "v0.30.1", - "v0.30.0", - "v0.29.3", - "v0.29.1", - "v0.29.0", - "v0.28.1", - "v0.28.0", - "v0.27.1", - "v0.27.0", - "v0.26.1", - "v0.26.0", - "v0.25.1", - "v0.25.0", - "v0.23.1", - "v0.23.0", - "v0.21.1", - "v0.21.0", - "v0.20.0", - "v0.19.0", - "v0.18.1", - "v0.18.0", - "v0.17.0", - "v0.16.0", - "0.14.1", - "v0.14.1", - "v0.14.0", - "v0.12.0", - "v0.11.0" - ] - }, - { - "name": "minio", - "image": "minio/minio", - "tags": [ - "RELEASE.2023-01-12T02-06-16Z", - "RELEASE.2023-01-06T18-11-18Z", - "RELEASE.2023-01-02T09-40-09Z", - "RELEASE.2022-12-12T19-27-27Z", - "RELEASE.2022-12-07T00-56-37Z", - "RELEASE.2022-12-02T19-19-22Z", - "RELEASE.2022-11-29T23-40-49Z", - "RELEASE.2022-11-26T22-43-32Z", - "RELEASE.2022-11-17T23-20-09Z", - "RELEASE.2022-11-11T03-44-20Z", - "RELEASE.2022-11-10T18-20-21Z", - "RELEASE.2022-11-08T05-27-07Z", - "RELEASE.2022-10-29T06-21-33Z", - "RELEASE.2022-10-24T18-35-07Z.hotfix.7906ac5be", - "RELEASE.2022-10-24T18-35-07Z", - "RELEASE.2022-10-21T22-37-48Z", - "RELEASE.2022-10-20T00-55-09Z", - "RELEASE.2022-10-15T19-57-03Z", - "RELEASE.2022-10-08T20-11-00Z", - "RELEASE.2022-10-05T14-58-27Z", - "RELEASE.2022-10-02T19-29-29Z", - "RELEASE.2022-09-25T15-44-53Z", - "RELEASE.2022-09-22T18-57-27Z", - "RELEASE.2022-09-17T00-09-45Z.hotfix.f76e5da9f", - "RELEASE.2022-09-17T00-09-45Z.fips", - "RELEASE.2022-09-07T22-25-02Z.fips", - "RELEASE.2022-09-01T23-53-36Z.fips", - "RELEASE.2022-08-26T19-53-15Z.fips", - "RELEASE.2022-08-25T07-17-05Z.fips", - "RELEASE.2022-08-22T23-53-06Z.hotfix.5fa3967bb" - ] - }, - { - "name": "n8n", - "image": "n8nio/n8n", - "tags": [ - "0.210.1", - "0.210.0", - "0.209.4", - "0.209.3", - "0.209.2", - "0.209.1", - "0.209.0", - "0.208.1", - "0.208.0", - "0.207.1", - "0.207.0", - "0.206.1", - "0.205.0", - "0.204.0", - "0.203.1", - "0.202.1", - "0.202.0", - "0.201.0", - "0.200.1", - "0.199.0", - "0.198.1", - "0.198.0", - "0.197.1", - "0.197.0", - "0.196.0", - "0.195.5", - "0.195.4", - "0.195.3", - "0.195.2", - "0.195.1" - ] - }, - { - "name": "nocodb", - "image": "nocodb/nocodb", - "tags": [ - "0.101.2", - "0.101.0", - "0.100.1", - "0.99.2", - "0.99.0", - "0.98.3", - "0.98.1", - "0.97.0", - "0.96.3", - "0.96.1", - "0.92.4", - "0.92.0", - "0.91.8", - "0.91.6", - "0.91.1", - "0.90.11", - "0.90.8", - "0.90.5", - "0.90.3", - "0.90.1", - "0.84.16", - "0.84.14", - "0.84.10", - "0.84.9", - "0.84.7", - "0.84.3", - "0.83.8", - "0.83.5", - "0.83.2", - "0.83.0" - ] - }, - { - "name": "openblocks", - "image": "openblocksdev/openblocks-ce", - "tags": ["1.1.3", "1.1.1", "1.0.21"] - }, - { - "name": "plausibleanalytics-arm", - "image": "plausible/analytics", - "tags": [ - "v1.5.1", - "v1.5.0", - "v1.4.3", - "v1.4.1", - "v1.4.0", - "v1.3.0", - "v1.2.0", - "v1.1.0", - "v1.0.0" - ] - }, - { - "name": "plausibleanalytics", - "image": "plausible/analytics", - "tags": [ - "v1.5.1", - "v1.5.0", - "v1.4.3", - "v1.4.1", - "v1.4.0", - "v1.3.0", - "v1.2.0", - "v1.1.0", - "v1.0.0" - ] - }, - { "name": "pocketbase", "image": "coollabsio/pocketbase", "tags": ["0.11.0", "0.10.2", "0.8.0"] }, - { - "name": "searxng", - "image": "searxng/searxng", - "tags": [ - "2023.01.15-52d41559", - "2023.01.15-13b0c251", - "2023.01.14-b720a495", - "2023.01.14-449aebae", - "2023.01.14-18d895ff", - "2023.01.09-afd71a6c", - "2023.01.09-a90ed481", - "2023.01.08-54e63839", - "2023.01.08-4e735b28", - "2023.01.08-217395b8", - "2023.01.08-0c429d70", - "2023.01.07-cb7b0916", - "2023.01.07-a98c5156", - "2023.01.07-633ba8b1", - "2023.01.07-4e355564", - "2023.01.06-b241015e", - "2023.01.06-269a72ee", - "2023.01.05-aba969cc", - "2022.12.30-b6d98be7", - "2022.12.30-647a0aa9", - "2022.12.30-17516290", - "2022.12.29-d531f893", - "2022.12.29-9b31976c", - "2022.12.29-76cd808a", - "2022.12.29-3ec58b06", - "2022.12.29-174e6851", - "2022.12.26-0d489617", - "2022.12.23-e8f72d70", - "2022.12.23-a2d506d4", - "2022.12.22-d75ae7c8" - ] - }, - { - "name": "trilium", - "image": "zadam/trilium", - "tags": [ - "0.58.4", - "0.57.4", - "0.57.2", - "0.56.1", - "0.55.1", - "0.54.2", - "0.53.2", - "0.52.4", - "0.52.2", - "0.51.2", - "0.50.3", - "0.50.1", - "0.49.5", - "0.49.3", - "0.48.9", - "0.48.7", - "0.48.4", - "0.48.2", - "0.47.8", - "0.47.6", - "0.47.4", - "0.47.2", - "0.46.7", - "0.46.5", - "0.45.10", - "0.45.9", - "0.45.7", - "0.45.5", - "0.45.3", - "0.44.8" - ] - }, - { - "name": "umami-postgresql", - "image": "ghcr.io/umami-software/umami", - "tags": [ - "postgresql-v1.39.5", - "postgresql-v1.39.4", - "postgresql-v1.39.3", - "postgresql-v1.39.2", - "postgresql-v1.39.1", - "postgresql-v1.39.0", - "postgresql-v1.38.0", - "postgresql-v1.37.0", - "postgresql-v1.36.1", - "postgresql-v1.36.0", - "postgresql-v1.35.0", - "postgresql-v1.34.0", - "postgresql-v1.33.3", - "postgresql-latest", - "mysql-v1.39.5", - "mysql-v1.39.4", - "mysql-v1.39.3", - "mysql-v1.39.2", - "mysql-v1.39.1", - "mysql-v1.39.0", - "mysql-v1.38.0", - "mysql-v1.37.0", - "mysql-v1.36.1", - "mysql-v1.36.0", - "mysql-v1.35.0", - "mysql-v1.34.0", - "mysql-v1.33.3", - "mysql-latest" - ] - }, - { - "name": "umami", - "image": "ghcr.io/umami-software/umami", - "tags": [ - "postgresql-v1.39.5", - "postgresql-v1.39.4", - "postgresql-v1.39.3", - "postgresql-v1.39.2", - "postgresql-v1.39.1", - "postgresql-v1.39.0", - "postgresql-v1.38.0", - "postgresql-v1.37.0", - "postgresql-v1.36.1", - "postgresql-v1.36.0", - "postgresql-v1.35.0", - "postgresql-v1.34.0", - "postgresql-v1.33.3", - "postgresql-latest", - "mysql-v1.39.5", - "mysql-v1.39.4", - "mysql-v1.39.3", - "mysql-v1.39.2", - "mysql-v1.39.1", - "mysql-v1.39.0", - "mysql-v1.38.0", - "mysql-v1.37.0", - "mysql-v1.36.1", - "mysql-v1.36.0", - "mysql-v1.35.0", - "mysql-v1.34.0", - "mysql-v1.33.3", - "mysql-latest" - ] - }, - { - "name": "uptimekuma", - "image": "louislam/uptime-kuma", - "tags": [ - "1.19.4", - "1.19.3", - "1.19.2", - "1.19.1", - "1.19.0", - "1.18.5", - "1.18.4", - "1.18.3", - "1.18.2", - "1.18.1", - "1.18.0", - "1.17.1", - "1.17.0", - "1.16.1", - "1.16.0", - "1.15.1", - "1.15.0", - "1.14.1", - "1.14.0", - "1.13.2", - "1.13.1", - "1.13.0", - "1.12.1", - "1.11.4", - "1.11.3", - "1.11.2", - "1.11.1", - "1.11.0", - "1.10.2", - "1.10.1" - ] - }, - { - "name": "vaultwarden", - "image": "vaultwarden/server", - "tags": [ - "1.27.0", - "1.26.0", - "1.25.2", - "1.25.1", - "1.25.0", - "1.24.0", - "1.23.1", - "1.23.0", - "1.22.2", - "1.22.1", - "1.22.0", - "1.21.0" - ] - }, - { - "name": "vscodeserver", - "image": "codercom/code-server", - "tags": [ - "4.9.1", - "4.9.0", - "4.8.3", - "4.8.2", - "4.8.1", - "4.8.0", - "4.7.0", - "4.6.0", - "4.5.1", - "4.4.0", - "4.2.0", - "4.0.2", - "3.11.1", - "3.10.2", - "3.10.0", - "3.9.3", - "3.9.1", - "3.8.1", - "3.7.4", - "3.7.2", - "3.7.0", - "3.6.1", - "3.5.0", - "3.4.0", - "3.3.0", - "3.2.0", - "3.1.1", - "3.1.0", - "3.0.2", - "3.0.0" - ] - }, - { - "name": "weblate", - "image": "weblate/weblate", - "tags": [ - "latest", - "edge-2023-01-13-e824b551f23c3679467e38b06366744a06aa3b0c", - "edge-2023-01-13-468b996565e6b62edb78d40b515c476e0d860273", - "edge-2023-01-12-fe3d58b14f119eb5501220e9f096949c2e1ec2d3", - "edge-2023-01-12-112f75f9ee9e118ad493215f89742e6e091be8d0", - "edge-2023-01-11-f7bb190993e329d1529694e8cc7f5e0a80ccd615", - "edge-2023-01-11-e8ef3183aa7723f32c2b60c7c3b89910f2c7c593", - "edge-2023-01-11-155231f6cde18a65e3f35093d66dd0ce93aa7154", - "edge-2023-01-10-e47516e4022f87c019e61998b556b69111187aa9", - "edge-2023-01-10-98c6b38c746165adb27b2a8e93a74fa9ab64f17c", - "edge-2023-01-10-1df5c9dd96a6d8650f6881942fecbe33e1884295", - "edge-2023-01-09-7029b7b6c630be7cdac07d1629573dd2b81bc05f", - "edge-2023-01-09-4b05a878aa25b2c544a4e77027769b5934ec561f", - "edge-2023-01-07-df50259ff209720b0fd3c983bd7a5d01b564149c", - "edge-2023-01-07-719b3034de0ed369cd63554ea652f11142b9a540", - "edge-2023-01-07-14d4aec6cd8e1e6e0b7c1dc3a9cf4a74ccfc5e37", - "edge-2023-01-07-00f2d980c4388d799ba0df3a27c10bf941b7edd3", - "edge-2023-01-04-e92fe933b22e36e92f6de57a0ecb16752852d815", - "edge-2023-01-04-4c413c6ba33c39a6b7f3238f255bc09c69e96345", - "edge-2023-01-02-fd57652cce9dd2a12c7ff1dc644f5a969e57ea76", - "edge-2023-01-02-738d270784bca0cfaaef59e6cfb8c307f68a9939", - "edge-2023-01-02-3f6a7a183bbda4dc2e8d7e468370adc6f9e50527", - "edge-2023-01-01-5580a7a4755bf71403c85999fb4dff2078293403", - "edge-2022-12-29-8f422f23930880809a26d1207f9e43315ef54ad2", - "edge-2022-12-28-375fd99195711641246ccd09e3ce394d6928eaef", - "edge-2022-12-25-358c8ce6071f2781ae027bbc3a46d906ce08d9e7", - "edge-2022-12-24-3e1503494ce06ad6ff32f02db1a7d59224e5c860", - "edge-2022-12-21-cac4b09f943fe97700e3a33b7caf23277d2fcc11", - "edge-2022-12-21-3a8dd1bf66a7295f3512346bc1c97d55c5649dcf", - "edge-2022-12-16-e93caa3b014543b716b946f2c7fbf4a8f9be6099" - ] - }, - { - "name": "whoogle", - "image": "benbusby/whoogle-search", - "tags": [ - "0.8.0", - "0.7.3", - "0.7.1", - "0.6.0", - "0.5.3", - "0.5.1", - "0.4.1", - "0.3.2", - "v0.3.0", - "0.1.2", - "0.1.0" - ] - }, - { - "name": "wordpress-only", - "image": "library/wordpress", - "tags": [ - "php8.2-fpm-alpine", - "php8.2-fpm", - "php8.2-apache", - "php8.2", - "php8.1-fpm-alpine", - "php8.1-fpm", - "php8.1-apache", - "php8.1", - "php8.0-fpm-alpine", - "php8.0-fpm", - "php8.0-apache", - "php8.0", - "php7.4-fpm-alpine", - "php7.4-fpm", - "php7.4-apache", - "php7.4", - "php7.3-fpm-alpine", - "php7.3-fpm", - "php7.3-apache", - "php7.3", - "php7.2-fpm-alpine", - "php7.2-fpm", - "php7.2-apache", - "php7.2", - "php7.1-fpm-alpine", - "php7.1-fpm", - "php7.1-apache", - "php7.1", - "php7.0-fpm-alpine", - "php7.0-fpm" - ] - }, - { - "name": "wordpress", - "image": "library/wordpress", - "tags": [ - "php8.2-fpm-alpine", - "php8.2-fpm", - "php8.2-apache", - "php8.2", - "php8.1-fpm-alpine", - "php8.1-fpm", - "php8.1-apache", - "php8.1", - "php8.0-fpm-alpine", - "php8.0-fpm", - "php8.0-apache", - "php8.0", - "php7.4-fpm-alpine", - "php7.4-fpm", - "php7.4-apache", - "php7.4", - "php7.3-fpm-alpine", - "php7.3-fpm", - "php7.3-apache", - "php7.3", - "php7.2-fpm-alpine", - "php7.2-fpm", - "php7.2-apache", - "php7.2", - "php7.1-fpm-alpine", - "php7.1-fpm", - "php7.1-apache", - "php7.1", - "php7.0-fpm-alpine", - "php7.0-fpm" - ] - } -] diff --git a/apps/api/templates.json b/apps/api/templates.json deleted file mode 100644 index 5c1b4188b..000000000 --- a/apps/api/templates.json +++ /dev/null @@ -1 +0,0 @@ -[{"templateVersion":"1.0.0","defaultVersion":"9.22","documentation":"https://docs.directus.io/getting-started/introduction.html","type":"directus-postgresql","name":"Directus","subname":"(PostgreSQL)","description":"Directus is a free and open-source headless CMS framework for managing custom SQL-based databases.","labels":["CMS","headless"],"services":{"$$id":{"name":"Directus","depends_on":["$$id-postgresql","$$id-redis"],"image":"directus/directus:$$core_version","volumes":["$$id-uploads:/directus/uploads","$$id-database:/directus/database","$$id-extensions:/directus/extensions"],"environment":["KEY=$$secret_key","SECRET=$$secret_secret","DB_CLIENT=pg","DB_CONNECTION_STRING=$$secret_db_connection_string","CACHE_ENABLED=true","CACHE_STORE=redis","CACHE_REDIS=$$secret_cache_redis","ADMIN_EMAIL=$$config_admin_email","ADMIN_PASSWORD=$$secret_admin_password","CACHE_AUTO_PURGE=true","PUBLIC_URL=$$config_public_url"],"ports":["8055"]},"$$id-postgresql":{"name":"Directus PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Directus Redis","depends_on":[],"image":"redis:7.0.4-alpine","command":"--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5","volumes":["$$id-redis:/data"],"environment":[]}},"variables":[{"id":"$$config_public_url","name":"PUBLIC_URL","label":"Public URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_db_connection_string","name":"DB_CONNECTION_STRING","label":"Directus Database Url","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"Database","defaultValue":"directus","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$secret_cache_redis","name":"CACHE_REDIS","label":"Redis Url","defaultValue":"redis://$$id-redis:6379","description":""},{"id":"$$config_admin_email","name":"ADMIN_EMAIL","label":"Initial Admin Email","defaultValue":"admin@example.com","description":"The email address of the first user that is automatically created. You can change it later in Directus."},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"The password of the first user that is automatically created.","showOnConfiguration":true},{"id":"$$secret_key","name":"KEY","label":"Key","defaultValue":"$$generate_password","description":"Unique identifier for the project.","showOnConfiguration":true},{"id":"$$secret_secret","name":"SECRET","label":"Secret","defaultValue":"$$generate_password","description":"Secret string for the project.","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"v1.3.8","documentation":"https://github.com/LibreTranslate/LibreTranslate","description":"Free and Open Source Machine Translation API. 100% self-hosted, offline capable and easy to setup.","type":"libretranslate","name":"Libretranslate","labels":["translator","argos","python","libretranslate"],"services":{"$$id":{"name":"Libretranslate","image":"libretranslate/libretranslate:$$core_version","environment":["LT_HOST=0.0.0.0","LT_SUGGESTIONS=true","LT_CHAR_LIMIT=$$config_lt_char_limit","LT_REQ_LIMIT=$$config_lt_req_limit","LT_BATCH_LIMIT=$$config_lt_batch_limit","LT_GA_ID=$$config_lt_ga_id","LT_DISABLE_WEB_UI=$$config_lt_web_ui"],"volumes":["$$id-libretranslate:/libretranslate"],"ports":["5000"]}},"variables":[{"id":"$$config_lt_char_limit","name":"LT_CHAR_LIMIT","label":"Char limit","defaultValue":"5000","description":"Set character limit."},{"id":"$$config_lt_req_limit","name":"LT_REQ_LIMIT","label":"Request limit","defaultValue":"5000","description":"Set maximum number of requests per minute per client."},{"id":"$$config_lt_batch_limit","name":"LT_BATCH_LIMIT","label":"Batch Limit","defaultValue":"5000","description":"Set maximum number of texts to translate in a batch request."},{"id":"$$config_lt_ga_id","name":"LT_GA_ID","label":"Google Analytics ID","defaultValue":"","description":"Enable Google Analytics on the API client page by providing an ID"},{"id":"$$config_lt_web_ui","name":"LT_DISABLE_WEB_UI","label":"Web UI","defaultValue":"false","description":"Disable or enable web ui. True or false."}]},{"templateVersion":"1.0.0","defaultVersion":"0.8.0","documentation":"https://github.com/benbusby/whoogle-search","type":"whoogle","name":"Whoogle Search","description":"A self-hosted, ad-free, privacy-respecting metasearch engine","labels":["search","google"],"services":{"$$id":{"name":"Whoogle Search","documentation":"https://github.com/benbusby/whoogle-search","depends_on":[],"image":"benbusby/whoogle-search:$$core_version","cap_drop":["ALL"],"environment":["WHOOGLE_USER=$$config_whoogle_username","WHOOGLE_PASS=$$secret_whoogle_password","WHOOGLE_CONFIG_PREFERENCES_KEY=$$secret_whoogle_preferences_key"],"ulimits":{"nofile":{"soft":262144,"hard":262144}},"ports":["5000"]}},"variables":[{"id":"$$config_whoogle_username","name":"WHOOGLE_USER","label":"Whoogle User","defaultValue":"$$generate_username","description":"Username to log into Whoogle"},{"id":"$$secret_whoogle_password","name":"WHOOGLE_PASSWORD","label":"Whoogle Password","defaultValue":"$$generate_password","description":"Password to log into Whoogle","showOnConfiguration":true},{"id":"$$secret_whoogle_preferences_key","name":"WHOOGLE_CONFIG_PREFERENCES_KEY","label":"Whoogle preferences key","defaultValue":"$$generate_password","description":"password to encrypt preferences"}]},{"templateVersion":"1.0.0","defaultVersion":"1.1.5","documentation":"https://docs.openblocks.dev/","type":"openblocks","name":"Openblocks","description":"The Open Source Retool Alternative","services":{"$$id":{"image":"openblocksdev/openblocks-ce:$$core_version","volumes":["$$id-stacks-data:/openblocks-stacks"],"ports":["3000"]}}},{"templateVersion":"1.0.0","defaultVersion":"0.12.3","documentation":"https://pocketbase.io/docs/","type":"pocketbase","name":"Pocketbase","description":"Open Source realtime backend in 1 file","services":{"$$id":{"image":"coollabsio/pocketbase:$$core_version","volumes":["$$id-data:/app/pb_data"],"ports":["8080"]}}},{"templateVersion":"1.0.0","defaultVersion":"v1.5.1","documentation":"https://plausible.io/doc/","type":"plausibleanalytics-arm","name":"Plausible Analytics (ARM)","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DB=$$config_postgres_db"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Registration","defaultValue":"true","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"1.18","documentation":"https://docs.gitea.io","type":"gitea","name":"Gitea","description":"Gitea is a community managed lightweight code hosting solution written in Go.","labels":["storage","git"],"services":{"$$id":{"name":"Gitea","documentation":"https://docs.gitea.io","image":"gitea/gitea:$$core_version","volumes":["$$id-data:/data","/etc/timezone:/etc/timezone:ro","/etc/localtime:/etc/localtime:ro"],"environment":["USER_UID=1000","USER_GID=1000","DOMAIN=$$config_domain","SSH_DOMAIN=$$config_ssh_domain","ROOT_URL=$$config_root_url","SECRET_KEY=$$secret_secret_key","INTERNAL_TOKEN=$$secret_internal_token","SSH_PORT=22","START_SSH_SERVER=$$config_start_ssh_server"],"ports":["3000","22"],"proxy":[{"port":"22","hostPort":"$$config_hostport_ssh"}]}},"variables":[{"id":"$$config_hostport_ssh","name":"SSH_PORT","label":"SSH Port","defaultValue":"8022","description":"","required":true},{"id":"$$config_domain","name":"DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ssh_domain","name":"SSH_DOMAIN","label":"SSH Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_start_ssh_server","name":"START_SSH_SERVER","label":"Start SSH Server","defaultValue":"true","description":""},{"id":"$$config_root_url","name":"ROOT_URL","label":"Root URL of Gitea","defaultValue":"$$generate_fqdn_slash","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(32)","description":""},{"id":"$$secret_internal_token","name":"INTERNAL_TOKEN","label":"Internal JWT Token","defaultValue":"$$generate_token","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"20.0","documentation":"https://www.keycloak.org/documentation","type":"keycloak","name":"Keycloak","description":"Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more.","labels":["authentication","authorization","oidconnect","saml2"],"services":{"$$id":{"name":"Keycloak","command":"start --db=postgres --features=token-exchange --import-realm","depends_on":["$$id-postgresql"],"image":"quay.io/keycloak/keycloak:$$core_version","volumes":["$$id-import:/opt/keycloak/data/import"],"environment":["KC_HEALTH_ENABLED=true","KC_PROXY=edge","KC_DB=postgres","KC_HOSTNAME=$$config_keycloak_domain","KEYCLOAK_ADMIN=$$config_admin_user","KEYCLOAK_ADMIN_PASSWORD=$$secret_keycloak_admin_password","KC_DB_PASSWORD=$$secret_postgres_password","KC_DB_USERNAME=$$config_postgres_user","KC_DB_URL=$$secret_keycloak_database_url"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_keycloak_domain","name":"KEYCLOAK_DOMAIN","label":"Keycloak Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_keycloak_database_url","name":"KEYCLOAK_DATABASE_URL","label":"Keycloak Database Url","defaultValue":"jdbc:postgresql://$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$config_admin_user","name":"KEYCLOAK_ADMIN","label":"Keycloak Admin User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_keycloak_admin_password","name":"KEYCLOAK_ADMIN_PASSWORD","label":"Keycloak Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"keycloak","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.7","documentation":"https://github.com/freyacodes/Lavalink","description":"Standalone audio sending node based on Lavaplayer.","type":"lavalink","name":"Lavalink","labels":["discord","discord bot","audio","lavalink","jda"],"services":{"$$id":{"name":"Lavalink","image":"fredboat/lavalink:$$core_version","environment":[],"volumes":["$$id-lavalink:/lavalink"],"ports":["$$config_port"],"files":[{"location":"/opt/Lavalink/application.yml","content":"server:\n port: 2333\n address: 0.0.0.0\nlavalink:\n server:\n password: \"$$secret_password\"\n sources:\n youtube: true\n bandcamp: true\n soundcloud: true\n twitch: true\n vimeo: true\n http: true\n local: false\n\nlogging:\n file:\n path: ./logs/\n\n level:\n root: INFO\n lavalink: INFO\n\n logback:\n rollingpolicy:\n max-file-size: 1GB\n max-history: 30"}]}},"variables":[{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"v1.9.6","documentation":"https://docs.appsmith.com/getting-started/setup/instance-configuration/","type":"appsmith","name":"Appsmith","description":"Fastest way to build internal apps over any database or API.","services":{"$$id":{"image":"appsmith/appsmith-ce:$$core_version","environment":["APPSMITH_MAIL_ENABLED=$$config_appsmith_mail_enabled","APPSMITH_DISABLE_TELEMETRY=$$config_appsmith_disable_telemetry","APPSMITH_DISABLE_INTERCOM=$$config_appsmith_disable_intercom"],"volumes":["$$id-stacks-data:/appsmith-stacks"],"ports":["80"]}},"variables":[{"id":"$$config_appsmith_mail_enabled","name":"APPSMITH_MAIL_ENABLED","label":"Enable Mail","defaultValue":"false","description":""},{"id":"$$config_appsmith_disable_telemetry","name":"APPSMITH_DISABLE_TELEMETRY","label":"Disable Telemetry","defaultValue":"true","description":""},{"id":"$$config_appsmith_disable_intercom","name":"APPSMITH_DISABLE_INTERCOM","label":"Disable Intercom","defaultValue":"true","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"0.58.4","documentation":"https://hub.docker.com/r/zadam/trilium","description":"A hierarchical note taking application with focus on building large personal knowledge bases.","labels":["personal","knowledge","notes","wiki"],"type":"trilium","name":"Trilium Notes","services":{"$$id":{"image":"zadam/trilium:$$core_version","environment":[],"volumes":["$$id-trilium:/home/node/trilium-data"],"ports":["8080"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.19.4","documentation":"https://hub.docker.com/r/louislam/uptime-kuma","description":"A free & fancy self-hosted monitoring tool.","labels":["uptime"],"type":"uptimekuma","name":"UptimeKuma","services":{"$$id":{"image":"louislam/uptime-kuma:$$core_version","environment":[],"volumes":["$$id-uptimekuma:/app/data"],"ports":["3001"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"6.0","documentation":"https://hub.docker.com/r/silviof/docker-languagetool","description":"A multilingual grammar, style and spell checker.","type":"languagetool","name":"LanguageTool","services":{"$$id":{"image":"silviof/docker-languagetool:$$core_version","environment":[],"volumes":["$$id-ngrams:/ngrams"],"ports":["8010"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.27.0","documentation":"https://hub.docker.com/r/vaultwarden/server","description":"Bitwarden compatible server written in Rust.","type":"vaultwarden","name":"VaultWarden","labels":["bitwarden","password manager"],"services":{"$$id":{"image":"vaultwarden/server:$$core_version","environment":[],"volumes":["$$id-data:/data"],"ports":["80"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"9.3.2","documentation":"https://hub.docker.com/r/grafana/grafana","type":"grafana","name":"Grafana","description":"Grafana allows you to query, visualize, alert on and understand your metrics.","labels":["monitoring","metrics","dashboard"],"services":{"$$id":{"image":"grafana/grafana:$$core_version","environment":[],"volumes":["$$id-config:/etc/grafana","$$id-grafana:/var/lib/grafana"],"ports":["3000"]}},"variables":[]},{"templateVersion":"1.0.0","defaultVersion":"1.2.0","documentation":"https://appwrite.io/docs","type":"appwrite","name":"Appwrite","description":"Secure Backend Server for Web, Mobile & Flutter Developers.","labels":["serverless","backend","storage","api"],"services":{"$$id":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_LOCALE=$$config__app_locale","_APP_CONSOLE_WHITELIST_ROOT=$$config__app_console_whitelist_root","_APP_CONSOLE_WHITELIST_EMAILS=$$config__app_console_whitelist_emails","_APP_CONSOLE_WHITELIST_IPS=$$config__app_console_whitelist_ips","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_SYSTEM_RESPONSE_FORMAT=$$config__app_system_response_format","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPTIONS_FORCE_HTTPS=$$config__app_options_force_https","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_STORAGE_LIMIT=$$config__app_storage_limit","_APP_STORAGE_PREVIEW_LIMIT=$$config__app_storage_preview_limit","_APP_STORAGE_ANTIVIRUS=$$config__app_storage_antivirus_enabled","_APP_STORAGE_ANTIVIRUS_HOST=$$config__app_storage_antivirus_host","_APP_STORAGE_ANTIVIRUS_PORT=$$config__app_storage_antivirus_port","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_FUNCTIONS_SIZE_LIMIT=$$config__app_functions_size_limit","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STATSD_HOST=$$config__app_statsd_host","_APP_STATSD_PORT=$$config__app_statsd_port","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-config:/storage/config","$$id-certificates:/storage/certificates","$$id-functions:/storage/functions"],"ports":["80"],"proxy":[{"port":"80"}]},"$$id-executor":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_FUNCTIONS_BUILD_TIMEOUT=$$config__app_functions_build_timeout","_APP_FUNCTIONS_CONTAINERS=$$config__app_functions_containers","_APP_FUNCTIONS_RUNTIMES=$$config__app_functions_runtimes","_APP_FUNCTIONS_CPUS=$$config__app_functions_cpus","_APP_FUNCTIONS_MEMORY=$$config__app_functions_memory_allocated","_APP_FUNCTIONS_MEMORY_SWAP=$$config__app_functions_memory_swap","_APP_FUNCTIONS_INACTIVE_THRESHOLD=$$config__app_functions_inactive_threshold","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-functions:/storage/functions","$$id-builds:/storage/builds","/var/run/docker.sock:/var/run/docker.sock","/tmp:/tmp:rw"],"entrypoint":"executor"},"$$id-influxdb":{"image":"appwrite/influxdb:1.5.0","environment":[],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-maintenance":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_MAINTENANCE_INTERVAL=$$config__app_maintenance_interval","_APP_MAINTENANCE_RETENTION_EXECUTION=$$config__app_maintenance_retention_execution","_APP_MAINTENANCE_RETENTION_CACHE=$$config__app_maintenance_retention_cache","_APP_MAINTENANCE_RETENTION_ABUSE=$$config__app_maintenance_retention_abuse","_APP_MAINTENANCE_RETENTION_AUDIT=$$config__app_maintenance_retention_audit","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"maintenance"},"$$id-mariadb":{"image":"mariadb:10.7","command":"--innodb-flush-method fsync","environment":["MARIADB_ROOT_PASSWORD=$$secret__app_db_root_pass","MARIADB_DATABASE=$$config__app_db_schema","MARIADB_USER=$$config__app_db_user","MARIADB_PASSWORD=$$secret__app_db_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-mariadb:/var/lib/mysql"]},"$$id-realtime":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_WORKER_PER_CORE=$$config__app_worker_per_core","_APP_OPTIONS_ABUSE=$$config__app_options_abuse","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_USAGE_STATS=$$config__app_usage_stats","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"realtime","proxy":[{"port":"80","pathPrefix":"/v1/realtime"}]},"$$id-redis":{"image":"redis:7.0.4-alpine","command":"--maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5","environment":[],"volumes":["$$id-redis:/data"]},"$$id-schedule":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"schedule"},"$$id-telegraf":{"image":"appwrite/telegraf:1.4.0","environment":["_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-influxdb:/var/lib/influxdb"]},"$$id-usage-database":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type database"},"$$id-usage":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_INFLUXDB_HOST=$$config__app_influxdb_host","_APP_INFLUXDB_PORT=$$config__app_influxdb_port","_APP_USAGE_TIMESERIES_INTERVAL=$$config__app_usage_timeseries_interval","_APP_USAGE_DATABASE_INTERVAL=$$config__app_usage_database_interval","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"usage --type timeseries"},"$$id-worker-audits":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-audits"},"$$id-worker-builds":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-builds"},"$$id-worker-certificates":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_DOMAIN=$$config__app_domain","_APP_DOMAIN_TARGET=$$config__app_domain_target","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-config:/storage/config","$$id-certificates:/storage/certificates"],"entrypoint":"worker-certificates"},"$$id-worker-databases":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-databases"},"$$id-worker-deletes":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_STORAGE_DEVICE=$$config__app_storage_device","_APP_STORAGE_S3_ACCESS_KEY=$$secret__app_storage_s3_access_key","_APP_STORAGE_S3_SECRET=$$secret__app_storage_s3_secret","_APP_STORAGE_S3_REGION=$$config__app_storage_s3_region","_APP_STORAGE_S3_BUCKET=$$config__app_storage_s3_bucket","_APP_STORAGE_DO_SPACES_ACCESS_KEY=$$secret__app_storage_do_spaces_access_key","_APP_STORAGE_DO_SPACES_SECRET=$$secret__app_storage_do_spaces_secret","_APP_STORAGE_DO_SPACES_REGION=$$config__app_storage_do_spaces_region","_APP_STORAGE_DO_SPACES_BUCKET=$$config__app_storage_do_spaces_bucket","_APP_STORAGE_BACKBLAZE_ACCESS_KEY=$$secret__app_storage_backblaze_access_key","_APP_STORAGE_BACKBLAZE_SECRET=$$secret__app_storage_backblaze_secret","_APP_STORAGE_BACKBLAZE_REGION=$$config__app_storage_backblaze_region","_APP_STORAGE_BACKBLAZE_BUCKET=$$config__app_storage_backblaze_bucket","_APP_STORAGE_LINODE_ACCESS_KEY=$$secret__app_storage_linode_access_key","_APP_STORAGE_LINODE_SECRET=$$secret__app_storage_linode_secret","_APP_STORAGE_LINODE_REGION=$$config__app_storage_linode_region","_APP_STORAGE_LINODE_BUCKET=$$config__app_storage_linode_bucket","_APP_STORAGE_WASABI_ACCESS_KEY=$$secret__app_storage_wasabi_access_key","_APP_STORAGE_WASABI_SECRET=$$secret__app_storage_wasabi_secret","_APP_STORAGE_WASABI_REGION=$$config__app_storage_wasabi_region","_APP_STORAGE_WASABI_BUCKET=$$config__app_storage_wasabi_bucket","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":["$$id-uploads:/storage/uploads","$$id-cache:/storage/cache","$$id-functions:/storage/functions","$$id-builds:/storage/builds","$$id-certificates:/storage/certificates"],"entrypoint":"worker-deletes"},"$$id-worker-functions":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_DB_HOST=$$config__app_db_host","_APP_DB_PORT=$$config__app_db_port","_APP_DB_SCHEMA=$$config__app_db_schema","_APP_DB_USER=$$config__app_db_user","_APP_DB_PASS=$$secret__app_db_pass","_APP_FUNCTIONS_TIMEOUT=$$config__app_functions_timeout","_APP_EXECUTOR_SECRET=$$secret__app_executor_secret","_APP_EXECUTOR_HOST=$$config__app_executor_host","_APP_USAGE_STATS=$$config__app_usage_stats","DOCKERHUB_PULL_USERNAME=$$config_dockerhub_pull_username","DOCKERHUB_PULL_PASSWORD=$$secret_dockerhub_pull_password","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-functions"},"$$id-worker-mails":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_EMAIL_NAME=$$config__app_system_email_name","_APP_SYSTEM_EMAIL_ADDRESS=$$config__app_system_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMTP_HOST=$$config__app_smtp_host","_APP_SMTP_PORT=$$config__app_smtp_port","_APP_SMTP_SECURE=$$config__app_smtp_secure","_APP_SMTP_USERNAME=$$config__app_smtp_username","_APP_SMTP_PASSWORD=$$secret__app_smtp_password","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-mails"},"$$id-worker-messaging":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_SMS_PROVIDER=$$config__app_sms_provider","_APP_SMS_FROM=$$config__app_sms_from","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-messaging"},"$$id-worker-webhooks":{"image":"appwrite/appwrite:$$core_version","environment":["_APP_ENV=$$config__app_env","_APP_OPENSSL_KEY_V1=$$secret__app_openssl_key_v1","_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=$$config__app_system_security_email_address","_APP_REDIS_HOST=$$config__app_redis_host","_APP_REDIS_PORT=$$config__app_redis_port","_APP_REDIS_USER=$$config__app_redis_user","_APP_REDIS_PASS=$$secret__app_redis_pass","_APP_LOGGING_PROVIDER=$$config__app_logging_provider","_APP_LOGGING_CONFIG=$$config__app_logging_config","OPEN_RUNTIMES_NETWORK=$$config_open_runtimes_network"],"volumes":[],"entrypoint":"worker-webhooks"}},"variables":[{"id":"$$config__app_influxdb_host","name":"_APP_INFLUXDB_HOST","label":"InfluxDB | _APP_INFLUXDB_HOST","defaultValue":"$$id-influxdb","description":""},{"id":"$$config__app_influxdb_port","name":"_APP_INFLUXDB_PORT","label":"InfluxDB | _APP_INFLUXDB_PORT","defaultValue":"8086","description":"InfluxDB server TCP port."},{"id":"$$config__app_env","name":"_APP_ENV","label":"General | _APP_ENV","defaultValue":"production","description":"Set your server running environment."},{"id":"$$config__app_worker_per_core","name":"_APP_WORKER_PER_CORE","label":"General | _APP_WORKER_PER_CORE","defaultValue":"6","description":"Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance."},{"id":"$$config__app_locale","name":"_APP_LOCALE","label":"General | _APP_LOCALE","defaultValue":"en","description":"Set your Appwrite's locale. By default, the locale is set to 'en'."},{"id":"$$config__app_console_whitelist_root","name":"_APP_CONSOLE_WHITELIST_ROOT","label":"General | _APP_CONSOLE_WHITELIST_ROOT","defaultValue":"enabled","description":"This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled."},{"id":"$$config__app_console_whitelist_emails","name":"_APP_CONSOLE_WHITELIST_EMAILS","label":"General | _APP_CONSOLE_WHITELIST_EMAILS","defaultValue":"","description":"This option allows you to limit creation of new users on the Appwrite console. This option is very useful for small teams or sole developers. To enable it, pass a list of allowed email addresses separated by a comma."},{"id":"$$config__app_console_whitelist_ips","name":"_APP_CONSOLE_WHITELIST_IPS","label":"General | _APP_CONSOLE_WHITELIST_IPS","defaultValue":"","description":"This last option allows you to limit creation of users in Appwrite console for users sharing the same set of IP addresses. This option is very useful for team working with a VPN service or a company IP.\\n\\nTo enable/activate this option, pass a list of allowed IP addresses separated by a comma."},{"id":"$$config__app_system_email_name","name":"_APP_SYSTEM_EMAIL_NAME","label":"General | _APP_SYSTEM_EMAIL_NAME","defaultValue":"Appwrite","description":"This is the sender name value that will appear on email messages sent to developers from the Appwrite console. You can use url encoded strings for spaces and special chars."},{"id":"$$config__app_system_email_address","name":"_APP_SYSTEM_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_EMAIL_ADDRESS","defaultValue":"team@appwrite.io","description":"This is the sender email address that will appear on email messages sent to developers from the Appwrite console. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users' SPAM folders."},{"id":"$$config__app_system_security_email_address","name":"_APP_SYSTEM_SECURITY_EMAIL_ADDRESS","label":"General | _APP_SYSTEM_SECURITY_EMAIL_ADDRESS","defaultValue":"certs@appwrite.io","description":"This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload."},{"id":"$$config__app_system_response_format","name":"_APP_SYSTEM_RESPONSE_FORMAT","label":"General | _APP_SYSTEM_RESPONSE_FORMAT","defaultValue":"","description":"Use this environment variable to set the default Appwrite HTTP response format to support an older version of Appwrite. This option is useful to overcome breaking changes between versions. You can also use the X-Appwrite-Response-Format HTTP request header to overwrite the response for a specific request. This variable accepts any valid Appwrite version. To use the current version format, leave the value of the variable empty."},{"id":"$$config__app_options_abuse","name":"_APP_OPTIONS_ABUSE","label":"General | _APP_OPTIONS_ABUSE","defaultValue":"enabled","description":"Allows you to disable abuse checks and API rate limiting. By default, set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not recommended to disable this check-in a production environment."},{"id":"$$config__app_options_force_https","name":"_APP_OPTIONS_FORCE_HTTPS","label":"General | _APP_OPTIONS_FORCE_HTTPS","defaultValue":"disabled","description":"Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the 'Strict-Transport-Security' header to all HTTP responses."},{"id":"$$secret__app_openssl_key_v1","name":"_APP_OPENSSL_KEY_V1","label":"General | _APP_OPENSSL_KEY_V1","defaultValue":"$$generate_hex(256)","description":"This is your server private secret key that is used to encrypt all sensitive data on your server. Appwrite server encrypts all secret data on your server like webhooks, HTTP passwords, user sessions, and storage files. Keep it a secret and have a backup for it."},{"id":"$$config__app_domain","name":"_APP_DOMAIN","label":"General | _APP_DOMAIN","defaultValue":"$$generate_domain","description":"Your Appwrite domain address. When setting a public suffix domain, Appwrite will attempt to issue a valid SSL certificate automatically. When used with a dev domain, Appwrite will assign a self-signed SSL certificate. The default value is 'localhost'."},{"id":"$$config__app_domain_target","name":"_APP_DOMAIN_TARGET","label":"General | _APP_DOMAIN_TARGET","defaultValue":"$$generate_fqdn","description":"A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite '_APP_DOMAIN' variable. The default value is 'localhost'."},{"id":"$$config__app_redis_host","name":"_APP_REDIS_HOST","label":"Redis | _APP_REDIS_HOST","defaultValue":"$$id-redis","description":""},{"id":"$$config__app_redis_port","name":"_APP_REDIS_PORT","label":"Redis | _APP_REDIS_PORT","defaultValue":"6379","description":"Redis server TCP port."},{"id":"$$config__app_redis_user","name":"_APP_REDIS_USER","label":"Redis | _APP_REDIS_USER","defaultValue":"","description":"Redis server user. This is an optional variable. Default value is an empty string."},{"id":"$$secret__app_redis_pass","name":"_APP_REDIS_PASS","label":"Redis | _APP_REDIS_PASS","defaultValue":"","description":"Redis server password. This is an optional variable. Default value is an empty string."},{"id":"$$config__app_db_host","name":"_APP_DB_HOST","label":"MariaDB | _APP_DB_HOST","defaultValue":"$$id-mariadb","description":""},{"id":"$$config__app_db_port","name":"_APP_DB_PORT","label":"MariaDB | _APP_DB_PORT","defaultValue":"3306","description":"MariaDB server TCP port."},{"id":"$$config__app_db_schema","name":"_APP_DB_SCHEMA","label":"MariaDB | _APP_DB_SCHEMA","defaultValue":"appwrite","description":"MariaDB server database schema."},{"id":"$$config__app_db_user","name":"_APP_DB_USER","label":"MariaDB | _APP_DB_USER","defaultValue":"user","description":"MariaDB server user name."},{"id":"$$secret__app_db_root_pass","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB | MARIADB_ROOT_PASSWORD","defaultValue":"$$generate_hex(16)","description":"MariaDB server root user password."},{"id":"$$secret__app_db_pass","name":"_APP_DB_PASS","label":"MariaDB | _APP_DB_PASS","defaultValue":"$$generate_hex(16)","description":"MariaDB server user password."},{"id":"$$config__app_smtp_host","name":"_APP_SMTP_HOST","label":"SMTP | _APP_SMTP_HOST","defaultValue":"","description":"SMTP server host name address. Use an empty string to disable all mail sending from the server. The default value for this variable is an empty string."},{"id":"$$config__app_smtp_port","name":"_APP_SMTP_PORT","label":"SMTP | _APP_SMTP_PORT","defaultValue":"","description":"SMTP server TCP port. Empty by default."},{"id":"$$config__app_smtp_secure","name":"_APP_SMTP_SECURE","label":"SMTP | _APP_SMTP_SECURE","defaultValue":"","description":"SMTP secure connection protocol. Empty by default, change to 'tls' if running on a secure connection."},{"id":"$$config__app_smtp_username","name":"_APP_SMTP_USERNAME","label":"SMTP | _APP_SMTP_USERNAME","defaultValue":"","description":"SMTP server user name. Empty by default."},{"id":"$$secret__app_smtp_password","name":"_APP_SMTP_PASSWORD","label":"SMTP | _APP_SMTP_PASSWORD","defaultValue":"","description":"SMTP server user password. Empty by default."},{"id":"$$config__app_usage_stats","name":"_APP_USAGE_STATS","label":"General | _APP_USAGE_STATS","defaultValue":"enabled","description":"This variable allows you to disable the collection and displaying of usage stats. This value is set to 'enabled' by default, to disable the usage stats set the value to 'disabled'. When disabled, it's recommended to turn off the Worker Usage, Influxdb and Telegraf containers for better resource usage."},{"id":"$$config__app_storage_limit","name":"_APP_STORAGE_LIMIT","label":"Storage | _APP_STORAGE_LIMIT","defaultValue":"30000000","description":"Maximum file size allowed for file upload. The default value is 30MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_preview_limit","name":"_APP_STORAGE_PREVIEW_LIMIT","label":"Storage | _APP_STORAGE_PREVIEW_LIMIT","defaultValue":"20000000","description":"Maximum file size allowed for file image preview. The default value is 20MB. You should pass your size limit value in bytes."},{"id":"$$config__app_storage_antivirus_enabled","name":"_APP_STORAGE_ANTIVIRUS","label":"Storage | _APP_STORAGE_ANTIVIRUS","defaultValue":"disabled","description":"This variable allows you to disable the internal anti-virus scans. This value is set to 'disabled' by default, to enable the scans set the value to 'enabled'. Before enabling, you must add the ClamAV service and depend on it on main Appwrite service."},{"id":"$$config__app_storage_antivirus_host","name":"_APP_STORAGE_ANTIVIRUS_HOST","label":"Storage | _APP_STORAGE_ANTIVIRUS_HOST","defaultValue":"clamav","description":"ClamAV server host name address."},{"id":"$$config__app_storage_antivirus_port","name":"_APP_STORAGE_ANTIVIRUS_PORT","label":"Storage | _APP_STORAGE_ANTIVIRUS_PORT","defaultValue":"3310","description":"ClamAV server TCP port."},{"id":"$$config__app_storage_device","name":"_APP_STORAGE_DEVICE","label":"Storage | _APP_STORAGE_DEVICE","defaultValue":"Local","description":"Select default storage device. The default value is 'Local'. List of supported adapters are 'Local', 'S3', 'DOSpaces', 'Backblaze', 'Linode' and 'Wasabi'."},{"id":"$$secret__app_storage_s3_access_key","name":"_APP_STORAGE_S3_ACCESS_KEY","label":"Storage | _APP_STORAGE_S3_ACCESS_KEY","defaultValue":"","description":"AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console."},{"id":"$$secret__app_storage_s3_secret","name":"_APP_STORAGE_S3_SECRET","label":"Storage | _APP_STORAGE_S3_SECRET","defaultValue":"","description":"AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console."},{"id":"$$config__app_storage_s3_region","name":"_APP_STORAGE_S3_REGION","label":"Storage | _APP_STORAGE_S3_REGION","defaultValue":"us-east-1","description":"AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console."},{"id":"$$config__app_storage_s3_bucket","name":"_APP_STORAGE_S3_BUCKET","label":"Storage | _APP_STORAGE_S3_BUCKET","defaultValue":"","description":"AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console."},{"id":"$$secret__app_storage_do_spaces_access_key","name":"_APP_STORAGE_DO_SPACES_ACCESS_KEY","label":"Storage | _APP_STORAGE_DO_SPACES_ACCESS_KEY","defaultValue":"","description":"DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console."},{"id":"$$secret__app_storage_do_spaces_secret","name":"_APP_STORAGE_DO_SPACES_SECRET","label":"Storage | _APP_STORAGE_DO_SPACES_SECRET","defaultValue":"","description":"DigitalOcean spaces secret key. Required when the storage adapter is set to DOSpaces. You can get your secret key from your DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_region","name":"_APP_STORAGE_DO_SPACES_REGION","label":"Storage | _APP_STORAGE_DO_SPACES_REGION","defaultValue":"us-east-1","description":"DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console."},{"id":"$$config__app_storage_do_spaces_bucket","name":"_APP_STORAGE_DO_SPACES_BUCKET","label":"Storage | _APP_STORAGE_DO_SPACES_BUCKET","defaultValue":"","description":"DigitalOcean spaces bucket. Required when storage adapter is set to DOSpaces. You can create spaces in your DigitalOcean console."},{"id":"$$secret__app_storage_backblaze_access_key","name":"_APP_STORAGE_BACKBLAZE_ACCESS_KEY","label":"Storage | _APP_STORAGE_BACKBLAZE_ACCESS_KEY","defaultValue":"","description":"Backblaze access key. Required when the storage adapter is set to Backblaze. Your Backblaze keyID will be your access key. You can get your keyID from your Backblaze console."},{"id":"$$secret__app_storage_backblaze_secret","name":"_APP_STORAGE_BACKBLAZE_SECRET","label":"Storage | _APP_STORAGE_BACKBLAZE_SECRET","defaultValue":"","description":"Backblaze secret key. Required when the storage adapter is set to Backblaze. Your Backblaze applicationKey will be your secret key. You can get your applicationKey from your Backblaze console."},{"id":"$$config__app_storage_backblaze_region","name":"_APP_STORAGE_BACKBLAZE_REGION","label":"Storage | _APP_STORAGE_BACKBLAZE_REGION","defaultValue":"us-west-004","description":"Backblaze region. Required when storage adapter is set to Backblaze. You can find your region info from your Backblaze console."},{"id":"$$config__app_storage_backblaze_bucket","name":"_APP_STORAGE_BACKBLAZE_BUCKET","label":"Storage | _APP_STORAGE_BACKBLAZE_BUCKET","defaultValue":"","description":"Backblaze bucket. Required when storage adapter is set to Backblaze. You can create your bucket from your Backblaze console."},{"id":"$$secret__app_storage_linode_access_key","name":"_APP_STORAGE_LINODE_ACCESS_KEY","label":"Storage | _APP_STORAGE_LINODE_ACCESS_KEY","defaultValue":"","description":"Linode object storage access key. Required when the storage adapter is set to Linode. You can get your access key from your Linode console."},{"id":"$$secret__app_storage_linode_secret","name":"_APP_STORAGE_LINODE_SECRET","label":"Storage | _APP_STORAGE_LINODE_SECRET","defaultValue":"","description":"Linode object storage secret key. Required when the storage adapter is set to Linode. You can get your secret key from your Linode console."},{"id":"$$config__app_storage_linode_region","name":"_APP_STORAGE_LINODE_REGION","label":"Storage | _APP_STORAGE_LINODE_REGION","defaultValue":"eu-central-1","description":"Linode object storage region. Required when storage adapter is set to Linode. You can find your region info from your Linode console."},{"id":"$$config__app_storage_linode_bucket","name":"_APP_STORAGE_LINODE_BUCKET","label":"Storage | _APP_STORAGE_LINODE_BUCKET","defaultValue":"","description":"Linode object storage bucket. Required when storage adapter is set to Linode. You can create buckets in your Linode console."},{"id":"$$secret__app_storage_wasabi_access_key","name":"_APP_STORAGE_WASABI_ACCESS_KEY","label":"Storage | _APP_STORAGE_WASABI_ACCESS_KEY","defaultValue":"","description":"Wasabi access key. Required when the storage adapter is set to Wasabi. You can get your access key from your Wasabi console."},{"id":"$$secret__app_storage_wasabi_secret","name":"_APP_STORAGE_WASABI_SECRET","label":"Storage | _APP_STORAGE_WASABI_SECRET","defaultValue":"","description":"Wasabi secret key. Required when the storage adapter is set to Wasabi. You can get your secret key from your Wasabi console."},{"id":"$$config__app_storage_wasabi_region","name":"_APP_STORAGE_WASABI_REGION","label":"Storage | _APP_STORAGE_WASABI_REGION","defaultValue":"eu-central-1","description":"Wasabi region. Required when storage adapter is set to Wasabi. You can find your region info from your Wasabi console."},{"id":"$$config__app_storage_wasabi_bucket","name":"_APP_STORAGE_WASABI_BUCKET","label":"Storage | _APP_STORAGE_WASABI_BUCKET","defaultValue":"","description":"Wasabi bucket. Required when storage adapter is set to Wasabi. You can create buckets in your Wasabi console."},{"id":"$$config__app_functions_size_limit","name":"_APP_FUNCTIONS_SIZE_LIMIT","label":"Functions | _APP_FUNCTIONS_SIZE_LIMIT","defaultValue":"30000000","description":"The maximum size deployment in bytes. The default value is 30MB."},{"id":"$$config__app_functions_timeout","name":"_APP_FUNCTIONS_TIMEOUT","label":"Functions | _APP_FUNCTIONS_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_build_timeout","name":"_APP_FUNCTIONS_BUILD_TIMEOUT","label":"Functions | _APP_FUNCTIONS_BUILD_TIMEOUT","defaultValue":"900","description":"The maximum number of seconds allowed as a timeout value when building a new function. The default value is 900 seconds."},{"id":"$$config__app_functions_containers","name":"_APP_FUNCTIONS_CONTAINERS","label":"Functions | _APP_FUNCTIONS_CONTAINERS","defaultValue":"10","description":"The maximum number of containers Appwrite is allowed to keep alive in the background for function environments. Running containers allow faster execution time as there is no need to recreate each container every time a function gets executed. The default value is 10."},{"id":"$$config__app_functions_cpus","name":"_APP_FUNCTIONS_CPUS","label":"Functions | _APP_FUNCTIONS_CPUS","defaultValue":"","description":"The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it's empty, CPU limit will be disabled."},{"id":"$$config__app_functions_memory_allocated","name":"_APP_FUNCTIONS_MEMORY","label":"Functions | _APP_FUNCTIONS_MEMORY","defaultValue":"","description":"The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, memory limit will be disabled."},{"id":"$$config__app_functions_memory_swap","name":"_APP_FUNCTIONS_MEMORY_SWAP","label":"Functions | _APP_FUNCTIONS_MEMORY_SWAP","defaultValue":"","description":"The maximum amount of swap memory a single cloud function is allowed to use in megabytes. The default value is empty. When it's empty, swap memory limit will be disabled."},{"id":"$$config__app_functions_runtimes","name":"_APP_FUNCTIONS_RUNTIMES","label":"Functions | _APP_FUNCTIONS_RUNTIMES","defaultValue":"node-18.0","description":"This option allows you to limit the available environments for cloud functions. This option is very useful for low-cost servers to safe disk space.\nTo enable/activate this option, pass a list of allowed environments separated by a comma.\nCurrently, supported environments are: node-14.5, node-16.0, node-18.0, php-8.0, php-8.1, ruby-3.0, ruby-3.1, python-3.8, python-3.9, python-3.10, deno-1.21, deno-1.24, dart-2.15, dart-2.16, dart-2.17, dotnet-3.1, dotnet-6.0, java-8.0, java-11.0, java-17.0, java-18.0, swift-5.5, kotlin-1.6, cpp-17.0"},{"id":"$$secret__app_executor_secret","name":"_APP_EXECUTOR_SECRET","label":"Functions | _APP_EXECUTOR_SECRET","defaultValue":"$$generate_hex(16)","description":"The secret key used by Appwrite to communicate with the function executor."},{"id":"$$config__app_executor_host","name":"_APP_EXECUTOR_HOST","label":"","defaultValue":"http://$$id-executor/v1","description":""},{"id":"$$config__app_logging_provider","name":"_APP_LOGGING_PROVIDER","label":"General | _APP_LOGGING_PROVIDER","defaultValue":"","description":"This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, to enable the logger set the value to one of 'sentry', 'raygun', 'appsignal', 'logowl'"},{"id":"$$config__app_logging_config","name":"_APP_LOGGING_CONFIG","label":"General | _APP_LOGGING_CONFIG","defaultValue":"","description":"This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be 'SENTRY_API_KEY;SENTRY_APP_ID'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket."},{"id":"$$config__app_statsd_host","name":"_APP_STATSD_HOST","label":"","defaultValue":"$$id-telegraf","description":""},{"id":"$$config__app_statsd_port","name":"_APP_STATSD_PORT","label":"StatsD | _APP_STATSD_PORT","defaultValue":"8125","description":"StatsD server TCP port."},{"id":"$$config__app_maintenance_interval","name":"_APP_MAINTENANCE_INTERVAL","label":"Functions | _APP_MAINTENANCE_INTERVAL","defaultValue":"86400","description":"Interval value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_execution","name":"_APP_MAINTENANCE_RETENTION_EXECUTION","label":"Functions | _APP_MAINTENANCE_RETENTION_EXECUTION","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain execution logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_maintenance_retention_cache","name":"_APP_MAINTENANCE_RETENTION_CACHE","label":"Functions | _APP_MAINTENANCE_RETENTION_CACHE","defaultValue":"2592000","description":"The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days)."},{"id":"$$config__app_maintenance_retention_abuse","name":"_APP_MAINTENANCE_RETENTION_ABUSE","label":"Functions | _APP_MAINTENANCE_RETENTION_ABUSE","defaultValue":"86400","description":"The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day)."},{"id":"$$config__app_maintenance_retention_audit","name":"_APP_MAINTENANCE_RETENTION_AUDIT","label":"Functions | _APP_MAINTENANCE_RETENTION_AUDIT","defaultValue":"1209600","description":"The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days)."},{"id":"$$config__app_sms_provider","name":"_APP_SMS_PROVIDER","label":"Phone | _APP_SMS_PROVIDER","defaultValue":"","description":"Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. Available providers are twilio, text-magic, telesign, msg91, and vonage."},{"id":"$$config__app_sms_from","name":"_APP_SMS_FROM","label":"Phone | _APP_SMS_FROM","defaultValue":"","description":"Phone number used for sending out messages. Must start with a leading '+' and maximum of 15 digits without spaces (+123456789)."},{"id":"$$config__app_functions_inactive_threshold","name":"_APP_FUNCTIONS_INACTIVE_THRESHOLD","label":"Functions | _APP_FUNCTIONS_INACTIVE_THRESHOLD","defaultValue":"60","description":"The minimum time a function can be inactive before it's container is shutdown and put to sleep. The default value is 60 seconds"},{"id":"$$config_open_runtimes_network","name":"OPEN_RUNTIMES_NETWORK","label":"","defaultValue":"$$generate_network","description":""},{"id":"$$config_dockerhub_pull_username","name":"DOCKERHUB_PULL_USERNAME","label":"Functions | DOCKERHUB_PULL_USERNAME","defaultValue":"","description":"The username for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$secret_dockerhub_pull_password","name":"DOCKERHUB_PULL_PASSWORD","label":"Functions | DOCKERHUB_PULL_PASSWORD","defaultValue":"","description":"The password for hub.docker.com. This variable is used to pull images from hub.docker.com."},{"id":"$$config__app_usage_timeseries_interval","name":"_APP_USAGE_TIMESERIES_INTERVAL","label":"General | _APP_USAGE_TIMESERIES_INTERVAL","defaultValue":"30","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats and syncing it to mariadb from InfluxDB. The default value is 30 seconds."},{"id":"$$config__app_usage_database_interval","name":"_APP_USAGE_DATABASE_INTERVAL","label":"General | _APP_USAGE_DATABASE_INTERVAL","defaultValue":"900","description":"Interval value containing the number of seconds that the Appwrite usage process should wait before aggregating stats from data in Appwrite Database. The default value is 15 minutes."}]},{"templateVersion":"1.0.0","defaultVersion":"latest","documentation":"https://docs.weblate.org/en/latest/admin/install/docker.html","description":"A copylefted libre software web-based continuous localization system.","type":"weblate","name":"Weblate","labels":["translate","localization"],"services":{"$$id":{"name":"Weblate","depends_on":["$$id-postgresql","$$id-redis"],"image":"weblate/weblate:$$core_version","volumes":["$$id-data:/app/data"],"environment":["WEBLATE_SITE_DOMAIN=$$config_weblate_site_domain","WEBLATE_ADMIN_PASSWORD=$$secret_weblate_admin_password","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_USER=$$config_postgres_user","POSTGRES_DATABASE=$$config_postgres_db","POSTGRES_HOST=$$id-postgresql","POSTGRES_PORT=5432","REDIS_HOST=$$id-redis"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_weblate_site_domain","name":"WEBLATE_SITE_DOMAIN","label":"Weblate Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$secret_weblate_admin_password","name":"WEBLATE_ADMIN_PASSWORD","label":"Weblate Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"weblate","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"2023.01.15-52d41559","documentation":"https://docs.searxng.org/","type":"searxng","name":"SearXNG","description":"Free internet metasearch engine which aggregates results from more than 70 search services.","services":{"$$id":{"name":"SearXNG","depends_on":["$$id-redis"],"image":"searxng/searxng:$$core_version","volumes":["$$id-searxng:/etc/searxng"],"environment":["SEARXNG_BASE_URL=$$config_searxng_base_url"],"ports":["8080"],"cap_drop":["ALL"],"cap_add":["CHOWN","SETGID","SETUID","DAC_OVERRIDE"],"files":[{"location":"/etc/searxng/settings.yml","content":"\n # see https://docs.searxng.org/admin/engines/settings.html#use-default-settings\n use_default_settings: true\n server:\n secret_key: $$secret_secret_key\n limiter: true\n image_proxy: true\n ui:\n static_use_hash: true\n redis:\n url: redis://:$$secret_redis_password@$$id-redis:6379/0"}]},"$$id-redis":{"name":"Redis","command":"redis-server --requirepass $$secret_redis_password --save \"\" --appendonly \"no\"","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-redis-data:/data"],"environment":["REDIS_PASSWORD=$$secret_redis_password"],"ports":[],"cap_drop":["ALL"],"cap_add":["SETGID","SETUID","DAC_OVERRIDE"]}},"variables":[{"id":"$$config_searxng_base_url","name":"SEARXNG_BASE_URL","label":"SearXNG Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_secret_key","name":"SECRET_KEY","label":"Secret Key","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$secret_redis_password","name":"REDIS_PASSWORD","label":"Redis Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v3.0.2","documentation":"https://glitchtip.com/documentation","type":"glitchtip","name":"GlitchTip","description":"Simple, open source error tracking.","labels":["sentry","bugsnag"],"services":{"$$id":{"name":"GlitchTip","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","volumes":[],"environment":["PORT=$$config_port","GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","EMAIL_HOST=$$config_email_host","EMAIL_PORT=$$config_email_port","EMAIL_HOST_USER=$$config_email_host_user","EMAIL_HOST_PASSWORD=$$secret_email_host_password","EMAIL_USE_TLS=$$config_email_use_tls","EMAIL_USE_SSL=$$config_email_use_ssl","EMAIL_BACKEND=$$config_email_backend","MAILGUN_API_KEY=$$secret_mailgun_api_key","SENDGRID_API_KEY=$$secret_sendgrid_api_key","ENABLE_OPEN_USER_REGISTRATION=$$config_enable_open_user_registration","DJANGO_SUPERUSER_EMAIL=$$config_django_superuser_email","DJANGO_SUPERUSER_PASSWORD=$$secret_django_superuser_password","DJANGO_SUPERUSER_USERNAME=$$config_django_superuser_username","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":["8000"]},"$$id-worker":{"name":"Celery Worker","command":"./bin/run-celery-with-beat.sh","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url","CELERY_WORKER_CONCURRENCY=$$config_celery_worker_concurrency"],"ports":[]},"$$id-migrate":{"exclude":true,"name":"Migrate","command":"./manage.py migrate","depends_on":["$$id-postgresql","$$id-redis"],"image":"glitchtip/glitchtip:$$core_version","environment":["GLITCHTIP_DOMAIN=$$config_glitchtip_domain","SECRET_KEY=$$secret_secret_key","DATABASE_URL=$$secret_database_url","REDIS_URL=$$secret_redis_url","DEFAULT_FROM_EMAIL=$$config_default_from_email","EMAIL_URL=$$secret_email_url"],"ports":[]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:14-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]},"$$id-redis":{"name":"Redis","depends_on":[],"image":"redis:7-alpine","volumes":["$$id-postgresql-redis-data:/data"],"environment":[],"ports":[]}},"variables":[{"id":"$$config_django_superuser_username","name":"DJANGO_SUPERUSER_USERNAME","label":"Django Superuser Username","defaultValue":"$$generate_username","description":""},{"id":"$$secret_django_superuser_password","name":"DJANGO_SUPERUSER_PASSWORD","label":"Django Superuser Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_port","name":"PORT","label":"GlitchTip Port","defaultValue":"8000","description":""},{"id":"$$config_celery_worker_concurrency","main":"$$id-worker","name":"CELERY_WORKER_CONCURRENCY","label":"Celery Worker Concurrency","defaultValue":"2","description":""},{"id":"$$config_glitchtip_domain","name":"GLITCHTIP_DOMAIN","label":"GlitchTip Domain","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_email_url","name":"EMAIL_URL","label":"SMTP Email URL","defaultValue":"smtp://$$config_email_host_user:$$secret_email_host_password@$$config_email_host:$$config_email_port","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_redis_url","name":"REDIS_URL","label":"Redis URL","defaultValue":"redis://$$id-redis:6379/0","description":""},{"id":"$$config_default_from_email","name":"DEFAULT_FROM_EMAIL","label":"Default Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$config_email_host","name":"EMAIL_HOST","label":"Email SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_port","name":"EMAIL_PORT","label":"Email SMTP Port","defaultValue":"25","description":""},{"id":"$$config_email_host_user","name":"EMAIL_HOST_USER","label":"Email SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_host_password","name":"EMAIL_HOST_PASSWORD","label":"Email SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_use_tls","name":"EMAIL_USE_TLS","label":"Email Use TLS","defaultValue":"false","description":""},{"id":"$$config_email_use_ssl","name":"EMAIL_USE_SSL","label":"Email Use SSL","defaultValue":"false","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":""},{"id":"$$config_email_backend","name":"EMAIL_BACKEND","label":"Email Backend","defaultValue":"","description":""},{"id":"$$secret_mailgun_api_key","name":"MAILGUN_API_KEY","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$secret_sendgrid_api_key","name":"SENDGRID_API_KEY","label":"Sendgrid API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_enable_open_user_registration","name":"ENABLE_OPEN_USER_REGISTRATION","label":"Enable Open User Registration","defaultValue":"true","description":""},{"id":"$$config_django_superuser_email","name":"DJANGO_SUPERUSER_EMAIL","label":"Django Superuser Email","defaultValue":"noreply@example.com","description":""},{"id":"$$config_postgres_user","main":"$$id-postgresql","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","main":"$$id-postgresql","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","main":"$$id-postgresql","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"glitchtip","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v2.16.1","documentation":"https://hasura.io/docs/latest/index/","type":"hasura","name":"Hasura","description":"Instant realtime GraphQL APIs on any Postgres application, existing or new.","labels":["graphql","database"],"services":{"$$id":{"name":"Hasura","depends_on":["$$id-postgresql"],"image":"hasura/graphql-engine:$$core_version","volumes":[],"environment":["HASURA_GRAPHQL_ENABLE_CONSOLE=$$config_hasura_graphql_enable_console","HASURA_GRAPHQL_METADATA_DATABASE_URL=$$secret_hasura_graphql_metadata_database_url","HASURA_GRAPHQL_ADMIN_SECRET=$$secret_hasura_graphql_admin_secret"],"ports":["8080"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[]}},"variables":[{"id":"$$config_hasura_graphql_enable_console","name":"HASURA_GRAPHQL_ENABLE_CONSOLE","label":"Enable Hasura Console","defaultValue":"true","description":""},{"id":"$$secret_hasura_graphql_metadata_database_url","name":"HASURA_GRAPHQL_METADATA_DATABASE_URL","label":"Hasura Metadata Database URL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hasura_graphql_admin_secret","name":"HASURA_GRAPHQL_ADMIN_SECRET","label":"Hasura Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"hasura","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"postgresql-v1.39.5","documentation":"https://umami.is/docs/getting-started","type":"umami-postgresql","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"user_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n\n PRIMARY KEY (\"event_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n\n PRIMARY KEY (\"view_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n\n PRIMARY KEY (\"session_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"website_id\")\n );\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n\n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n\n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n\n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"postgresql-v1.39.5","documentation":"https://umami.is/docs/getting-started","type":"umami","name":"Umami","subname":"(PostgreSQL)","description":"A simple, easy to use, self-hosted web analytics solution.","services":{"$$id":{"name":"Umami","depends_on":["$$id-postgresql"],"image":"ghcr.io/umami-software/umami:$$core_version","volumes":[],"environment":["ADMIN_PASSWORD=$$secret_admin_password","DATABASE_URL=$$secret_database_url","DATABASE_TYPE=$$config_database_type","HASH_SALT=$$secret_hash_salt"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"],"ports":[],"files":[{"location":"/docker-entrypoint-initdb.d/schema.postgresql.sql","content":"\n -- CreateTable\n CREATE TABLE \"account\" (\n \"user_id\" SERIAL NOT NULL,\n \"username\" VARCHAR(255) NOT NULL,\n \"password\" VARCHAR(60) NOT NULL,\n \"is_admin\" BOOLEAN NOT NULL DEFAULT false,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"updated_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"user_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"event\" (\n \"event_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"event_type\" VARCHAR(50) NOT NULL,\n \"event_value\" VARCHAR(50) NOT NULL,\n\n PRIMARY KEY (\"event_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"pageview\" (\n \"view_id\" SERIAL NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"session_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"url\" VARCHAR(500) NOT NULL,\n \"referrer\" VARCHAR(500),\n\n PRIMARY KEY (\"view_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"session\" (\n \"session_id\" SERIAL NOT NULL,\n \"session_uuid\" UUID NOT NULL,\n \"website_id\" INTEGER NOT NULL,\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n \"hostname\" VARCHAR(100),\n \"browser\" VARCHAR(20),\n \"os\" VARCHAR(20),\n \"device\" VARCHAR(20),\n \"screen\" VARCHAR(11),\n \"language\" VARCHAR(35),\n \"country\" CHAR(2),\n\n PRIMARY KEY (\"session_id\")\n );\n\n -- CreateTable\n CREATE TABLE \"website\" (\n \"website_id\" SERIAL NOT NULL,\n \"website_uuid\" UUID NOT NULL,\n \"user_id\" INTEGER NOT NULL,\n \"name\" VARCHAR(100) NOT NULL,\n \"domain\" VARCHAR(500),\n \"share_id\" VARCHAR(64),\n \"created_at\" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,\n\n PRIMARY KEY (\"website_id\")\n );\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"account.username_unique\" ON \"account\"(\"username\");\n\n -- CreateIndex\n CREATE INDEX \"event_created_at_idx\" ON \"event\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"event_session_id_idx\" ON \"event\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"event_website_id_idx\" ON \"event\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_created_at_idx\" ON \"pageview\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_session_id_idx\" ON \"pageview\"(\"session_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_idx\" ON \"pageview\"(\"website_id\");\n\n -- CreateIndex\n CREATE INDEX \"pageview_website_id_session_id_created_at_idx\" ON \"pageview\"(\"website_id\", \"session_id\", \"created_at\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"session.session_uuid_unique\" ON \"session\"(\"session_uuid\");\n\n -- CreateIndex\n CREATE INDEX \"session_created_at_idx\" ON \"session\"(\"created_at\");\n\n -- CreateIndex\n CREATE INDEX \"session_website_id_idx\" ON \"session\"(\"website_id\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.website_uuid_unique\" ON \"website\"(\"website_uuid\");\n\n -- CreateIndex\n CREATE UNIQUE INDEX \"website.share_id_unique\" ON \"website\"(\"share_id\");\n\n -- CreateIndex\n CREATE INDEX \"website_user_id_idx\" ON \"website\"(\"user_id\");\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"event\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"session_id\") REFERENCES \"session\"(\"session_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"pageview\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"session\" ADD FOREIGN KEY (\"website_id\") REFERENCES \"website\"(\"website_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n -- AddForeignKey\n ALTER TABLE \"website\" ADD FOREIGN KEY (\"user_id\") REFERENCES \"account\"(\"user_id\") ON DELETE CASCADE ON UPDATE CASCADE;\n\n insert into account (username, password, is_admin) values ('admin', '$$hashed$$secret_admin_password', true);"}]}},"variables":[{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db","description":""},{"id":"$$secret_hash_salt","name":"HASH_SALT","label":"Hash Salt","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_database_type","name":"DATABASE_TYPE","label":"Database Type","defaultValue":"postgresql","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"umami","description":""},{"id":"$$secret_admin_password","name":"ADMIN_PASSWORD","label":"Initial Admin Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"v0.30.5","documentation":"https://docs.meilisearch.com/learn/getting_started/quick_start.html","type":"meilisearch","name":"MeiliSearch","description":"A lightning Fast, Ultra Relevant, and Typo-Tolerant Search Engine.","services":{"$$id":{"name":"MeiliSearch","documentation":"https://docs.meilisearch.com/","depends_on":[],"image":"getmeili/meilisearch:$$core_version","volumes":["$$id-datams:/meili_data/data.ms","$$id-data:/meili_data","$$id-snapshot:/snapshot","$$id-dump:/dumps"],"environment":["MEILI_MASTER_KEY=$$secret_meili_master_key"],"ports":["7700"]}},"variables":[{"id":"$$secret_meili_master_key","name":"MEILI_MASTER_KEY","label":"Master Key","defaultValue":"$$generate_hex(64)","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","ignore":true,"defaultVersion":"5.30.0","documentation":"https://docs.ghost.org","arch":"amd64","type":"ghost-mariadb","name":"Ghost","subname":"(MariaDB)","description":"Free and open source blogging platform.","labels":["cms","blog"],"services":{"$$id":{"name":"Ghost","depends_on":["$$id-mariadb"],"image":"bitnami/ghost:$$core_version","volumes":["$$id-ghost:/bitnami/ghost"],"environment":["url=$$config_url","GHOST_HOST=$$config_ghost_host","GHOST_ENABLE_HTTPS=$$config_ghost_enable_https","GHOST_EMAIL=$$config_ghost_email","GHOST_PASSWORD=$$secret_ghost_password","GHOST_DATABASE_HOST=$$config_ghost_database_host","GHOST_DATABASE_USER=$$config_mariadb_user","GHOST_DATABASE_PASSWORD=$$secret_ghost_database_password","GHOST_DATABASE_NAME=$$config_mariadb_database","GHOST_DATABASE_PORT_NUMBER=3306"],"ports":["2368"]},"$$id-mariadb":{"name":"MariaDB","depends_on":[],"image":"bitnami/mariadb:latest","volumes":["$$id-mariadb:/bitnami/mariadb"],"environment":["MARIADB_USER=$$config_mariadb_user","MARIADB_PASSWORD=$$secret_mariadb_password","MARIADB_DATABASE=$$config_mariadb_database","MARIADB_ROOT_USER=$$config_mariadb_root_user","MARIADB_ROOT_PASSWORD=$$secret_mariadb_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_ghost_host","name":"GHOST_HOST","label":"Ghost Host","defaultValue":"$$generate_domain","description":""},{"id":"$$config_ghost_enable_https","name":"GHOST_ENABLE_HTTPS","label":"Ghost Enable HTTPS","defaultValue":"no","description":""},{"id":"$$config_ghost_email","name":"GHOST_EMAIL","label":"Ghost Default Email","defaultValue":"admin@example.com","description":""},{"id":"$$secret_ghost_password","name":"GHOST_PASSWORD","label":"Ghost Default Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_ghost_database_host","name":"GHOST_DATABASE_HOST","label":"Ghost Database Host","defaultValue":"$$id-mariadb","description":""},{"id":"$$config_ghost_database_user","name":"GHOST_DATABASE_USER","label":"MariaDB User","defaultValue":"$$config_mariadb_user","description":""},{"id":"$$secret_ghost_database_password","name":"GHOST_DATABASE_PASSWORD","label":"MariaDB Password","defaultValue":"$$secret_mariadb_password","description":""},{"id":"$$config_ghost_database_name","name":"GHOST_DATABASE_NAME","label":"MariaDB Database","defaultValue":"$$config_mariadb_database","description":""},{"id":"$$config_mariadb_user","name":"MARIADB_USER","label":"MariaDB User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_password","name":"MARIADB_PASSWORD","label":"MariaDB Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mariadb_database","name":"MARIADB_DATABASE","label":"MariaDB Database","defaultValue":"ghost","description":""},{"id":"$$config_mariadb_root_user","name":"MARIADB_ROOT_USER","label":"MariaDB Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mariadb_root_password","name":"MARIADB_ROOT_PASSWORD","label":"MariaDB Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"5.30.0","documentation":"https://docs.ghost.org","type":"ghost-only","name":"Ghost","subname":"(without Database)","description":"Free and open source blogging platform.","services":{"$$id":{"name":"Ghost","image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_database__connection__user","database__connection__password=$$secret_database__connection__password","database__connection__database=$$config_database__connection__database"],"ports":["2368"]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","required":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"","description":"","required":true,"placeholder":"db.coolify.io"},{"id":"$$config_database__connection__user","name":"database__connection__user","label":"Database User","defaultValue":"","description":"","placeholder":"ghost","required":true},{"id":"$$secret_database__connection__password","name":"database__connection__password","label":"Database Password","defaultValue":"","description":"","placeholder":"superSecretP4ssword","showOnConfiguration":true,"required":true},{"id":"$$config_database__connection__database","name":"database__connection__database","label":"Database Name","defaultValue":"","description":"","placeholder":"ghost_db","required":true}]},{"templateVersion":"1.0.0","defaultVersion":"5.30.0","documentation":"https://docs.ghost.org","type":"ghost-mysql","name":"Ghost","subname":"(MySQL)","description":"Ghost is a free and open source blogging platform.","services":{"$$id":{"name":"Ghost","depends_on":["$$id-mysql"],"image":"ghost:$$core_version","volumes":["$$id-ghost:/var/lib/ghost/content"],"environment":["url=$$config_url","database__client=$$config_database__client","database__connection__host=$$config_database__connection__host","database__connection__user=$$config_mysql_user","database__connection__password=$$secret_mysql_password","database__connection__database=$$config_mysql_database"],"ports":["2368"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"mysql:8.0","volumes":["$$id-mysql:/var/lib/mysql"],"environment":["MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password","MYSQL_DATABASE=$$config_mysql_database","MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password"],"ports":[]}},"variables":[{"id":"$$config_url","name":"url","label":"URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_database__client","name":"database__client","label":"Database Client","defaultValue":"mysql","description":"","readOnly":true},{"id":"$$config_database__connection__host","name":"database__connection__host","label":"Database Host","defaultValue":"$$id-mysql","description":""},{"id":"$$config_mysql_user","main":"$$id-mysql","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_mysql_password","main":"$$id-mysql","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_mysql_database","main":"$$id-mysql","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"ghost","description":""},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"php8.2","documentation":"https://wordpress.org/","type":"wordpress","name":"WordPress","subname":"(MySQL)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","depends_on":["$$id-mysql"],"image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_USER=$$config_mysql_user","WORDPRESS_DB_PASSWORD=$$secret_mysql_password","WORDPRESS_DB_NAME=$$config_mysql_database","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]},"$$id-mysql":{"name":"MySQL","depends_on":[],"image":"bitnami/mysql:5.7","imageArm":"mysql:8.0","volumes":["$$id-mysql-data:/bitnami/mysql/data"],"volumesArm":["$$id-mysql-data:/var/lib/mysql"],"environment":["MYSQL_ROOT_PASSWORD=$$secret_mysql_root_password","MYSQL_ROOT_USER=$$config_mysql_root_user","MYSQL_DATABASE=$$config_mysql_database","MYSQL_USER=$$config_mysql_user","MYSQL_PASSWORD=$$secret_mysql_password"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"$$id-mysql","description":"","readOnly":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"WordPress Config Extra","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"},{"id":"$$secret_mysql_root_password","name":"MYSQL_ROOT_PASSWORD","label":"MySQL Root Password","defaultValue":"$$generate_password","description":"","readOnly":true},{"id":"$$config_mysql_root_user","name":"MYSQL_ROOT_USER","label":"MySQL Root User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$config_mysql_database","name":"MYSQL_DATABASE","label":"MySQL Database","defaultValue":"wordpress","description":"","readOnly":true},{"id":"$$config_mysql_user","name":"MYSQL_USER","label":"MySQL User","defaultValue":"$$generate_username","description":"","readOnly":true},{"id":"$$secret_mysql_password","name":"MYSQL_PASSWORD","label":"MySQL Password","defaultValue":"$$generate_password","description":"","readOnly":true}]},{"templateVersion":"1.0.0","defaultVersion":"php8.2","documentation":"https://wordpress.org/","type":"wordpress-only","name":"WordPress","subname":"(without DB)","description":"A content management system based on PHP.","labels":["wordpress","php","cms"],"services":{"$$id":{"name":"WordPress","image":"wordpress:$$core_version","volumes":["$$id-wordpress-data:/var/www/html"],"environment":["WORDPRESS_DB_HOST=$$config_wordpress_db_host","WORDPRESS_DB_PORT=$$config_wordpress_db_port","WORDPRESS_DB_USER=$$config_wordpress_db_user","WORDPRESS_DB_PASSWORD=$$secret_wordpress_db_password","WORDPRESS_DB_NAME=$$config_wordpress_db_name","WORDPRESS_CONFIG_EXTRA=$$config_wordpress_config_extra"],"ports":["80"]}},"variables":[{"id":"$$config_wordpress_db_host","name":"WORDPRESS_DB_HOST","label":"Database Host","defaultValue":"","description":"","placeholder":"db.coollabs.io","required":true},{"id":"$$config_wordpress_db_port","name":"WORDPRESS_DB_PORT","label":"Database Port","defaultValue":"","description":"","placeholder":"3306","required":true},{"id":"$$config_wordpress_db_user","name":"WORDPRESS_DB_USER","label":"Database User","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$secret_wordpress_db_password","name":"WORDPRESS_DB_PASSWORD","label":"Database Password","defaultValue":"","description":"","placeholder":"supers3cr3tpassw0rd!","required":true,"showOnConfiguration":true},{"id":"$$config_wordpress_db_name","name":"WORDPRESS_DB_NAME","label":"Database Name","defaultValue":"","description":"","placeholder":"wordpress","required":true},{"id":"$$config_wordpress_config_extra","name":"WORDPRESS_CONFIG_EXTRA","label":"Extra Config","defaultValue":"","description":"","type":"textarea","placeholder":"define('WP_DEBUG', true);\ndefine('WP_DEBUG_LOG', true);\ndefine('WP_DEBUG_DISPLAY', false);\n@ini_set('display_errors', 0);\n"}]},{"templateVersion":"1.0.0","defaultVersion":"4.9.1","documentation":"https://coder.com/docs/coder-oss/latest","type":"vscodeserver","name":"VSCode Server","description":"Visual Studio Code on a remote server, accessible through the browser.","labels":["vscode","ide"],"services":{"$$id":{"name":"VSCode Server","depends_on":[],"image":"codercom/code-server:$$core_version","volumes":["$$id-vscodeserver-data:/home/coder","$$id-keys-directory:/root/.ssh","$$id-theme-and-plugin-directory:/root/.local/share/code-server"],"environment":["PASSWORD=$$secret_password"],"ports":["8080"]}},"variables":[{"id":"$$secret_password","name":"PASSWORD","label":"Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"RELEASE.2023-01-12T02-06-16Z","documentation":"https://min.io/docs/minio","type":"minio","name":"MinIO","description":"A cloud storage server compatible with Amazon S3.","labels":["storage","s3"],"services":{"$$id":{"name":"MinIO","command":"server /data --console-address :9001","depends_on":[],"image":"minio/minio:$$core_version","volumes":["$$id-minio-data:/data","$$id-data-write:/files"],"environment":["MINIO_SERVER_URL=$$config_coolify_fqdn_minio_console","MINIO_BROWSER_REDIRECT_URL=$$config_minio_browser_redirect_url","MINIO_DOMAIN=$$config_minio_domain","MINIO_ROOT_USER=$$config_minio_root_user","MINIO_ROOT_PASSWORD=$$secret_minio_root_password"],"ports":["9000","9001"],"proxy":[{"port":"9000","domain":"$$config_coolify_fqdn_minio_console"},{"port":"9001"}]}},"variables":[{"id":"$$config_coolify_fqdn_minio_console","name":"MINIO_SERVER_URL","label":"MinIO Server URL","defaultValue":"","description":"Specify the URL hostname the MinIO Console should use for connecting to the MinIO Server.","required":true},{"id":"$$config_minio_browser_redirect_url","name":"MINIO_BROWSER_REDIRECT_URL","label":"Browser Redirect URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$config_minio_domain","name":"MINIO_DOMAIN","label":"Domain","defaultValue":"$$generate_domain","description":""},{"id":"$$config_minio_root_user","name":"MINIO_ROOT_USER","label":"Root User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_minio_root_password","name":"MINIO_ROOT_PASSWORD","label":"Root User Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true}]},{"templateVersion":"1.0.0","defaultVersion":"stable","documentation":"https://fider.io/docs","type":"fider","name":"Fider","description":"A platform to collect and organize customer feedback.","labels":["suggestion","feedback"],"services":{"$$id":{"name":"Fider","image":"getfider/fider:$$core_version","depends_on":["$$id-postgresql"],"environment":["BASE_URL=$$config_base_url","DATABASE_URL=$$secret_database_url","JWT_SECRET=$$secret_jwt_secret","EMAIL_NOREPLY=$$config_email_noreply","EMAIL_MAILGUN_API=$$secret_email_mailgun_api","EMAIL_MAILGUN_REGION=$$config_email_mailgun_region","EMAIL_MAILGUN_DOMAIN=$$config_email_mailgun_domain","EMAIL_SMTP_HOST=$$config_email_smtp_host","EMAIL_SMTP_PORT=$$config_email_smtp_port","EMAIL_SMTP_USER=$$config_email_smtp_user","EMAIL_SMTP_PASSWORD=$$secret_email_smtp_password","EMAIL_SMTP_ENABLE_STARTTLS=$$config_email_smtp_enable_starttls"],"ports":["3000"]},"$$id-postgresql":{"name":"PostgreSQL","depends_on":[],"image":"postgres:12-alpine","volumes":["$$id-postgresql-data:/var/lib/postgresql/data"],"environment":["POSTGRES_USER=$$config_postgres_user","POSTGRES_PASSWORD=$$secret_postgres_password","POSTGRES_DB=$$config_postgres_db"]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgres_user:$$secret_postgres_password@$$id-postgresql:5432/$$config_postgres_db?sslmode=disable","description":""},{"id":"$$secret_jwt_secret","name":"JWT_SECRET","label":"JWT Secret","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_email_noreply","name":"EMAIL_NOREPLY","label":"No Reply Email Address","defaultValue":"noreply@example.com","description":""},{"id":"$$secret_email_mailgun_api","name":"EMAIL_MAILGUN_API","label":"Mailgun API Key","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_mailgun_region","name":"EMAIL_MAILGUN_REGION","label":"Mailgun Region","defaultValue":"EU","description":""},{"id":"$$config_email_mailgun_domain","name":"EMAIL_MAILGUN_DOMAIN","label":"Mailgun Domain","defaultValue":"","description":""},{"id":"$$config_email_smtp_host","name":"EMAIL_SMTP_HOST","label":"SMTP Host","defaultValue":"","description":""},{"id":"$$config_email_smtp_port","name":"EMAIL_SMTP_PORT","label":"SMTP Port","defaultValue":"587","description":""},{"id":"$$config_email_smtp_user","name":"EMAIL_SMTP_USER","label":"SMTP User","defaultValue":"","description":""},{"id":"$$secret_email_smtp_password","name":"EMAIL_SMTP_PASSWORD","label":"SMTP Password","defaultValue":"","description":"","showOnConfiguration":true},{"id":"$$config_email_smtp_enable_starttls","name":"EMAIL_SMTP_ENABLE_STARTTLS","label":"SMTP Enable StartTLS","defaultValue":"false","description":""},{"id":"$$config_postgres_user","name":"POSTGRES_USER","label":"PostgreSQL User","defaultValue":"$$generate_username","description":""},{"id":"$$secret_postgres_password","name":"POSTGRES_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":""},{"id":"$$config_postgres_db","name":"POSTGRES_DB","label":"PostgreSQL Database","defaultValue":"$$generate_username","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"0.215.1","documentation":"https://docs.n8n.io","type":"n8n","name":"n8n.io","description":"A free and open node based Workflow Automation Tool.","labels":["workflow","automation","ifttt","zapier","nodered"],"services":{"$$id":{"name":"N8n","depends_on":[],"image":"n8nio/n8n:$$core_version","volumes":["$$id-data:/root/.n8n","$$id-data-write:/files","/var/run/docker.sock:/var/run/docker.sock"],"environment":["WEBHOOK_URL=$$config_webhook_url"],"ports":["5678"]}},"variables":[{"id":"$$config_webhook_url","name":"WEBHOOK_URL","label":"Webhook URL","defaultValue":"$$generate_fqdn","description":""}]},{"templateVersion":"1.0.0","defaultVersion":"v1.5.1","documentation":"https://plausible.io/doc/","arch":"amd64","type":"plausibleanalytics","name":"Plausible Analytics","description":"A lightweight and open-source website analytics tool.","labels":["analytics","statistics","plausible","gdpr","no-cookie","google analytics"],"services":{"$$id":{"name":"Plausible Analytics","command":"sh -c \"sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run\"","depends_on":["$$id-postgresql","$$id-clickhouse"],"image":"plausible/analytics:$$core_version","environment":["ADMIN_USER_EMAIL=$$config_admin_user_email","ADMIN_USER_NAME=$$config_admin_user_name","ADMIN_USER_PWD=$$secret_admin_user_pwd","BASE_URL=$$config_base_url","SECRET_KEY_BASE=$$secret_secret_key_base","DISABLE_AUTH=$$config_disable_auth","DISABLE_REGISTRATION=$$config_disable_registration","DATABASE_URL=$$secret_database_url","CLICKHOUSE_DATABASE_URL=$$secret_clickhouse_database_url"],"ports":["8000"]},"$$id-postgresql":{"name":"PostgreSQL","image":"bitnami/postgresql:13","volumes":["$$id-postgresql-data:/bitnami/postgresql"],"environment":["POSTGRESQL_PASSWORD=$$secret_postgresql_password","POSTGRESQL_USERNAME=$$config_postgresql_username","POSTGRESQL_DATABASE=$$config_postgresql_database"]},"$$id-clickhouse":{"name":"Clickhouse","volumes":["$$id-clickhouse-data:/var/lib/clickhouse"],"image":"clickhouse/clickhouse-server:22.6-alpine","ulimits":{"nofile":{"soft":262144,"hard":262144}},"files":[{"location":"/etc/clickhouse-server/users.d/logging.xml","content":"warningtrue"},{"location":"/etc/clickhouse-server/config.d/logging.xml","content":"00"},{"location":"/docker-entrypoint-initdb.d/init.query","content":"CREATE DATABASE IF NOT EXISTS plausible;"},{"location":"/docker-entrypoint-initdb.d/init-db.sh","content":"clickhouse client --queries-file /docker-entrypoint-initdb.d/init.query"}]}},"variables":[{"id":"$$config_base_url","name":"BASE_URL","label":"Base URL","defaultValue":"$$generate_fqdn","description":"You must set this to the FQDN of the Plausible Analytics instance. This is used to generate the links to the Plausible Analytics instance."},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL for PostgreSQL","defaultValue":"postgresql://$$config_postgresql_username:$$secret_postgresql_password@$$id-postgresql:5432/$$config_postgresql_database","description":""},{"id":"$$secret_clickhouse_database_url","name":"CLICKHOUSE_DATABASE_URL","label":"Database URL for Clickhouse","defaultValue":"http://$$id-clickhouse:8123/plausible","description":""},{"id":"$$config_admin_user_email","name":"ADMIN_USER_EMAIL","label":"Admin Email Address","defaultValue":"admin@example.com","description":"This is the admin email. Please change it."},{"id":"$$config_admin_user_name","name":"ADMIN_USER_NAME","label":"Admin User Name","defaultValue":"$$generate_username","description":"This is the admin username. Please change it."},{"id":"$$secret_admin_user_pwd","name":"ADMIN_USER_PWD","label":"Admin User Password","defaultValue":"$$generate_password","description":"This is the admin password. Please change it.","showOnConfiguration":true},{"id":"$$secret_secret_key_base","name":"SECRET_KEY_BASE","label":"Secret Key Base","defaultValue":"$$generate_hex(64)","description":""},{"id":"$$config_disable_auth","name":"DISABLE_AUTH","label":"Authentication","defaultValue":"false","description":""},{"id":"$$config_disable_registration","name":"DISABLE_REGISTRATION","label":"Registration","defaultValue":"true","description":""},{"id":"$$config_postgresql_username","main":"$$id-postgresql","name":"POSTGRESQL_USERNAME","label":"PostgreSQL Username","defaultValue":"postgresql","description":""},{"id":"$$secret_postgresql_password","main":"$$id-postgresql","name":"POSTGRESQL_PASSWORD","label":"PostgreSQL Password","defaultValue":"$$generate_password","description":"","showOnConfiguration":true},{"id":"$$config_postgresql_database","main":"$$id-postgresql","name":"POSTGRESQL_DATABASE","label":"PostgreSQL Database","defaultValue":"plausible","description":""},{"id":"$$config_scriptName","name":"SCRIPT_NAME","label":"Custom Script Name","defaultValue":"plausible.js","description":"This is the default script name."}]},{"templateVersion":"1.0.0","defaultVersion":"0.104.2","documentation":"https://docs.nocodb.com","type":"nocodb","name":"NocoDB","description":"Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadsheet.","labels":["database","airtable","spreadsheet"],"services":{"$$id":{"name":"NocoDB","image":"nocodb/nocodb:$$core_version","environment":["PORT=$$config_port","NC_DB=$$config_nc_db","DATABASE_URL=$$secret_database_url","NC_PUBLIC_URL=$$config_public_url","NC_AUTH_JWT_SECRET=$$secret_auth_jwt_secret","NC_SENTRY_DSN=$$secret_sentry_dsn","NC_CONNECT_TO_EXTERNAL_DB_DISABLED=$$config_connect_to_external_db_disabled","NC_DISABLE_TELE=$$config_disable_tele"],"volumes":["$$id-data:/usr/app/data"],"ports":["8080"]}},"variables":[{"id":"$$config_nc_db","name":"NC_DB","label":"Database","defaultValue":"","description":"MySQL, PostgreSQL and MSSQL connection urls supported. If absent: A local SQLite will be created in root folder."},{"id":"$$config_port","name":"PORT","label":"Port","defaultValue":"8080","description":""},{"id":"$$secret_database_url","name":"DATABASE_URL","label":"Database URL","defaultValue":"","description":"JDBC URL Format. Can be used instead of NC_DB. Used in 1-Click Heroku deployment."},{"id":"$$config_public_url","name":"NC_PUBLIC_URL","label":"Public URL","defaultValue":"","description":"Used for sending Email invitations. If absent: Best guess from http request params."},{"id":"$$secret_auth_jwt_secret","name":"NC_AUTH_JWT_SECRET","label":"Auth JWT Secret","defaultValue":"$$generate_hex(64)","description":"JWT secret used for auth and storing other secrets. If absent: A Random secret will be generated."},{"id":"$$secret_sentry_dsn","name":"NC_SENTRY_DSN","label":"Sentry DSN","defaultValue":"","description":"For Sentry monitoring."},{"id":"$$config_connect_to_external_db_disabled","name":"NC_CONNECT_TO_EXTERNAL_DB_DISABLED","label":"Disable External Database","defaultValue":"0","description":"Disable Project creation with external database. (Enter \"1\" to disable)."},{"id":"$$config_disable_tele","name":"NC_DISABLE_TELE","label":"NocoDB Disable Telemetry","defaultValue":"1","description":"Disable telemetry (Enter \"1\" to disable)."}]}] \ No newline at end of file diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json deleted file mode 100644 index 8900da4f0..000000000 --- a/apps/api/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "esModuleInterop": true, - "resolveJsonModule":true, - "downlevelIteration": true - } - } \ No newline at end of file diff --git a/apps/others/backup/.dockerignore b/apps/others/backup/.dockerignore deleted file mode 100644 index 1b8c356ca..000000000 --- a/apps/others/backup/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -backup/* \ No newline at end of file diff --git a/apps/others/backup/Dockerfile b/apps/others/backup/Dockerfile deleted file mode 100644 index 178d70ab7..000000000 --- a/apps/others/backup/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -ARG PNPM_VERSION=7.17.1 - -FROM node:18-slim as build -WORKDIR /app -RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION} - -COPY ./package*.json . -RUN pnpm install -p -COPY . . - -# Production build -FROM node:18-slim -ARG DOCKER_VERSION=20.10.18 -ARG TARGETPLATFORM -ENV NODE_ENV production - -WORKDIR /app - -RUN apt update && apt -y install curl -RUN npm --no-update-notifier --no-fund --global install pnpm@${PNPM_VERSION} -RUN curl -SL https://cdn.coollabs.io/bin/$TARGETPLATFORM/docker-$DOCKER_VERSION -o /usr/bin/docker -RUN chmod +x /usr/bin/docker -COPY --from=minio/mc:latest /usr/bin/mc /usr/bin/mc -COPY --from=build /app/ . - -ENV CHECKPOINT_DISABLE=1 -CMD node /app/src/index.mjs \ No newline at end of file diff --git a/apps/others/backup/backups/.gitkeep b/apps/others/backup/backups/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/others/backup/package.json b/apps/others/backup/package.json deleted file mode 100644 index cfabe22a5..000000000 --- a/apps/others/backup/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "backup", - "version": "0.0.1", - "description": "", - "author": "Andras Bacsai", - "license": "Apache-2.0", - "main": "index.mjs", - "type": "module", - "scripts": { - "start": "NODE_ENV=production node src/index.mjs", - "dev": "pnpm cleanup && NODE_ENV=development node src/index.mjs", - "build": "docker build -t backup .", - "test": "pnpm build && docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock -v /root/devel/coolify/apps/backup/backups:/app/backups -e CONTAINERS_TO_BACKUP='clatmhc6000008lvb5a5tnvsk:database:mysql:local' backup", - "cleanup": "rm -rf backups/*" - }, - "keywords": [], - "dependencies": { - "@aws-sdk/client-s3": "^3.222.0", - "@aws-sdk/lib-storage": "^3.222.0", - "cuid": "2.1.8", - "dotenv": "16.0.3", - "zx": "7.1.1" - } -} \ No newline at end of file diff --git a/apps/others/backup/src/index.mjs b/apps/others/backup/src/index.mjs deleted file mode 100644 index 4707d3a43..000000000 --- a/apps/others/backup/src/index.mjs +++ /dev/null @@ -1,126 +0,0 @@ -import * as dotenv from 'dotenv'; -dotenv.config() - -import 'zx/globals'; -import cuid from 'cuid'; -import { S3, PutObjectCommand } from "@aws-sdk/client-s3"; -import fs from 'fs'; - -const isDev = process.env.NODE_ENV === 'development' -$.verbose = !!isDev - -if (!process.env.CONTAINERS_TO_BACKUP && !isDev) { - console.log(chalk.red(`No containers to backup!`)) - process.exit(1) -} -const mysqlGzipLocal = 'clb6c9ue4000a8lputdd5g1cl:database:mysql:gzip:local'; -const mysqlRawLocal = 'clb6c9ue4000a8lputdd5g1cl:database:mysql:raw:local'; -const postgresqlGzipLocal = 'clb6c15yi00008lpuezop7cy0:database:postgresql:gzip:local'; -const postgresqlRawLocal = 'clb6c15yi00008lpuezop7cy0:database:postgresql:raw:local'; - -const minio = 'clb6c9ue4000a8lputdd5g1cl:database:mysql:gzip:minio|http|min.arm.coolify.io|backups||'; -const digitalOcean = 'clb6c9ue4000a8lputdd5g1cl:database:mysql:gzip:do|https|fra1.digitaloceanspaces.com|backups||'; - -const devContainers = [mysqlGzipLocal, mysqlRawLocal, postgresqlGzipLocal, postgresqlRawLocal] - -const containers = isDev - ? devContainers - : process.env.CONTAINERS_TO_BACKUP.split(',') - -const backup = async (container) => { - const id = cuid() - const [name, backupType, type, zipped, storage] = container.split(':') - const directory = `backups`; - const filename = zipped === 'raw' - ? `${name}-${type}-${backupType}-${new Date().getTime()}.sql` - : `${name}-${type}-${backupType}-${new Date().getTime()}.tgz` - const backup = `${directory}/${filename}`; - - try { - await $`docker inspect ${name.split(' ')[0]}`.quiet() - if (backupType === 'database') { - if (type === 'mysql') { - console.log(chalk.blue(`Backing up ${name}:${type}...`)) - const { stdout: rootPassword } = await $`docker exec ${name} printenv MYSQL_ROOT_PASSWORD`.quiet() - if (zipped === 'raw') { - await $`docker exec ${name} sh -c "exec mysqldump --all-databases -uroot -p${rootPassword.trim()}" > ${backup}` - } else if (zipped === 'gzip') { - await $`docker exec ${name} sh -c "exec mysqldump --all-databases -uroot -p${rootPassword.trim()}" | gzip > ${backup}` - } - } - if (type === 'postgresql') { - console.log(chalk.blue(`Backing up ${name}:${type}...`)) - const { stdout: userPassword } = await $`docker exec ${name} printenv POSTGRES_PASSWORD` - const { stdout: user } = await $`docker exec ${name} printenv POSTGRES_USER` - if (zipped === 'raw') { - await $`docker exec ${name} sh -c "exec pg_dumpall -c -U${user.trim()}" -W${userPassword.trim()}> ${backup}` - } else if (zipped === 'gzip') { - await $`docker exec ${name} sh -c "exec pg_dumpall -c -U${user.trim()}" -W${userPassword.trim()} | gzip > ${backup}` - } - } - const [storageType, ...storageArgs] = storage.split('|') - if (storageType !== 'local') { - let s3Protocol, s3Url, s3Bucket, s3Key, s3Secret = null - if (storageArgs.length > 0) { - [s3Protocol, s3Url, s3Bucket, s3Key, s3Secret] = storageArgs - } - if (storageType === 'minio') { - if (!s3Protocol || !s3Url || !s3Bucket || !s3Key || !s3Secret) { - console.log(chalk.red(`Invalid storage arguments for ${name}:${type}!`)) - return - } - await $`mc alias set ${id} ${s3Protocol}://${s3Url} ${s3Key} ${s3Secret}` - await $`mc stat ${id}` - await $`mc cp ${backup} ${id}/${s3Bucket}` - await $`rm ${backup}` - await $`mc alias rm ${id}` - } else if (storageType === 'do') { - if (!s3Protocol || !s3Url || !s3Bucket || !s3Key || !s3Secret) { - console.log(chalk.red(`Invalid storage arguments for ${name}:${type}!`)) - return - } - console.log({ s3Protocol, s3Url, s3Bucket, s3Key, s3Secret }) - console.log(chalk.blue(`Uploading ${name}:${type} to DigitalOcean Spaces...`)) - const readstream = fs.createReadStream(backup) - const bucketParams = { - Bucket: s3Bucket, - Key: filename, - Body: readstream - }; - const s3Client = new S3({ - forcePathStyle: false, - endpoint: `${s3Protocol}://${s3Url}`, - region: "us-east-1", - credentials: { - accessKeyId: s3Key, - secretAccessKey: s3Secret - }, - }); - try { - const data = await s3Client.send(new PutObjectCommand(bucketParams)); - console.log(chalk.green("Successfully uploaded backup: " + - bucketParams.Bucket + - "/" + - bucketParams.Key - ) - ); - return data; - } catch (err) { - console.log("Error", err); - } - } - } - } - - console.log(chalk.green(`Backup of ${name}:${type} complete!`)) - } catch (error) { - console.log(chalk.red(`Backup of ${name}:${type} failed!`)) - console.log(chalk.red(error)) - } -} -const promises = [] -for (const container of containers) { - // await backup(container); - promises.push(backup(container)) -} -await Promise.all(promises) \ No newline at end of file diff --git a/apps/others/i18n/.env.example b/apps/others/i18n/.env.example deleted file mode 100644 index 8bdf45a64..000000000 --- a/apps/others/i18n/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -WEBLATE_INSTANCE_URL=http://localhost -WEBLATE_COMPONENT_NAME=coolify -WEBLATE_TOKEN= -TRANSLATION_DIR= \ No newline at end of file diff --git a/apps/others/i18n/.gitignore b/apps/others/i18n/.gitignore deleted file mode 100644 index df67586b0..000000000 --- a/apps/others/i18n/.gitignore +++ /dev/null @@ -1 +0,0 @@ -locales/* \ No newline at end of file diff --git a/apps/others/i18n/index.mjs b/apps/others/i18n/index.mjs deleted file mode 100644 index 85e146073..000000000 --- a/apps/others/i18n/index.mjs +++ /dev/null @@ -1,63 +0,0 @@ -import dotenv from 'dotenv'; -dotenv.config() -import fs from 'fs' -import path from 'path' -import { fileURLToPath } from 'url'; -import Gettext from 'node-gettext' -import { po } from 'gettext-parser' -import got from 'got'; -const __filename = fileURLToPath(import.meta.url); - -const __dirname = path.dirname(__filename); - -const weblateInstanceURL = process.env.WEBLATE_INSTANCE_URL; -const weblateComponentName = process.env.WEBLATE_COMPONENT_NAME -const token = process.env.WEBLATE_TOKEN; - -const translationsDir = process.env.TRANSLATION_DIR; -const translationsPODir = './locales'; -const locales = [] -const domain = 'locale' - -const translations = await got(`${weblateInstanceURL}/api/components/${weblateComponentName}/glossary/translations/?format=json`, { - headers: { - "Authorization": `Token ${token}` - } -}).json() -for (const translation of translations.results) { - const code = translation.language_code - locales.push(code) - - const fileUrl = translation.file_url.replace('=json', '=po') - const file = await got(fileUrl, { - headers: { - "Authorization": `Token ${token}` - } - }).text() - fs.writeFileSync(path.join(__dirname, translationsPODir, domain + '-' + code + '.po'), file) -} - - -const gt = new Gettext() - -locales.forEach((locale) => { - let json = {} - const fileName = `${domain}-${locale}.po` - const translationsFilePath = path.join(translationsPODir, fileName) - const translationsContent = fs.readFileSync(translationsFilePath) - - const parsedTranslations = po.parse(translationsContent) - const a = gt.gettext(parsedTranslations) - for (const [key, value] of Object.entries(a)) { - if (key === 'translations') { - for (const [key1, value1] of Object.entries(value)) { - if (key1 !== '') { - for (const [key2, value2] of Object.entries(value1)) { - json[value2.msgctxt] = value2.msgstr[0] - } - } - } - } - } - fs.writeFileSync(`${translationsDir}/${locale}.json`, JSON.stringify(json)) -}) \ No newline at end of file diff --git a/apps/others/i18n/package.json b/apps/others/i18n/package.json deleted file mode 100644 index bb4534514..000000000 --- a/apps/others/i18n/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "i18n-converter", - "description": "Convert Weblate translations to sveltekit-i18n", - "license": "Apache-2.0", - "scripts": { - "translate": "node index.mjs" - }, - "type": "module", - "dependencies": { - "node-gettext": "3.0.0", - "gettext-parser": "6.0.0", - "got": "12.3.1", - "dotenv": "16.0.2" - } -} \ No newline at end of file diff --git a/apps/ui/.eslintignore b/apps/ui/.eslintignore deleted file mode 100644 index 38972655f..000000000 --- a/apps/ui/.eslintignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/apps/ui/.eslintrc.cjs b/apps/ui/.eslintrc.cjs deleted file mode 100644 index 3ccf435f0..000000000 --- a/apps/ui/.eslintrc.cjs +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], - plugins: ['svelte3', '@typescript-eslint'], - ignorePatterns: ['*.cjs'], - overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], - settings: { - 'svelte3/typescript': () => require('typescript') - }, - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020 - }, - env: { - browser: true, - es2017: true, - node: true - } -}; diff --git a/apps/ui/.npmrc b/apps/ui/.npmrc deleted file mode 100644 index b6f27f135..000000000 --- a/apps/ui/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/apps/ui/.prettierignore b/apps/ui/.prettierignore deleted file mode 100644 index 38972655f..000000000 --- a/apps/ui/.prettierignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/apps/ui/.prettierrc b/apps/ui/.prettierrc deleted file mode 100644 index ff2677efd..000000000 --- a/apps/ui/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100 -} diff --git a/apps/ui/package.json b/apps/ui/package.json deleted file mode 100644 index d40dba2a0..000000000 --- a/apps/ui/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "ui", - "description": "Coolify's SvelteKit UI", - "license": "Apache-2.0", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "package": "svelte-kit package", - "preview": "svelte-kit preview", - "test": "playwright test", - "check": "svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --check --plugin-search-dir=. . && eslint .", - "format": "prettier --write --plugin-search-dir=. ." - }, - "devDependencies": { - "@floating-ui/dom": "1.0.6", - "@playwright/test": "1.28.0", - "@popperjs/core": "2.11.6", - "@sveltejs/kit": "1.0.0-next.405", - "@types/js-cookie": "3.0.2", - "@typescript-eslint/eslint-plugin": "5.44.0", - "@typescript-eslint/parser": "5.44.0", - "autoprefixer": "10.4.13", - "classnames": "2.3.2", - "eslint": "8.28.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-svelte3": "4.0.0", - "flowbite": "1.5.4", - "flowbite-svelte": "0.28.0", - "postcss": "8.4.19", - "prettier": "2.7.1", - "prettier-plugin-svelte": "2.8.1", - "svelte": "3.53.1", - "svelte-check": "2.9.2", - "svelte-preprocess": "4.10.7", - "tailwindcss": "3.2.4", - "tailwindcss-scrollbar": "0.1.0", - "tslib": "2.4.1", - "typescript": "4.9.3", - "vite": "3.2.4" - }, - "type": "module", - "dependencies": { - "@sentry/svelte": "7.21.1", - "@sentry/tracing": "7.21.1", - "@sveltejs/adapter-static": "1.0.0-next.48", - "@tailwindcss/typography": "0.5.8", - "cuid": "2.1.8", - "daisyui": "2.41.0", - "dayjs": "1.11.6", - "js-cookie": "3.0.1", - "js-yaml": "4.1.0", - "p-limit": "4.0.0", - "socket.io-client": "4.5.3", - "svelte-select": "4.4.7", - "sveltekit-i18n": "2.2.2" - } -} \ No newline at end of file diff --git a/apps/ui/playwright.config.ts b/apps/ui/playwright.config.ts deleted file mode 100644 index 87c293e5a..000000000 --- a/apps/ui/playwright.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PlaywrightTestConfig } from '@playwright/test'; - -const config: PlaywrightTestConfig = { - webServer: { - command: 'npm run build && npm run preview', - port: 3000 - } -}; - -export default config; diff --git a/apps/ui/postcss.config.cjs b/apps/ui/postcss.config.cjs deleted file mode 100644 index 054c147cb..000000000 --- a/apps/ui/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } -}; diff --git a/apps/ui/src/app.d.ts b/apps/ui/src/app.d.ts deleted file mode 100644 index ebc7c2668..000000000 --- a/apps/ui/src/app.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/// - -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces -declare namespace App { - // interface Locals {} - // interface Platform {} - // interface Session {} - interface Stuff { - service: any; - application: any; - isRunning: boolean; - appId: string; - readOnly: boolean; - source: string; - settings: string; - database: Record; - versions: string; - privatePort: string; - } -} - -declare const GITPOD_WORKSPACE_URL: string - - \ No newline at end of file diff --git a/apps/ui/src/app.html b/apps/ui/src/app.html deleted file mode 100644 index 16d104bc7..000000000 --- a/apps/ui/src/app.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - %sveltekit.head% - - -
%sveltekit.body%
- - diff --git a/apps/ui/src/hooks.ts b/apps/ui/src/hooks.ts deleted file mode 100644 index df3284d5a..000000000 --- a/apps/ui/src/hooks.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as Sentry from '@sentry/svelte'; -export async function handle({ event, resolve }) { - const response = await resolve(event, { ssr: false }); - return response; -} -export const handleError = ({ error, event }) => { - Sentry.captureException(error, { event }); - - return { - message: 'Whoops!', - code: error?.code ?? 'UNKNOWN' - }; -}; \ No newline at end of file diff --git a/apps/ui/src/lib/api.ts b/apps/ui/src/lib/api.ts deleted file mode 100644 index 058ec083f..000000000 --- a/apps/ui/src/lib/api.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { dev } from '$app/env'; -import Cookies from 'js-cookie'; - -export function getAPIUrl() { - if (GITPOD_WORKSPACE_URL) { - const { href } = new URL(GITPOD_WORKSPACE_URL); - const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, ''); - return newURL; - } - if (CODESANDBOX_HOST) { - return `https://${CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`; - } - return dev - ? `http://${window.location.hostname}:3001` - : 'http://localhost:3000'; -} -export function getWebhookUrl(type: string) { - if (GITPOD_WORKSPACE_URL) { - const { href } = new URL(GITPOD_WORKSPACE_URL); - const newURL = href.replace('https://', 'https://3001-').replace(/\/$/, ''); - if (type === 'github') { - return `${newURL}/webhooks/github/events`; - } - if (type === 'gitlab') { - return `${newURL}/webhooks/gitlab/events`; - } - } - if (CODESANDBOX_HOST) { - const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`; - if (type === 'github') { - return `${newURL}/webhooks/github/events`; - } - if (type === 'gitlab') { - return `${newURL}/webhooks/gitlab/events`; - } - } - return `https://webhook.site/0e5beb2c-4e9b-40e2-a89e-32295e570c21/events`; -} -async function send({ - method, - path, - data = null, - headers, - timeout = 120000 -}: { - method: string; - path: string; - data?: any; - headers?: any; - timeout?: number; -}): Promise> { - const token = Cookies.get('token'); - const controller = new AbortController(); - const id = setTimeout(() => controller.abort(), timeout); - const opts: any = { method, headers: {}, body: null, signal: controller.signal }; - if (data && Object.keys(data).length > 0) { - const parsedData = data; - for (const [key, value] of Object.entries(data)) { - if (value === '') { - parsedData[key] = null; - } - } - if (parsedData) { - opts.headers['Content-Type'] = 'application/json'; - opts.body = JSON.stringify(parsedData); - } - } - - if (headers) { - opts.headers = { - ...opts.headers, - ...headers - }; - } - if (token && !path.startsWith('https://')) { - opts.headers = { - ...opts.headers, - Authorization: `Bearer ${token}` - }; - } - if (!path.startsWith('https://')) { - path = `/api/v1${path}`; - } - - if (dev && !path.startsWith('https://')) { - path = `${getAPIUrl()}${path}`; - } - if (method === 'POST' && data && !opts.body) { - opts.body = data; - } - const response = await fetch(`${path}`, opts); - - clearTimeout(id); - - const contentType = response.headers.get('content-type'); - - let responseData = {}; - if (contentType) { - if (contentType?.indexOf('application/json') !== -1) { - responseData = await response.json(); - } else if (contentType?.indexOf('text/plain') !== -1) { - responseData = await response.text(); - } else { - return {}; - } - } else { - return {}; - } - if (!response.ok) { - if ( - response.status === 401 && - !path.startsWith('https://api.github') && - !path.includes('/v4/') - ) { - Cookies.remove('token'); - } - - throw responseData; - } - return responseData; -} - -export function get(path: string, headers?: Record): Promise> { - return send({ method: 'GET', path, headers }); -} - -export function del( - path: string, - data: Record, - headers?: Record -): Promise> { - return send({ method: 'DELETE', path, data, headers }); -} - -export function post( - path: string, - data: Record | FormData, - headers?: Record -): Promise> { - return send({ method: 'POST', path, data, headers }); -} - -export function put( - path: string, - data: Record, - headers?: Record -): Promise> { - return send({ method: 'PUT', path, data, headers }); -} diff --git a/apps/ui/src/lib/common.ts b/apps/ui/src/lib/common.ts deleted file mode 100644 index 9ed667f43..000000000 --- a/apps/ui/src/lib/common.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { addToast } from '$lib/store'; - -export const asyncSleep = (delay: number) => - new Promise((resolve) => setTimeout(resolve, delay)); - -export let initials = (str:string) => (str||'').split(' ').map( (wrd) => wrd[0]).join('') - -export function errorNotification(error: any | { message: string }): void { - if (error.message) { - if (error.message === 'Cannot read properties of undefined (reading \'postMessage\')') { - return addToast({ - message: 'Currently there is background process in progress. Please try again later.', - type: 'error', - }); - } - addToast({ - message: error.message, - type: 'error', - }); - } else { - addToast({ - message: 'Ooops, something is not okay, are you okay?', - type: 'error', - }); - } - console.error(JSON.stringify(error)); -} - -export function getDomain(domain: string) { - return domain?.replace('https://', '').replace('http://', ''); -} -export function dashify(str: string, options?: any): string { - if (typeof str !== 'string') return str; - return str - .trim() - .replace(/\W/g, (m) => (/[À-ž]/.test(m) ? m : '-')) - .replace(/^-+|-+$/g, '') - .replace(/-{2,}/g, (m) => (options && options.condense ? '-' : m)) - .toLowerCase(); -} - -export const dateOptions: any = { - year: 'numeric', - month: 'short', - day: '2-digit', - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - hour12: false -}; - -export const staticDeployments = [ - 'react', - 'vuejs', - 'static', - 'svelte', - 'gatsby', - 'php', - 'astro', - 'eleventy' -]; -export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel', 'heroku']; - - -export function generateRemoteEngine(destination: any) { - return `ssh://${destination.user}@${destination.ipAddress}:${destination.port}`; -} - -export function changeQueryParams(buildId: string) { - const queryParams = new URLSearchParams(window.location.search); - queryParams.set('buildId', buildId); - // @ts-ignore - return history.pushState(null, null, '?' + queryParams.toString()); -} - - -export function handlerNotFoundLoad(error: any, url: URL) { - if (error?.status === 404) { - return { - status: 302, - redirect: '/' - }; - } - return { - status: 500, - error: new Error(`Could not load ${url}`) - }; -} - -export function getRndInteger(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} diff --git a/apps/ui/src/lib/components/Beta.svelte b/apps/ui/src/lib/components/Beta.svelte deleted file mode 100644 index 279401fcf..000000000 --- a/apps/ui/src/lib/components/Beta.svelte +++ /dev/null @@ -1 +0,0 @@ - BETA \ No newline at end of file diff --git a/apps/ui/src/lib/components/ContextMenu.svelte b/apps/ui/src/lib/components/ContextMenu.svelte deleted file mode 100644 index fe4a2ce51..000000000 --- a/apps/ui/src/lib/components/ContextMenu.svelte +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/apps/ui/src/lib/components/CopyPasswordField.svelte b/apps/ui/src/lib/components/CopyPasswordField.svelte deleted file mode 100644 index b25817ac1..000000000 --- a/apps/ui/src/lib/components/CopyPasswordField.svelte +++ /dev/null @@ -1,156 +0,0 @@ - - -
- {#if !isPasswordField || showPassword} - {#if textarea} - - {:else} - - {/if} - {:else} - - {/if} - -
-
- {#if isPasswordField} - -
(showPassword = !showPassword)}> - {#if showPassword} - - - - {:else} - - - - - {/if} -
- {/if} - {#if value && isHttps} - -
- - - - - -
- {/if} -
-
-
diff --git a/apps/ui/src/lib/components/CopyVolumeField.svelte b/apps/ui/src/lib/components/CopyVolumeField.svelte deleted file mode 100644 index 4491f312f..000000000 --- a/apps/ui/src/lib/components/CopyVolumeField.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - -
-

{value}

-
- {#if isHttps} -
- - - - - -
- {/if} -
-
- - diff --git a/apps/ui/src/lib/components/DeleteIcon.svelte b/apps/ui/src/lib/components/DeleteIcon.svelte deleted file mode 100644 index f04b3952c..000000000 --- a/apps/ui/src/lib/components/DeleteIcon.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - diff --git a/apps/ui/src/lib/components/DocLink.svelte b/apps/ui/src/lib/components/DocLink.svelte deleted file mode 100644 index 803b48583..000000000 --- a/apps/ui/src/lib/components/DocLink.svelte +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - {text} - {#if isExternal} - - {/if} - -{#if !text} - See details in the documentation -{/if} diff --git a/apps/ui/src/lib/components/Explainer.svelte b/apps/ui/src/lib/components/Explainer.svelte deleted file mode 100644 index 924ce70d6..000000000 --- a/apps/ui/src/lib/components/Explainer.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - -
- - - - - -
diff --git a/apps/ui/src/lib/components/ExternalLink.svelte b/apps/ui/src/lib/components/ExternalLink.svelte deleted file mode 100644 index 62f2e312a..000000000 --- a/apps/ui/src/lib/components/ExternalLink.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/Loading.svelte b/apps/ui/src/lib/components/Loading.svelte deleted file mode 100644 index 99a1054ea..000000000 --- a/apps/ui/src/lib/components/Loading.svelte +++ /dev/null @@ -1,67 +0,0 @@ - - -{#if fullscreen} -
- -
-{:else} -
- -
-{/if} - - diff --git a/apps/ui/src/lib/components/LoadingLogs.svelte b/apps/ui/src/lib/components/LoadingLogs.svelte deleted file mode 100644 index 7add70c19..000000000 --- a/apps/ui/src/lib/components/LoadingLogs.svelte +++ /dev/null @@ -1,45 +0,0 @@ - - -
-
-
-
- - diff --git a/apps/ui/src/lib/components/LocalePicker.svelte b/apps/ui/src/lib/components/LocalePicker.svelte deleted file mode 100644 index 6efdd176c..000000000 --- a/apps/ui/src/lib/components/LocalePicker.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - -
- -
diff --git a/apps/ui/src/lib/components/PageLoader.svelte b/apps/ui/src/lib/components/PageLoader.svelte deleted file mode 100644 index 2b52fd917..000000000 --- a/apps/ui/src/lib/components/PageLoader.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - -
-
-
- - diff --git a/apps/ui/src/lib/components/ServiceStatus.svelte b/apps/ui/src/lib/components/ServiceStatus.svelte deleted file mode 100644 index f6cb092f5..000000000 --- a/apps/ui/src/lib/components/ServiceStatus.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - -{#if serviceStatus.isExcluded} - Excluded -{:else if serviceStatus.isRunning} - Running -{:else if serviceStatus.isStopped || serviceStatus.isExited} - Stopped -{:else if serviceStatus.isRestarting} - Restarting -{/if} diff --git a/apps/ui/src/lib/components/Setting.svelte b/apps/ui/src/lib/components/Setting.svelte deleted file mode 100644 index 555323b37..000000000 --- a/apps/ui/src/lib/components/Setting.svelte +++ /dev/null @@ -1,87 +0,0 @@ - - -
-
- - -
-
-
- -
- Use setting - - - - -
-
- -{#if dataTooltip} - {dataTooltip} -{/if} diff --git a/apps/ui/src/lib/components/SimpleExplainer.svelte b/apps/ui/src/lib/components/SimpleExplainer.svelte deleted file mode 100644 index 6a3198c27..000000000 --- a/apps/ui/src/lib/components/SimpleExplainer.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - -
{@html text}
\ No newline at end of file diff --git a/apps/ui/src/lib/components/Toast.svelte b/apps/ui/src/lib/components/Toast.svelte deleted file mode 100644 index bb34929cd..000000000 --- a/apps/ui/src/lib/components/Toast.svelte +++ /dev/null @@ -1,64 +0,0 @@ - - - -
dispatch('click')} - on:mouseover={() => dispatch('pause')} - on:focus={() => dispatch('pause')} - on:mouseout={() => dispatch('resume')} - on:blur={() => dispatch('resume')} - class={` flex flex-row justify-center alert shadow-lg text-white hover:scale-105 transition-all duration-100 cursor-pointer rounded ${success()}`} - class:alert-error={type === 'error'} - class:alert-info={type === 'info'} -> - {#if type === 'success'} - - {:else if type === 'error'} - - {:else if type === 'info'} - - {/if} - -
diff --git a/apps/ui/src/lib/components/Toasts.svelte b/apps/ui/src/lib/components/Toasts.svelte deleted file mode 100644 index 031840b38..000000000 --- a/apps/ui/src/lib/components/Toasts.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - -{#if $toasts.length > 0} -
- -
-{/if} - - diff --git a/apps/ui/src/lib/components/Tooltip.svelte b/apps/ui/src/lib/components/Tooltip.svelte deleted file mode 100644 index e0591a9d4..000000000 --- a/apps/ui/src/lib/components/Tooltip.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/Trend.svelte b/apps/ui/src/lib/components/Trend.svelte deleted file mode 100644 index cdc363516..000000000 --- a/apps/ui/src/lib/components/Trend.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - -{#if trend === 'up'} - - - - - - -{:else if trend === 'down'} - - - - - - - -{/if} diff --git a/apps/ui/src/lib/components/UpdateAvailable.svelte b/apps/ui/src/lib/components/UpdateAvailable.svelte deleted file mode 100644 index 802b57b49..000000000 --- a/apps/ui/src/lib/components/UpdateAvailable.svelte +++ /dev/null @@ -1,207 +0,0 @@ - - -
- {#if $appSession.teamId === '0'} - {#if $isUpdateAvailable} - - New Version Available! - {/if} - {/if} -
diff --git a/apps/ui/src/lib/components/Upload.svelte b/apps/ui/src/lib/components/Upload.svelte deleted file mode 100644 index 992d91153..000000000 --- a/apps/ui/src/lib/components/Upload.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -
- - - - -
- -
diff --git a/apps/ui/src/lib/components/Usage.svelte b/apps/ui/src/lib/components/Usage.svelte deleted file mode 100644 index 37a30d840..000000000 --- a/apps/ui/src/lib/components/Usage.svelte +++ /dev/null @@ -1,173 +0,0 @@ - - -
-
-
-

- {server.name} - {#if server.remoteEngine} - - {/if} -

-
- {#if server?.remoteIpAddress} -

{server?.remoteIpAddress}

- {:else} -

localhost

- {/if} -
-
- {#if $appSession.teamId === '0'} - - {/if} - {#if loading.usage} - - {/if} -
-
-
-
-
-
-
-
-
Total Memory
-
- {(usage?.memory?.totalMemMb).toFixed(0)}MB -
-
- -
-
Used Memory
-
- {(usage?.memory?.usedMemMb).toFixed(0)}MB -
-
- -
-
Free Memory
-
- {(usage?.memory?.freeMemPercentage).toFixed(0)}% -
-
-
- -
-
-
Total CPU
-
- {usage?.cpu?.count} -
-
- -
-
CPU Usage
-
- {usage?.cpu?.usage}% -
-
- -
-
Load Average (5,10,30mins)
-
{usage?.cpu?.load}
-
-
-
-
-
Total Disk
-
- {usage?.disk?.totalGb}GB -
-
- -
-
Used Disk
-
- {usage?.disk?.usedGb}GB -
-
- -
-
Free Disk
-
- {usage?.disk?.freePercentage}% -
-
-
-
-
diff --git a/apps/ui/src/lib/components/badges/DestinationBadge.svelte b/apps/ui/src/lib/components/badges/DestinationBadge.svelte deleted file mode 100644 index b566ef7bd..000000000 --- a/apps/ui/src/lib/components/badges/DestinationBadge.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - -{#if (name || '').length > 0} - - {initials(name)} - - {name} -{/if} diff --git a/apps/ui/src/lib/components/badges/PublicBadge.svelte b/apps/ui/src/lib/components/badges/PublicBadge.svelte deleted file mode 100644 index 6784c97e9..000000000 --- a/apps/ui/src/lib/components/badges/PublicBadge.svelte +++ /dev/null @@ -1,19 +0,0 @@ -
- - - - - - - - -
diff --git a/apps/ui/src/lib/components/badges/StatusBadge.svelte b/apps/ui/src/lib/components/badges/StatusBadge.svelte deleted file mode 100644 index 01badcee2..000000000 --- a/apps/ui/src/lib/components/badges/StatusBadge.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - -{#await getting} - ... -{:then status} - - {status} - -{/await} diff --git a/apps/ui/src/lib/components/badges/TeamsBadge.svelte b/apps/ui/src/lib/components/badges/TeamsBadge.svelte deleted file mode 100644 index eab9d80d8..000000000 --- a/apps/ui/src/lib/components/badges/TeamsBadge.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - - - {#each teams as team} - - Team: {initials(team.name)} - - {team.name} - {/each} - diff --git a/apps/ui/src/lib/components/grids/Grid3.svelte b/apps/ui/src/lib/components/grids/Grid3.svelte deleted file mode 100644 index 905655e04..000000000 --- a/apps/ui/src/lib/components/grids/Grid3.svelte +++ /dev/null @@ -1,5 +0,0 @@ -
- -
diff --git a/apps/ui/src/lib/components/svg/applications/ApplicationIcons.svelte b/apps/ui/src/lib/components/svg/applications/ApplicationIcons.svelte deleted file mode 100644 index 387653c87..000000000 --- a/apps/ui/src/lib/components/svg/applications/ApplicationIcons.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - -{#if application.buildPack?.toLowerCase() === 'rust'} - -{:else if application.buildPack?.toLowerCase() === 'node'} - -{:else if application.buildPack?.toLowerCase() === 'react'} - -{:else if application.buildPack?.toLowerCase() === 'svelte'} - -{:else if application.buildPack?.toLowerCase() === 'vuejs'} - -{:else if application.buildPack?.toLowerCase() === 'php'} - -{:else if application.buildPack?.toLowerCase() === 'python'} - -{:else if application.buildPack?.toLowerCase() === 'static'} - -{:else if application.buildPack?.toLowerCase() === 'nestjs'} - -{:else if application.buildPack?.toLowerCase() === 'nuxtjs'} - -{:else if application.buildPack?.toLowerCase() === 'nextjs'} - -{:else if application.buildPack?.toLowerCase() === 'gatsby'} - -{:else if application.buildPack?.toLowerCase() === 'docker'} - -{:else if application.buildPack?.toLowerCase() === 'astro'} - -{:else if application.buildPack?.toLowerCase() === 'eleventy'} - -{:else if application.buildPack?.toLowerCase() === 'deno'} - -{:else if application.buildPack?.toLowerCase() === 'laravel'} - -{:else if application.buildPack?.toLowerCase() === 'heroku'} - -{:else if application.buildPack?.toLowerCase() === 'compose'} - -{:else if application.simpleDockerfile} - -{/if} diff --git a/apps/ui/src/lib/components/svg/applications/Astro.svelte b/apps/ui/src/lib/components/svg/applications/Astro.svelte deleted file mode 100644 index 2344372ab..000000000 --- a/apps/ui/src/lib/components/svg/applications/Astro.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Compose.svelte b/apps/ui/src/lib/components/svg/applications/Compose.svelte deleted file mode 100644 index f0482e776..000000000 --- a/apps/ui/src/lib/components/svg/applications/Compose.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -docker compose logo diff --git a/apps/ui/src/lib/components/svg/applications/Deno.svelte b/apps/ui/src/lib/components/svg/applications/Deno.svelte deleted file mode 100644 index 25eee8132..000000000 --- a/apps/ui/src/lib/components/svg/applications/Deno.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/applications/Docker.svelte b/apps/ui/src/lib/components/svg/applications/Docker.svelte deleted file mode 100644 index 74ba0ebf0..000000000 --- a/apps/ui/src/lib/components/svg/applications/Docker.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Eleventy.svelte b/apps/ui/src/lib/components/svg/applications/Eleventy.svelte deleted file mode 100644 index b2d8d6122..000000000 --- a/apps/ui/src/lib/components/svg/applications/Eleventy.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Gatsby.svelte b/apps/ui/src/lib/components/svg/applications/Gatsby.svelte deleted file mode 100644 index d67a63417..000000000 --- a/apps/ui/src/lib/components/svg/applications/Gatsby.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Heroku.svelte b/apps/ui/src/lib/components/svg/applications/Heroku.svelte deleted file mode 100644 index dff845bc2..000000000 --- a/apps/ui/src/lib/components/svg/applications/Heroku.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Laravel.svelte b/apps/ui/src/lib/components/svg/applications/Laravel.svelte deleted file mode 100644 index d13694a8c..000000000 --- a/apps/ui/src/lib/components/svg/applications/Laravel.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - -Logomark diff --git a/apps/ui/src/lib/components/svg/applications/Nestjs.svelte b/apps/ui/src/lib/components/svg/applications/Nestjs.svelte deleted file mode 100644 index ac0f8af3f..000000000 --- a/apps/ui/src/lib/components/svg/applications/Nestjs.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Nextjs.svelte b/apps/ui/src/lib/components/svg/applications/Nextjs.svelte deleted file mode 100644 index 9ed0227d1..000000000 --- a/apps/ui/src/lib/components/svg/applications/Nextjs.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Nodejs.svelte b/apps/ui/src/lib/components/svg/applications/Nodejs.svelte deleted file mode 100644 index 93140f08f..000000000 --- a/apps/ui/src/lib/components/svg/applications/Nodejs.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/applications/Nuxtjs.svelte b/apps/ui/src/lib/components/svg/applications/Nuxtjs.svelte deleted file mode 100644 index cb2a66ff4..000000000 --- a/apps/ui/src/lib/components/svg/applications/Nuxtjs.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/PHP.svelte b/apps/ui/src/lib/components/svg/applications/PHP.svelte deleted file mode 100644 index d52ab0dd5..000000000 --- a/apps/ui/src/lib/components/svg/applications/PHP.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Python.svelte b/apps/ui/src/lib/components/svg/applications/Python.svelte deleted file mode 100644 index 17abb0f6d..000000000 --- a/apps/ui/src/lib/components/svg/applications/Python.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/React.svelte b/apps/ui/src/lib/components/svg/applications/React.svelte deleted file mode 100644 index c0867ffc8..000000000 --- a/apps/ui/src/lib/components/svg/applications/React.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Rust.svelte b/apps/ui/src/lib/components/svg/applications/Rust.svelte deleted file mode 100644 index 97bcee903..000000000 --- a/apps/ui/src/lib/components/svg/applications/Rust.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Static.svelte b/apps/ui/src/lib/components/svg/applications/Static.svelte deleted file mode 100644 index 14cbb0ce8..000000000 --- a/apps/ui/src/lib/components/svg/applications/Static.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/applications/Svelte.svelte b/apps/ui/src/lib/components/svg/applications/Svelte.svelte deleted file mode 100644 index cfa96c59d..000000000 --- a/apps/ui/src/lib/components/svg/applications/Svelte.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/Vuejs.svelte b/apps/ui/src/lib/components/svg/applications/Vuejs.svelte deleted file mode 100644 index 5ead6229d..000000000 --- a/apps/ui/src/lib/components/svg/applications/Vuejs.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/applications/index.ts b/apps/ui/src/lib/components/svg/applications/index.ts deleted file mode 100644 index 7bbe7b55b..000000000 --- a/apps/ui/src/lib/components/svg/applications/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -//@ts-nocheck -export { default as Rust } from './Rust.svelte'; -export { default as Nodejs } from './Nodejs.svelte'; -export { default as React } from './React.svelte'; -export { default as Svelte } from './Svelte.svelte'; -export { default as Vuejs } from './Vuejs.svelte'; -export { default as Php } from './PHP.svelte'; -export { default as Python } from './Python.svelte'; -export { default as Static } from './Static.svelte'; -export { default as Nestjs } from './Nestjs.svelte'; -export { default as Nuxtjs } from './Nuxtjs.svelte'; -export { default as Nextjs } from './Nextjs.svelte'; -export { default as Gatsby } from './Gatsby.svelte'; -export { default as Docker } from './Docker.svelte'; -export { default as Astro } from './Astro.svelte'; -export { default as Eleventy } from './Eleventy.svelte'; -export { default as Deno } from './Deno.svelte'; -export { default as Laravel } from './Laravel.svelte'; -export { default as Heroku } from './Heroku.svelte'; -export { default as Compose } from './Compose.svelte'; diff --git a/apps/ui/src/lib/components/svg/databases/Clickhouse.svelte b/apps/ui/src/lib/components/svg/databases/Clickhouse.svelte deleted file mode 100644 index dd237a48c..000000000 --- a/apps/ui/src/lib/components/svg/databases/Clickhouse.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/databases/CouchDB.svelte b/apps/ui/src/lib/components/svg/databases/CouchDB.svelte deleted file mode 100644 index 411c4928d..000000000 --- a/apps/ui/src/lib/components/svg/databases/CouchDB.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/databases/DatabaseIcons.svelte b/apps/ui/src/lib/components/svg/databases/DatabaseIcons.svelte deleted file mode 100644 index 4a00860af..000000000 --- a/apps/ui/src/lib/components/svg/databases/DatabaseIcons.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -{#if type === 'mysql'} - -{:else if type === 'postgresql'} - -{:else if type === 'mongodb'} - -{:else if type === 'mariadb'} - -{:else if type === 'redis'} - -{:else if type === 'couchdb'} - -{:else if type === 'edgedb'} - -{/if} diff --git a/apps/ui/src/lib/components/svg/databases/EdgeDB.svelte b/apps/ui/src/lib/components/svg/databases/EdgeDB.svelte deleted file mode 100644 index 57fdebed5..000000000 --- a/apps/ui/src/lib/components/svg/databases/EdgeDB.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/databases/MariaDB.svelte b/apps/ui/src/lib/components/svg/databases/MariaDB.svelte deleted file mode 100644 index 5bf504bcc..000000000 --- a/apps/ui/src/lib/components/svg/databases/MariaDB.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/databases/MongoDB.svelte b/apps/ui/src/lib/components/svg/databases/MongoDB.svelte deleted file mode 100644 index fbb261aa2..000000000 --- a/apps/ui/src/lib/components/svg/databases/MongoDB.svelte +++ /dev/null @@ -1,90 +0,0 @@ - - - - - diff --git a/apps/ui/src/lib/components/svg/databases/MySQL.svelte b/apps/ui/src/lib/components/svg/databases/MySQL.svelte deleted file mode 100644 index 095093214..000000000 --- a/apps/ui/src/lib/components/svg/databases/MySQL.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/databases/PostgreSQL.svelte b/apps/ui/src/lib/components/svg/databases/PostgreSQL.svelte deleted file mode 100644 index 3021508a6..000000000 --- a/apps/ui/src/lib/components/svg/databases/PostgreSQL.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/apps/ui/src/lib/components/svg/databases/Redis.svelte b/apps/ui/src/lib/components/svg/databases/Redis.svelte deleted file mode 100644 index 24a7dc797..000000000 --- a/apps/ui/src/lib/components/svg/databases/Redis.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/databases/index.ts b/apps/ui/src/lib/components/svg/databases/index.ts deleted file mode 100644 index e200b5311..000000000 --- a/apps/ui/src/lib/components/svg/databases/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -//@ts-nocheck -export { default as Clickhouse } from './Clickhouse.svelte'; -export { default as CouchDB } from './CouchDB.svelte'; -export { default as MariaDB } from './MariaDB.svelte'; -export { default as MongoDB } from './MongoDB.svelte'; -export { default as MySQL } from './MySQL.svelte'; -export { default as PostgreSQL } from './PostgreSQL.svelte'; -export { default as Redis } from './Redis.svelte'; -export { default as EdgeDB } from './EdgeDB.svelte'; - - diff --git a/apps/ui/src/lib/components/svg/servers/LocalDockerIcon.svelte b/apps/ui/src/lib/components/svg/servers/LocalDockerIcon.svelte deleted file mode 100644 index f3ab3be56..000000000 --- a/apps/ui/src/lib/components/svg/servers/LocalDockerIcon.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - diff --git a/apps/ui/src/lib/components/svg/servers/RemoteDockerIcon.svelte b/apps/ui/src/lib/components/svg/servers/RemoteDockerIcon.svelte deleted file mode 100644 index 1d00a6900..000000000 --- a/apps/ui/src/lib/components/svg/servers/RemoteDockerIcon.svelte +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/apps/ui/src/lib/components/svg/services/ServiceIcons.svelte b/apps/ui/src/lib/components/svg/services/ServiceIcons.svelte deleted file mode 100644 index bf241753a..000000000 --- a/apps/ui/src/lib/components/svg/services/ServiceIcons.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - -{#if name} - {`Icon -{/if} diff --git a/apps/ui/src/lib/components/svg/sources/GithubIcon.svelte b/apps/ui/src/lib/components/svg/sources/GithubIcon.svelte deleted file mode 100644 index b56210177..000000000 --- a/apps/ui/src/lib/components/svg/sources/GithubIcon.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/apps/ui/src/lib/components/svg/sources/GitlabIcon.svelte b/apps/ui/src/lib/components/svg/sources/GitlabIcon.svelte deleted file mode 100644 index 3e8c8a84c..000000000 --- a/apps/ui/src/lib/components/svg/sources/GitlabIcon.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/apps/ui/src/lib/container/status.ts b/apps/ui/src/lib/container/status.ts deleted file mode 100644 index a700e652b..000000000 --- a/apps/ui/src/lib/container/status.ts +++ /dev/null @@ -1,73 +0,0 @@ -// -// Maps Container ID x Operation Status -// -// Example response of $status => {'123asdf': 'degraded', '124asdf': 'running'} - -import { writable, get as getStore } from 'svelte/store'; -import { get } from '$lib/api'; - -export let containerStatus = writable({}); - -let PERMITED_STATUS = ['loading', 'running', 'healthy', 'building', 'degraded', 'stopped', 'error']; - -// refreshStatus([{id}]) -export async function refreshStatus(list: Array) { - for (const item of list) { - setStatus(item.id, 'loading'); - getStatus(item, true); - } -} - -export async function getStatus(resource: any, force: boolean = false) { - const { id, buildPack, dualCerts, engine, simpleDockerfile } = resource; - let newStatus = 'stopped'; - - // Already set and we're not forcing - if (getStore(containerStatus)[id] && !force) return getStore(containerStatus)[id]; - - try { - if (buildPack || simpleDockerfile) { // Application - const response = await get(`/applications/${id}/status`); - newStatus = parseApplicationsResponse(response); - } else if (typeof dualCerts !== 'undefined') { // Service - const response = await get(`/services/${id}/status`); - newStatus = parseServiceResponse(response); - } else if (typeof engine !== 'undefined') { // Destination/Server - const response = await get(`/destinations/${id}/status`); - newStatus = response.isRunning ? 'running' : 'stopped'; - } else { // Database - const response = await get(`/databases/${id}/status`); - newStatus = response.isRunning ? 'running' : 'stopped'; - } - } catch (error) { - newStatus = 'error'; - } - - setStatus(id, newStatus); - // console.log("GOT:", id, newStatus) - return newStatus -} - -const setStatus = (thingId, newStatus) => { - if (!PERMITED_STATUS.includes(newStatus)) - throw (`Change to ${newStatus} is not permitted. Try: ${PERMITED_STATUS.join(', ')}`); - containerStatus.update(n => Object.assign(n, { thingId: newStatus })); -}; - -// -- Response Parsing - -function parseApplicationsResponse(list: Array) { - if (list.length === 0) return 'stopped'; - if (list.length === 1) return list[0].status.isRunning ? 'running' : 'stopped'; - return allWorking(list.map((el: any) => el.status.isRunning)) -} - -function parseServiceResponse(response: any) { - if (Object.keys(response).length === 0) return 'stopped'; - let list = Object.keys(response).map((el) => el.status.isRunning) - return allWorking(list) ? 'running' : 'degraded' -} - -function allWorking(list: Array) { - return list.reduce((acum: boolean, res: boolean) => acum && res) ? 'running' : 'degraded'; -} diff --git a/apps/ui/src/lib/dayjs.ts b/apps/ui/src/lib/dayjs.ts deleted file mode 100644 index 9ff5b0a1a..000000000 --- a/apps/ui/src/lib/dayjs.ts +++ /dev/null @@ -1,7 +0,0 @@ -import dayjs from 'dayjs'; -import utc from 'dayjs/plugin/utc.js'; -import relativeTime from 'dayjs/plugin/relativeTime.js'; -dayjs.extend(utc); -dayjs.extend(relativeTime); - -export { dayjs as day }; diff --git a/apps/ui/src/lib/lang.json b/apps/ui/src/lib/lang.json deleted file mode 100644 index f5485ece0..000000000 --- a/apps/ui/src/lib/lang.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fr": "Français", - "pt": "Português", - "es": "Espanhol", - "ko": "Korean", - "en": "English" -} diff --git a/apps/ui/src/lib/locales/en.json b/apps/ui/src/lib/locales/en.json deleted file mode 100644 index f04ae6d8e..000000000 --- a/apps/ui/src/lib/locales/en.json +++ /dev/null @@ -1,341 +0,0 @@ -{ - "layout": { - "update_done": "Update completed.", - "wait_new_version_startup": "Waiting for the new version to start...", - "new_version": "New version reachable. Reloading...", - "switch_to_a_different_team": "Switch to a different team...", - "update_available": "Update Available" - }, - "error": { - "you_can_find_your_way_back": "You can find your way back", - "here": "here", - "you_are_lost": "Ooops you are lost! But don't be afraid!" - }, - "index": { - "dashboard": "Dashboard", - "applications": "Applications", - "destinations": "Destinations", - "git_sources": "Git Sources", - "databases": "Databases", - "services": "Services", - "teams": "Teams", - "not_implemented_yet": "Not implemented yet", - "database": "Database", - "settings": "Settings", - "global_settings": "Global Settings", - "secret": "Secret", - "team": "Team", - "logout": "Logout" - }, - "login": { - "already_logged_in": "Already logged in...", - "authenticating": "Authenticating...", - "login": "Login" - }, - "forms": { - "password": "Password", - "email": "Email address", - "passwords_not_match": "Passwords do not match.", - "password_again": "Password again", - "save": "Save", - "saving": "Saving...", - "name": "Name", - "value": "Value", - "action": "Actions", - "is_required": "is required.", - "add": "Add", - "set": "Set", - "remove": "Remove", - "path": "Path", - "confirm_continue": "Are you sure to continue?", - "must_be_stopped_to_modify": "Must be stopped to modify.", - "port": "Port", - "default": "default", - "base_directory": "Base Directory", - "publish_directory": "Publish Directory", - "generated_automatically_after_start": "Generated automatically after start", - "roots_password": "Root's Password", - "root_user": "Root User", - "eg": "eg", - "user": "User", - "loading": "Loading...", - "version": "Version", - "host": "Host", - "already_used_for": "{{type}} already used for", - "configuration": "Configuration", - "engine": "Engine", - "network": "Network", - "ip_address": "IP Address", - "ssh_private_key": "SSH Private Key", - "type": "Type", - "html_url": "HTML URL", - "api_url": "API URL", - "organization": "Organization", - "new_password": "New password", - "super_secure_new_password": "Super secure new password", - "submit": "Submit", - "default_email_address": "Default Email Address", - "default_password": "Default Password", - "username": "Username", - "root_db_user": "Root DB User", - "root_db_password": "Root DB Password", - "api_port": "API Port", - "verifying": "Verifying", - "verify_emails_without_smtp": "Verify emails without SMTP", - "extra_config": "Extra Config", - "select_a_service": "Select a Service", - "select_a_service_version": "Select a Service version", - "removing": "Removing...", - "remove_domain": "Remove domain", - "public_port_range": "Public Port Range", - "public_port_range_explainer": "Ports used to expose databases/services/internal services.
Add them to your firewall (if applicable).

You can specify a range of ports, eg: 9000-9100", - "no_actions_available": "No actions available", - "admin_api_key": "Admin API key" - }, - "register": { - "register": "Register", - "registering": "Registering...", - "first_user": "You are registering the first user. It will be the administrator of your Coolify instance." - }, - "reset": { - "reset_password": "Reset", - "invalid_secret_key": "Invalid secret key.", - "secret_key": "Secret Key", - "find_path_secret_key": "You can find it in ~/coolify/.env (COOLIFY_SECRET_KEY)" - }, - "application": { - "configuration": { - "buildpack": { - "choose_this_one": "Choose this one..." - }, - "branch_already_in_use": "This branch is already used by another application. Webhooks won't work in this case for both applications. Are you sure you want to use it?", - "no_repositories_configured": "No repositories configured for your Git Application.", - "configure_it_now": "Configure it now", - "loading_repositories": "Loading repositories ...", - "select_a_repository": "Please select a repository", - "loading_branches": "Loading branches ...", - "select_a_repository_first": "Please select a repository first", - "select_a_branch": "Please select a branch", - "loading_groups": "Loading groups...", - "select_a_group": "Please select a group", - "loading_projects": "Loading projects...", - "select_a_project": "Please select a project", - "no_projects_found": "No projects found", - "no_branches_found": "No branches found", - "configure_build_pack": "Configure Build Pack", - "scanning_repository_suggest_build_pack": "Scanning repository to suggest a build pack for you...", - "found_lock_file": "Found lock file for {{packageManager}}.
Using it for predefined commands commands.", - "configure_destination": "Configure Destination", - "no_configurable_destination": "No configurable Destination found", - "select_a_repository_project": "Select a Repository / Project", - "select_a_git_source": "Select a Git Source", - "no_configurable_git": "No configurable Git Source found", - "configuration_missing": "Configuration missing" - }, - "build": { - "queued_waiting_exec": "Queued and waiting for execution.", - "build_logs_of": "Build logs of", - "running": "Running", - "queued": "Queued", - "finished_in": "Finished in", - "load_more": "Load More", - "no_logs": "No logs found", - "waiting_logs": "Waiting for the logs..." - }, - "preview": { - "need_during_buildtime": "Need during buildtime?", - "setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first.
Useful for creating staging environments.", - "values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating staging environments.", - "redeploy": "Redeploy", - "no_previews_available": "No previews available" - }, - "secrets": { - "secret_saved": "Secret saved.", - "use_isbuildsecret": "Use isBuildSecret", - "secrets_for": "Secrets for" - }, - "storage": { - "path_is_required": "Path is required.", - "storage_saved": "Storage saved.", - "storage_updated": "Storage updated.", - "storage_deleted": "Storage deleted.", - "persistent_storage_explainer": "You can specify any folder that you want to be persistent across deployments.

/example means it will preserve /example between deployments.

Your application's data is copied to /app inside the container, you can preserve data under it as well, like /app/db.

This is useful for storing data such as a database (SQLite) or a cache." - }, - "deployment_queued": "Deployment queued.", - "confirm_to_delete": "Are you sure you would like to delete '{{name}}'?", - "stop_application": "Stop Application", - "permission_denied_stop_application": "You do not have permission to stop the application.", - "rebuild_application": "Rebuild Application", - "permission_denied_rebuild_application": "You do not have permission to rebuild application.", - "build_and_start_application": "Deploy", - "permission_denied_build_and_start_application": "You do not have permission to deploy application.", - "configurations": "Configurations", - "secret": "Secrets", - "persistent_storage": "Persistent Storage", - "previews": "Previews", - "logs": "Application Logs", - "build_logs": "Build Logs", - "delete_application": "Delete", - "permission_denied_delete_application": "You do not have permission to delete this application", - "domain_already_in_use": "Domain {{domain}} is already used.", - "dns_not_set_error": "DNS not set correctly or propogated for {{domain}}.

Please check your DNS settings.", - "domain_required": "Domain is required.", - "settings_saved": "Configuration saved.", - "dns_not_set_partial_error": "DNS not set", - "domain_not_valid": "Could not resolve domain or it's not pointing to the server IP address.

Please check your DNS configuration and try again.", - "git_source": "Git Source", - "git_repository": "Git Repository", - "build_pack": "Build Pack", - "base_image": "Deployment Image", - "base_image_explainer": "Image that will be used for the deployment.", - "base_build_image": "Build Image", - "base_build_image_explainer": "Image that will be used during the build process.", - "destination": "Destination", - "application": "Application", - "url_fqdn": "URL (FQDN)", - "domain_fqdn": "Domain (FQDN)", - "https_explainer": "If you specify https, the application will be accessible only over https. SSL certificate will be generated for you.
If you specify www, the application will be redirected (302) from non-www and vice versa.

To modify the domain, you must first stop the application.

You must set your DNS to point to the server IP in advance.", - "ssl_www_and_non_www": "Generate SSL for www and non-www?", - "ssl_explainer": "It will generate certificates for both www and non-www.
You need to have both DNS entries set in advance.

Useful if you expect to have visitors on both.", - "install_command": "Install Command", - "build_command": "Build Command", - "start_command": "Start Command", - "directory_to_use_explainer": "Directory to use as the base for all commands.
Could be useful with monorepos.", - "publish_directory_explainer": "Directory containing all the assets for deployment.
For example: dist,_site or public.", - "features": "Features", - "enable_automatic_deployment": "Enable Automatic Deployment", - "enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.", - "enable_mr_pr_previews": "Enable MR/PR Previews", - "expose_a_port": "Expose a port", - "enable_preview_deploy_mr_pr_requests": "Enable preview deployments from pull or merge requests.", - "debug_logs": "Debug Logs", - "enable_debug_log_during_build": "Enable debug logs during build phase.
Sensitive information could be visible and saved in logs.", - "cant_activate_auto_deploy_without_repo": "Cannot activate automatic deployments until only one application is defined for this repository / branch.", - "no_applications_found": "No applications found", - "secret__batch_dot_env": "Paste .env file", - "batch_secrets": "Batch add secrets" - }, - "general": "General", - "database": { - "default_database": "Default Database", - "generated_automatically_after_set_to_public": "Generated automatically after set to public", - "connection_string": "Connection String", - "set_public": "Set it public", - "warning_database_public": "Your database will be reachable over the internet.
Take security seriously in this case!", - "change_append_only_mode": "Change append only mode", - "warning_append_only": "Useful if you would like to restore redis data from a backup.
Database restart is required.", - "select_database_type": "Select a Database type", - "select_database_version": "Select a Database version", - "confirm_stop": "Are you sure you would like to stop {{name}}?", - "stop_database": "Stop", - "permission_denied_stop_database": "You do not have permission to stop the database.", - "start_database": "Start", - "permission_denied_start_database": "You do not have permission to start the database.", - "delete_database": "Delete", - "permission_denied_delete_database": "You do not have permission to delete a Database", - "no_databases_found": "No databases found", - "logs": "Logs" - }, - "destination": { - "delete_destination": "Delete", - "permission_denied_delete_destination": "You do not have permission to delete this destination", - "add_to_coolify": "Add to Coolify", - "coolify_proxy_stopped": "Coolify Proxy stopped!", - "coolify_proxy_started": "Coolify Proxy started!", - "confirm_restart_proxy": "Are you sure you want to restart the proxy? Everything will be reconfigured in ~10 secs.", - "coolify_proxy_restarting": "Coolify Proxy restarting...", - "restarting_please_wait": "Restarting... please wait...", - "force_restart_proxy": "Force restart proxy", - "use_coolify_proxy": "Use Coolify Proxy?", - "no_destination_found": "No destination found", - "new_error_network_already_exists": "Network {{network}} already configured for another team!", - "new": { - "saving_and_configuring_proxy": "Saving...", - "install_proxy": "This will install a proxy on the destination to allow you to access your applications and services without any manual configuration (recommended for Docker).

Databases will have their own proxy.", - "add_new_destination": "Add New Destination", - "predefined_destinations": "Predefined destinations" - } - }, - "sources": { - "local_docker": "Local Docker", - "remote_docker": "Remote Docker", - "organization_explainer": "Fill it if you would like to use an organization's as your Git Source. Otherwise your user will be used." - }, - "source": { - "new": { - "git_source": "Add New Git Source", - "official_providers": "Official providers" - }, - "no_git_sources_found": "No git sources found", - "delete_git_source": "Delete", - "permission_denied": "You do not have permission to delete a Git Source", - "create_new_app": "Create new {{name}} App", - "change_app_settings": "Change {{name}} App Settings", - "install_repositories": "Install Repositories", - "application_id": "Application ID", - "group_name": "Group Name", - "oauth_id": "OAuth ID", - "oauth_id_explainer": "The OAuth ID is the unique identifier of the GitLab application.
You can find it in the URL of your GitLab OAuth Application.", - "register_oauth_gitlab": "Register new OAuth application on GitLab", - "gitlab": { - "self_hosted": "Instance-wide application (self-hosted)", - "user_owned": "User owned application", - "group_owned": "Group owned application", - "gitlab_application_type": "GitLab Application Type", - "already_configured": "GitLab App is already configured." - }, - "github": { - "redirecting": "Redirecting to Github..." - } - }, - "services": { - "all_email_verified": "All emails are verified. You can login now.", - "generate_www_non_www_ssl": "It will generate certificates for both www and non-www.
You need to have both DNS entries set in advance.

Service needs to be restarted." - }, - "service": { - "stop_service": "Stop", - "permission_denied_stop_service": "You do not have permission to stop the service.", - "start_service": "Start", - "permission_denied_start_service": "You do not have permission to start the service.", - "delete_service": "Delete", - "permission_denied_delete_service": "You do not have permission to delete a service.", - "no_service": "No services found", - "logs": "Logs" - }, - "setting": { - "change_language": "Change Language", - "permission_denied": "You do not have permission to do this. \\nAsk an admin to modify your permissions.", - "domain_removed": "Domain removed", - "ssl_explainer": "If you specify https, Coolify will be accessible only over https. SSL certificate will be generated for you.
If you specify www, Coolify will be redirected (302) from non-www and vice versa.

WARNING: If you change an already set domain, it will break webhooks and other integrations! You need to manually update them.", - "must_remove_domain_before_changing": "Must remove the domain before you can change this setting.", - "registration_allowed": "Registration allowed?", - "registration_allowed_explainer": "Allow further registrations to the application.
It's turned off after the first registration.", - "coolify_proxy_settings": "Coolify Proxy Settings", - "credential_stat_explainer": "Credentials for stats page.", - "auto_update_enabled": "Auto update enabled?", - "auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running.", - "generate_www_non_www_ssl": "It will generate certificates for both www and non-www.
You need to have both DNS entries set in advance.", - "is_dns_check_enabled": "DNS check enabled?", - "is_dns_check_enabled_explainer": "You can disable DNS check before creating SSL certificates.

Turning it off is useful when Coolify is behind a reverse proxy or tunnel." - }, - "team": { - "pending_invitations": "Pending invitations", - "accept": "Accept", - "delete": "Delete", - "member": "member(s)", - "root": "(root)", - "invited_with_permissions": "Invited to {{teamName}} with {{permission}} permission.", - "members": "Members", - "root_team_explainer": "This is the root team. That means members of this group can manage instance wide settings and have all the priviliges in Coolify (imagine like root user on Linux).", - "permission": "Permission", - "you": "You", - "promote_to": "Promote to {{grade}}", - "revoke_invitation": "Revoke invitation", - "pending_invitation": "Pending invitation", - "invite_new_member": "Invite new member", - "send_invitation": "Send invitation", - "invite_only_register_explainer": "You can only invite registered users.", - "admin": "Admin", - "read": "Read" - } -} diff --git a/apps/ui/src/lib/locales/es.json b/apps/ui/src/lib/locales/es.json deleted file mode 100644 index 54eb5f716..000000000 --- a/apps/ui/src/lib/locales/es.json +++ /dev/null @@ -1,341 +0,0 @@ -{ - "layout":{ - "update_done":"Actualización completada.", - "wait_new_version_startup":"Esperando que comience la nueva versión.", - "new_version":"Nueva versión accesible. Recargando.", - "switch_to_a_different_team":"Cambia a otro equipo.", - "update_available":"Actualización disponible" - }, - "error":{ - "you_can_find_your_way_back":"Puedes encontrar tu camino de vuelta", - "here":"Aquí.", - "you_are_lost":"¡Estás perdido! ¡Pero no tengas miedo!" - }, - "index":{ - "dashboard":"Dashboard", - "applications":"Aplicaciones", - "destinations":"Destinos", - "git_sources":"Fuentes Git", - "databases":"Bases de datos", - "services":"Servicios", - "teams":"Equipos", - "not_implemented_yet":"Aún no se ha aplicado", - "database":"Base de datos", - "settings":"Ajustes", - "global_settings":"Ajustes mundiales", - "secret":"Secret", - "team":"Equipo", - "logout":"Cerrar sesión" - }, - "login":{ - "already_logged_in":"Ya se ha registrado.", - "authenticating":"Autenticando.", - "login":"Iniciar sesión" - }, - "forms":{ - "password":"Contraseña", - "email":"Dirección de correo electrónico", - "passwords_not_match":"Las contraseñas no coinciden.", - "password_again":"Contraseña de nuevo", - "save":"Guardar", - "saving":"Salvando.", - "name":"Nombre", - "value":"Valor", - "action":"Acciones", - "is_required":"es necesario.", - "add":"Añadir", - "set":"Set", - "remove":"Retirar", - "path":"Camino", - "confirm_continue":"¿Estás seguro de continuar?", - "must_be_stopped_to_modify":"Debe ser detenido para modificar.", - "port":"Puerto", - "default":"Por defecto", - "base_directory":"Base Directory", - "publish_directory":"Publish Directory", - "generated_automatically_after_start":"Generado automáticamente después del inicio", - "roots_password":"La contraseña de Root", - "root_user":"Usuario raíz", - "eg":"eg", - "user":"Usuario", - "loading":"Carga.", - "version":"Versión", - "host":"Host", - "already_used_for":"##########################################################################################################################################################################################################################################################", - "configuration":"Configuración", - "engine":"Motor", - "network":"Red", - "ip_address":"Dirección IP", - "ssh_private_key":"SSH Clave privada", - "type":"Tipo", - "html_url":"URL", - "api_url":"API", - "organization":"Organización", - "new_password":"Nueva contraseña", - "super_secure_new_password":"Super seguro nueva contraseña", - "submit":"Submit", - "default_email_address":"Dirección de correo electrónico predeterminada", - "default_password":"Contraseña predeterminada", - "username":"Nombre de usuario", - "root_db_user":"Root DB Usuario", - "root_db_password":"Root DB Contraseña", - "api_port":"API Port", - "verifying":"Verificación", - "verify_emails_without_smtp":"Verificar correos electrónicos sin SMTP", - "extra_config":"Extra Config", - "select_a_service":"Seleccione un Servicio", - "select_a_service_version":"Seleccione una versión de servicio", - "removing":"Retirándose.", - "remove_domain":"Eliminar el dominio", - "public_port_range":"Public Port Range", - "public_port_range_explainer":"Puertos utilizados para exponer bases de datos/servicios/servicios internos. Añádalos a su cortafuegos (si es aplicable).Seguido se indicará una gama de puertos, por ejemplo: tachuelas clase='text-settings '9000-9100 seg/span", - "no_actions_available":"No se dispone de medidas", - "admin_api_key":"Clave de API de Admin" - }, - "register":{ - "register":"Registro", - "registering":"Registro.", - "first_user":"Está registrando al primer usuario. Será el administrador de tu instancia de Coolify." - }, - "reset":{ - "reset_password":"Reset", - "invalid_secret_key":"Una llave secreta inválida.", - "secret_key":"Secret Key", - "find_path_secret_key":"Puedes encontrarlo en ~coolify/.env (COOLIFY_SECRET_KEY)" - }, - "application":{ - "configuration":{ - "buildpack":{ - "choose_this_one":"Elige esta." - }, - "branch_already_in_use":"Esta rama ya es utilizada por otra aplicación. Webhooks no funcionará en este caso para ambas aplicaciones. ¿Seguro que quieres usarlo?", - "no_repositories_configured":"No hay repositorios configurados para su aplicación Git.", - "configure_it_now":"Configure ahora", - "loading_repositories":"Carga de repositorios ...", - "select_a_repository":"Seleccione un repositorio", - "loading_branches":"Cargando ramas ...", - "select_a_repository_first":"Por favor seleccione un repositorio primero", - "select_a_branch":"Por favor seleccione una rama", - "loading_groups":"Grupos de carga.", - "select_a_group":"Seleccione un grupo", - "loading_projects":"Cargando proyectos.", - "select_a_project":"Seleccione un proyecto", - "no_projects_found":"No se han encontrado proyectos", - "no_branches_found":"No hay ramas encontradas", - "configure_build_pack":"Configure Build Pack", - "scanning_repository_suggest_build_pack":"Repositorio de exploración para sugerir un paquete de construcción para usted.", - "found_lock_file":"encontrado archivo de bloqueo para {{packageManager}}.Seguido de comandos predefinidos.", - "configure_destination":"Configurar Destino", - "no_configurable_destination":"No hay destino configurable encontrado", - "select_a_repository_project":"Seleccione un Repositorio / Proyecto", - "select_a_git_source":"Seleccione una fuente de Git", - "no_configurable_git":"No se encontró una fuente de Git configurable", - "configuration_missing":"Falta de configuración" - }, - "build":{ - "queued_waiting_exec":"Queued and waiting for execution.", - "build_logs_of":"Construir registros de", - "running":"Corriendo", - "queued":"Queued", - "finished_in":"Terminado en", - "load_more":"Carga más", - "no_logs":"No hay registros encontrados", - "waiting_logs":"Esperando los registros." - }, - "preview":{ - "need_during_buildtime":"¿Necesitas durante el tiempo de construcción?", - "setup_secret_app_first":"Puede añadir secretos a las implementaciones PR/MR. Por favor, agregue secretos a la aplicación primero. √≠br]Useful for creating יspan class='text-settings 'staging won/span environments.", - "values_overwriting_app_secrets":"Estos valores sobrescriben los secretos de aplicación en las implementaciones PR/MR. Útil para la creación de clase 0'text-settings 'estaging significan ambientes / paño.", - "redeploy":"Redistribución", - "no_previews_available":"No hay vistas previas disponibles" - }, - "secrets":{ - "secret_saved":"Secreto salvado.", - "use_isbuildsecret":"Use isBuildSecret", - "secrets_for":"Secretos para" - }, - "storage":{ - "path_is_required":"Se requiere camino.", - "storage_saved":"Almacenamiento guardado.", - "storage_updated":"Almacenamiento actualizado.", - "storage_deleted":"Almacenamiento eliminado.", - "persistent_storage_explainer":"Puede especificar cualquier carpeta que desee ser persistente a través de las implementaciones.Seguido de la clase='text-settings 'ejemplo observado/span significa que preservará {{packageManager}}. \nL'utiliser pour les commandes prédéfinies.", - "loading_branches": "Chargement des branches...", - "loading_groups": "Chargement des groupes...", - "loading_projects": "Chargement des projets...", - "loading_repositories": "Chargement des dépôts Git...", - "no_branches_found": "Aucune branche trouvée", - "no_configurable_destination": "Aucune destination configurable trouvée", - "no_configurable_git": "Aucune source Git configurable trouvée", - "no_projects_found": "Aucun projet trouvé", - "no_repositories_configured": "Aucun dépôt Git configuré pour votre application.", - "scanning_repository_suggest_build_pack": "Analyse du dépôt pour vous suggérer un pack de Build...", - "select_a_branch": "Veuillez sélectionner une branche", - "select_a_git_source": "Sélectionnez une source Git", - "select_a_group": "Veuillez sélectionner un groupe", - "select_a_project": "Veuillez sélectionner un projet", - "select_a_repository": "Veuillez sélectionner un dépôt", - "select_a_repository_first": "Veuillez d'abord sélectionner un dépôt", - "select_a_repository_project": "Sélectionnez un dépôt / projet" - }, - "configurations": "Configurations", - "confirm_to_delete": "Voulez-vous vraiment supprimer '{{name}}'?", - "debug_logs": "Journaux de débogage", - "delete_application": "Supprimer l'application", - "deployment_queued": "Déploiement en file d'attente.", - "destination": "Destination", - "directory_to_use_explainer": "Répertoire à utiliser comme base pour toutes les commandes.
Pourrait être utile avec monorepos.", - "dns_not_set_error": "DNS non défini ou propagé pour {{domain}}.

Veuillez vérifier vos paramètres DNS.", - "dns_not_set_partial_error": "DNS non défini", - "domain_already_in_use": "Le domaine {{domain}} est déjà utilisé.", - "domain_fqdn": "Domaine (FQDN)", - "url_fqdn": "URL (FQDN)", - "enable_auto_deploy_webhooks": "Activez le déploiement automatique via des webhooks.", - "enable_automatic_deployment": "Activer le déploiement automatique", - "enable_debug_log_during_build": "Activez les journaux de débogage pendant la phase de build.
Les informations sensibles peuvent être visibles et enregistrées dans les journaux.", - "enable_mr_pr_previews": "Activer les aperçus MR/PR", - "enable_preview_deploy_mr_pr_requests": "Activez les déploiements de prévisualisation à partir de demandes d'extraction ou de fusion.", - "expose_a_port": "Exposer un port", - "features": "Caractéristiques", - "git_repository": "Dépôt Git", - "git_source": "Source Git", - "https_explainer": "Si vous spécifiez https, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.
Si vous spécifiez www, l'application sera redirigée (302) à partir de non-www et vice versa \n.

Pour modifier le domaine, vous devez d'abord arrêter l'application.

Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.", - "install_command": "Commande d'installation", - "logs": "Journaux des applications", - "no_applications_found": "Aucune application trouvée", - "permission_denied_build_and_start_application": "Vous n'êtes pas autorisé à créer et à démarrer l'application.", - "permission_denied_delete_application": "Vous n'êtes pas autorisé à supprimer cette application", - "permission_denied_rebuild_application": "Vous n'êtes pas autorisé à re-build l'application.", - "permission_denied_stop_application": "Vous n'êtes pas autorisé à arrêter l'application.", - "persistent_storage": "Stockage persistant", - "preview": { - "need_during_buildtime": "Besoin pendant la build ?", - "no_previews_available": "Aucun aperçu disponible", - "redeploy": "Redéployer", - "setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n
Utile pour créer des environnements de mise en scène.", - "values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements de mise en scène." - }, - "previews": "Aperçus", - "publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n
Par exemple : dist,_site ou public.", - "rebuild_application": "Re-build l'application", - "secret": "secrets", - "secrets": { - "secret_saved": "Secret enregistré.", - "secrets_for": "secrets pour", - "use_isbuildsecret": "Utiliser isBuildSecret" - }, - "settings_saved": "Paramètres sauvegardés.", - "ssl_explainer": "Il générera des certificats pour www et non-www. \n
Vous devez avoir les deux entrées DNS définies à l'avance.

Utile si vous prévoyez d'avoir des visiteurs sur les deux.", - "ssl_www_and_non_www": "Générer SSL pour www et non-www ?", - "start_command": "Démarrer la commande", - "stop_application": "Arrêter l'application", - "storage": { - "path_is_required": "Le chemin est requis.", - "persistent_storage_explainer": "Vous pouvez spécifier n'importe quel dossier que vous souhaitez conserver dans les déploiements. \n
Ceci est utile pour stocker des données telles qu'une base de données (SQLite) ou un cache.", - "storage_deleted": "Stockage supprimé.", - "storage_saved": "Stockage enregistré.", - "storage_updated": "Stockage mis à jour." - } - }, - "database": { - "change_append_only_mode": "Changer le mode d'ajout uniquement", - "confirm_stop": "Êtes-vous sûr de vouloir arrêter {{name}} ?", - "connection_string": "Connexion string", - "default_database": "Base de données par défaut", - "delete_database": "Supprimer la base de données", - "generated_automatically_after_set_to_public": "Généré automatiquement après avoir été défini sur public", - "no_databases_found": "Aucune base de données trouvée", - "permission_denied_delete_database": "Vous n'êtes pas autorisé à supprimer une base de données", - "permission_denied_start_database": "Vous n'êtes pas autorisé à démarrer la base de données.", - "permission_denied_stop_database": "Vous n'êtes pas autorisé à arrêter la base de données.", - "select_database_type": "Sélectionnez un type de base de données", - "select_database_version": "Sélectionnez une version de la base de données", - "set_public": "Rendre public", - "start_database": "Démarrer la base de données", - "stop_database": "Arrêter la base de données", - "warning_append_only": "Utile si vous souhaitez restaurer des données Redis à partir d'une sauvegarde.
Le redémarrage de la base de données est nécessaire.", - "warning_database_public": "Votre base de données sera accessible depuis Internet. \n
Prenez la sécurité au sérieux dans ce cas!" - }, - "destination": { - "add_to_coolify": "Ajouter à Coolify", - "confirm_restart_proxy": "Voulez-vous vraiment redémarrer le proxy? \nTout sera reconfiguré en ~10 secondes.", - "coolify_proxy_restarting": "Redémarrage du Proxy Coolify...", - "coolify_proxy_started": "Proxy Coolify démarré!", - "coolify_proxy_stopped": "Proxy Coolify arrêté!", - "delete_destination": "Supprimer le destinataire", - "force_restart_proxy": "Forcer le redémarrage du proxy", - "new": { - "add_new_destination": "Ajouter une nouvelle destination", - "install_proxy": "Cela installera un proxy sur la destination pour vous permettre d'accéder à vos applications et services sans aucune configuration manuelle (recommandé pour Docker).

Les bases de données auront leur propre proxy.", - "predefined_destinations": "Destinations prédéfinies", - "saving_and_configuring_proxy": "Enregistrement et configuration du proxy..." - }, - "new_error_network_already_exists": "Réseau {{network}} déjà configuré pour une autre équipe !", - "no_destination_found": "Aucune destination trouvée", - "permission_denied_delete_destination": "Vous n'êtes pas autorisé à supprimer cette destination", - "restarting_please_wait": "Redémarrage... veuillez patienter...", - "use_coolify_proxy": "Utiliser le Proxy Coolify ?" - }, - "error": { - "here": "ici", - "you_are_lost": "Oups vous êtes perdu ! \nMais n'ayez pas peur !", - "you_can_find_your_way_back": "Tu peux retrouver ton chemin" - }, - "forms": { - "action": "action", - "add": "Ajouter", - "already_used_for": "{{type}} déjà utilisé pour", - "api_port": "Port API", - "api_url": "URL de l'API", - "base_directory": "Répertoire de base", - "configuration": "Configuration", - "confirm_continue": "Êtes-vous sûr de continuer ?", - "default": "défaut", - "default_email_address": "Adresse e-mail par défaut", - "default_password": "Mot de passe par défaut", - "eg": "ex", - "email": "Adresse e-mail", - "engine": "Moteur", - "extra_config": "Configuration supplémentaire", - "generated_automatically_after_start": "Généré automatiquement après le démarrage", - "host": "Hôte", - "html_url": "URL HTML", - "ip_address": "Adresse IP", - "is_required": "est requis.", - "loading": "Chargement...", - "must_be_stopped_to_modify": "Doit être arrêté pour être modifié.", - "name": "Nom", - "network": "Réseau", - "new_password": "Nouveau mot de passe", - "no_actions_available": "Aucune action disponible", - "organization": "Organisation", - "password": "Mot de passe", - "password_again": "Mot de passe à nouveau", - "passwords_not_match": "Les mots de passe ne correspondent pas.", - "path": "Chemin", - "port": "Port", - "public_port_range": "Gamme de ports publics", - "public_port_range_explainer": "Ports utilisés pour exposer les bases de données/services/services internes.
Ajoutez-les à votre pare-feu (le cas échéant).

Vous pouvez spécifier une plage de ports, par exemple : 9000-9100", - "publish_directory": "Publier le répertoire", - "remove": "Retirer", - "remove_domain": "Supprimer le domaine", - "removing": "Suppression...", - "root_db_password": "Mot de passe root de la base de données", - "root_db_user": "Utilisateur root de la base de données", - "root_user": "Utilisateur root", - "roots_password": "Mot de passe de l'utilisateur root", - "save": "sauvegarder", - "saving": "Sauvegarde...", - "select_a_service": "Sélectionnez un service", - "select_a_service_version": "Sélectionnez une version de service", - "set": "Régler", - "ssh_private_key": "Clé privée SSH", - "submit": "Nous faire parvenir", - "super_secure_new_password": "Nouveau mot de passe super sécurisé", - "type": "Taper", - "user": "Utilisateur", - "username": "Nom d'utilisateur", - "value": "Valeur", - "verify_emails_without_smtp": "Vérifier les e-mails sans SMTP", - "verifying": "Vérification", - "version": "Version" - }, - "general": "Général", - "index": { - "applications": "Applications", - "dashboard": "Tableau de bord", - "database": "Base de données", - "databases": "Bases de données", - "destinations": "Destinations", - "git_sources": "Sources Git", - "global_settings": "Paramètres globaux", - "logout": "Se déconnecter", - "not_implemented_yet": "Pas encore implémenté", - "secret": "Secret", - "services": "Services", - "settings": "Réglages", - "team": "Équipe", - "teams": "Équipes" - }, - "layout": { - "new_version": "Nouvelle version accessible. \nRechargement...", - "switch_to_a_different_team": "Changer d'équipe...", - "update_available": "Mise à jour disponible", - "update_done": "Mise à jour terminée.", - "wait_new_version_startup": "En attendant le lancement de la nouvelle version..." - }, - "login": { - "already_logged_in": "Déjà connecté...", - "authenticating": "Authentification...", - "login": "Connexion" - }, - "register": { - "first_user": "Vous enregistrez le premier utilisateur. \nCe sera l'administrateur de votre instance Coolify.", - "register": "S'inscrire" - }, - "reset": { - "find_path_secret_key": "Vous pouvez le trouver dans ~/coolify/.env (COOLIFY_SECRET_KEY)", - "invalid_secret_key": "Clé secrète invalide.", - "reset_password": "Réinitialiser", - "secret_key": "Clef secrète" - }, - "service": { - "delete_service": "Supprimer le service", - "no_service": "Aucun service trouvé", - "permission_denied_delete_service": "Vous n'êtes pas autorisé à supprimer un service.", - "permission_denied_start_service": "Vous n'êtes pas autorisé à démarrer le service.", - "permission_denied_stop_service": "Vous n'êtes pas autorisé à arrêter le service.", - "start_service": "Démarrer le service", - "stop_service": "Stopper le service" - }, - "services": { - "all_email_verified": "Tous les e-mails sont vérifiés. \nVous pouvez vous connecter maintenant.", - "generate_www_non_www_ssl": "Il générera des certificats pour www et non-www. \n
Vous devez avoir les deux entrées DNS définies à l'avance.

Le service devra être redémarré." - }, - "setting": { - "coolify_proxy_settings": "Paramètres du proxy Coolify", - "credential_stat_explainer": "Identifiants pour la page statistiques.", - "domain_removed": "Domaine supprimé", - "must_remove_domain_before_changing": "Vous devez supprimer le domaine avant de pouvoir modifier ce paramètre.", - "permission_denied": "Vous n'avez pas la permission de faire cela. \n\\nDemandez à un administrateur de modifier vos autorisations.", - "registration_allowed": "Inscription autorisée ?", - "registration_allowed_explainer": "Autoriser d'autres inscriptions à l'application. \n
Il est désactivé après la première inscription.", - "ssl_explainer": "Si vous spécifiez https, Coolify sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.
Si vous spécifiez www, Coolify sera redirigé (302) à partir de non-www et vice versa." - }, - "source": { - "application_id": "ID d'application", - "change_app_settings": "Modifier les paramètres de l'application {{name}}", - "create_new_app": "Créer une nouvelle application {{name}}", - "delete_git_source": "Supprimer la source Git", - "github": { - "redirecting": "Redirection vers Github..." - }, - "gitlab": { - "already_configured": "L'application GitLab est déjà configurée.", - "gitlab_application_type": "Type d'application GitLab", - "group_owned": "Application détenue par le groupe", - "self_hosted": "Application à l'échelle de l'instance (auto-hébergée)", - "user_owned": "Application appartenant à l'utilisateur" - }, - "group_name": "Nom de groupe", - "install_repositories": "Installer les dépôts", - "new": { - "git_source": "Ajouter une nouvelle source Git", - "official_providers": "Fournisseurs officiels" - }, - "no_git_sources_found": "Aucune source git trouvée", - "oauth_id": "ID OAuth", - "oauth_id_explainer": "L'identifiant OAuth est l'identifiant unique de l'application GitLab. \n
Vous pouvez le trouver dans l'URL de votre application GitLab OAuth.", - "permission_denied": "Vous n'êtes pas autorisé à supprimer une source Git", - "register_oauth_gitlab": "Enregistrer une nouvelle application OAuth sur GitLab" - }, - "sources": { - "local_docker": "Docker local", - "organization_explainer": "Remplissez-le si vous souhaitez utiliser une organisation comme source Git. \nSinon, votre utilisateur sera utilisé.", - "remote_docker": "Station d'accueil à distance" - }, - "team": { - "accept": "J'accepte", - "admin": "Administrateur", - "delete": "Supprimer", - "invite_new_member": "Inviter un nouveau membre", - "invite_only_register_explainer": "Vous ne pouvez inviter que des utilisateurs enregistrés pour le moment - sera bientôt prolongé.", - "invited_with_permissions": "Invité à {{teamName}} avec {{permission}} autorisation.", - "member": "membre(s)", - "members": "Membres", - "pending_invitation": "Invitation en attente", - "pending_invitations": "Invitations en attente", - "permission": "Autorisation", - "promote_to": "Promouvoir à {{grade}}", - "read": "Lire", - "revoke_invitation": "Révoquer l'invitation", - "root": "(suprême)", - "root_team_explainer": "Il s'agit de l'équipe suprême. \nCela signifie que les membres de ce groupe peuvent gérer les paramètres à l'échelle de l'instance et avoir tous les privilèges dans Coolify (imaginez comme un utilisateur root sous Linux).", - "send_invitation": "Envoyer une invitation", - "you": "Toi" - } -} diff --git a/apps/ui/src/lib/locales/ko.json b/apps/ui/src/lib/locales/ko.json deleted file mode 100644 index e575f7740..000000000 --- a/apps/ui/src/lib/locales/ko.json +++ /dev/null @@ -1,341 +0,0 @@ -{ - "layout":{ - "update_done":"업데이트가 완료되었습니다.", - "wait_new_version_startup":"새 버전이 시작되기를 기다리는 중...", - "new_version":"새 버전을 사용할 수 있습니다. 새로고침 중...", - "switch_to_a_different_team":"다른팀으로 갈아타세요...", - "update_available":"업데이트 가능" - }, - "error":{ - "you_can_find_your_way_back":"돌아갈 길을 찾을 수 있다", - "here":"여기", - "you_are_lost":"앗 길을 잃으셨군요! 그러나 두려워하지 마십시오!" - }, - "index":{ - "dashboard":"계기반", - "applications":"애플리케이션", - "destinations":"목적지", - "git_sources":"힘내 소스", - "databases":"데이터베이스", - "services":"서비스", - "teams":"팀", - "not_implemented_yet":"아직 구현되지 않음", - "database":"데이터 베이스", - "settings":"설정", - "global_settings":"전역 설정", - "secret":"비밀", - "team":"팀", - "logout":"로그 아웃" - }, - "login":{ - "already_logged_in":"이미 로그인...", - "authenticating":"인증 중...", - "login":"로그인" - }, - "forms":{ - "password":"비밀번호", - "email":"이메일 주소", - "passwords_not_match":"비밀번호가 일치하지 않습니다.", - "password_again":"비밀번호를 다시", - "save":"구하다", - "saving":"절약...", - "name":"이름", - "value":"값", - "action":"행위", - "is_required":"필요합니다.", - "add":"추가하다", - "set":"세트", - "remove":"제거하다", - "path":"길", - "confirm_continue":"계속하시겠습니까?", - "must_be_stopped_to_modify":"수정하려면 중지해야 합니다.", - "port":"포트", - "default":"기본", - "base_directory":"기본 디렉토리", - "publish_directory":"디렉토리 게시", - "generated_automatically_after_start":"시작 후 자동으로 생성됨", - "roots_password":"루트의 비밀번호", - "root_user":"루트 사용자", - "eg":"예", - "user":"사용자", - "loading":"로드 중...", - "version":"버전", - "host":"주최자", - "already_used_for":"{{type}}이(가) 이미 사용됨", - "configuration":"구성", - "engine":"엔진", - "network":"회로망", - "ip_address":"IP 주소", - "ssh_private_key":"SSH 개인 키", - "type":"유형", - "html_url":"HTML URL", - "api_url":"API URL", - "organization":"조직", - "new_password":"새 비밀번호", - "super_secure_new_password":"매우 안전한 새 비밀번호", - "submit":"제출하다", - "default_email_address":"기본 이메일 주소", - "default_password":"기본 비밀번호", - "username":"사용자 이름", - "root_db_user":"루트 DB 사용자", - "root_db_password":"루트 DB 비밀번호", - "api_port":"API 포트", - "verifying":"확인 중", - "verify_emails_without_smtp":"SMTP 없이 이메일 확인", - "extra_config":"추가 구성", - "select_a_service":"서비스 선택", - "select_a_service_version":"서비스 버전 선택", - "removing":"풀이...", - "remove_domain":"도메인 제거", - "public_port_range":"공용 포트 범위", - "public_port_range_explainer":"데이터베이스/서비스/내부 서비스를 노출하는 데 사용되는 포트입니다.
방화벽에 추가합니다(해당되는 경우).

포트 범위를 지정할 수 있습니다(예: ). 9000-9100", - "no_actions_available":"사용 가능한 작업이 없습니다.", - "admin_api_key":"관리 API 키" - }, - "register":{ - "register":"등록하다", - "registering":"등록 중...", - "first_user":"첫 번째 사용자를 등록하고 있습니다. Coolify 인스턴스의 관리자가 됩니다." - }, - "reset":{ - "reset_password":"초기화", - "invalid_secret_key":"잘못된 비밀 키입니다.", - "secret_key":"비밀 키", - "find_path_secret_key":"~/coolify/.env(COOLIFY_SECRET_KEY)에서 찾을 수 있습니다." - }, - "application":{ - "configuration":{ - "buildpack":{ - "choose_this_one":"이걸 선택..." - }, - "branch_already_in_use":"이 분기는 이미 다른 응용 프로그램에서 사용하고 있습니다. 이 경우 두 애플리케이션 모두에 대해 Webhook이 작동하지 않습니다. 사용하시겠습니까?", - "no_repositories_configured":"Git 애플리케이션에 대해 구성된 저장소가 없습니다.", - "configure_it_now":"지금 구성", - "loading_repositories":"저장소 로드 중...", - "select_a_repository":"저장소를 선택하십시오", - "loading_branches":"브랜치 로드 중...", - "select_a_repository_first":"먼저 저장소를 선택하십시오", - "select_a_branch":"지점을 선택해 주세요", - "loading_groups":"그룹 로드 중...", - "select_a_group":"그룹을 선택하세요.", - "loading_projects":"프로젝트 로드 중...", - "select_a_project":"프로젝트를 선택하세요.", - "no_projects_found":"프로젝트를 찾을 수 없습니다.", - "no_branches_found":"지점을 찾을 수 없습니다", - "configure_build_pack":"빌드 팩 구성", - "scanning_repository_suggest_build_pack":"빌드 팩을 제안하기 위해 저장소를 검색하는 중...", - "found_lock_file":"{{packageManager}}에 대한 잠금 파일을 찾았습니다.
사전 정의된 명령 명령에 사용합니다.", - "configure_destination":"대상 구성", - "no_configurable_destination":"구성 가능한 대상을 찾을 수 없습니다.", - "select_a_repository_project":"리포지토리/프로젝트 선택", - "select_a_git_source":"Git 소스 선택", - "no_configurable_git":"구성 가능한 Git 소스를 찾을 수 없습니다.", - "configuration_missing":"구성 누락" - }, - "build":{ - "queued_waiting_exec":"큐에 넣고 실행을 기다리고 있습니다.", - "build_logs_of":"빌드 로그", - "running":"달리기", - "queued":"대기 중", - "finished_in":"완료", - "load_more":"더 찾아보기", - "no_logs":"로그를 찾을 수 없습니다.", - "waiting_logs":"로그를 기다리는 중..." - }, - "preview":{ - "need_during_buildtime":"빌드 시간에 필요하십니까?", - "setup_secret_app_first":"PR/MR 배포에 비밀을 추가할 수 있습니다. 먼저 응용 프로그램에 비밀을 추가하십시오.
스테이징 환경을 만드는 데 유용합니다.", - "values_overwriting_app_secrets":"이러한 값은 PR/MR 배포에서 애플리케이션 비밀을 덮어씁니다. 스테이징 환경을 만드는 데 유용합니다.", - "redeploy":"재배포", - "no_previews_available":"사용 가능한 미리보기가 없습니다." - }, - "secrets":{ - "secret_saved":"비밀이 저장되었습니다.", - "use_isbuildsecret":"isBuildSecret 사용", - "secrets_for":"비밀" - }, - "storage":{ - "path_is_required":"경로는 필수 항목입니다.", - "storage_saved":"저장용량이 저장되었습니다.", - "storage_updated":"스토리지가 업데이트되었습니다.", - "storage_deleted":"스토리지가 삭제되었습니다.", - "persistent_storage_explainer":"배포 간에 유지하려는 모든 폴더를 지정할 수 있습니다.
/example/app/를 보존함을 의미합니다. /app과 같은 컨테이너의 example은 애플리케이션의 루트 디렉토리입니다.

데이터베이스(SQLite) 또는 캐시와 같은 데이터를 저장하는 데 유용합니다." - }, - "deployment_queued":"배포가 대기 중입니다.", - "confirm_to_delete":"'{{name}}'을(를) 삭제하시겠습니까?", - "stop_application":"애플리케이션 중지", - "permission_denied_stop_application":"애플리케이션을 중지할 권한이 없습니다.", - "rebuild_application":"애플리케이션 재구축", - "permission_denied_rebuild_application":"애플리케이션을 다시 빌드할 권한이 없습니다.", - "build_and_start_application":"배포", - "permission_denied_build_and_start_application":"애플리케이션을 배포할 권한이 없습니다.", - "configurations":"구성", - "secret":"비밀", - "persistent_storage":"영구 스토리지", - "previews":"미리보기", - "logs":"애플리케이션 로그", - "build_logs":"빌드 로그", - "delete_application":"삭제", - "permission_denied_delete_application":"이 애플리케이션을 삭제할 권한이 없습니다.", - "domain_already_in_use":"도메인 {{domain}}은(는) 이미 사용 중입니다.", - "dns_not_set_error":"DNS가 올바르게 설정되지 않았거나 {{domain}}에 대해 전파되었습니다.

DNS 설정을 확인하십시오.", - "domain_required":"도메인은 필수 항목입니다.", - "settings_saved":"구성이 저장되었습니다.", - "dns_not_set_partial_error":"DNS가 설정되지 않았습니다.", - "domain_not_valid":"도메인을 확인할 수 없거나 서버 IP 주소를 가리키지 않습니다.

DNS 구성을 확인하고 다시 시도하십시오.", - "git_source":"힘내 소스", - "git_repository":"Git 저장소", - "build_pack":"빌드 팩", - "base_image":"배포 이미지", - "base_image_explainer":"배포에 사용할 이미지입니다.", - "base_build_image":"빌드 이미지", - "base_build_image_explainer":"빌드 프로세스 중에 사용될 이미지입니다.", - "destination":"목적지", - "application":"신청", - "url_fqdn":"URL(FQDN)", - "domain_fqdn":"도메인(FQDN)", - "https_explainer":"https를 지정하면 https를 통해서만 애플리케이션에 액세스할 수 있습니다. SSL 인증서가 생성됩니다.
www를 지정하면 애플리케이션이 www가 아닌 ​​곳에서 리디렉션(302)되거나 그 반대의 경우도 마찬가지입니다.
< br>도메인을 수정하려면 먼저 애플리케이션을 중지해야 합니다.

미리 DNS가 서버 IP를 가리키도록 설정해야 합니다.", - "ssl_www_and_non_www":"www 및 www가 없는 SSL을 생성하시겠습니까?", - "ssl_explainer":"www 및 non-www 모두에 대한 인증서를 생성합니다.
미리 두 DNS 항목을 설정해야 합니다.

두 DNS 항목 모두에 방문자가 있을 것으로 예상되는 경우 유용합니다.", - "install_command":"설치 명령", - "build_command":"빌드 명령", - "start_command":"시작 명령", - "directory_to_use_explainer":"모든 명령의 기반으로 사용할 디렉토리입니다.
monorepos와 함께 유용할 수 있습니다.", - "publish_directory_explainer":"배포를 위한 모든 자산이 포함된 디렉터리입니다.
예: dist,_site 또는 public< /스팬>.", - "features":"특징", - "enable_automatic_deployment":"자동 배포 활성화", - "enable_auto_deploy_webhooks":"웹훅을 통한 자동 배포를 활성화합니다.", - "enable_mr_pr_previews":"MR/PR 미리보기 활성화", - "expose_a_port":"포트 노출", - "enable_preview_deploy_mr_pr_requests":"끌어오기 또는 병합 요청에서 미리보기 배포를 활성화합니다.", - "debug_logs":"디버그 로그", - "enable_debug_log_during_build":"빌드 단계에서 디버그 로그를 활성화합니다.
민감한 정보가 표시되고 로그에 저장될 수 있습니다.", - "cant_activate_auto_deploy_without_repo":"이 리포지토리/분기에 대해 하나의 애플리케이션만 정의될 때까지 자동 배포를 활성화할 수 없습니다.", - "no_applications_found":"애플리케이션을 찾을 수 없습니다.", - "secret__batch_dot_env":".env 파일 붙여넣기", - "batch_secrets":"일괄 추가 비밀" - }, - "general":"일반적인", - "database":{ - "default_database":"기본 데이터베이스", - "generated_automatically_after_set_to_public":"public으로 설정 후 자동 생성", - "connection_string":"연결 문자열", - "set_public":"공개 설정", - "warning_database_public":"인터넷을 통해 데이터베이스에 연결할 수 있습니다.
이 경우 보안을 심각하게 생각하십시오!", - "change_append_only_mode":"추가 전용 모드 변경", - "warning_append_only":"백업에서 redis 데이터를 복원하려는 경우에 유용합니다.
데이터베이스를 다시 시작해야 합니다.", - "select_database_type":"데이터베이스 유형 선택", - "select_database_version":"데이터베이스 버전 선택", - "confirm_stop":"{{name}}을(를) 중지하시겠습니까?", - "stop_database":"중지", - "permission_denied_stop_database":"데이터베이스를 중지할 권한이 없습니다.", - "start_database":"시작", - "permission_denied_start_database":"데이터베이스를 시작할 권한이 없습니다.", - "delete_database":"삭제", - "permission_denied_delete_database":"데이터베이스를 삭제할 권한이 없습니다.", - "no_databases_found":"데이터베이스를 찾을 수 없습니다.", - "logs":"로그" - }, - "destination":{ - "delete_destination":"삭제", - "permission_denied_delete_destination":"이 목적지를 삭제할 권한이 없습니다.", - "add_to_coolify":"Coolify에 추가", - "coolify_proxy_stopped":"Coolify 프록시가 중지되었습니다!", - "coolify_proxy_started":"Coolify 프록시가 시작되었습니다!", - "confirm_restart_proxy":"프록시를 다시 시작하시겠습니까? 모든 것이 ~10초 안에 재구성됩니다.", - "coolify_proxy_restarting":"Coolify 프록시 다시 시작 중...", - "restarting_please_wait":"다시 시작 중입니다... 잠시만 기다려 주십시오...", - "force_restart_proxy":"강제 재시작 프록시", - "use_coolify_proxy":"Coolify 프록시를 사용하시겠습니까?", - "no_destination_found":"목적지를 찾을 수 없습니다", - "new_error_network_already_exists":"다른 팀에 대해 네트워크 {{network}}이(가) 이미 구성되었습니다!", - "new":{ - "saving_and_configuring_proxy":"절약...", - "install_proxy":"그러면 수동 구성 없이 애플리케이션과 서비스에 액세스할 수 있도록 대상에 프록시가 설치됩니다(Docker에 권장됨).

데이터베이스에는 자체 프록시가 있습니다.", - "add_new_destination":"새 목적지 추가", - "predefined_destinations":"사전 정의된 목적지" - } - }, - "sources":{ - "local_docker":"로컬 도커", - "remote_docker":"원격 도커", - "organization_explainer":"조직을 Git 소스로 사용하려면 입력하십시오. 그렇지 않으면 사용자가 사용됩니다." - }, - "source":{ - "new":{ - "git_source":"새 Git 소스 추가", - "official_providers":"공식 제공업체" - }, - "no_git_sources_found":"git 소스를 찾을 수 없습니다.", - "delete_git_source":"삭제", - "permission_denied":"Git 소스를 삭제할 권한이 없습니다.", - "create_new_app":"새 {{name}} 앱 만들기", - "change_app_settings":"{{name}} 앱 설정 변경", - "install_repositories":"저장소 설치", - "application_id":"애플리케이션 ID", - "group_name":"그룹 이름", - "oauth_id":"인증 ID", - "oauth_id_explainer":"OAuth ID는 GitLab 애플리케이션의 고유 식별자입니다.
GitLab OAuth 애플리케이션의 URL에서 찾을 수 있습니다.", - "register_oauth_gitlab":"GitLab에 새 OAuth 애플리케이션 등록", - "gitlab":{ - "self_hosted":"인스턴스 전체 애플리케이션(자체 호스팅)", - "user_owned":"사용자 소유 애플리케이션", - "group_owned":"그룹 소유 애플리케이션", - "gitlab_application_type":"GitLab 애플리케이션 유형", - "already_configured":"GitLab 앱이 이미 구성되어 있습니다." - }, - "github":{ - "redirecting":"Github으로 리디렉션 중..." - } - }, - "services":{ - "all_email_verified":"모든 이메일이 확인되었습니다. 지금 로그인할 수 있습니다.", - "generate_www_non_www_ssl":"www 및 non-www 모두에 대한 인증서를 생성합니다.
미리 두 DNS 항목을 설정해야 합니다.

서비스를 다시 시작해야 합니다." - }, - "service":{ - "stop_service":"중지", - "permission_denied_stop_service":"서비스를 중지할 권한이 없습니다.", - "start_service":"시작", - "permission_denied_start_service":"서비스를 시작할 권한이 없습니다.", - "delete_service":"삭제", - "permission_denied_delete_service":"서비스를 삭제할 권한이 없습니다.", - "no_service":"서비스를 찾을 수 없습니다.", - "logs":"로그" - }, - "setting":{ - "change_language":"언어 변경", - "permission_denied":"이 작업을 수행할 권한이 없습니다. \\n관리자에게 권한 수정을 요청하세요.", - "domain_removed":"도메인이 삭제됨", - "ssl_explainer":"https를 지정하면 Coolify는 https를 통해서만 액세스할 수 있습니다. SSL 인증서가 자동으로 생성됩니다.
www를 지정하면 Coolify가 www가 아닌 ​​곳에서 리디렉션(302)되거나 그 반대의 경우도 마찬가지입니다.

경고: 이미 설정된 도메인을 변경하면 웹훅 및 기타 통합이 중단됩니다! 수동으로 업데이트해야 합니다.", - "must_remove_domain_before_changing":"이 설정을 변경하려면 먼저 도메인을 제거해야 합니다.", - "registration_allowed":"등록이 허용됩니까?", - "registration_allowed_explainer":"애플리케이션에 대한 추가 등록을 허용합니다.
최초 등록 후에는 꺼져 있습니다.", - "coolify_proxy_settings":"Coolify 프록시 설정", - "credential_stat_explainer":"통계 페이지에 대한 자격 증명입니다.", - "auto_update_enabled":"자동 업데이트가 활성화되었습니까?", - "auto_update_enabled_explainer":"Coolify에 대한 자동 업데이트를 활성화합니다. 실행 중인 빌드 프로세스가 없는 경우 배후에서 자동으로 수행됩니다.", - "generate_www_non_www_ssl":"www 및 non-www 모두에 대한 인증서를 생성합니다.
미리 두 DNS 항목을 설정해야 합니다.", - "is_dns_check_enabled":"DNS 확인이 활성화되었습니까?", - "is_dns_check_enabled_explainer":"SSL 인증서를 생성하기 전에 DNS 확인을 비활성화할 수 있습니다.

Coolify가 역방향 프록시 또는 터널 뒤에 있을 때 비활성화하는 것이 유용합니다." - }, - "team":{ - "pending_invitations":"대기 중인 초대", - "accept":"수용하다", - "delete":"삭제", - "member":"회원", - "root":"(뿌리)", - "invited_with_permissions":"{{permission}} 권한으로 {{teamName}}에 초대되었습니다.", - "members":"회원", - "root_team_explainer":"루트 팀입니다. 즉, 이 그룹의 구성원은 인스턴스 전체 설정을 관리하고 Coolify의 모든 권한을 가질 수 있습니다(Linux의 루트 사용자와 같은 경우).", - "permission":"허가", - "you":"너", - "promote_to":"{{grade}}(으)로 승격", - "revoke_invitation":"초대 취소", - "pending_invitation":"대기 중인 초대", - "invite_new_member":"새 회원 초대", - "send_invitation":"초대장을 보내다", - "invite_only_register_explainer":"등록된 사용자만 초대할 수 있습니다.", - "admin":"관리자", - "read":"읽다" - } -} \ No newline at end of file diff --git a/apps/ui/src/lib/locales/pt.json b/apps/ui/src/lib/locales/pt.json deleted file mode 100644 index 0ac5deb46..000000000 --- a/apps/ui/src/lib/locales/pt.json +++ /dev/null @@ -1,341 +0,0 @@ -{ - "layout":{ - "update_done":"Atualização completa.", - "wait_new_version_startup":"Aguardando a nova versão iniciar...", - "new_version":"Nova versão acessível. Recarregando...", - "switch_to_a_different_team":"Mudar para uma equipa diferente...", - "update_available":"Atualização disponível" - }, - "error":{ - "you_can_find_your_way_back":"Você pode encontrar o seu caminho de volta", - "here":"aqui", - "you_are_lost":"Ooops você está perdido! Mas não tenha medo!" - }, - "index":{ - "dashboard":"Painel", - "applications":"Formulários", - "destinations":"Destinos", - "git_sources":"Fontes Git", - "databases":"Bancos de dados", - "services":"Serviços", - "teams":"Equipes", - "not_implemented_yet":"Ainda não implementado", - "database":"Base de dados", - "settings":"Definições", - "global_settings":"Configurações globais", - "secret":"Segredo", - "team":"Equipe", - "logout":"Sair" - }, - "login":{ - "already_logged_in":"Já logado...", - "authenticating":"Autenticando...", - "login":"Conecte-se" - }, - "forms":{ - "password":"Senha", - "email":"Endereço de email", - "passwords_not_match":"As senhas não coincidem.", - "password_again":"Senha novamente", - "save":"Salvar", - "saving":"Salvando...", - "name":"Nome", - "value":"Valor", - "action":"Ações", - "is_required":"É necessário.", - "add":"Adicionar", - "set":"Definir", - "remove":"Remover", - "path":"Caminho", - "confirm_continue":"Tem certeza de continuar?", - "must_be_stopped_to_modify":"Deve ser parado para modificar.", - "port":"Porta", - "default":"predefinição", - "base_directory":"Diretório base", - "publish_directory":"Publicar diretório", - "generated_automatically_after_start":"Gerado automaticamente após o início", - "roots_password":"Senha do Root", - "root_user":"Usuário raiz", - "eg":"por exemplo", - "user":"Do utilizador", - "loading":"Carregando...", - "version":"Versão", - "host":"Hospedeiro", - "already_used_for":"{{type}} já usado para", - "configuration":"Configuração", - "engine":"Motor", - "network":"Rede", - "ip_address":"Endereço de IP", - "ssh_private_key":"Chave privada SSH", - "type":"Modelo", - "html_url":"URL HTML", - "api_url":"URL da API", - "organization":"Organização", - "new_password":"Nova Senha", - "super_secure_new_password":"Nova senha super segura", - "submit":"Enviar", - "default_email_address":"Endereço de e-mail padrão", - "default_password":"Senha padrão", - "username":"Nome de usuário", - "root_db_user":"Usuário raiz do banco de dados", - "root_db_password":"Senha do banco de dados raiz", - "api_port":"Porta API", - "verifying":"Verificando", - "verify_emails_without_smtp":"Verifique e-mails sem SMTP", - "extra_config":"Configuração extra", - "select_a_service":"Selecione um serviço", - "select_a_service_version":"Selecione uma versão do serviço", - "removing":"Removendo...", - "remove_domain":"Remover domínio", - "public_port_range":"Intervalo de portas públicas", - "public_port_range_explainer":"Portas usadas para expor bancos de dados/serviços/serviços internos.
Adicione-os ao seu firewall (se aplicável).

Você pode especificar um intervalo de portas, por exemplo: 9000-9100", - "no_actions_available":"Nenhuma ação disponível", - "admin_api_key":"Chave de API de administrador" - }, - "register":{ - "register":"Registro", - "registering":"Registrando...", - "first_user":"Você está registrando o primeiro usuário. Será o administrador da sua instância Coolify." - }, - "reset":{ - "reset_password":"Redefinir", - "invalid_secret_key":"Chave secreta inválida.", - "secret_key":"Chave secreta", - "find_path_secret_key":"Você pode encontrá-lo em ~/coolify/.env (COOLIFY_SECRET_KEY)" - }, - "application":{ - "configuration":{ - "buildpack":{ - "choose_this_one":"Escolha este..." - }, - "branch_already_in_use":"Esta ramificação já é usada por outro aplicativo. Os webhooks não funcionarão neste caso para ambos os aplicativos. Tem certeza de que deseja usá-lo?", - "no_repositories_configured":"Nenhum repositório configurado para seu aplicativo Git.", - "configure_it_now":"Configure agora", - "loading_repositories":"Carregando repositórios...", - "select_a_repository":"Selecione um repositório", - "loading_branches":"Carregando ramos...", - "select_a_repository_first":"Selecione um repositório primeiro", - "select_a_branch":"Selecione uma filial", - "loading_groups":"Carregando grupos...", - "select_a_group":"Selecione um grupo", - "loading_projects":"Carregando projetos...", - "select_a_project":"Por favor selecione um projeto", - "no_projects_found":"Nenhum projeto encontrado", - "no_branches_found":"Nenhuma ramificação encontrada", - "configure_build_pack":"Configurar pacote de compilação", - "scanning_repository_suggest_build_pack":"Verificando repositório para sugerir um pacote de compilação para você...", - "found_lock_file":"Arquivo de bloqueio encontrado para {{packageManager}}.
Usando-o para comandos de comandos predefinidos.", - "configure_destination":"Configurar destino", - "no_configurable_destination":"Nenhum destino configurável encontrado", - "select_a_repository_project":"Selecione um Repositório/Projeto", - "select_a_git_source":"Selecione uma fonte Git", - "no_configurable_git":"Nenhuma fonte Git configurável encontrada", - "configuration_missing":"Configuração ausente" - }, - "build":{ - "queued_waiting_exec":"Na fila e aguardando execução.", - "build_logs_of":"Construir registros de", - "running":"Corrida", - "queued":"Enfileiradas", - "finished_in":"Terminando em", - "load_more":"Carregue mais", - "no_logs":"Nenhum registro encontrado", - "waiting_logs":"Aguardando os logs..." - }, - "preview":{ - "need_during_buildtime":"Precisa durante o tempo de construção?", - "setup_secret_app_first":"Você pode adicionar segredos a implantações de PR/MR. Por favor, adicione segredos ao aplicativo primeiro.
Útil para criar ambientes de preparação.", - "values_overwriting_app_secrets":"Esses valores substituem os segredos do aplicativo em implantações PR/MR. Útil para criar ambientes de preparação.", - "redeploy":"Reimplantar", - "no_previews_available":"Nenhuma visualização disponível" - }, - "secrets":{ - "secret_saved":"Segredo salvo.", - "use_isbuildsecret":"Use isBuildSecret", - "secrets_for":"Segredos para" - }, - "storage":{ - "path_is_required":"O caminho é obrigatório.", - "storage_saved":"Armazenamento salvo.", - "storage_updated":"Armazenamento atualizado.", - "storage_deleted":"Armazenamento excluído.", - "persistent_storage_explainer":"Você pode especificar qualquer pasta que deseja que seja persistente nas implantações.
/example significa que ela preservará /app/ example no contêiner, pois /app é o diretório raiz para seu aplicativo.

Isto é útil para armazenar dados como um banco de dados (SQLite) ou um cache." - }, - "deployment_queued":"Implantação em fila.", - "confirm_to_delete":"Tem certeza de que deseja excluir '{{name}}'?", - "stop_application":"Parar aplicativo", - "permission_denied_stop_application":"Você não tem permissão para parar o aplicativo.", - "rebuild_application":"Reconstruir aplicativo", - "permission_denied_rebuild_application":"Você não tem permissão para reconstruir o aplicativo.", - "build_and_start_application":"Implantar", - "permission_denied_build_and_start_application":"Você não tem permissão para implantar o aplicativo.", - "configurations":"Configurações", - "secret":"Segredos", - "persistent_storage":"Armazenamento persistente", - "previews":"Visualizações", - "logs":"Registros de aplicativos", - "build_logs":"Construir registros", - "delete_application":"Excluir", - "permission_denied_delete_application":"Você não tem permissão para excluir este aplicativo", - "domain_already_in_use":"O domínio {{domain}} já está em uso.", - "dns_not_set_error":"DNS não definido corretamente ou propagado para {{domain}}.

Verifique suas configurações de DNS.", - "domain_required":"O domínio é obrigatório.", - "settings_saved":"Configuração salva.", - "dns_not_set_partial_error":"DNS não definido", - "domain_not_valid":"Não foi possível resolver o domínio ou não está apontando para o endereço IP do servidor.

Verifique sua configuração de DNS e tente novamente.", - "git_source":"Fonte do Git", - "git_repository":"Repositório Git", - "build_pack":"Pacote de compilação", - "base_image":"Imagem de implantação", - "base_image_explainer":"Imagem que será usada para a implantação.", - "base_build_image":"Construir imagem", - "base_build_image_explainer":"Imagem que será usada durante o processo de compilação.", - "destination":"Destino", - "application":"Inscrição", - "url_fqdn":"URL (FQDN)", - "domain_fqdn":"Domínio (FQDN)", - "https_explainer":"Se você especificar https, o aplicativo será acessível apenas por https. O certificado SSL será gerado para você.
Se você especificar www, o aplicativo será redirecionado (302) de não www e vice-versa.
< br>Para modificar o domínio, você deve primeiro parar o aplicativo.

Você deve configurar seu DNS para apontar para o IP do servidor com antecedência.", - "ssl_www_and_non_www":"Gerar SSL para www e não www?", - "ssl_explainer":"Ele irá gerar certificados para www e não www.
Você precisa ter ambas as entradas DNS definidas com antecedência.

Útil se você espera receber visitantes em ambas.", - "install_command":"Comando de instalação", - "build_command":"Comando de compilação", - "start_command":"Comando Iniciar", - "directory_to_use_explainer":"Diretório a ser usado como base para todos os comandos.
Pode ser útil com monorepos.", - "publish_directory_explainer":"Diretório contendo todos os ativos para implantação.
Por exemplo: dist,_site ou public< /span>.", - "features":"Características", - "enable_automatic_deployment":"Ativar implantação automática", - "enable_auto_deploy_webhooks":"Habilite a implantação automática por meio de webhooks.", - "enable_mr_pr_previews":"Ativar visualizações de MR/PR", - "expose_a_port":"Expor uma porta", - "enable_preview_deploy_mr_pr_requests":"Habilite implantações de visualização de solicitações de pull ou mesclagem.", - "debug_logs":"Registros de depuração", - "enable_debug_log_during_build":"Ative os registros de depuração durante a fase de compilação.
Informações confidenciais podem ser visíveis e salvas nos registros.", - "cant_activate_auto_deploy_without_repo":"Não é possível ativar implantações automáticas até que apenas um aplicativo seja definido para este repositório/ramificação.", - "no_applications_found":"Nenhum aplicativo encontrado", - "secret__batch_dot_env":"Colar arquivo .env", - "batch_secrets":"Adicionar segredos em lote" - }, - "general":"Em geral", - "database":{ - "default_database":"Banco de dados padrão", - "generated_automatically_after_set_to_public":"Gerado automaticamente após definido como público", - "connection_string":"Cadeia de conexão", - "set_public":"Defina-o como público", - "warning_database_public":"Seu banco de dados estará acessível pela internet.
Leve a segurança a sério neste caso!", - "change_append_only_mode":"Alterar o modo somente anexar", - "warning_append_only":"Útil se você deseja restaurar dados redis de um backup.
É necessário reiniciar o banco de dados.", - "select_database_type":"Selecione um tipo de banco de dados", - "select_database_version":"Selecione uma versão do banco de dados", - "confirm_stop":"Tem certeza de que deseja parar {{name}}?", - "stop_database":"Pare", - "permission_denied_stop_database":"Você não tem permissão para parar o banco de dados.", - "start_database":"Começar", - "permission_denied_start_database":"Você não tem permissão para iniciar o banco de dados.", - "delete_database":"Excluir", - "permission_denied_delete_database":"Você não tem permissão para excluir um banco de dados", - "no_databases_found":"Nenhum banco de dados encontrado", - "logs":"Histórico" - }, - "destination":{ - "delete_destination":"Excluir", - "permission_denied_delete_destination":"Você não tem permissão para excluir este destino", - "add_to_coolify":"Adicionar ao Coolify", - "coolify_proxy_stopped":"Coolify Proxy parou!", - "coolify_proxy_started":"Coolify Proxy iniciado!", - "confirm_restart_proxy":"Tem certeza de que deseja reiniciar o proxy? Tudo será reconfigurado em ~10 segundos.", - "coolify_proxy_restarting":"Coolify Proxy reiniciando...", - "restarting_please_wait":"Reiniciando... aguarde...", - "force_restart_proxy":"Forçar reinicialização do proxy", - "use_coolify_proxy":"Usar Coolify Proxy?", - "no_destination_found":"Nenhum destino encontrado", - "new_error_network_already_exists":"Rede {{network}} já configurada para outra equipe!", - "new":{ - "saving_and_configuring_proxy":"Salvando...", - "install_proxy":"Isso instalará um proxy no destino para permitir que você acesse seus aplicativos e serviços sem qualquer configuração manual (recomendado para o Docker).

Os bancos de dados terão seu próprio proxy.", - "add_new_destination":"Adicionar novo destino", - "predefined_destinations":"Destinos predefinidos" - } - }, - "sources":{ - "local_docker":"Docker local", - "remote_docker":"Docker remoto", - "organization_explainer":"Preencha-o se quiser usar o de uma organização como seu Git Source. Caso contrário, seu usuário será usado." - }, - "source":{ - "new":{ - "git_source":"Adicionar nova fonte Git", - "official_providers":"Fornecedores oficiais" - }, - "no_git_sources_found":"Nenhuma fonte git encontrada", - "delete_git_source":"Excluir", - "permission_denied":"Você não tem permissão para excluir uma fonte Git", - "create_new_app":"Criar novo aplicativo {{name}}", - "change_app_settings":"Alterar {{name}} configurações do aplicativo", - "install_repositories":"Instalar repositórios", - "application_id":"ID do aplicativo", - "group_name":"Nome do grupo", - "oauth_id":"ID OAuth", - "oauth_id_explainer":"O OAuth ID é o identificador exclusivo do aplicativo GitLab.
Você pode encontrá-lo no URL do seu aplicativo GitLab OAuth.", - "register_oauth_gitlab":"Registre um novo aplicativo OAuth no GitLab", - "gitlab":{ - "self_hosted":"Aplicativo em toda a instância (auto-hospedado)", - "user_owned":"Aplicativo de propriedade do usuário", - "group_owned":"Aplicativo de propriedade do grupo", - "gitlab_application_type":"Tipo de aplicativo GitLab", - "already_configured":"O aplicativo GitLab já está configurado." - }, - "github":{ - "redirecting":"Redirecionando para o Github..." - } - }, - "services":{ - "all_email_verified":"Todos os e-mails são verificados. Você pode entrar agora.", - "generate_www_non_www_ssl":"Ele irá gerar certificados para www e não www.
Você precisa ter ambas as entradas DNS definidas com antecedência.

O serviço precisa ser reiniciado." - }, - "service":{ - "stop_service":"Pare", - "permission_denied_stop_service":"Você não tem permissão para interromper o serviço.", - "start_service":"Começar", - "permission_denied_start_service":"Você não tem permissão para iniciar o serviço.", - "delete_service":"Excluir", - "permission_denied_delete_service":"Você não tem permissão para excluir um serviço.", - "no_service":"Nenhum serviço encontrado", - "logs":"Histórico" - }, - "setting":{ - "change_language":"Mudar idioma", - "permission_denied":"Você não tem permissão para fazer isso. \\nPeça a um administrador para modificar suas permissões.", - "domain_removed":"Domínio removido", - "ssl_explainer":"Se você especificar https, o Coolify será acessível apenas por https. O certificado SSL será gerado para você.
Se você especificar www, Coolify será redirecionado (302) de não www e vice-versa.

AVISO: Se você alterar um domínio já definido, isso interromperá webhooks e outras integrações! Você precisa atualizá-los manualmente.", - "must_remove_domain_before_changing":"Deve remover o domínio antes de alterar esta configuração.", - "registration_allowed":"Registro permitido?", - "registration_allowed_explainer":"Permitir mais registros no aplicativo.
É desligado após o primeiro registro.", - "coolify_proxy_settings":"Configurações de proxy do Coolify", - "credential_stat_explainer":"Credenciais para a página de estatísticas.", - "auto_update_enabled":"Atualização automática habilitada?", - "auto_update_enabled_explainer":"Habilite atualizações automáticas para Coolify. Isso será feito automaticamente nos bastidores, se não houver nenhum processo de compilação em execução.", - "generate_www_non_www_ssl":"Ele irá gerar certificados para www e não www.
Você precisa ter ambas as entradas de DNS configuradas antecipadamente.", - "is_dns_check_enabled":"Verificação de DNS habilitada?", - "is_dns_check_enabled_explainer":"Você pode desabilitar a verificação de DNS antes de criar certificados SSL.

Desligá-la é útil quando o Coolify está atrás de um proxy reverso ou túnel." - }, - "team":{ - "pending_invitations":"Convites pendentes", - "accept":"Aceitar", - "delete":"Excluir", - "member":"membros)", - "root":"(raiz)", - "invited_with_permissions":"Convidado para {{teamName}} com permissão {{permission}}.", - "members":"Membros", - "root_team_explainer":"Esta é a equipe raiz. Isso significa que os membros deste grupo podem gerenciar as configurações de toda a instância e ter todos os privilégios no Coolify (imagine como usuário root no Linux).", - "permission":"Permissão", - "you":"Você", - "promote_to":"Promover para {{grade}}", - "revoke_invitation":"Revogar convite", - "pending_invitation":"Convite pendente", - "invite_new_member":"Convidar novo membro", - "send_invitation":"Enviar convite", - "invite_only_register_explainer":"Você só pode convidar usuários registrados.", - "admin":"Administrador", - "read":"Ler" - } -} \ No newline at end of file diff --git a/apps/ui/src/lib/store.ts b/apps/ui/src/lib/store.ts deleted file mode 100644 index 52bf6eb4c..000000000 --- a/apps/ui/src/lib/store.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { dev } from '$app/env'; -import cuid from 'cuid'; -import Cookies from 'js-cookie'; -import { writable, readable, type Writable } from 'svelte/store'; -import { io as ioClient } from 'socket.io-client'; -const socket = ioClient(dev ? `http://${window.location.hostname}:3001` : '/', { auth: { token: Cookies.get('token') }, autoConnect: false }); - -export const io = socket; -interface AppSession { - isRegistrationEnabled: boolean; - ipv4: string | null, - ipv6: string | null, - version: string | null, - userId: string | null, - teamId: string | null, - permission: string, - isAdmin: boolean, - whiteLabeled: boolean, - whiteLabeledDetails: { - icon: string | null, - }, - tokens: { - github: string | null, - gitlab: string | null, - }, - pendingInvitations: Array, - isARM: boolean -} -interface AddToast { - type?: "info" | "success" | "error", - message: string, - timeout?: number | undefined -} -export const updateLoading: Writable = writable(false); -export const isUpdateAvailable: Writable = writable(false); -export const latestVersion: Writable = writable('latest'); -export const search: any = writable('') -export const loginEmail: Writable = writable() -export const appSession: Writable = writable({ - isRegistrationEnabled: false, - ipv4: null, - ipv6: null, - version: null, - userId: null, - teamId: null, - permission: 'read', - isAdmin: false, - whiteLabeled: false, - whiteLabeledDetails: { - icon: null - }, - tokens: { - github: null, - gitlab: null - }, - pendingInvitations: [], - isARM: false -}); -export const disabledButton: Writable = writable(false); -export const isDeploymentEnabled: Writable = writable(false); -export function checkIfDeploymentEnabledApplications(application: any) { - return !!( - (application.buildPack === 'compose') || - (application.fqdn || application.settings?.isBot) && - ((application.gitSource && - application.repository && - application.buildPack) || application.simpleDockerfile) && - application.destinationDocker - - ); -} -export function checkIfDeploymentEnabledServices( service: any) { - return ( - service.fqdn && - service.destinationDocker && - service.version && - service.type - ); -} -export const status: Writable = writable({ - application: { - statuses: [], - overallStatus: 'stopped', - loading: false, - restarting: false, - initialLoading: true - }, - service: { - statuses: [], - overallStatus: 'stopped', - loading: false, - startup: {}, - initialLoading: true - }, - database: { - isRunning: false, - isExited: false, - loading: false, - initialLoading: true, - isPublic: false - } - -}); - -export const features = readable({ - beta: window.localStorage.getItem('beta') === 'true', - latestVersion: window.localStorage.getItem('latestVersion') -}); - -export const location: Writable = writable(null) -export const setLocation = (resource: any, settings?: any) => { - if (resource.settings.isBot && resource.exposePort) { - disabledButton.set(false); - return location.set(`http://${dev ? 'localhost' : settings.ipv4}:${resource.exposePort}`) - } - if (GITPOD_WORKSPACE_URL && resource.exposePort) { - const { href } = new URL(GITPOD_WORKSPACE_URL); - const newURL = href - .replace('https://', `https://${resource.exposePort}-`) - .replace(/\/$/, ''); - return location.set(newURL) - } else if (CODESANDBOX_HOST) { - const newURL = `https://${CODESANDBOX_HOST.replace(/\$PORT/, resource.exposePort)}` - return location.set(newURL) - } - if (resource.fqdn) { - return location.set(resource.fqdn) - } else { - location.set(null); - disabledButton.set(false); - } -} - -export const toasts: any = writable([]) - -export const dismissToast = (id: string) => { - toasts.update((all: any) => all.filter((t: any) => t.id !== id)) -} -export const pauseToast = (id: string) => { - toasts.update((all: any) => { - const index = all.findIndex((t: any) => t.id === id); - if (index > -1) clearTimeout(all[index].timeoutInterval); - return all; - }) -} -export const resumeToast = (id: string) => { - toasts.update((all: any) => { - const index = all.findIndex((t: any) => t.id === id); - if (index > -1) { - all[index].timeoutInterval = setTimeout(() => { - dismissToast(id) - }, all[index].timeout) - } - return all; - }) -} - -export const addToast = (toast: AddToast) => { - const id = cuid(); - const defaults = { - id, - type: 'info', - timeout: 2000, - } - let t: any = { ...defaults, ...toast } - if (t.timeout) t.timeoutInterval = setTimeout(() => dismissToast(id), t.timeout) - toasts.update((all: any) => [t, ...all]) -} - -export const selectedBuildId: any = writable(null) - -type State = { - requests: Array; -}; -export const state = writable({ - requests: [], -}); diff --git a/apps/ui/src/lib/templates.ts b/apps/ui/src/lib/templates.ts deleted file mode 100644 index d71fd9323..000000000 --- a/apps/ui/src/lib/templates.ts +++ /dev/null @@ -1,410 +0,0 @@ -function defaultBuildAndDeploy(packageManager: string) { - return { - installCommand: - packageManager === 'npm' ? `${packageManager} install` : `${packageManager} install`, - buildCommand: - packageManager === 'npm' ? `${packageManager} run build` : `${packageManager} build`, - startCommand: - packageManager === 'npm' ? `${packageManager} run start` : `${packageManager} start` - }; -} -export function findBuildPack(pack: string, packageManager = 'npm') { - const metaData = buildPacks.find((b) => b.name === pack); - if (pack === 'node') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - buildCommand: null, - publishDirectory: null, - port: null - }; - } - if (pack === 'static') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: 80 - }; - } - if (pack === 'docker' || pack === 'compose') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: null - }; - } - - if (pack === 'svelte') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: 'public', - port: 80 - }; - } - if (pack === 'nestjs') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - startCommand: - packageManager === 'npm' ? 'npm run start:prod' : `${packageManager} run start:prod`, - publishDirectory: null, - port: 3000 - }; - } - if (pack === 'react') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: 'build', - port: 80 - }; - } - if (pack === 'nextjs') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: null, - port: 3000, - deploymentType: 'node' - }; - } - if (pack === 'gatsby') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: 'public', - port: 80 - }; - } - if (pack === 'vuejs') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: 'dist', - port: 80 - }; - } - if (pack === 'nuxtjs') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: null, - port: 3000, - deploymentType: 'node' - }; - } - if (pack === 'preact') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: 'build', - port: 80 - }; - } - if (pack === 'php') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: 80 - }; - } - if (pack === 'rust') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: 3000 - }; - } - if (pack === 'astro') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: `dist`, - port: 80 - }; - } - if (pack === 'eleventy') { - return { - ...metaData, - ...defaultBuildAndDeploy(packageManager), - publishDirectory: `_site`, - port: 80 - }; - } - if (pack === 'python') { - return { - ...metaData, - startCommand: null, - port: 8000 - }; - } - if (pack === 'deno') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: 8000 - }; - } - if (pack === 'laravel') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: 80 - }; - } - if (pack === 'heroku') { - return { - ...metaData, - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: 5000 - }; - } - return { - name: 'node', - fancyName: 'Node.js', - hoverColor: 'hover:bg-green-700', - color: 'bg-green-700', - installCommand: null, - buildCommand: null, - startCommand: null, - publishDirectory: null, - port: null - }; -} -export const buildPacks = [ - { - name: 'node', - type: 'base', - fancyName: 'Node.js', - hoverColor: 'hover:bg-green-700', - color: 'bg-green-700', - isCoolifyBuildPack: true, - }, - { - name: 'static', - type: 'base', - fancyName: 'Static', - hoverColor: 'hover:bg-orange-700', - color: 'bg-orange-700', - isCoolifyBuildPack: true, - }, - - { - name: 'php', - type: 'base', - fancyName: 'PHP', - hoverColor: 'hover:bg-indigo-700', - color: 'bg-indigo-700', - isCoolifyBuildPack: true, - }, - { - name: 'laravel', - type: 'specific', - base: 'php', - fancyName: 'Laravel', - hoverColor: 'hover:bg-indigo-700', - color: 'bg-indigo-700', - isCoolifyBuildPack: true, - }, - { - name: 'docker', - type: 'base', - fancyName: 'Docker', - hoverColor: 'hover:bg-sky-700', - color: 'bg-sky-700', - isCoolifyBuildPack: true, - }, - { - name: 'compose', - type: 'base', - fancyName: 'Docker Compose', - hoverColor: 'hover:bg-sky-700', - color: 'bg-sky-700', - isCoolifyBuildPack: true, - }, - { - name: 'svelte', - type: 'specific', - base: 'node', - fancyName: 'Svelte', - hoverColor: 'hover:bg-orange-700', - color: 'bg-orange-700', - isCoolifyBuildPack: true, - }, - { - name: 'vuejs', - type: 'specific', - base: 'node', - fancyName: 'VueJS', - hoverColor: 'hover:bg-green-700', - color: 'bg-green-700', - isCoolifyBuildPack: true, - }, - { - name: 'nuxtjs', - type: 'specific', - base: 'node', - fancyName: 'NuxtJS', - hoverColor: 'hover:bg-green-700', - color: 'bg-green-700', - isCoolifyBuildPack: true, - }, - { - name: 'gatsby', - type: 'specific', - base: 'node', - fancyName: 'Gatsby', - hoverColor: 'hover:bg-blue-700', - color: 'bg-blue-700', - isCoolifyBuildPack: true, - }, - { - name: 'astro', - type: 'specific', - base: 'node', - fancyName: 'Astro', - hoverColor: 'hover:bg-pink-700', - color: 'bg-pink-700', - isCoolifyBuildPack: true, - }, - { - name: 'eleventy', - type: 'specific', - base: 'node', - fancyName: 'Eleventy', - hoverColor: 'hover:bg-red-700', - color: 'bg-red-700', - isCoolifyBuildPack: true, - }, - { - name: 'react', - type: 'specific', - base: 'node', - fancyName: 'React', - hoverColor: 'hover:bg-blue-700', - color: 'bg-blue-700', - isCoolifyBuildPack: true, - }, - { - name: 'preact', - type: 'specific', - base: 'node', - fancyName: 'Preact', - hoverColor: 'hover:bg-blue-700', - color: 'bg-blue-700', - isCoolifyBuildPack: true, - }, - { - name: 'nextjs', - type: 'specific', - base: 'node', - fancyName: 'NextJS', - hoverColor: 'hover:bg-blue-700', - color: 'bg-blue-700', - isCoolifyBuildPack: true, - }, - { - name: 'nestjs', - type: 'specific', - base: 'node', - fancyName: 'NestJS', - hoverColor: 'hover:bg-red-700', - color: 'bg-red-700', - isCoolifyBuildPack: true, - }, - { - name: 'rust', - type: 'base', - fancyName: 'Rust', - hoverColor: 'hover:bg-pink-700', - color: 'bg-pink-700', - isCoolifyBuildPack: true, - }, - { - name: 'python', - type: 'base', - fancyName: 'Python', - hoverColor: 'hover:bg-green-700', - color: 'bg-green-700', - isCoolifyBuildPack: true, - }, - { - name: 'deno', - type: 'base', - fancyName: 'Deno', - hoverColor: 'hover:bg-green-700', - color: 'bg-green-700', - isCoolifyBuildPack: true, - }, - { - name: 'heroku', - type: 'base', - fancyName: 'Heroku', - hoverColor: 'hover:bg-purple-700', - color: 'bg-purple-700', - isHerokuBuildPack: true, - } -]; -export const scanningTemplates = { - '@sveltejs/kit': { - buildPack: 'nodejs' - }, - astro: { - buildPack: 'astro' - }, - '@11ty/eleventy': { - buildPack: 'eleventy' - }, - svelte: { - buildPack: 'svelte' - }, - '@nestjs/core': { - buildPack: 'nestjs' - }, - next: { - buildPack: 'nextjs' - }, - nuxt: { - buildPack: 'nuxtjs' - }, - 'react-scripts': { - buildPack: 'react' - }, - 'parcel-bundler': { - buildPack: 'static' - }, - '@vue/cli-service': { - buildPack: 'vuejs' - }, - vuejs: { - buildPack: 'vuejs' - }, - gatsby: { - buildPack: 'gatsby' - }, - 'preact-cli': { - buildPack: 'react' - } -}; diff --git a/apps/ui/src/lib/translations.ts b/apps/ui/src/lib/translations.ts deleted file mode 100644 index 9cc71a118..000000000 --- a/apps/ui/src/lib/translations.ts +++ /dev/null @@ -1,47 +0,0 @@ -import i18n from 'sveltekit-i18n'; -import { derived, writable } from "svelte/store"; -import lang from './lang.json'; - -export let currentLocale = writable("en"); -export let debugTranslation = writable(false); - -/** @type {import('sveltekit-i18n').Config} */ -export const config = { - fallbackLocale: 'en', - translations: { - en: { lang }, - es: { lang }, - pt: { lang }, - ko: { lang }, - fr: { lang } - }, - loaders: [ - { - locale: 'en', - key: '', - loader: async () => (await import('./locales/en.json')).default - }, - { - locale: 'es', - key: '', - loader: async () => (await import('./locales/es.json')).default - }, - { - locale: 'pt', - key: '', - loader: async () => (await import('./locales/pt.json')).default - }, - { - locale: 'fr', - key: '', - loader: async () => (await import('./locales/fr.json')).default - }, - { - locale: 'ko', - key: '', - loader: async () => (await import('./locales/ko.json')).default - } - ] -}; - -export const { t, locales, locale, loadTranslations } = new i18n(config); \ No newline at end of file diff --git a/apps/ui/src/routes/_NewResource.svelte b/apps/ui/src/routes/_NewResource.svelte deleted file mode 100644 index c02221d02..000000000 --- a/apps/ui/src/routes/_NewResource.svelte +++ /dev/null @@ -1,147 +0,0 @@ - - - - diff --git a/apps/ui/src/routes/__error.svelte b/apps/ui/src/routes/__error.svelte deleted file mode 100644 index ac32b098e..000000000 --- a/apps/ui/src/routes/__error.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - - - -
-
{status}
-
{$t('error.you_are_lost')}
-
- {$t('error.you_can_find_your_way_back')} - {$t('error.here')} -
-
-
{error.message} {error.stack}
-
-
diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte deleted file mode 100644 index 4e68957b2..000000000 --- a/apps/ui/src/routes/__layout.svelte +++ /dev/null @@ -1,557 +0,0 @@ - - - - - - {#if !$appSession.whiteLabeled} - Coolify - - {:else if $appSession.whiteLabeledDetails.icon} - Coolify - - {/if} - - -{#if $navigating} -
- -
-{/if} - -
- -
- {#if $appSession.userId} - Dashboard - Servers - IAM - Settings - Logout - - {#if $appSession.whiteLabeled} - Powered by Coolify - {/if} - {/if} - -
-
- -
-
-
- -
diff --git a/apps/ui/src/routes/applications/[id]/_Menu.svelte b/apps/ui/src/routes/applications/[id]/_Menu.svelte deleted file mode 100644 index bd57f648e..000000000 --- a/apps/ui/src/routes/applications/[id]/_Menu.svelte +++ /dev/null @@ -1,323 +0,0 @@ - - - diff --git a/apps/ui/src/routes/applications/[id]/_PreviewSecret.svelte b/apps/ui/src/routes/applications/[id]/_PreviewSecret.svelte deleted file mode 100644 index 3dba6e30f..000000000 --- a/apps/ui/src/routes/applications/[id]/_PreviewSecret.svelte +++ /dev/null @@ -1,133 +0,0 @@ - - -
-
- {#if index === 0 || length === 0} - - {/if} - - -
-
- {#if index === 0 || length === 0} - - {/if} - - -
-
- {#if index === 0 || length === 0} - - {/if} - - -
- -
-
-
- {#if index === 0 || length === 0} -
-
diff --git a/apps/ui/src/routes/applications/[id]/_Secret.svelte b/apps/ui/src/routes/applications/[id]/_Secret.svelte deleted file mode 100644 index ce7c9e962..000000000 --- a/apps/ui/src/routes/applications/[id]/_Secret.svelte +++ /dev/null @@ -1,193 +0,0 @@ - - -
-
- {#if (index === 0 && !isNewSecret) || length === 0} - - {/if} - - -
-
- {#if (index === 0 && !isNewSecret) || length === 0} - - {/if} - - -
-
- {#if (index === 0 && !isNewSecret) || length === 0} - - {/if} - - -
- -
-
-
- {#if (index === 0 && !isNewSecret) || length === 0} -
-
diff --git a/apps/ui/src/routes/applications/[id]/_Storage.svelte b/apps/ui/src/routes/applications/[id]/_Storage.svelte deleted file mode 100644 index 6eeb2c66b..000000000 --- a/apps/ui/src/routes/applications/[id]/_Storage.svelte +++ /dev/null @@ -1,138 +0,0 @@ - - -
- {#if storage.predefined} -
- - -
- {:else} -
- {#if storage.applicationId} - {#if storage.oldPath} - - {:else if !storage.hostPath} - - {/if} - {/if} - - {#if isNew} -
- - - -
- {:else if storage.hostPath} - - {/if} - - -
- {#if isNew} -
- -
- {:else} -
- -
- {/if} -
-
- {/if} -
diff --git a/apps/ui/src/routes/applications/[id]/__layout.svelte b/apps/ui/src/routes/applications/[id]/__layout.svelte deleted file mode 100644 index a61b27486..000000000 --- a/apps/ui/src/routes/applications/[id]/__layout.svelte +++ /dev/null @@ -1,530 +0,0 @@ - - - - -
- -
- {#if $status.application.overallStatus === 'degraded' && application.buildPack !== 'compose'} - - - - - - - - Application Error - - {/if} - {#if stopping} - - {:else if $status.application.initialLoading} - - {:else if $status.application.overallStatus === 'healthy'} - {#if application.buildPack !== 'compose'} - - {/if} - - - {:else if $isDeploymentEnabled && !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)} - - {#if $status.application.overallStatus === 'degraded'} - - {/if} - {/if} - {#if $location && $status.application.overallStatus === 'healthy'} - - - - - - Open - {/if} -
-
-
- {#if !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)} - - {/if} -
- -
-
diff --git a/apps/ui/src/routes/applications/[id]/configuration/_BuildPack.svelte b/apps/ui/src/routes/applications/[id]/configuration/_BuildPack.svelte deleted file mode 100644 index c6872dede..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/_BuildPack.svelte +++ /dev/null @@ -1,72 +0,0 @@ - - -
handleSubmit(buildPack.name)}> - -
diff --git a/apps/ui/src/routes/applications/[id]/configuration/_GithubRepositories.svelte b/apps/ui/src/routes/applications/[id]/configuration/_GithubRepositories.svelte deleted file mode 100644 index 593436950..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/_GithubRepositories.svelte +++ /dev/null @@ -1,181 +0,0 @@ - - -{#if repositories.length === 0 && loading.repositories === false} -
-
{$t('application.configuration.no_repositories_configured')}
- -
-{:else} -
-
-
- - -
- - { - showSave = false; - projects = []; - branches = []; - selected.group = null; - selected.project = null; - selected.branch = null; - }} - value={selected.group} - isDisabled={loading.base} - isClearable={false} - items={groups} - labelIdentifier="full_name" - optionIdentifier="id" - /> -
-
- - { - showSave = false; - selected.branch = null; - }} - value={selected.branch} - isClearable={false} - items={branches} - isSearchable={true} - labelIdentifier="name" - optionIdentifier="web_url" - /> -
-
-
- - {#if tryAgain} -
- An error occurred during authenticating with GitLab. Please check your GitLab Source - configuration here. -
- - {/if} -
- diff --git a/apps/ui/src/routes/applications/[id]/configuration/_PublicRepository.svelte b/apps/ui/src/routes/applications/[id]/configuration/_PublicRepository.svelte deleted file mode 100644 index f6468856f..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/_PublicRepository.svelte +++ /dev/null @@ -1,197 +0,0 @@ - - -
-
-
-
- -
- -
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
diff --git a/apps/ui/src/routes/applications/[id]/configuration/destination.svelte b/apps/ui/src/routes/applications/[id]/configuration/destination.svelte deleted file mode 100644 index be4b171fa..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/destination.svelte +++ /dev/null @@ -1,202 +0,0 @@ - - - - -
- {#if !destinations || ownDestinations.length === 0} -
-
- {$t('application.configuration.no_configurable_destination')} -
-
- - - -
-
- {:else} -
- {#each ownDestinations as destination} - - {/each} -
- {#if otherDestinations.length > 0 && $appSession.teamId === '0'} -
Other Destinations
- {/if} -
- {#each otherDestinations as destination} - - {/each} -
- {/if} -
diff --git a/apps/ui/src/routes/applications/[id]/configuration/registry.svelte b/apps/ui/src/routes/applications/[id]/configuration/registry.svelte deleted file mode 100644 index c122f8d68..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/registry.svelte +++ /dev/null @@ -1,93 +0,0 @@ - - - - -
-
- {#if registries.length > 0} - {#each registries as registry} - - {/each} - {:else} -
-
No registries found.
- -
- {/if} -
-
diff --git a/apps/ui/src/routes/applications/[id]/configuration/repository.svelte b/apps/ui/src/routes/applications/[id]/configuration/repository.svelte deleted file mode 100644 index c29788763..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/repository.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - - - -{#if application.gitSource.type === 'github'} - -{:else if application.gitSource.type === 'gitlab'} - -{/if} diff --git a/apps/ui/src/routes/applications/[id]/configuration/source.svelte b/apps/ui/src/routes/applications/[id]/configuration/source.svelte deleted file mode 100644 index 20db4c7bb..000000000 --- a/apps/ui/src/routes/applications/[id]/configuration/source.svelte +++ /dev/null @@ -1,275 +0,0 @@ - - - - -
- {#if !filteredSources} -
Git App
-
-
-
- {$t('application.configuration.no_configurable_git')} -
-
- - - -
-
-
- {/if} - {#if ownSources.length > 0 || otherSources.length > 0} -
Integrated with Git App
- {/if} - {#if ownSources.length > 0 || otherSources.length > 0} -
-
- {#each ownSources as source} -
-
- {#if source?.type === 'gitlab'} - - - - {:else if source?.type === 'github'} - - - - {/if} -
- handleSubmit(source.id)}> - - -
- {/each} -
- - {#if otherSources.length > 0 && $appSession.teamId === '0'} -
Other Sources
- {/if} -
- {#each otherSources as source} -
-
handleSubmit(source.id)}> - -
-
- {/each} -
-
- {/if} -
-
Public Repository from Git
- -
- -
-
Simple Dockerfile
- -
-
-
-
-
-