Merge branch '1.9.x' into fix/preview-conditional-transforms

This commit is contained in:
Torsten Dittmann
2026-05-08 15:06:08 +04:00
committed by GitHub
151 changed files with 7265 additions and 1887 deletions
+1 -1
View File
@@ -25,6 +25,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: AI Moderator
uses: github/ai-moderator@v1
uses: github/ai-moderator@81159c370785e295c97461ade67d7c33576e9319 # v1.1.4
with:
token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Issue Labeler
uses: github/issue-labeler@v3.4
uses: github/issue-labeler@c1b0f9f52a63158c4adc09425e858e87b32e9685 # v3.4
with:
configuration-path: .github/labeler.yml
enable-versioned-regex: false
+58 -58
View File
@@ -32,7 +32,7 @@ jobs:
actions: read
security-events: write
contents: read
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@v2.3.3"
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@c51854704019a247608d928f370c98740469d4b5" # v2.3.5
security:
name: Checks / Image
@@ -43,13 +43,13 @@ jobs:
security-events: write
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
submodules: 'recursive'
- name: Build the Docker image
uses: docker/build-push-action@v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
push: false
@@ -58,7 +58,7 @@ jobs:
target: production
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@0.35.0
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: 'pr_image:${{ github.sha }}'
format: 'sarif'
@@ -66,7 +66,7 @@ jobs:
severity: 'CRITICAL,HIGH'
- name: Run Trivy vulnerability scanner on source code
uses: aquasecurity/trivy-action@0.35.0
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: 'fs'
scan-ref: '.'
@@ -76,14 +76,14 @@ jobs:
skip-setup-trivy: true
- name: Upload image scan results
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
if: always() && hashFiles('trivy-image-results.sarif') != ''
with:
sarif_file: 'trivy-image-results.sarif'
category: 'trivy-image'
- name: Upload source code scan results
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
if: always() && hashFiles('trivy-fs-results.sarif') != ''
with:
sarif_file: 'trivy-fs-results.sarif'
@@ -94,10 +94,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup PHP
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0
with:
php-version: '8.3'
tools: composer:v2
@@ -119,7 +119,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
@@ -127,7 +127,7 @@ jobs:
if: github.event_name == 'pull_request'
- name: Setup PHP
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0
with:
php-version: '8.3'
tools: composer:v2
@@ -144,10 +144,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup PHP
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0
with:
php-version: '8.3'
tools: composer:v2
@@ -157,7 +157,7 @@ jobs:
run: composer install --prefer-dist --no-progress --ignore-platform-reqs
- name: Cache PHPStan result cache
uses: actions/cache@v4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: .phpstan-cache
key: phpstan-${{ github.sha }}
@@ -172,10 +172,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup PHP
uses: shivammathur/setup-php@v2
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0
with:
php-version: '8.3'
extensions: swoole
@@ -193,10 +193,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24'
@@ -212,7 +212,7 @@ jobs:
steps:
- name: Generate matrix
id: generate
uses: actions/github-script@v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const allDatabases = ['MariaDB', 'PostgreSQL', 'MongoDB'];
@@ -253,28 +253,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Build and push Appwrite
uses: docker/build-push-action@v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
push: true
@@ -297,16 +297,16 @@ jobs:
packages: read
steps:
- name: checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -327,7 +327,7 @@ jobs:
run: docker compose exec -T appwrite vars
- name: Run Unit Tests
uses: itznotabug/php-retry@v3
uses: itznotabug/php-retry@d6bef45a8bff490babfb613e33b00d133e4062f0 # v3
with:
max_attempts: 2
retry_wait_seconds: 60
@@ -350,16 +350,16 @@ jobs:
packages: read
steps:
- name: checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -385,7 +385,7 @@ jobs:
done
- name: Run General Tests
uses: itznotabug/php-retry@v3
uses: itznotabug/php-retry@d6bef45a8bff490babfb613e33b00d133e4062f0 # v3
with:
max_attempts: 2
retry_wait_seconds: 60
@@ -445,26 +445,26 @@ jobs:
]
include:
- service: Databases
runner: blacksmith-4vcpu-ubuntu-2404
runner: runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/family=c7/volume=120g
paratest_processes: 3
timeout_minutes: 30
- service: Sites
runner: blacksmith-4vcpu-ubuntu-2404
runner: runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/family=c7/volume=120g
- service: Functions
runner: blacksmith-4vcpu-ubuntu-2404
runner: runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/family=c7/volume=120g
- service: Avatars
runner: blacksmith-4vcpu-ubuntu-2404
runner: runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/family=c7/volume=120g
- service: Realtime
runner: blacksmith-4vcpu-ubuntu-2404
runner: runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/family=c7/volume=120g
- service: TablesDB
runner: blacksmith-4vcpu-ubuntu-2404
runner: runs-on=${{ github.run_id }}/runner=4cpu-linux-x64/family=c7/volume=120g
paratest_processes: 3
timeout_minutes: 30
- service: Migrations
paratest_processes: 1
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set environment
run: |
@@ -488,13 +488,13 @@ jobs:
fi
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -525,7 +525,7 @@ jobs:
done
- name: Run tests
uses: itznotabug/php-retry@v3
uses: itznotabug/php-retry@d6bef45a8bff490babfb613e33b00d133e4062f0 # v3
with:
max_attempts: 2
retry_wait_seconds: 60
@@ -573,18 +573,18 @@ jobs:
mode: ${{ fromJSON(needs.matrix.outputs.modes) }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -607,7 +607,7 @@ jobs:
docker compose up -d --quiet-pull --wait
- name: Run tests
uses: itznotabug/php-retry@v3
uses: itznotabug/php-retry@d6bef45a8bff490babfb613e33b00d133e4062f0 # v3
with:
max_attempts: 2
retry_wait_seconds: 60
@@ -640,16 +640,16 @@ jobs:
mode: ${{ fromJSON(needs.matrix.outputs.modes) }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -679,7 +679,7 @@ jobs:
done
- name: Run tests
uses: itznotabug/php-retry@v3
uses: itznotabug/php-retry@d6bef45a8bff490babfb613e33b00d133e4062f0 # v3
with:
max_attempts: 2
retry_wait_seconds: 60
@@ -711,18 +711,18 @@ jobs:
packages: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v4
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -735,7 +735,7 @@ jobs:
docker tag ${{ env.REGISTRY_IMAGE }}:${{ github.sha }} ${{ env.IMAGE }}:after
- name: Setup k6
uses: grafana/setup-k6-action@ffe7d7290dfa715e48c2ccc924d068444c94bde2
uses: grafana/setup-k6-action@db07bd9765aac508ef18982e52ab937fe633a065 # v1.2.1
with:
k6-version: ${{ env.K6_VERSION }}
@@ -774,7 +774,7 @@ jobs:
- name: Benchmark before
if: steps.benchmark_before_start.outcome == 'success'
continue-on-error: true
uses: grafana/run-k6-action@a15e2072ede004e8d46141e33d7f7dad8ad08d9d
uses: grafana/run-k6-action@de51a7390bdf0ac85a3bef493691bd71d4c7c158 # v1.4.0
env:
APPWRITE_ENDPOINT: 'http://localhost/v1'
APPWRITE_BENCHMARK_ITERATIONS: '5'
@@ -826,7 +826,7 @@ jobs:
- name: Benchmark after
id: benchmark_after
continue-on-error: true
uses: grafana/run-k6-action@a15e2072ede004e8d46141e33d7f7dad8ad08d9d
uses: grafana/run-k6-action@de51a7390bdf0ac85a3bef493691bd71d4c7c158 # v1.4.0
env:
APPWRITE_ENDPOINT: 'http://localhost/v1'
APPWRITE_BENCHMARK_ITERATIONS: '5'
@@ -846,7 +846,7 @@ jobs:
- name: Comment on PR
if: always()
uses: actions/github-script@v8
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
BENCHMARK_BASE_REF: ${{ github.event.pull_request.base.ref }}
BENCHMARK_HEAD_REF: ${{ github.event.pull_request.head.ref }}
@@ -856,7 +856,7 @@ jobs:
await comment({ github, context, core });
- name: Save results
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() }}
with:
name: benchmark-results
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Cleanup
run: |
+4 -4
View File
@@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -47,14 +47,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
+6 -6
View File
@@ -10,13 +10,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
- name: Build the Docker image
run: DOCKER_BUILDKIT=1 docker build . --target production -t appwrite_image:latest
- name: Run Trivy vulnerability scanner on image
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: 'appwrite_image:latest'
format: 'sarif'
@@ -24,7 +24,7 @@ jobs:
ignore-unfixed: 'false'
severity: 'CRITICAL,HIGH'
- name: Upload Docker Image Scan Results
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
if: always() && hashFiles('trivy-image-results.sarif') != ''
with:
sarif_file: 'trivy-image-results.sarif'
@@ -35,16 +35,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Trivy vulnerability scanner on filesystem
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: 'fs'
format: 'sarif'
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Code Scan Results
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
if: always() && hashFiles('trivy-fs-results.sarif') != ''
with:
sarif_file: 'trivy-fs-results.sarif'
+6 -6
View File
@@ -12,33 +12,33 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: appwrite/cloud
tags: |
type=ref,event=tag
- name: Build & Publish to DockerHub
uses: docker/build-push-action@v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64
+6 -6
View File
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -20,20 +20,20 @@ jobs:
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: appwrite/appwrite
tags: |
@@ -42,7 +42,7 @@ jobs:
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
platforms: linux/amd64,linux/arm64
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set SDK type
id: set-sdk
@@ -49,7 +49,7 @@ jobs:
docker compose exec appwrite sdks --platform=${{ steps.set-sdk.outputs.platform }} --sdk=${{ steps.set-sdk.outputs.sdk_type }} --version=latest --git=no
sudo chown -R $USER:$USER ./app/sdks/${{ steps.set-sdk.outputs.platform }}-${{ steps.set-sdk.outputs.sdk_type }}
- uses: actions/setup-node@v4
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue has been labeled as a 'question', indicating that it requires additional information from the requestor. It has been inactive for 7 days. If no further activity occurs, this issue will be closed in 14 days."
+7
View File
@@ -1523,6 +1523,13 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_team_confirm'),
'type' => Database::INDEX_KEY,
'attributes' => ['teamInternalId', 'confirm'],
'lengths' => [],
'orders' => [],
],
],
],
+22 -1
View File
@@ -404,6 +404,13 @@ $platformCollections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_teamInternalId'),
'type' => Database::INDEX_KEY,
'attributes' => ['teamInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -635,6 +642,13 @@ $platformCollections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_project_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -1007,7 +1021,14 @@ $platformCollections = [
'attributes' => ['projectInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
]
],
[
'$id' => ID::custom('_key_project_id'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
+30
View File
@@ -623,6 +623,11 @@ return [
'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.',
'code' => 408,
],
Exception::FUNCTION_ASYNCHRONOUS_TIMEOUT => [
'name' => Exception::FUNCTION_ASYNCHRONOUS_TIMEOUT,
'description' => 'Asynchronous function execution timed out. Ensure the execution duration doesn\'t exceed the configured function timeout.',
'code' => 408,
],
Exception::FUNCTION_TEMPLATE_NOT_FOUND => [
'name' => Exception::FUNCTION_TEMPLATE_NOT_FOUND,
'description' => 'Function Template with the requested ID could not be found.',
@@ -687,6 +692,11 @@ return [
'description' => 'Build with the requested ID failed. Please check the logs for more information.',
'code' => 400,
],
Exception::BUILD_TIMEOUT => [
'name' => Exception::BUILD_TIMEOUT,
'description' => 'Build timed out. Increase the build timeout via the `_APP_COMPUTE_BUILD_TIMEOUT` environment variable, or simplify the build to complete within the limit.',
'code' => 408,
],
/** Deployments */
Exception::DEPLOYMENT_NOT_FOUND => [
@@ -1236,6 +1246,26 @@ return [
'description' => 'The specified database type is not supported for CSV import or export operations.',
'code' => 400,
],
Exception::MIGRATION_SOURCE_PROJECT_ID_REQUIRED => [
'name' => Exception::MIGRATION_SOURCE_PROJECT_ID_REQUIRED,
'description' => 'A source projectId is required for Appwrite migrations. Provide it in the migration credentials.',
'code' => 400,
],
Exception::MIGRATION_SOURCE_PROJECT_NOT_FOUND => [
'name' => Exception::MIGRATION_SOURCE_PROJECT_NOT_FOUND,
'description' => 'The source project for the provided projectId was not found. Verify the projectId and the API key has access to it.',
'code' => 404,
],
Exception::MIGRATION_SOURCE_TYPE_INVALID => [
'name' => Exception::MIGRATION_SOURCE_TYPE_INVALID,
'description' => 'The migration source type is invalid. Use one of the supported source types.',
'code' => 400,
],
Exception::MIGRATION_DESTINATION_TYPE_INVALID => [
'name' => Exception::MIGRATION_DESTINATION_TYPE_INVALID,
'description' => 'The migration destination type is invalid. Use one of the supported destination types.',
'code' => 400,
],
/** Realtime */
Exception::REALTIME_MESSAGE_FORMAT_INVALID => [
+2 -2
View File
@@ -59,8 +59,8 @@ $admins = [
'oauth2.write',
'mocks.read',
'mocks.write',
'policies.read',
'policies.write',
'project.policies.read',
'project.policies.write',
'templates.read',
'templates.write',
'projects.write',
+23 -9
View File
@@ -44,11 +44,23 @@ return [
"category" => "Project",
],
"policies.read" => [
"description" =>
"Access to read project\'s policies. Replaced by \'project.policies.read\' for more granular control",
"category" => "Project",
'deprecated' => true,
],
"policies.write" => [
"description" =>
"Access to update project\'s policies. Replaces by \'project.policies.write\' for more granular control",
"category" => "Project",
'deprecated' => true,
],
"project.policies.read" => [
"description" =>
"Access to read project\'s policies",
"category" => "Project",
],
"policies.write" => [
"project.policies.write" => [
"description" =>
"Access to update project\'s policies",
"category" => "Project",
@@ -286,6 +298,16 @@ return [
'category' => 'Messaging',
],
// Proxy
'rules.read' => [
'description' => 'Access to read proxy rules.',
'category' => 'Proxy',
],
'rules.write' => [
'description' => 'Access to create, update, and delete proxy rules.',
'category' => 'Proxy',
],
// Other
"webhooks.read" => [
"description" =>
@@ -339,12 +361,4 @@ return [
'description' => 'Access to create, update, and delete resources under VCS service.',
'category' => 'Other',
],
'rules.read' => [
'description' => 'Access to read proxy rules.',
'category' => 'Other',
],
'rules.write' => [
'description' => 'Access to create, update, and delete proxy rules.',
'category' => 'Other',
],
];
+20 -20
View File
@@ -332,15 +332,15 @@ Http::post('/v1/account')
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
throw new Exception(Exception::USER_EMAIL_FREE);
}
@@ -830,11 +830,11 @@ Http::patch('/v1/account/sessions/:sessionId')
$refreshToken = $session->getAttribute('providerRefreshToken', '');
$oAuthProviders = Config::getParam('oAuthProviders') ?? [];
$className = $oAuthProviders[$provider]['class'] ?? null;
if (!empty($provider) && ($className === null || !\class_exists($className))) {
if (!empty($refreshToken) && ($className === null || !\class_exists($className))) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
if (!empty($provider) && \class_exists($className)) {
if ($className !== null && \class_exists($className)) {
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
@@ -1676,15 +1676,15 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
$failureRedirect(Exception::GENERAL_INVALID_EMAIL);
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
$failureRedirect(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
$failureRedirect(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
$failureRedirect(Exception::USER_EMAIL_FREE);
}
@@ -1817,15 +1817,15 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
$failureRedirect(Exception::GENERAL_INVALID_EMAIL);
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
$failureRedirect(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
$failureRedirect(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
$failureRedirect(Exception::USER_EMAIL_FREE);
}
@@ -2175,15 +2175,15 @@ Http::post('/v1/account/tokens/magic-url')
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
throw new Exception(Exception::USER_EMAIL_FREE);
}
@@ -2496,15 +2496,15 @@ Http::post('/v1/account/tokens/email')
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
throw new Exception(Exception::USER_EMAIL_FREE);
}
@@ -3417,15 +3417,15 @@ Http::patch('/v1/account/email')
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && $emailMetadata['emailIsDisposable']) {
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && $emailMetadata['emailIsCanonical'] === false) {
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && $emailMetadata['emailIsFree']) {
throw new Exception(Exception::USER_EMAIL_FREE);
}
+6 -6
View File
@@ -131,15 +131,15 @@ function createUser(Hash $hash, string $userId, ?string $email, ?string $passwor
} catch (\Throwable) {
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
throw new Exception(Exception::USER_EMAIL_FREE);
}
@@ -1563,15 +1563,15 @@ Http::patch('/v1/users/:userId/email')
} catch (\Throwable) {
}
if (($plan['supportsDisposableEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
if ((($project->getId() === 'console') || ($plan['supportsDisposableEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['disposableEmails'] ?? false) && ($emailMetadata['emailIsDisposable'] ?? false)) {
throw new Exception(Exception::USER_EMAIL_DISPOSABLE);
}
if (($plan['supportsCanonicalEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
if ((($project->getId() === 'console') || ($plan['supportsCanonicalEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['canonicalEmails'] ?? false) && ($emailMetadata['emailIsCanonical'] ?? true) === false) {
throw new Exception(Exception::USER_EMAIL_NOT_CANONICAL);
}
if (($plan['supportsFreeEmailValidation'] ?? false) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
if ((($project->getId() === 'console') || ($plan['supportsFreeEmailValidation'] ?? false)) && ($project->getAttribute('auths', [])['freeEmails'] ?? false) && ($emailMetadata['emailIsFree'] ?? false)) {
throw new Exception(Exception::USER_EMAIL_FREE);
}
+33 -20
View File
@@ -28,6 +28,7 @@ use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
use Appwrite\Utopia\Request\Filters\V22 as RequestV22;
use Appwrite\Utopia\Request\Filters\V23 as RequestV23;
use Appwrite\Utopia\Request\Filters\V24 as RequestV24;
use Appwrite\Utopia\Request\Filters\V25 as RequestV25;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
@@ -38,7 +39,9 @@ use Appwrite\Utopia\Response\Filters\V21 as ResponseV21;
use Appwrite\Utopia\Response\Filters\V22 as ResponseV22;
use Appwrite\Utopia\Response\Filters\V23 as ResponseV23;
use Appwrite\Utopia\Response\Filters\V24 as ResponseV24;
use Appwrite\Utopia\Response\Filters\V25 as ResponseV25;
use Appwrite\Utopia\View;
use Executor\Exception\Timeout as ExecutorTimeout;
use Executor\Executor;
use MaxMind\Db\Reader;
use Swoole\Http\Request as SwooleRequest;
@@ -579,26 +582,30 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
'site' => '',
};
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $resource->getAttribute('timeout', 30),
image: $runtime['image'],
source: $source,
entrypoint: $entrypoint,
version: $version,
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $runtimeEntrypoint,
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
logging: $resource->getAttribute('logging', true),
requestTimeout: 30,
responseFormat: Executor::RESPONSE_FORMAT_ARRAY_HEADERS
);
try {
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $resource->getAttribute('timeout', 30),
image: $runtime['image'],
source: $source,
entrypoint: $entrypoint,
version: $version,
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $runtimeEntrypoint,
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
logging: $resource->getAttribute('logging', true),
requestTimeout: 30,
responseFormat: Executor::RESPONSE_FORMAT_ARRAY_HEADERS
);
} catch (ExecutorTimeout $th) {
throw new AppwriteException(AppwriteException::FUNCTION_SYNCHRONOUS_TIMEOUT, previous: $th);
}
$headerOverrides = [];
@@ -904,6 +911,9 @@ Http::init()
if (version_compare($requestFormat, '1.9.3', '<')) {
$request->addFilter(new RequestV24());
}
if (version_compare($requestFormat, '1.9.4', '<')) {
$request->addFilter(new RequestV25());
}
}
$localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
@@ -928,6 +938,9 @@ Http::init()
*/
$responseFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($responseFormat) {
if (version_compare($responseFormat, '1.9.4', '<')) {
$response->addFilter(new ResponseV25());
}
if (version_compare($responseFormat, '1.9.3', '<')) {
$response->addFilter(new ResponseV24());
}
+22 -12
View File
@@ -3,7 +3,6 @@
use Appwrite\Auth\Key;
use Appwrite\Auth\MFA\Type\TOTP;
use Appwrite\Bus\Events\RequestCompleted;
use Appwrite\Event\Build;
use Appwrite\Event\Context\Audit as AuditContext;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
@@ -20,6 +19,8 @@ use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Functions\EventProcessor;
use Appwrite\Platform\Modules\Storage\Config\CacheControl;
use Appwrite\Platform\Modules\Storage\Config\StorageCacheControl;
use Appwrite\SDK\Method;
use Appwrite\Usage\Context;
use Appwrite\Utopia\Database\Documents\User;
@@ -489,7 +490,6 @@ Http::init()
->inject('auditContext')
->inject('queueForDeletes')
->inject('queueForDatabase')
->inject('queueForBuilds')
->inject('usage')
->inject('queueForFunctions')
->inject('queueForMails')
@@ -503,7 +503,8 @@ Http::init()
->inject('telemetry')
->inject('platform')
->inject('authorization')
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Messaging $queueForMessaging, AuditContext $auditContext, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Context $usage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) {
->inject('cacheControlForStorage')
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Messaging $queueForMessaging, AuditContext $auditContext, Delete $queueForDeletes, EventDatabase $queueForDatabase, Context $usage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization, callable $cacheControlForStorage) {
$response->setUser($user);
$request->setUser($user);
@@ -618,12 +619,10 @@ Http::init()
$queueForDatabase->setProject($project);
$queueForMessaging->setProject($project);
$queueForFunctions->setProject($project);
$queueForBuilds->setProject($project);
$queueForMails->setProject($project);
/* Auto-set platforms */
$queueForFunctions->setPlatform($platform);
$queueForBuilds->setPlatform($platform);
$queueForMails->setPlatform($platform);
$useCache = $route->getLabel('cache', false);
@@ -643,6 +642,7 @@ Http::init()
$data = $cache->load($key, $timestamp);
if (! empty($data) && ! $cacheLog->isEmpty()) {
$cacheControl = \sprintf('private, max-age=%d', $timestamp);
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
$type = $parts[0];
@@ -695,6 +695,21 @@ Http::init()
])));
}
}
if ($isImageTransformation) {
$cacheControl = $cacheControlForStorage(new StorageCacheControl(
source: CacheControl::SOURCE_CACHE,
user: $user,
maxAge: $timestamp,
project: $project,
bucket: $bucket,
file: $file,
resourceToken: $resourceToken,
fileSecurity: $fileSecurity,
cacheLog: $cacheLog,
route: $route,
));
}
}
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
@@ -707,7 +722,7 @@ Http::init()
}
$response
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
->addHeader('Cache-Control', $cacheControl)
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($cacheLog->getAttribute('mimeType'));
$storageCacheOperationsCounter->add(1, ['result' => 'hit']);
@@ -800,7 +815,6 @@ Http::shutdown()
->inject('publisherForUsage')
->inject('queueForDeletes')
->inject('queueForDatabase')
->inject('queueForBuilds')
->inject('queueForMessaging')
->inject('queueForFunctions')
->inject('queueForWebhooks')
@@ -812,7 +826,7 @@ Http::shutdown()
->inject('bus')
->inject('apiKey')
->inject('mode')
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Audit $publisherForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode) use ($parseLabel) {
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Audit $publisherForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode) use ($parseLabel) {
$responsePayload = $response->getPayload();
@@ -961,10 +975,6 @@ Http::shutdown()
$queueForDatabase->trigger();
}
if (! empty($queueForBuilds->getType())) {
$queueForBuilds->trigger();
}
if (! empty($queueForMessaging->getType())) {
$queueForMessaging->trigger();
}
+3 -2
View File
@@ -44,14 +44,15 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4324;
const APP_VERSION_STABLE = '1.9.3';
const APP_CACHE_BUSTER = 4325;
const APP_VERSION_STABLE = '1.9.4';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
const APP_DATABASE_ATTRIBUTE_URL = 'url';
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
const APP_DATABASE_ATTRIBUTE_BIGINT_RANGE = 'bigintRange';
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
const APP_DATABASE_ATTRIBUTE_POINT = 'point';
const APP_DATABASE_ATTRIBUTE_LINE = 'line';
+7
View File
@@ -36,6 +36,13 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_INTEGER);
// BigInt uses a dedicated bigintRange format name to avoid clobbering `intRange`.
Structure::addFormat(APP_DATABASE_ATTRIBUTE_BIGINT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_BIGINT);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
+4
View File
@@ -11,6 +11,7 @@ use Appwrite\Utopia\Response\Model\AlgoScryptModified;
use Appwrite\Utopia\Response\Model\AlgoSha;
use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\Attribute;
use Appwrite\Utopia\Response\Model\AttributeBigInt;
use Appwrite\Utopia\Response\Model\AttributeBoolean;
use Appwrite\Utopia\Response\Model\AttributeDatetime;
use Appwrite\Utopia\Response\Model\AttributeEmail;
@@ -37,6 +38,7 @@ use Appwrite\Utopia\Response\Model\Branch;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Column;
use Appwrite\Utopia\Response\Model\ColumnBigInt;
use Appwrite\Utopia\Response\Model\ColumnBoolean;
use Appwrite\Utopia\Response\Model\ColumnDatetime;
use Appwrite\Utopia\Response\Model\ColumnEmail;
@@ -297,6 +299,7 @@ Response::setModel(new Attribute());
Response::setModel(new AttributeList());
Response::setModel(new AttributeString());
Response::setModel(new AttributeInteger());
Response::setModel(new AttributeBigInt());
Response::setModel(new AttributeFloat());
Response::setModel(new AttributeBoolean());
Response::setModel(new AttributeEmail());
@@ -330,6 +333,7 @@ Response::setModel(new Column());
Response::setModel(new ColumnList());
Response::setModel(new ColumnString());
Response::setModel(new ColumnInteger());
Response::setModel(new ColumnBigInt());
Response::setModel(new ColumnFloat());
Response::setModel(new ColumnBoolean());
Response::setModel(new ColumnEmail());
+10
View File
@@ -2,12 +2,14 @@
use Appwrite\Event\Event;
use Appwrite\Event\Publisher\Audit as AuditPublisher;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Event\Publisher\Certificate as CertificatePublisher;
use Appwrite\Event\Publisher\Execution as ExecutionPublisher;
use Appwrite\Event\Publisher\Migration as MigrationPublisher;
use Appwrite\Event\Publisher\Screenshot as ScreenshotPublisher;
use Appwrite\Event\Publisher\StatsResources as StatsResourcesPublisher;
use Appwrite\Event\Publisher\Usage as UsagePublisher;
use Appwrite\Platform\Modules\Storage\Config\StorageCacheControl;
use Appwrite\Utopia\Database\Documents\User;
use Executor\Executor;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
@@ -112,6 +114,10 @@ $container->set('publisherForStatsResources', fn (Publisher $publisher) => new S
$publisher,
new Queue(System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME))
), ['publisher']);
$container->set('publisherForBuilds', fn (Publisher $publisher) => new BuildPublisher(
$publisher,
new Queue(System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME))
), ['publisher']);
/**
* Platform configuration
@@ -198,6 +204,10 @@ $container->set('cache', function (Group $pools, Telemetry $telemetry) {
return $cache;
}, ['pools', 'telemetry']);
$container->set('cacheControlForStorage', fn () => function (StorageCacheControl $config): string {
return \sprintf('private, max-age=%d', $config->maxAge);
});
$container->set('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
$port = System::getEnv('_APP_REDIS_PORT', 6379);
-4
View File
@@ -4,7 +4,6 @@ use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Key;
use Appwrite\Databases\TransactionState;
use Appwrite\Event\Build;
use Appwrite\Event\Context\Audit as AuditContext;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
@@ -123,9 +122,6 @@ return function (Container $container): void {
$container->set('queueForMails', function (Publisher $publisher) {
return new Mail($publisher);
}, ['publisher']);
$container->set('queueForBuilds', function (Publisher $publisher) {
return new Build($publisher);
}, ['publisher']);
$container->set('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
-5
View File
@@ -1,6 +1,5 @@
<?php
use Appwrite\Event\Build;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
@@ -342,10 +341,6 @@ return function (Container $container): void {
return new Mail($publisher);
}, ['publisher']);
$container->set('queueForBuilds', function (Publisher $publisher) {
return new Build($publisher);
}, ['publisher']);
$container->set('queueForDeletes', function (Publisher $publisher) {
return new Delete($publisher);
}, ['publisher']);
+4 -4
View File
@@ -74,13 +74,13 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.22.*",
"utopia-php/migration": "1.9.*",
"utopia-php/migration": "1.*",
"utopia-php/platform": "0.13.*",
"utopia-php/pools": "1.*",
"utopia-php/span": "1.1.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.17.*",
"utopia-php/servers": "0.3.*",
"utopia-php/queue": "0.18.*",
"utopia-php/servers": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "2.*",
"utopia-php/system": "0.10.*",
@@ -93,7 +93,7 @@
"chillerlan/php-qrcode": "4.3.*",
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "11.*",
"webonyx/graphql-php": "15.31.*",
"webonyx/graphql-php": "15.32.*",
"league/csv": "9.14.*",
"enshrined/svg-sanitize": "0.22.*"
},
Generated
+148 -121
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "bd45829c252971301370d62300be106d",
"content-hash": "ec2ad489c60f0102f0dfab223b6d1fe4",
"packages": [
{
"name": "adhocore/jwt",
@@ -2641,16 +2641,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b",
"reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b",
"shasum": ""
},
"require": {
@@ -2663,7 +2663,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
"dev-main": "3.7-dev"
}
},
"autoload": {
@@ -2688,7 +2688,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0"
},
"funding": [
{
@@ -2699,25 +2699,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-25T14:21:43+00:00"
"time": "2026-04-13T15:52:40+00:00"
},
{
"name": "symfony/http-client",
"version": "v7.4.8",
"version": "v7.4.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "01933e626c3de76bea1e22641e205e78f6a34342"
"reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/01933e626c3de76bea1e22641e205e78f6a34342",
"reference": "01933e626c3de76bea1e22641e205e78f6a34342",
"url": "https://api.github.com/repos/symfony/http-client/zipball/7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6",
"reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6",
"shasum": ""
},
"require": {
@@ -2785,7 +2789,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.4.8"
"source": "https://github.com/symfony/http-client/tree/v7.4.9"
},
"funding": [
{
@@ -2805,20 +2809,20 @@
"type": "tidelift"
}
],
"time": "2026-03-30T12:55:43+00:00"
"time": "2026-04-29T13:25:15+00:00"
},
{
"name": "symfony/http-client-contracts",
"version": "v3.6.0",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
"reference": "75d7043853a42837e68111812f4d964b01e5101c"
"reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c",
"reference": "75d7043853a42837e68111812f4d964b01e5101c",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d",
"reference": "4a2d00c37651c0bdc2b9e1c773487a8bf4edb12d",
"shasum": ""
},
"require": {
@@ -2831,7 +2835,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
"dev-main": "3.7-dev"
}
},
"autoload": {
@@ -2867,7 +2871,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0"
"source": "https://github.com/symfony/http-client-contracts/tree/v3.7.0"
},
"funding": [
{
@@ -2878,12 +2882,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-29T11:18:49+00:00"
"time": "2026-03-06T13:17:50+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@@ -3212,16 +3220,16 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.6.1",
"version": "v3.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
"reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a",
"reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a",
"shasum": ""
},
"require": {
@@ -3239,7 +3247,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
"dev-main": "3.7-dev"
}
},
"autoload": {
@@ -3275,7 +3283,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
"source": "https://github.com/symfony/service-contracts/tree/v3.7.0"
},
"funding": [
{
@@ -3295,7 +3303,7 @@
"type": "tidelift"
}
],
"time": "2025-07-15T11:30:57+00:00"
"time": "2026-03-28T09:44:51+00:00"
},
{
"name": "tbachert/spi",
@@ -3658,21 +3666,21 @@
},
{
"name": "utopia-php/cli",
"version": "0.23.2",
"version": "0.23.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cli.git",
"reference": "145b91fef827853bcceaa3ab8ca2b1d6faaca2ab"
"reference": "3c45ae5bcdcd3c7916e1909d74c60b8e771610db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/145b91fef827853bcceaa3ab8ca2b1d6faaca2ab",
"reference": "145b91fef827853bcceaa3ab8ca2b1d6faaca2ab",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/3c45ae5bcdcd3c7916e1909d74c60b8e771610db",
"reference": "3c45ae5bcdcd3c7916e1909d74c60b8e771610db",
"shasum": ""
},
"require": {
"php": ">=7.4",
"utopia-php/servers": "0.3.*"
"utopia-php/servers": "0.4.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -3703,9 +3711,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cli/issues",
"source": "https://github.com/utopia-php/cli/tree/0.23.2"
"source": "https://github.com/utopia-php/cli/tree/0.23.3"
},
"time": "2026-04-27T09:19:04+00:00"
"time": "2026-05-05T04:38:59+00:00"
},
{
"name": "utopia-php/compression",
@@ -3850,22 +3858,23 @@
},
{
"name": "utopia-php/database",
"version": "5.4.1",
"version": "5.7.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "688d9422b5ff42ac2ecc29397d94891cfd772e93"
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/688d9422b5ff42ac2ecc29397d94891cfd772e93",
"reference": "688d9422b5ff42ac2ecc29397d94891cfd772e93",
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-mongodb": "*",
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.4",
"utopia-php/cache": "1.*",
"utopia-php/console": "0.1.*",
@@ -3903,9 +3912,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/5.4.1"
"source": "https://github.com/utopia-php/database/tree/5.7.0"
},
"time": "2026-04-29T07:32:59+00:00"
"time": "2026-05-06T01:04:08+00:00"
},
{
"name": "utopia-php/detector",
@@ -4271,23 +4280,23 @@
},
{
"name": "utopia-php/http",
"version": "0.34.24",
"version": "0.34.25",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "d1eced0627c5a9fceddf53992ed97d664b810d33"
"reference": "76be330d4197bae680eb4ccc29c573456fe91904"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/d1eced0627c5a9fceddf53992ed97d664b810d33",
"reference": "d1eced0627c5a9fceddf53992ed97d664b810d33",
"url": "https://api.github.com/repos/utopia-php/http/zipball/76be330d4197bae680eb4ccc29c573456fe91904",
"reference": "76be330d4197bae680eb4ccc29c573456fe91904",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/compression": "0.1.*",
"utopia-php/di": "0.3.*",
"utopia-php/servers": "0.3.*",
"utopia-php/servers": "0.4.0",
"utopia-php/telemetry": "0.2.*",
"utopia-php/validators": "0.2.*"
},
@@ -4321,9 +4330,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.34.24"
"source": "https://github.com/utopia-php/http/tree/0.34.25"
},
"time": "2026-04-24T12:16:53+00:00"
"time": "2026-05-05T04:39:15+00:00"
},
{
"name": "utopia-php/image",
@@ -4530,16 +4539,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.9.5",
"version": "1.10.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "952a4dfe232702f80e45c35129466a8d8cb4c599"
"reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/952a4dfe232702f80e45c35129466a8d8cb4c599",
"reference": "952a4dfe232702f80e45c35129466a8d8cb4c599",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/759d6d61b327313cbeeeb4ea0c3e2459164b4827",
"reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827",
"shasum": ""
},
"require": {
@@ -4565,7 +4574,25 @@
"Utopia\\Migration\\": "src/Migration"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"Utopia\\Tests\\": "tests/Migration"
}
},
"scripts": {
"test": [
"./vendor/bin/phpunit"
],
"lint": [
"./vendor/bin/pint --test"
],
"format": [
"./vendor/bin/pint"
],
"check": [
"./vendor/bin/phpstan analyse --level 3 src tests --memory-limit 2G"
]
},
"license": [
"MIT"
],
@@ -4578,10 +4605,10 @@
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.9.5"
"source": "https://github.com/utopia-php/migration/tree/1.10.1",
"issues": "https://github.com/utopia-php/migration/issues"
},
"time": "2026-04-29T11:19:13+00:00"
"time": "2026-05-07T07:23:57+00:00"
},
{
"name": "utopia-php/mongo",
@@ -4646,26 +4673,26 @@
},
{
"name": "utopia-php/platform",
"version": "0.13.0",
"version": "0.13.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/platform.git",
"reference": "d23af5349a7ea9ee11f9920a13626226f985522e"
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/d23af5349a7ea9ee11f9920a13626226f985522e",
"reference": "d23af5349a7ea9ee11f9920a13626226f985522e",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
"reference": "a20cb8b20a1e4c9886309c2d033a0292ba0937b9",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-redis": "*",
"php": ">=8.1",
"utopia-php/cli": "0.23.*",
"utopia-php/http": "0.34.*",
"utopia-php/queue": "0.17.*",
"utopia-php/servers": "0.3.*"
"php": ">=8.3",
"utopia-php/cli": "0.23.3",
"utopia-php/http": "0.34.25",
"utopia-php/queue": "0.18.2",
"utopia-php/servers": "0.4.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -4691,9 +4718,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/platform/issues",
"source": "https://github.com/utopia-php/platform/tree/0.13.0"
"source": "https://github.com/utopia-php/platform/tree/0.13.2"
},
"time": "2026-04-17T09:57:18+00:00"
"time": "2026-05-05T06:00:26+00:00"
},
{
"name": "utopia-php/pools",
@@ -4803,25 +4830,24 @@
},
{
"name": "utopia-php/queue",
"version": "0.17.0",
"version": "0.18.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f"
"reference": "f85ca003c99ff475708c05466643d067403c0c22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/0fbc7d7312f5cf76ec112513fb93317000901f5f",
"reference": "0fbc7d7312f5cf76ec112513fb93317000901f5f",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/f85ca003c99ff475708c05466643d067403c0c22",
"reference": "f85ca003c99ff475708c05466643d067403c0c22",
"shasum": ""
},
"require": {
"php": ">=8.3",
"php-amqplib/php-amqplib": "^3.7",
"utopia-php/di": "0.3.*",
"utopia-php/fetch": "0.5.*",
"utopia-php/pools": "1.*",
"utopia-php/servers": "0.3.*",
"utopia-php/servers": "0.4.0",
"utopia-php/telemetry": "0.2.*",
"utopia-php/validators": "0.2.*"
},
@@ -4864,9 +4890,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.17.0"
"source": "https://github.com/utopia-php/queue/tree/0.18.2"
},
"time": "2026-03-23T16:21:31+00:00"
"time": "2026-05-05T04:38:59+00:00"
},
{
"name": "utopia-php/registry",
@@ -4922,16 +4948,16 @@
},
{
"name": "utopia-php/servers",
"version": "0.3.0",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/servers.git",
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52"
"reference": "7db346ef377503efe0acafe0791085270cd9ed70"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/servers/zipball/235be31200df9437fc96a1c270ffef4c64fafe52",
"reference": "235be31200df9437fc96a1c270ffef4c64fafe52",
"url": "https://api.github.com/repos/utopia-php/servers/zipball/7db346ef377503efe0acafe0791085270cd9ed70",
"reference": "7db346ef377503efe0acafe0791085270cd9ed70",
"shasum": ""
},
"require": {
@@ -4970,9 +4996,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/servers/issues",
"source": "https://github.com/utopia-php/servers/tree/0.3.0"
"source": "https://github.com/utopia-php/servers/tree/0.4.0"
},
"time": "2026-03-13T11:31:42+00:00"
"time": "2026-05-05T04:08:30+00:00"
},
{
"name": "utopia-php/span",
@@ -5020,16 +5046,16 @@
},
{
"name": "utopia-php/storage",
"version": "2.0.1",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "8a2e3a86fd01aaed675884146665308c2122264e"
"reference": "64e132a3768e22243eda36fe4262da22fd204f3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/8a2e3a86fd01aaed675884146665308c2122264e",
"reference": "8a2e3a86fd01aaed675884146665308c2122264e",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/64e132a3768e22243eda36fe4262da22fd204f3c",
"reference": "64e132a3768e22243eda36fe4262da22fd204f3c",
"shasum": ""
},
"require": {
@@ -5066,22 +5092,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/2.0.1"
"source": "https://github.com/utopia-php/storage/tree/2.0.2"
},
"time": "2026-04-29T09:05:48+00:00"
"time": "2026-05-01T15:06:16+00:00"
},
{
"name": "utopia-php/system",
"version": "0.10.1",
"version": "0.10.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/system.git",
"reference": "7c1669533bb9c285de19191270c8c1439161a78a"
"reference": "04229a822b147c1abaf1a92fb42c2d7aad4625df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/system/zipball/7c1669533bb9c285de19191270c8c1439161a78a",
"reference": "7c1669533bb9c285de19191270c8c1439161a78a",
"url": "https://api.github.com/repos/utopia-php/system/zipball/04229a822b147c1abaf1a92fb42c2d7aad4625df",
"reference": "04229a822b147c1abaf1a92fb42c2d7aad4625df",
"shasum": ""
},
"require": {
@@ -5122,9 +5148,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/system/issues",
"source": "https://github.com/utopia-php/system/tree/0.10.1"
"source": "https://github.com/utopia-php/system/tree/0.10.2"
},
"time": "2026-03-15T21:07:41+00:00"
"time": "2026-05-05T14:33:41+00:00"
},
{
"name": "utopia-php/telemetry",
@@ -5384,16 +5410,16 @@
},
{
"name": "webonyx/graphql-php",
"version": "v15.31.5",
"version": "v15.32.3",
"source": {
"type": "git",
"url": "https://github.com/webonyx/graphql-php.git",
"reference": "089c4ef7e112df85788cfe06596278a8f99f4aa9"
"reference": "993bf0bea17f870412ad8a90f60c41cb8d5f1145"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/089c4ef7e112df85788cfe06596278a8f99f4aa9",
"reference": "089c4ef7e112df85788cfe06596278a8f99f4aa9",
"url": "https://api.github.com/repos/webonyx/graphql-php/zipball/993bf0bea17f870412ad8a90f60c41cb8d5f1145",
"reference": "993bf0bea17f870412ad8a90f60c41cb8d5f1145",
"shasum": ""
},
"require": {
@@ -5402,16 +5428,16 @@
"php": "^7.4 || ^8"
},
"require-dev": {
"amphp/amp": "^2.6",
"amphp/http-server": "^2.1",
"amphp/amp": "^2.6 || ^3",
"amphp/http-server": "^2.1 || ^3",
"dms/phpunit-arraysubset-asserts": "dev-master",
"ergebnis/composer-normalize": "^2.28",
"friendsofphp/php-cs-fixer": "3.94.2",
"friendsofphp/php-cs-fixer": "3.95.1",
"mll-lab/php-cs-fixer-config": "5.13.0",
"nyholm/psr7": "^1.5",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "2.1.46",
"phpstan/phpstan": "2.1.51",
"phpstan/phpstan-phpunit": "2.0.16",
"phpstan/phpstan-strict-rules": "2.0.10",
"phpunit/phpunit": "^9.5 || ^10.5.21 || ^11",
@@ -5425,6 +5451,7 @@
"ticketswap/phpstan-error-formatter": "1.3.0"
},
"suggest": {
"amphp/amp": "To leverage async resolving on AMPHP platform (v3 with AmpFutureAdapter, v2 with AmpPromiseAdapter)",
"amphp/http-server": "To leverage async resolving with webserver on AMPHP platform",
"psr/http-message": "To use standard GraphQL server",
"react/promise": "To leverage async resolving on React PHP platform"
@@ -5447,7 +5474,7 @@
],
"support": {
"issues": "https://github.com/webonyx/graphql-php/issues",
"source": "https://github.com/webonyx/graphql-php/tree/v15.31.5"
"source": "https://github.com/webonyx/graphql-php/tree/v15.32.3"
},
"funding": [
{
@@ -5459,22 +5486,22 @@
"type": "open_collective"
}
],
"time": "2026-04-11T18:06:15+00:00"
"time": "2026-04-24T13:49:35+00:00"
}
],
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.25.1",
"version": "1.27.5",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "f21a556b9acdbf75bbdcdc90a078af641646eade"
"reference": "9faa38b48d422f3da764a719712905c83b3922cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/f21a556b9acdbf75bbdcdc90a078af641646eade",
"reference": "f21a556b9acdbf75bbdcdc90a078af641646eade",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9faa38b48d422f3da764a719712905c83b3922cb",
"reference": "9faa38b48d422f3da764a719712905c83b3922cb",
"shasum": ""
},
"require": {
@@ -5510,9 +5537,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/1.25.1"
"source": "https://github.com/appwrite/sdk-generator/tree/1.27.5"
},
"time": "2026-04-28T11:12:22+00:00"
"time": "2026-05-05T12:09:40+00:00"
},
{
"name": "brianium/paratest",
@@ -6619,16 +6646,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.5.23",
"version": "12.5.24",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969"
"reference": "d75dd30597caa80e72fad2ef7904601a30ef1046"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969",
"reference": "c54fcf3d6bcb6e96ac2f7e40097dc37b5f139969",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046",
"reference": "d75dd30597caa80e72fad2ef7904601a30ef1046",
"shasum": ""
},
"require": {
@@ -6697,7 +6724,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.23"
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24"
},
"funding": [
{
@@ -6705,7 +6732,7 @@
"type": "other"
}
],
"time": "2026-04-18T06:12:49+00:00"
"time": "2026-05-01T04:21:04+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -7690,16 +7717,16 @@
},
{
"name": "symfony/console",
"version": "v8.0.8",
"version": "v8.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7"
"reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7",
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7",
"url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d",
"reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d",
"shasum": ""
},
"require": {
@@ -7756,7 +7783,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v8.0.8"
"source": "https://github.com/symfony/console/tree/v8.0.9"
},
"funding": [
{
@@ -7776,7 +7803,7 @@
"type": "tidelift"
}
],
"time": "2026-03-30T15:14:47+00:00"
"time": "2026-04-29T15:02:55+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -0,0 +1 @@
Create a bigint attribute. Optionally, minimum and maximum values can be provided.
@@ -0,0 +1 @@
Update a bigint attribute. Changing the `default` value will not update already existing documents.
@@ -0,0 +1 @@
Create a bigint column. Optionally, minimum and maximum values can be provided.
@@ -0,0 +1 @@
Update a bigint column. Changing the `default` value will not update already existing rows.
@@ -1 +1 @@
Get a list of all branches from a GitHub repository in your installation. This endpoint returns the names of all branches in the repository and their total count. The GitHub installation must be properly configured and have access to the requested repository for this endpoint to work.
Get a list of branches from a GitHub repository in your installation. This endpoint supports filtering by a search term and pagination using query strings such as `Query.limit()`, `Query.offset()`, `Query.cursorAfter()`, and `Query.cursorBefore()`. It returns branch names along with the total number of matches. The GitHub installation must be properly configured and have access to the requested repository for this endpoint to work.
-146
View File
@@ -1,146 +0,0 @@
<?php
namespace Appwrite\Event;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Build extends Event
{
protected string $type = '';
protected ?Document $resource = null;
protected ?Document $deployment = null;
protected ?Document $template = null;
public function __construct(protected Publisher $publisher)
{
parent::__construct($publisher);
$this
->setQueue(System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME))
->setClass(System::getEnv('_APP_BUILDS_CLASS_NAME', Event::BUILDS_CLASS_NAME));
}
/**
* Sets template for the build event.
*
* @param Document $template
* @return self
*/
public function setTemplate(Document $template): self
{
$this->template = $template;
return $this;
}
/**
* Sets resource document for the build event.
*
* @param Document $resource
* @return self
*/
public function setResource(Document $resource): self
{
$this->resource = $resource;
return $this;
}
/**
* Returns set resource document for the build event.
*
* @return null|Document
*/
public function getResource(): ?Document
{
return $this->resource;
}
/**
* Sets deployment for the build event.
*
* @param Document $deployment
* @return self
*/
public function setDeployment(Document $deployment): self
{
$this->deployment = $deployment;
return $this;
}
/**
* Returns set deployment for the build event.
*
* @return null|Document
*/
public function getDeployment(): ?Document
{
return $this->deployment;
}
/**
* Sets type for the build event.
*
* @param string $type Can be `BUILD_TYPE_DEPLOYMENT` or `BUILD_TYPE_RETRY`.
* @return self
*/
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
/**
* Returns set type for the function event.
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Prepare payload for queue.
*
* @return array
*/
protected function preparePayload(): array
{
$platform = $this->platform;
if (empty($platform)) {
$platform = Config::getParam('platform', []);
}
return [
'project' => $this->project,
'resource' => $this->resource,
'deployment' => $this->deployment,
'type' => $this->type,
'template' => $this->template,
'platform' => $platform,
];
}
/**
* Resets event.
*
* @return self
*/
public function reset(): self
{
$this->type = '';
$this->resource = null;
$this->deployment = null;
$this->template = null;
$this->platform = [];
parent::reset();
return $this;
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Appwrite\Event\Message;
use Utopia\Config\Config;
use Utopia\Database\Document;
final class Build extends Base
{
public function __construct(
public readonly Document $project,
public readonly Document $resource,
public readonly Document $deployment,
public readonly string $type,
public readonly ?Document $template = null,
public readonly array $platform = [],
) {
}
public function toArray(): array
{
$platform = !empty($this->platform) ? $this->platform : Config::getParam('platform', []);
return [
'project' => $this->project->getArrayCopy(),
'resource' => $this->resource->getArrayCopy(),
'deployment' => $this->deployment->getArrayCopy(),
'type' => $this->type,
'template' => $this->template?->getArrayCopy(),
'platform' => $platform,
];
}
public static function fromArray(array $data): static
{
return new self(
project: new Document($data['project'] ?? []),
resource: new Document($data['resource'] ?? []),
deployment: new Document($data['deployment'] ?? []),
type: $data['type'] ?? '',
template: !empty($data['template']) ? new Document($data['template']) : null,
platform: $data['platform'] ?? [],
);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Appwrite\Event\Publisher;
use Appwrite\Event\Message\Build as BuildMessage;
use Utopia\Queue\Publisher;
use Utopia\Queue\Queue;
readonly class Build extends Base
{
public function __construct(
Publisher $publisher,
protected Queue $queue
) {
parent::__construct($publisher);
}
public function enqueue(BuildMessage $message, ?Queue $queue = null): string|bool
{
return $this->publish($queue ?? $this->queue, $message);
}
public function getSize(bool $failed = false, ?Queue $queue = null): int
{
return $this->getQueueSize($queue ?? $this->queue, $failed);
}
}
+6
View File
@@ -178,6 +178,7 @@ class Exception extends \Exception
public const string FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
public const string FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing';
public const string FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout';
public const string FUNCTION_ASYNCHRONOUS_TIMEOUT = 'function_asynchronous_timeout';
public const string FUNCTION_TEMPLATE_NOT_FOUND = 'function_template_not_found';
public const string FUNCTION_RUNTIME_NOT_DETECTED = 'function_runtime_not_detected';
public const string FUNCTION_EXECUTE_PERMISSION_MISSING = 'function_execute_permission_missing';
@@ -192,6 +193,7 @@ class Exception extends \Exception
public const string BUILD_ALREADY_COMPLETED = 'build_already_completed';
public const string BUILD_CANCELED = 'build_canceled';
public const string BUILD_FAILED = 'build_failed';
public const string BUILD_TIMEOUT = 'build_timeout';
/** Execution */
public const string EXECUTION_NOT_FOUND = 'execution_not_found';
@@ -346,6 +348,10 @@ class Exception extends \Exception
public const string MIGRATION_IN_PROGRESS = 'migration_in_progress';
public const string MIGRATION_PROVIDER_ERROR = 'migration_provider_error';
public const string MIGRATION_DATABASE_TYPE_UNSUPPORTED = 'migration_database_type_unsupported';
public const string MIGRATION_SOURCE_PROJECT_ID_REQUIRED = 'migration_source_project_id_required';
public const string MIGRATION_SOURCE_PROJECT_NOT_FOUND = 'migration_source_project_not_found';
public const string MIGRATION_SOURCE_TYPE_INVALID = 'migration_source_type_invalid';
public const string MIGRATION_DESTINATION_TYPE_INVALID = 'migration_destination_type_invalid';
/** Realtime */
public const string REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid';
+1
View File
@@ -96,6 +96,7 @@ abstract class Migration
'1.9.1' => 'V24',
'1.9.2' => 'V24',
'1.9.3' => 'V24',
'1.9.4' => 'V24',
];
/**
+20 -13
View File
@@ -2,7 +2,8 @@
namespace Appwrite\Platform\Modules\Compute;
use Appwrite\Event\Build;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\Filter\BranchDomain as BranchDomainFilter;
use Appwrite\Platform\Action;
@@ -57,7 +58,7 @@ class Base extends Action
return $allowedSpecifications[0] ?? APP_COMPUTE_SPECIFICATION_DEFAULT;
}
public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document
public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, BuildPublisher $publisherForBuilds, Document $template, GitHub $github, bool $activate, array $platform = [], string $referenceType = 'branch', string $reference = ''): Document
{
$deploymentId = ID::unique();
$entrypoint = $function->getAttribute('entrypoint', '');
@@ -150,16 +151,19 @@ class Base extends Action
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
]));
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $function,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
template: $template,
platform: $platform,
));
return $deployment;
}
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, Authorization $authorization, array $platform, string $referenceType = 'branch', string $reference = ''): Document
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, BuildPublisher $publisherForBuilds, Document $template, GitHub $github, bool $activate, Authorization $authorization, array $platform, string $referenceType = 'branch', string $reference = ''): Document
{
$deploymentId = ID::unique();
$providerInstallationId = $installation->getAttribute('providerInstallationId', '');
@@ -358,11 +362,14 @@ class Base extends Action
$this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform, $authorization);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
->setDeployment($deployment)
->setTemplate($template);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $site,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
template: $template,
platform: $platform,
));
return $deployment;
}
@@ -241,6 +241,10 @@ abstract class Action extends UtopiaAction
? UtopiaResponse::MODEL_ATTRIBUTE_INTEGER
: UtopiaResponse::MODEL_COLUMN_INTEGER,
Database::VAR_BIGINT => $isCollections
? UtopiaResponse::MODEL_ATTRIBUTE_BIGINT
: UtopiaResponse::MODEL_COLUMN_BIGINT,
Database::VAR_FLOAT => $isCollections
? UtopiaResponse::MODEL_ATTRIBUTE_FLOAT
: UtopiaResponse::MODEL_COLUMN_FLOAT,
@@ -540,6 +544,7 @@ abstract class Action extends UtopiaAction
switch ($attribute->getAttribute('format')) {
case APP_DATABASE_ATTRIBUTE_INT_RANGE:
case APP_DATABASE_ATTRIBUTE_BIGINT_RANGE:
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE:
$min ??= $attribute->getAttribute('formatOptions')['min'];
$max ??= $attribute->getAttribute('formatOptions')['max'];
@@ -548,14 +553,15 @@ abstract class Action extends UtopiaAction
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
}
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) {
$validator = new Range($min, $max, Database::VAR_INTEGER);
} else {
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_FLOAT_RANGE) {
$validator = new Range($min, $max, Database::VAR_FLOAT);
if (!is_null($default)) {
$default = \floatval($default);
}
} else {
// intRange and bigintRange share the same integer range semantics
$validator = new Range($min, $max, Range::TYPE_INTEGER);
}
if (!is_null($default) && !$validator->isValid($default)) {
@@ -0,0 +1,117 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
class Create extends Action
{
public static function getName(): string
{
return 'createBigIntAttribute';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint')
->desc('Create bigint attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/databases/create-bigint-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: $this->getResponseModel(),
)
],
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'tablesDB.createBigIntColumn',
),
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$min ??= \PHP_INT_MIN;
$max ??= \PHP_INT_MAX;
if ($min > $max) {
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
}
$validator = new Range($min, $max, Range::TYPE_INTEGER);
if (!\is_null($default) && !$validator->isValid($default)) {
throw new Exception($this->getInvalidValueException(), $validator->getDescription());
}
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_BIGINT,
'size' => 8,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$attribute->setAttribute('min', \intval($formatOptions['min']));
$attribute->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($attribute, $this->getResponseModel());
}
}
@@ -0,0 +1,106 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends Action
{
public static function getName(): string
{
return 'updateBigIntAttribute';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint/:key')
->desc('Update bigint attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/databases/update-bigint-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: $this->getResponseModel(),
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'tablesDB.updateBigIntColumn',
),
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.')
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_BIGINT,
default: $default,
required: $required,
min: $min,
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$attribute->setAttribute('min', \intval($formatOptions['min']));
$attribute->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($attribute, $this->getResponseModel());
}
}
@@ -290,13 +290,15 @@ class Create extends Action
}
if (isset($attribute['min']) || isset($attribute['max'])) {
$format = $type === Database::VAR_INTEGER
? APP_DATABASE_ATTRIBUTE_INT_RANGE
: APP_DATABASE_ATTRIBUTE_FLOAT_RANGE;
$format = match($type) {
Database::VAR_INTEGER => APP_DATABASE_ATTRIBUTE_INT_RANGE,
Database::VAR_BIGINT => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
default => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
};
$formatOptions = [
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
];
}
@@ -0,0 +1,70 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as BigIntCreate;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Create extends BigIntCreate
{
public static function getName(): string
{
return 'createBigIntColumn';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_COLUMN_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint')
->desc('Create bigint column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/tablesdb/create-bigint-column.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: $this->getResponseModel(),
)
]
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
}
@@ -0,0 +1,71 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as BigIntUpdate;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends BigIntUpdate
{
public static function getName(): string
{
return 'updateBigIntColumn';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_COLUMN_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint/:key')
->desc('Update bigint column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/tablesdb/update-bigint-column.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: $this->getResponseModel(),
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.')
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Column Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
}
@@ -2,6 +2,8 @@
namespace Appwrite\Platform\Modules\Databases\Services\Registry;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as CreateBigIntAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as UpdateBigIntAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as CreateBooleanAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as UpdateBooleanAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as CreateDatetimeAttribute;
@@ -171,6 +173,10 @@ class Legacy extends Base
$service->addAction(CreateIntegerAttribute::getName(), new CreateIntegerAttribute());
$service->addAction(UpdateIntegerAttribute::getName(), new UpdateIntegerAttribute());
// Attribute: BigInt
$service->addAction(CreateBigIntAttribute::getName(), new CreateBigIntAttribute());
$service->addAction(UpdateBigIntAttribute::getName(), new UpdateBigIntAttribute());
// Attribute: IP
$service->addAction(CreateIPAttribute::getName(), new CreateIPAttribute());
$service->addAction(UpdateIPAttribute::getName(), new UpdateIPAttribute());
@@ -5,6 +5,8 @@ namespace Appwrite\Platform\Modules\Databases\Services\Registry;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Create as CreateTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Delete as DeleteTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Get as GetTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Create as CreateBigInt;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Update as UpdateBigInt;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Create as CreateBoolean;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Update as UpdateBoolean;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Datetime\Create as CreateDatetime;
@@ -151,6 +153,10 @@ class TablesDB extends Base
$service->addAction(CreateInteger::getName(), new CreateInteger());
$service->addAction(UpdateInteger::getName(), new UpdateInteger());
// Column: BigInt
$service->addAction(CreateBigInt::getName(), new CreateBigInt());
$service->addAction(UpdateBigInt::getName(), new UpdateBigInt());
// Column: IP
$service->addAction(CreateIP::getName(), new CreateIP());
$service->addAction(UpdateIP::getName(), new UpdateIP());
@@ -2,8 +2,9 @@
namespace Appwrite\Platform\Modules\Functions\Http\Deployments;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -87,9 +88,10 @@ class Create extends Action
->inject('project')
->inject('deviceForFunctions')
->inject('deviceForLocal')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('plan')
->inject('authorization')
->inject('platform')
->callback($this->action(...));
}
@@ -106,9 +108,10 @@ class Create extends Action
Document $project,
Device $deviceForFunctions,
Device $deviceForLocal,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
array $plan,
Authorization $authorization
Authorization $authorization,
array $platform
) {
$activate = \strval($activate) === 'true' || \strval($activate) === '1';
@@ -272,10 +275,13 @@ class Create extends Action
}
// Start the build
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $function,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
platform: $platform,
));
} else {
if ($deployment->isEmpty()) {
$deployment = $dbForProject->createDocument('deployments', new Document([
@@ -2,8 +2,9 @@
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Duplicate;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
@@ -61,8 +62,10 @@ class Create extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('deviceForFunctions')
->inject('project')
->inject('platform')
->callback($this->action(...));
}
@@ -73,8 +76,10 @@ class Create extends Action
Response $response,
Database $dbForProject,
Event $queueForEvents,
Build $queueForBuilds,
Device $deviceForFunctions
BuildPublisher $publisherForBuilds,
Device $deviceForFunctions,
Document $project,
array $platform
) {
$function = $dbForProject->getDocument('functions', $functionId);
@@ -127,10 +132,13 @@ class Create extends Action
'latestDeploymentStatus' => $function->getAttribute('latestDeploymentStatus'),
]));
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $function,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
platform: $platform,
));
$queueForEvents
->setParam('functionId', $function->getId())
@@ -2,8 +2,9 @@
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Template;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -76,9 +77,10 @@ class Create extends Base
->inject('dbForPlatform')
->inject('queueForEvents')
->inject('project')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('gitHub')
->inject('authorization')
->inject('platform')
->callback($this->action(...));
}
@@ -96,9 +98,10 @@ class Create extends Base
Database $dbForPlatform,
Event $queueForEvents,
Document $project,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
GitHub $github,
Authorization $authorization
Authorization $authorization,
array $platform
) {
$function = $dbForProject->getDocument('functions', $functionId);
@@ -127,10 +130,11 @@ class Create extends Base
project: $project,
installation: $installation,
dbForProject: $dbForProject,
queueForBuilds: $queueForBuilds,
publisherForBuilds: $publisherForBuilds,
template: $template,
github: $github,
activate: $activate,
platform: $platform,
referenceType: $type,
reference: $reference
);
@@ -184,11 +188,14 @@ class Create extends Base
$this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform, $authorization);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $function,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
template: $template,
platform: $platform,
));
$queueForEvents
->setParam('functionId', $function->getId())
@@ -2,8 +2,8 @@
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Vcs;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -70,8 +70,9 @@ class Create extends Base
->inject('dbForPlatform')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('gitHub')
->inject('platform')
->callback($this->action(...));
}
@@ -86,8 +87,9 @@ class Create extends Base
Database $dbForPlatform,
Document $project,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
GitHub $github,
array $platform,
) {
$function = $dbForProject->getDocument('functions', $functionId);
@@ -105,10 +107,11 @@ class Create extends Base
project: $project,
installation: $installation,
dbForProject: $dbForProject,
queueForBuilds: $queueForBuilds,
publisherForBuilds: $publisherForBuilds,
template: $template,
github: $github,
activate: $activate,
platform: $platform,
reference: $reference,
referenceType: $type
);
@@ -17,6 +17,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Usage\Context;
use Appwrite\Utopia\Database\Documents\User;
use Appwrite\Utopia\Response;
use Executor\Exception\Timeout as ExecutorTimeout;
use Executor\Executor;
use MaxMind\Db\Reader;
use Utopia\Auth\Proofs\Token;
@@ -417,25 +418,29 @@ class Create extends Base
$source = $deployment->getAttribute('buildPath', '');
$extension = str_ends_with($source, '.tar') ? 'tar' : 'tar.gz';
$command = $version === 'v2' ? '' : "cp /tmp/code.$extension /mnt/code/code.$extension && nohup helpers/start.sh \"$command\"";
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $function->getAttribute('timeout', 0),
image: $runtime['image'],
source: $source,
entrypoint: $deployment->getAttribute('entrypoint', ''),
version: $version,
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $command,
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
logging: $function->getAttribute('logging', true),
requestTimeout: 30
);
try {
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $function->getAttribute('timeout', 0),
image: $runtime['image'],
source: $source,
entrypoint: $deployment->getAttribute('entrypoint', ''),
version: $version,
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $command,
cpus: $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
memory: $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
logging: $function->getAttribute('logging', true),
requestTimeout: 30
);
} catch (ExecutorTimeout $th) {
throw new AppwriteException(AppwriteException::FUNCTION_SYNCHRONOUS_TIMEOUT, previous: $th);
}
$headersFiltered = [];
foreach ($executionResponse['headers'] as $key => $value) {
@@ -2,9 +2,10 @@
namespace Appwrite\Platform\Modules\Functions\Http\Functions;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Event\Realtime;
use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Event\Webhook;
@@ -115,7 +116,7 @@ class Create extends Base
->inject('timelimit')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('queueForRealtime')
->inject('queueForWebhooks')
->inject('queueForFunctions')
@@ -157,7 +158,7 @@ class Create extends Base
callable $timelimit,
Document $project,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
Realtime $queueForRealtime,
Webhook $queueForWebhooks,
Func $queueForFunctions,
@@ -326,10 +327,11 @@ class Create extends Base
project: $project,
installation: $installation,
dbForProject: $dbForProject,
queueForBuilds: $queueForBuilds,
publisherForBuilds: $publisherForBuilds,
template: $template,
github: $github,
activate: true,
platform: $platform,
reference: $providerBranch,
referenceType: 'branch'
);
@@ -367,11 +369,14 @@ class Create extends Base
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
]));
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $function,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
template: $template,
platform: $platform,
));
}
$functionsDomain = $platform['functionsDomain'];
@@ -2,8 +2,8 @@
namespace Appwrite\Platform\Modules\Functions\Http\Functions;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
@@ -105,11 +105,12 @@ class Update extends Base
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('dbForPlatform')
->inject('gitHub')
->inject('executor')
->inject('authorization')
->inject('platform')
->callback($this->action(...));
}
@@ -139,11 +140,12 @@ class Update extends Base
Database $dbForProject,
Document $project,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
Database $dbForPlatform,
GitHub $github,
Executor $executor,
Authorization $authorization
Authorization $authorization,
array $platform
) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@@ -281,7 +283,7 @@ class Update extends Base
// Redeploy logic
if (!$isConnected && !empty($providerRepositoryId)) {
$this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true);
$this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $publisherForBuilds, new Document(), $github, true, $platform);
}
// Inform scheduler if function is still active
@@ -2,11 +2,13 @@
namespace Appwrite\Platform\Modules\Functions\Http\Variables;
use Appwrite\Event\Event as QueueEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -38,6 +40,7 @@ class Create extends Base
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('event', 'variables.[variableId].create')
->label('audits.event', 'variable.create')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
@@ -56,10 +59,12 @@ class Create extends Base
]
))
->param('functionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Function unique ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', false, ['dbForProject'])
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true)
->inject('response')
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('project')
@@ -69,10 +74,12 @@ class Create extends Base
public function action(
string $functionId,
string $variableId,
string $key,
string $value,
bool $secret,
Response $response,
QueueEvent $queueForEvents,
Database $dbForProject,
Database $dbForPlatform,
Document $project,
@@ -84,7 +91,7 @@ class Create extends Base
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$variableId = ID::unique();
$variableId = ($variableId === 'unique()') ? ID::unique() : $variableId;
$teamId = $project->getAttribute('teamId', '');
$variable = new Document([
@@ -120,6 +127,8 @@ class Create extends Base
'active' => $schedule->getAttribute('active'),
])));
$queueForEvents->setParam('variableId', $variable->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($variable, Response::MODEL_VARIABLE);
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Modules\Functions\Http\Variables;
use Appwrite\Event\Event as QueueEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -35,6 +36,7 @@ class Delete extends Base
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('event', 'variables.[variableId].delete')
->label('audits.event', 'variable.delete')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
@@ -56,6 +58,7 @@ class Delete extends Base
->param('functionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Function unique ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('response')
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
@@ -66,6 +69,7 @@ class Delete extends Base
string $functionId,
string $variableId,
Response $response,
QueueEvent $queueForEvents,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization
@@ -98,6 +102,8 @@ class Delete extends Base
'active' => $schedule->getAttribute('active'),
])));
$queueForEvents->setParam('variableId', $variable->getId());
$response->noContent();
}
}
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Modules\Functions\Http\Variables;
use Appwrite\Event\Event as QueueEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -38,6 +39,7 @@ class Update extends Base
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('event', 'variables.[variableId].update')
->label('audits.event', 'variable.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
@@ -57,10 +59,11 @@ class Update extends Base
))
->param('functionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Function unique ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('key', null, new Nullable(new Text(255, 0)), 'Variable key. Max length: 255 chars.', true)
->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true)
->inject('response')
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
@@ -70,10 +73,11 @@ class Update extends Base
public function action(
string $functionId,
string $variableId,
string $key,
?string $key,
?string $value,
?bool $secret,
Response $response,
QueueEvent $queueForEvents,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization
@@ -93,19 +97,27 @@ class Update extends Base
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
}
$variable
->setAttribute('key', $key)
->setAttribute('value', $value ?? $variable->getAttribute('value'))
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key, 'function']));
if (\is_null($key) && \is_null($value) && \is_null($secret)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID);
}
$updates = new Document();
if (!\is_null($key)) {
$updates->setAttribute('key', $key);
$updates->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key, 'function']));
}
if (!\is_null($value)) {
$updates->setAttribute('value', $value);
}
if (!\is_null($secret)) {
$updates->setAttribute('secret', $secret);
}
try {
$dbForProject->updateDocument('variables', $variable->getId(), new Document([
'key' => $key,
'value' => $value ?? $variable->getAttribute('value'),
'secret' => $secret ?? $variable->getAttribute('secret'),
'search' => implode(' ', [$variableId, $function->getId(), $key, 'function']),
]));
$variable = $dbForProject->updateDocument('variables', $variable->getId(), $updates);
} catch (DuplicateException $th) {
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
}
@@ -125,6 +137,8 @@ class Update extends Base
'active' => $schedule->getAttribute('active'),
])));
$queueForEvents->setParam('variableId', $variable->getId());
$response->dynamic($variable, Response::MODEL_VARIABLE);
}
}
@@ -7,12 +7,18 @@ use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\Queries\Variables;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Boolean;
class XList extends Base
{
@@ -51,22 +57,74 @@ class XList extends Base
)
)
->param('functionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Function unique ID.', false, ['dbForProject'])
->param('queries', [], new Variables(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Variables::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForProject')
->callback($this->action(...));
}
public function action(string $functionId, Response $response, Database $dbForProject)
{
/**
* @param array<string> $queries
*/
public function action(
string $functionId,
array $queries,
bool $includeTotal,
Response $response,
Database $dbForProject
) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$queries[] = Query::equal('resourceType', ['function']);
$queries[] = Query::equal('resourceInternalId', [$function->getSequence()]);
$queries[] = Query::orderAsc();
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$variableId = $cursor->getValue();
$cursorDocument = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('resourceType', ['function']),
Query::equal('resourceInternalId', [$function->getSequence()]),
]);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Variable '{$variableId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$variables = $dbForProject->find('variables', $queries);
$total = $includeTotal ? $dbForProject->count('variables', $filterQueries, APP_LIMIT_COUNT) : 0;
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'variables' => $function->getAttribute('vars', []),
'total' => \count($function->getAttribute('vars', [])),
'variables' => $variables,
'total' => $total,
]), Response::MODEL_VARIABLE_LIST);
}
}
@@ -10,11 +10,13 @@ use Appwrite\Event\Publisher\Screenshot;
use Appwrite\Event\Publisher\Usage as UsagePublisher;
use Appwrite\Event\Realtime;
use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Filter\BranchDomain as BranchDomainFilter;
use Appwrite\Usage\Context;
use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Vcs\Comment;
use Exception;
use Executor\Exception\Timeout as ExecutorTimeout;
use Executor\Executor;
use Swoole\Coroutine as Co;
use Utopia\Cache\Cache;
@@ -34,6 +36,7 @@ use Utopia\Detector\Detector\Rendering;
use Utopia\Logger\Log;
use Utopia\Platform\Action;
use Utopia\Queue\Message;
use Utopia\Span\Span;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Local;
use Utopia\System\System;
@@ -183,6 +186,12 @@ class Builds extends Action
array $platform,
int $timeout
): void {
Span::add('projectId', $project->getId());
Span::add('resourceId', $resource->getId());
Span::add('resourceType', $resource->getCollection());
Span::add('deploymentId', $deployment->getId());
Span::add('timeout', $timeout);
Console::info('Deployment action started');
$startTime = DateTime::now();
@@ -223,8 +232,12 @@ class Builds extends Action
$version = $this->getVersion($resource);
$runtime = $this->getRuntime($resource, $version);
Span::add('runtime', $resource->getAttribute($resource->getCollection() === 'sites' ? 'buildRuntime' : 'runtime', ''));
Span::add('version', $version);
$spec = Config::getParam('specifications')[$resource->getAttribute('buildSpecification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
Span::add('cpus', (float) ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT));
Span::add('memory', (int) ($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT));
// Realtime preparation
$event = "{$resource->getCollection()}.[{$resourceKey}].deployments.[deploymentId].update";
@@ -720,6 +733,9 @@ class Builds extends Action
);
Console::log('createRuntime finished');
} catch (ExecutorTimeout $error) {
Console::warning('createRuntime timed out');
$err = new AppwriteException(AppwriteException::BUILD_TIMEOUT, previous: $error);
} catch (\Throwable $error) {
Console::warning('createRuntime failed');
$err = $error;
@@ -1147,13 +1163,11 @@ class Builds extends Action
$message = \str_replace('{APPWRITE_DETECTION_SEPARATOR_START}', '', $message);
$message = \str_replace('{APPWRITE_DETECTION_SEPARATOR_END}', '', $message);
// Combine with previous logs if deployment got past build process
$previousLogs = '';
if (! is_null($deployment->getAttribute('buildSize', null))) {
$previousLogs = $deployment->getAttribute('buildLogs', '');
if (! empty($previousLogs)) {
$message = $previousLogs . "\n" . $message;
}
// Append error to whatever build logs were already streamed
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$previousLogs = $deployment->getAttribute('buildLogs', '');
if (! empty($previousLogs)) {
$message = $previousLogs . "\n" . $message;
}
$endTime = DateTime::now();
@@ -2,7 +2,7 @@
namespace Appwrite\Platform\Modules\Health\Http\Health\Queue\Builds;
use Appwrite\Event\Build;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Platform\Modules\Health\Http\Health\Queue\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -42,16 +42,16 @@ class Get extends Base
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('response')
->callback($this->action(...));
}
public function action(int|string $threshold, Build $queueForBuilds, Response $response): void
public function action(int|string $threshold, BuildPublisher $publisherForBuilds, Response $response): void
{
$threshold = (int) $threshold;
$size = $queueForBuilds->getSize();
$size = $publisherForBuilds->getSize();
$this->assertQueueThreshold($size, $threshold);
@@ -2,7 +2,6 @@
namespace Appwrite\Platform\Modules\Health\Http\Health\Queue\Failed;
use Appwrite\Event\Build;
use Appwrite\Event\Database;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
@@ -10,6 +9,7 @@ use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Publisher\Audit;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Event\Publisher\Certificate;
use Appwrite\Event\Publisher\Migration as MigrationPublisher;
use Appwrite\Event\Publisher\Screenshot;
@@ -83,7 +83,7 @@ class Get extends Base
->inject('publisherForUsage')
->inject('queueForWebhooks')
->inject('publisherForCertificates')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('queueForMessaging')
->inject('publisherForMigrations')
->inject('publisherForScreenshots')
@@ -103,7 +103,7 @@ class Get extends Base
UsagePublisher $publisherForUsage,
Webhook $queueForWebhooks,
Certificate $publisherForCertificates,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
Messaging $queueForMessaging,
MigrationPublisher $publisherForMigrations,
Screenshot $publisherForScreenshots,
@@ -120,7 +120,7 @@ class Get extends Base
System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME) => $publisherForUsage,
System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME) => $queueForWebhooks,
System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME) => $publisherForCertificates,
System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME) => $queueForBuilds,
System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME) => $publisherForBuilds,
System::getEnv('_APP_SCREENSHOTS_QUEUE_NAME', Event::SCREENSHOTS_QUEUE_NAME) => $publisherForScreenshots,
System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME) => $queueForMessaging,
System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME) => $publisherForMigrations,
@@ -13,6 +13,7 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\UID;
use Utopia\Migration\Destinations\OnDuplicate;
use Utopia\Migration\Sources\Appwrite as AppwriteSource;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
@@ -57,6 +58,7 @@ class Create extends Action
->param('endpoint', '', new URL(), 'Source Appwrite endpoint')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Source Project ID', false, ['dbForProject'])
->param('apiKey', '', new Text(512), 'Source API Key')
->param('onDuplicate', OnDuplicate::Fail->value, new WhiteList(OnDuplicate::values()), 'Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row.', true)
->inject('response')
->inject('dbForProject')
->inject('project')
@@ -71,6 +73,7 @@ class Create extends Action
string $endpoint,
string $projectId,
string $apiKey,
string $onDuplicate,
Response $response,
Database $dbForProject,
Document $project,
@@ -93,6 +96,9 @@ class Create extends Action
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => [],
'options' => [
'onDuplicate' => $onDuplicate,
],
]));
$queueForEvents->setParam('migrationId', $migration->getId());
@@ -20,6 +20,7 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Migration\Destinations\OnDuplicate;
use Utopia\Migration\Resource;
use Utopia\Migration\Sources\Appwrite as AppwriteSource;
use Utopia\Migration\Sources\CSV;
@@ -29,6 +30,7 @@ use Utopia\Platform\Scope\HTTP;
use Utopia\Storage\Device;
use Utopia\System\System;
use Utopia\Validator\Boolean;
use Utopia\Validator\WhiteList;
class Create extends Action
{
@@ -67,6 +69,7 @@ class Create extends Action
->param('fileId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'File ID.', false, ['dbForProject'])
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.')
->param('internalFile', false, new Boolean(), 'Is the file stored in an internal bucket?', true)
->param('onDuplicate', OnDuplicate::Fail->value, new WhiteList(OnDuplicate::values()), 'Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row.', true)
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
@@ -85,6 +88,7 @@ class Create extends Action
string $fileId,
string $resourceId,
bool $internalFile,
string $onDuplicate,
Response $response,
Database $dbForProject,
Database $dbForPlatform,
@@ -183,6 +187,7 @@ class Create extends Action
'options' => [
'path' => $newPath,
'size' => $fileSize,
'onDuplicate' => $onDuplicate,
],
]));
@@ -20,6 +20,7 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Migration\Destinations\OnDuplicate;
use Utopia\Migration\Resource;
use Utopia\Migration\Sources\Appwrite as AppwriteSource;
use Utopia\Migration\Sources\JSON as JSONSource;
@@ -29,6 +30,7 @@ use Utopia\Platform\Scope\HTTP;
use Utopia\Storage\Device;
use Utopia\System\System;
use Utopia\Validator\Boolean;
use Utopia\Validator\WhiteList;
class Create extends Action
{
@@ -66,6 +68,7 @@ class Create extends Action
->param('fileId', '', new UID(), 'File ID.')
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.')
->param('internalFile', false, new Boolean(), 'Is the file stored in an internal bucket?', true)
->param('onDuplicate', OnDuplicate::Fail->value, new WhiteList(OnDuplicate::values()), 'Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row.', true)
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
@@ -84,6 +87,7 @@ class Create extends Action
string $fileId,
string $resourceId,
bool $internalFile,
string $onDuplicate,
Response $response,
Database $dbForProject,
Database $dbForPlatform,
@@ -183,6 +187,7 @@ class Create extends Action
'options' => [
'path' => $newPath,
'size' => $fileSize,
'onDuplicate' => $onDuplicate,
],
]));
@@ -59,7 +59,7 @@ class Create extends Base
],
))
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', optional: false)
->param('duration', null, new Range(1, 3600), 'Time in seconds before ephemeral key expires. Maximum duration is 3600 seconds.', optional: false)
->param('duration', null, new Range(1, 3600), 'Time in seconds before ephemeral key expires. Maximum duration is 3600 seconds.', optional: false, example: 600)
->inject('response')
->inject('queueForEvents')
->inject('project')
@@ -11,7 +11,7 @@ use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
class Get extends Action
{
@@ -86,28 +86,28 @@ class Get extends Action
)
]
))
->param('provider', '', new Text(128), 'OAuth2 provider key. For example: github, google, apple.')
->param('providerId', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders', [])), true), 'OAuth2 provider key. For example: github, google, apple.', aliases: ['provider'])
->inject('response')
->inject('project')
->callback($this->action(...));
}
public function action(
string $provider,
string $providerId,
Response $response,
Document $project,
): void {
$providers = Config::getParam('oAuthProviders', []);
if (!\array_key_exists($provider, $providers) || !($providers[$provider]['enabled'] ?? false)) {
if (!\array_key_exists($providerId, $providers) || !($providers[$providerId]['enabled'] ?? false)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$actions = Base::getProviderActions();
if (!isset($actions[$provider])) {
if (!isset($actions[$providerId])) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$updateClass = $actions[$provider];
$updateClass = $actions[$providerId];
$action = new $updateClass();
$response->dynamic($action->buildReadResponse($project), $updateClass::getResponseModel());
@@ -82,13 +82,13 @@ class Update extends Base
'hint' => '',
],
[
'$id' => 'tokenUrl',
'$id' => 'tokenURL',
'name' => 'Token URL',
'example' => 'https://myoauth.com/oauth2/token',
'hint' => '',
],
[
'$id' => 'userInfoUrl',
'$id' => 'userInfoURL',
'name' => 'User Info URL',
'example' => 'https://myoauth.com/oauth2/userinfo',
'hint' => '',
@@ -127,8 +127,8 @@ class Update extends Base
->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true)
->param('wellKnownURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect well-known configuration URL. When provided, authorization, token, and user info endpoints can be discovered automatically. For example: https://myoauth.com/.well-known/openid-configuration', optional: true)
->param('authorizationURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect authorization endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/authorize', optional: true)
->param('tokenUrl', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect token endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/token', optional: true)
->param('userInfoUrl', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect user info endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/userinfo', optional: true)
->param('tokenURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect token endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/token', optional: true, aliases: ['tokenUrl'])
->param('userInfoURL', null, new Nullable(new URL(allowEmpty: true)), 'OpenID Connect user info endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/userinfo', optional: true, aliases: ['userInfoUrl'])
->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true)
->inject('response')
->inject('dbForPlatform')
@@ -151,8 +151,8 @@ class Update extends Base
static::getClientSecretParamName() => '',
'wellKnownURL' => $decoded['wellKnownEndpoint'] ?? '',
'authorizationURL' => $decoded['authorizationEndpoint'] ?? '',
'tokenUrl' => $decoded['tokenEndpoint'] ?? '',
'userInfoUrl' => $decoded['userInfoEndpoint'] ?? '',
'tokenURL' => $decoded['tokenEndpoint'] ?? '',
'userInfoURL' => $decoded['userInfoEndpoint'] ?? '',
]);
}
@@ -174,8 +174,8 @@ class Update extends Base
?string $clientSecret,
?string $wellKnownURL,
?string $authorizationURL,
?string $tokenUrl,
?string $userInfoUrl,
?string $tokenURL,
?string $userInfoURL,
?bool $enabled,
Response $response,
Database $dbForPlatform,
@@ -201,8 +201,8 @@ class Update extends Base
'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''),
'wellKnownEndpoint' => $wellKnownURL ?? ($existing['wellKnownEndpoint'] ?? ''),
'authorizationEndpoint' => $authorizationURL ?? ($existing['authorizationEndpoint'] ?? ''),
'tokenEndpoint' => $tokenUrl ?? ($existing['tokenEndpoint'] ?? ''),
'userInfoEndpoint' => $userInfoUrl ?? ($existing['userInfoEndpoint'] ?? ''),
'tokenEndpoint' => $tokenURL ?? ($existing['tokenEndpoint'] ?? ''),
'userInfoEndpoint' => $userInfoURL ?? ($existing['userInfoEndpoint'] ?? ''),
];
// When enabling, require either wellKnownEndpoint alone, or all three
@@ -215,7 +215,7 @@ class Update extends Base
&& !empty($merged['userInfoEndpoint']);
if (!$hasWellKnown && !$hasAllDiscovery) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Enabling OpenID Connect requires either wellKnownURL, or all of authorizationURL, tokenUrl, and userInfoUrl.');
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Enabling OpenID Connect requires either wellKnownURL, or all of authorizationURL, tokenURL, and userInfoURL.');
}
}
@@ -2,14 +2,21 @@
namespace Appwrite\Platform\Modules\Project\Http\Project\OAuth2;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Boolean;
class XList extends Action
{
@@ -43,15 +50,28 @@ class XList extends Action
)
]
))
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('project')
->callback($this->action(...));
}
/**
* @param array<string> $queries
*/
public function action(
array $queries,
bool $includeTotal,
Response $response,
Document $project,
): void {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$providers = Config::getParam('oAuthProviders', []);
$actions = Base::getProviderActions();
@@ -66,8 +86,16 @@ class XList extends Action
$documents[] = $action->buildReadResponse($project);
}
$total = $includeTotal ? \count($documents) : 0;
$grouped = Query::groupByType($queries);
$offset = $grouped['offset'] ?? 0;
$limit = $grouped['limit'] ?? null;
$documents = \array_slice($documents, $offset, $limit);
$response->dynamic(new Document([
'total' => \count($documents),
'total' => $total,
'providers' => $documents,
]), Response::MODEL_OAUTH2_PROVIDER_LIST);
}
@@ -27,7 +27,7 @@ class Get extends Action
->setHttpPath('/v1/project/policies/:policyId')
->desc('Get project policy')
->groups(['api', 'project'])
->label('scope', 'policies.read')
->label('scope', ['policies.read', 'project.policies.read'])
->label('sdk', new Method(
namespace: 'project',
group: 'policies',
@@ -31,7 +31,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/memberships-privacy')
->desc('Update membership privacy policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -31,7 +31,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/password-dictionary')
->desc('Update password dictionary policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -32,7 +32,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/password-history')
->desc('Update password history policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -31,7 +31,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/personal-data')
->desc('Update password personal data policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -31,7 +31,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/session-alerts')
->desc('Update session alert policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -31,7 +31,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/duration')
->desc('Update session duration policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -31,7 +31,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/session-invalidation')
->desc('Update session invalidation policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -32,7 +32,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/max-sessions')
->desc('Update session limit policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -32,7 +32,7 @@ class Update extends Action
->httpAlias('/v1/projects/:projectId/auth/limit')
->desc('Update user limit policy')
->groups(['api', 'project'])
->label('scope', 'policies.write')
->label('scope', ['policies.write', 'project.policies.write'])
->label('event', 'projects.[projectId].policies.[policy].update')
->label('audits.event', 'projects.[projectId].policies.[policy].update')
->label('audits.resource', 'project/{response.$id}')
@@ -33,7 +33,7 @@ class XList extends Action
->setHttpPath('/v1/project/policies')
->desc('List project policies')
->groups(['api', 'project'])
->label('scope', 'policies.read')
->label('scope', ['policies.read', 'project.policies.read'])
->label('sdk', new Method(
namespace: 'project',
group: 'policies',
@@ -53,7 +53,7 @@ class Create extends Action
)
],
))
->param('variableId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', false, ['dbForProject'])
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.')
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.')
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
@@ -72,7 +72,7 @@ class Create extends Action
QueueEvent $queueForEvents,
Database $dbForProject,
) {
$variableId = ($variableId == 'unique()') ? ID::unique() : $variableId;
$variableId = ($variableId === 'unique()') ? ID::unique() : $variableId;
$variable = new Document([
'$id' => $variableId,
@@ -51,7 +51,7 @@ class Delete extends Action
],
contentType: ContentType::NONE
))
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -44,7 +44,7 @@ class Get extends Action
)
]
))
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->callback($this->action(...));
@@ -52,7 +52,7 @@ class Update extends Action
)
]
))
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->param('key', null, new Nullable(new Text(255, 0)), 'Variable key. Max length: 255 chars.', true)
->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Logger\Log;
use Utopia\Platform\Scope\HTTP;
use Utopia\System\System;
@@ -43,12 +44,14 @@ class Create extends Action
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'createAPIRule',
description: <<<EOT
Create a new proxy rule for serving Appwrite's API on custom domain.
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
@@ -67,11 +70,21 @@ class Create extends Action
->inject('dbForPlatform')
->inject('platform')
->inject('log')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $domain, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, array $platform, Log $log)
{
public function action(
string $domain,
Response $response,
Document $project,
Certificate $publisherForCertificates,
Event $queueForEvents,
Database $dbForPlatform,
array $platform,
Log $log,
Authorization $authorization,
) {
$this->validateDomainRestrictions($domain, $platform);
// TODO: (@Meldiron) Remove after 1.7.x migration
@@ -108,7 +121,7 @@ class Create extends Action
}
try {
$rule = $dbForPlatform->createDocument('rules', $rule);
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
} catch (Duplicate $e) {
throw new Exception(Exception::RULE_ALREADY_EXISTS);
}
@@ -126,6 +139,13 @@ class Create extends Action
$queueForEvents->setParam('ruleId', $rule->getId());
// Rename 'created' status to 'unverified' for consistency.
// 'verifying' and 'verified' statuses stay as is.
// 'unverified' in the meaning of failed certificate generation stays as is.
if ($rule->getAttribute('status') === 'created') {
$rule->setAttribute('status', 'unverified');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($rule, Response::MODEL_PROXY_RULE);
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
@@ -38,12 +39,12 @@ class Delete extends Action
->label('audits.resource', 'rule/{request.ruleId}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'deleteRule',
description: <<<EOT
Delete a proxy rule by its unique ID.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
@@ -58,6 +59,7 @@ class Delete extends Action
->inject('dbForPlatform')
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -67,15 +69,16 @@ class Delete extends Action
Document $project,
Database $dbForPlatform,
DeleteEvent $queueForDeletes,
Event $queueForEvents
Event $queueForEvents,
Authorization $authorization,
) {
$rule = $dbForPlatform->getDocument('rules', $ruleId);
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
throw new Exception(Exception::RULE_NOT_FOUND);
}
$dbForPlatform->deleteDocument('rules', $rule->getId());
$authorization->skip(fn () => $dbForPlatform->deleteDocument('rules', $rule->getId()));
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Logger\Log;
use Utopia\Platform\Scope\HTTP;
@@ -45,12 +46,14 @@ class Create extends Action
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'createFunctionRule',
description: <<<EOT
Create a new proxy rule for executing Appwrite Function on custom domain.
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
@@ -72,11 +75,25 @@ class Create extends Action
->inject('dbForProject')
->inject('platform')
->inject('log')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $domain, string $functionId, string $branch, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform, Log $log)
{
public function action(
string $domain,
string $functionId,
string $branch,
Response $response,
Document $project,
Certificate $publisherForCertificates,
Event $queueForEvents,
Database $dbForPlatform,
Database $dbForProject,
array $platform,
Log $log,
Authorization $authorization,
) {
$this->validateDomainRestrictions($domain, $platform);
$function = $dbForProject->getDocument('functions', $functionId);
@@ -126,7 +143,7 @@ class Create extends Action
}
try {
$rule = $dbForPlatform->createDocument('rules', $rule);
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
} catch (Duplicate $e) {
throw new Exception(Exception::RULE_ALREADY_EXISTS);
}
@@ -144,6 +161,13 @@ class Create extends Action
$queueForEvents->setParam('ruleId', $rule->getId());
// Rename 'created' status to 'unverified' for consistency.
// 'verifying' and 'verified' statuses stay as is.
// 'unverified' in the meaning of failed certificate generation stays as is.
if ($rule->getAttribute('status') === 'created') {
$rule->setAttribute('status', 'unverified');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($rule, Response::MODEL_PROXY_RULE);
@@ -10,6 +10,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Scope\HTTP;
@@ -34,12 +35,12 @@ class Get extends Action
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'getRule',
description: <<<EOT
Get a proxy rule by its unique ID.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -51,6 +52,7 @@ class Get extends Action
->inject('response')
->inject('project')
->inject('dbForPlatform')
->inject('authorization')
->callback($this->action(...));
}
@@ -58,15 +60,16 @@ class Get extends Action
string $ruleId,
Response $response,
Document $project,
Database $dbForPlatform
Database $dbForPlatform,
Authorization $authorization,
) {
$rule = $dbForPlatform->getDocument('rules', $ruleId);
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
throw new Exception(Exception::RULE_NOT_FOUND);
}
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
$certificate = $authorization->skip(fn () => $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')));
// Give priority to certificate generation logs if present
if (!empty($certificate->getAttribute('logs', ''))) {
@@ -75,6 +78,13 @@ class Get extends Action
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
// Rename 'created' status to 'unverified' for consistency.
// 'verifying' and 'verified' statuses stay as is.
// 'unverified' in the meaning of failed certificate generation stays as is.
if ($rule->getAttribute('status') === 'created') {
$rule->setAttribute('status', 'unverified');
}
$response->dynamic($rule, Response::MODEL_PROXY_RULE);
}
}
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Logger\Log;
use Utopia\Platform\Scope\HTTP;
@@ -46,12 +47,14 @@ class Create extends Action
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'createRedirectRule',
description: <<<EOT
Create a new proxy rule for to redirect from custom domain to another domain.
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
@@ -75,11 +78,27 @@ class Create extends Action
->inject('dbForProject')
->inject('platform')
->inject('log')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $domain, string $url, int $statusCode, string $resourceId, string $resourceType, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform, Log $log)
{
public function action(
string $domain,
string $url,
int $statusCode,
string $resourceId,
string $resourceType,
Response $response,
Document $project,
Certificate $publisherForCertificates,
Event $queueForEvents,
Database $dbForPlatform,
Database $dbForProject,
array $platform,
Log $log,
Authorization $authorization,
) {
$this->validateDomainRestrictions($domain, $platform);
$collection = match ($resourceType) {
@@ -131,7 +150,7 @@ class Create extends Action
}
try {
$rule = $dbForPlatform->createDocument('rules', $rule);
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
} catch (Duplicate $e) {
throw new Exception(Exception::RULE_ALREADY_EXISTS);
}
@@ -149,6 +168,13 @@ class Create extends Action
$queueForEvents->setParam('ruleId', $rule->getId());
// Rename 'created' status to 'unverified' for consistency.
// 'verifying' and 'verified' statuses stay as is.
// 'unverified' in the meaning of failed certificate generation stays as is.
if ($rule->getAttribute('status') === 'created') {
$rule->setAttribute('status', 'unverified');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($rule, Response::MODEL_PROXY_RULE);
@@ -14,6 +14,7 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Logger\Log;
use Utopia\Platform\Scope\HTTP;
@@ -45,12 +46,14 @@ class Create extends Action
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'createSiteRule',
description: <<<EOT
Create a new proxy rule for serving Appwrite Site on custom domain.
Rule ID is automatically generated as MD5 hash of a rule domain for performance purposes.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
@@ -72,11 +75,25 @@ class Create extends Action
->inject('dbForProject')
->inject('platform')
->inject('log')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $domain, string $siteId, ?string $branch, Response $response, Document $project, Certificate $publisherForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject, array $platform, Log $log)
{
public function action(
string $domain,
string $siteId,
?string $branch,
Response $response,
Document $project,
Certificate $publisherForCertificates,
Event $queueForEvents,
Database $dbForPlatform,
Database $dbForProject,
array $platform,
Log $log,
Authorization $authorization,
) {
$this->validateDomainRestrictions($domain, $platform);
$site = $dbForProject->getDocument('sites', $siteId);
@@ -126,7 +143,7 @@ class Create extends Action
}
try {
$rule = $dbForPlatform->createDocument('rules', $rule);
$rule = $authorization->skip(fn () => $dbForPlatform->createDocument('rules', $rule));
} catch (Duplicate $e) {
throw new Exception(Exception::RULE_ALREADY_EXISTS);
}
@@ -144,6 +161,13 @@ class Create extends Action
$queueForEvents->setParam('ruleId', $rule->getId());
// Rename 'created' status to 'unverified' for consistency.
// 'verifying' and 'verified' statuses stay as is.
// 'unverified' in the meaning of failed certificate generation stays as is.
if ($rule->getAttribute('status') === 'created') {
$rule->setAttribute('status', 'unverified');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($rule, Response::MODEL_PROXY_RULE);
@@ -1,6 +1,6 @@
<?php
namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Verification;
namespace Appwrite\Platform\Modules\Proxy\Http\Rules\Status;
use Appwrite\Event\Event;
use Appwrite\Event\Publisher\Certificate;
@@ -13,6 +13,7 @@ use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Logger\Log;
use Utopia\Platform\Scope\HTTP;
@@ -32,8 +33,9 @@ class Update extends Action
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/proxy/rules/:ruleId/verification')
->desc('Update rule verification status')
->setHttpPath('/v1/proxy/rules/:ruleId/status')
->httpAlias('/v1/proxy/rules/:ruleId/verification')
->desc('Update rule status')
->groups(['api', 'proxy'])
->label('scope', 'rules.write')
->label('event', 'rules.[ruleId].update')
@@ -41,12 +43,12 @@ class Update extends Action
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'updateRuleVerification',
group: 'rules',
name: 'updateRuleStatus',
description: <<<EOT
Retry getting verification process of a proxy rule. This endpoint triggers domain verification by checking DNS records (CNAME) against the configured target domain. If verification is successful, a TLS certificate will be automatically provisioned for the domain.
If not succeeded yet, retry verification process of a proxy rule domain. This endpoint triggers domain verification by checking DNS records. If verification is successful, a TLS certificate will be automatically provisioned for the domain asynchronously in the background.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -61,6 +63,7 @@ class Update extends Action
->inject('project')
->inject('dbForPlatform')
->inject('log')
->inject('authorization')
->callback($this->action(...));
}
@@ -71,9 +74,10 @@ class Update extends Action
Event $queueForEvents,
Document $project,
Database $dbForPlatform,
Log $log
Log $log,
Authorization $authorization,
) {
$rule = $dbForPlatform->getDocument('rules', $ruleId);
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
throw new Exception(Exception::RULE_NOT_FOUND);
@@ -90,22 +94,22 @@ class Update extends Action
try {
$this->verifyRule($rule, $log);
// Reset logs and status for the rule
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
$rule = $authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'logs' => '',
'status' => RULE_STATUS_CERTIFICATE_GENERATING,
]));
])));
$certificateId = $rule->getAttribute('certificateId', '');
// Reset logs for the associated certificate.
if (!empty($certificateId)) {
$certificate = $dbForPlatform->updateDocument('certificates', $certificateId, new Document([
$certificate = $authorization->skip(fn () => $dbForPlatform->updateDocument('certificates', $certificateId, new Document([
'logs' => '',
]));
])));
}
} catch (Exception $err) {
$dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'$updatedAt' => DateTime::now(),
]));
])));
throw $err;
}
@@ -13,6 +13,7 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator\Boolean;
@@ -39,12 +40,12 @@ class XList extends Action
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
group: 'rules',
name: 'listRules',
description: <<<EOT
Get a list of all the proxy rules. You can use the query params to filter your results.
EOT,
auth: [AuthType::ADMIN],
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -53,21 +54,23 @@ class XList extends Action
]
))
->param('queries', [], new Rules(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Rules::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true, deprecated: true)
->inject('response')
->inject('project')
->inject('dbForPlatform')
->inject('authorization')
->callback($this->action(...));
}
public function action(
array $queries,
bool $total,
string $search,
bool $includeTotal,
Response $response,
Document $project,
Database $dbForPlatform
Database $dbForPlatform,
Authorization $authorization,
) {
try {
$queries = Query::parseQueries($queries);
@@ -91,7 +94,7 @@ class XList extends Action
}
$ruleId = $cursor->getValue();
$cursorDocument = $dbForPlatform->getDocument('rules', $ruleId);
$cursorDocument = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $ruleId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Rule '{$ruleId}' for the 'cursor' value not found.");
@@ -102,9 +105,9 @@ class XList extends Action
$filterQueries = Query::groupByType($queries)['filters'];
$rules = $dbForPlatform->find('rules', $queries);
$rules = $authorization->skip(fn () => $dbForPlatform->find('rules', $queries));
foreach ($rules as $rule) {
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
$certificate = $authorization->skip(fn () => $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', '')));
// Give priority to certificate generation logs if present
if (!empty($certificate->getAttribute('logs', ''))) {
@@ -112,11 +115,18 @@ class XList extends Action
}
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
// Rename 'created' status to 'unverified' for consistency.
// 'verifying' and 'verified' statuses stay as is.
// 'unverified' in the meaning of failed certificate generation stays as is.
if ($rule->getAttribute('status') === 'created') {
$rule->setAttribute('status', 'unverified');
}
}
$response->dynamic(new Document([
'rules' => $rules,
'total' => $includeTotal ? $dbForPlatform->count('rules', $filterQueries, APP_LIMIT_COUNT) : 0,
'total' => $total ? $authorization->skip(fn () => $dbForPlatform->count('rules', $filterQueries, APP_LIMIT_COUNT)) : 0,
]), Response::MODEL_PROXY_RULE_LIST);
}
}
@@ -8,7 +8,7 @@ use Appwrite\Platform\Modules\Proxy\Http\Rules\Function\Create as CreateFunction
use Appwrite\Platform\Modules\Proxy\Http\Rules\Get as GetRule;
use Appwrite\Platform\Modules\Proxy\Http\Rules\Redirect\Create as CreateRedirectRule;
use Appwrite\Platform\Modules\Proxy\Http\Rules\Site\Create as CreateSiteRule;
use Appwrite\Platform\Modules\Proxy\Http\Rules\Verification\Update as UpdateRuleVerification;
use Appwrite\Platform\Modules\Proxy\Http\Rules\Status\Update as UpdateRuleStatus;
use Appwrite\Platform\Modules\Proxy\Http\Rules\XList as ListRules;
use Utopia\Platform\Service;
@@ -26,6 +26,6 @@ class Http extends Service
$this->addAction(GetRule::getName(), new GetRule());
$this->addAction(ListRules::getName(), new ListRules());
$this->addAction(DeleteRule::getName(), new DeleteRule());
$this->addAction(UpdateRuleVerification::getName(), new UpdateRuleVerification());
$this->addAction(UpdateRuleStatus::getName(), new UpdateRuleStatus());
}
}
@@ -2,8 +2,9 @@
namespace Appwrite\Platform\Modules\Sites\Http\Deployments;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -85,7 +86,7 @@ class Create extends Action
->inject('queueForEvents')
->inject('deviceForSites')
->inject('deviceForLocal')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('plan')
->inject('authorization')
->inject('platform')
@@ -107,7 +108,7 @@ class Create extends Action
Event $queueForEvents,
Device $deviceForSites,
Device $deviceForLocal,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
array $plan,
Authorization $authorization,
array $platform,
@@ -315,10 +316,13 @@ class Create extends Action
}
// Start the build
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
->setDeployment($deployment);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $site,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
platform: $platform,
));
} else {
if ($deployment->isEmpty()) {
$deployment = $dbForProject->createDocument('deployments', new Document([
@@ -2,8 +2,9 @@
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Duplicate;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
@@ -63,7 +64,7 @@ class Create extends Action
->inject('dbForProject')
->inject('dbForPlatform')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('deviceForSites')
->inject('authorization')
->inject('platform')
@@ -79,7 +80,7 @@ class Create extends Action
Database $dbForProject,
Database $dbForPlatform,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
Device $deviceForSites,
Authorization $authorization,
array $platform
@@ -177,10 +178,13 @@ class Create extends Action
]))
);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
->setDeployment($deployment);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $site,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
platform: $platform,
));
$queueForEvents
->setParam('siteId', $site->getId())
@@ -2,8 +2,9 @@
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Template;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Message\Build as BuildMessage;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -77,7 +78,7 @@ class Create extends Base
->inject('dbForPlatform')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('gitHub')
->inject('authorization')
->inject('platform')
@@ -98,7 +99,7 @@ class Create extends Base
Database $dbForPlatform,
Document $project,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
GitHub $github,
Authorization $authorization,
array $platform
@@ -130,7 +131,7 @@ class Create extends Base
installation: $installation,
dbForProject: $dbForProject,
dbForPlatform: $dbForPlatform,
queueForBuilds: $queueForBuilds,
publisherForBuilds: $publisherForBuilds,
template: $template,
github: $github,
activate: $activate,
@@ -223,11 +224,14 @@ class Create extends Base
$this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform, $authorization);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
->setDeployment($deployment)
->setTemplate($template);
$publisherForBuilds->enqueue(new BuildMessage(
project: $project,
resource: $site,
deployment: $deployment,
type: BUILD_TYPE_DEPLOYMENT,
template: $template,
platform: $platform,
));
$queueForEvents
->setParam('siteId', $site->getId())
@@ -2,8 +2,8 @@
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Vcs;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -71,7 +71,7 @@ class Create extends Base
->inject('dbForPlatform')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('gitHub')
->inject('authorization')
->inject('platform')
@@ -89,7 +89,7 @@ class Create extends Base
Database $dbForPlatform,
Document $project,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
GitHub $github,
Authorization $authorization,
array $platform
@@ -111,7 +111,7 @@ class Create extends Base
installation: $installation,
dbForProject: $dbForProject,
dbForPlatform: $dbForPlatform,
queueForBuilds: $queueForBuilds,
publisherForBuilds: $publisherForBuilds,
template: $template,
github: $github,
activate: $activate,
@@ -2,8 +2,8 @@
namespace Appwrite\Platform\Modules\Sites\Http\Sites;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Publisher\Build as BuildPublisher;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\Platform\Modules\Compute\Validator\Specification;
@@ -99,10 +99,11 @@ class Update extends Base
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('publisherForBuilds')
->inject('dbForPlatform')
->inject('gitHub')
->inject('executor')
->inject('platform')
->callback($this->action(...));
}
@@ -133,10 +134,11 @@ class Update extends Base
Database $dbForProject,
Document $project,
Event $queueForEvents,
Build $queueForBuilds,
BuildPublisher $publisherForBuilds,
Database $dbForPlatform,
GitHub $github,
Executor $executor
Executor $executor,
array $platform
) {
if (!empty($adapter)) {
$configFramework = Config::getParam('frameworks')[$framework] ?? [];
@@ -279,7 +281,7 @@ class Update extends Base
// Redeploy logic
if (!$isConnected && !empty($providerRepositoryId)) {
$this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true);
$this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $publisherForBuilds, new Document(), $github, true, $platform);
}
$queueForEvents->setParam('siteId', $site->getId());
@@ -2,11 +2,13 @@
namespace Appwrite\Platform\Modules\Sites\Http\Variables;
use Appwrite\Event\Event as QueueEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -36,6 +38,7 @@ class Create extends Base
->groups(['api', 'sites'])
->label('scope', 'sites.write')
->label('resourceType', RESOURCE_TYPE_SITES)
->label('event', 'variables.[variableId].create')
->label('audits.event', 'variable.create')
->label('audits.resource', 'site/{request.siteId}')
->label('sdk', new Method(
@@ -54,16 +57,18 @@ class Create extends Base
]
))
->param('siteId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Site unique ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', false, ['dbForProject'])
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true)
->inject('response')
->inject('queueForEvents')
->inject('dbForProject')
->inject('project')
->callback($this->action(...));
}
public function action(string $siteId, string $key, string $value, bool $secret, Response $response, Database $dbForProject, Document $project)
public function action(string $siteId, string $variableId, string $key, string $value, bool $secret, Response $response, QueueEvent $queueForEvents, Database $dbForProject, Document $project)
{
$site = $dbForProject->getDocument('sites', $siteId);
@@ -71,7 +76,7 @@ class Create extends Base
throw new Exception(Exception::SITE_NOT_FOUND);
}
$variableId = ID::unique();
$variableId = ($variableId === 'unique()') ? ID::unique() : $variableId;
$teamId = $project->getAttribute('teamId', '');
$variable = new Document([
@@ -96,6 +101,8 @@ class Create extends Base
'live' => false,
]));
$queueForEvents->setParam('variableId', $variable->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($variable, Response::MODEL_VARIABLE);
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Modules\Sites\Http\Variables;
use Appwrite\Event\Event as QueueEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -33,6 +34,7 @@ class Delete extends Base
->groups(['api', 'sites'])
->label('scope', 'sites.write')
->label('resourceType', RESOURCE_TYPE_SITES)
->label('event', 'variables.[variableId].delete')
->label('audits.event', 'variable.delete')
->label('audits.resource', 'site/{request.siteId}')
->label('sdk', new Method(
@@ -54,11 +56,12 @@ class Delete extends Base
->param('siteId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Site unique ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('response')
->inject('queueForEvents')
->inject('dbForProject')
->callback($this->action(...));
}
public function action(string $siteId, string $variableId, Response $response, Database $dbForProject)
public function action(string $siteId, string $variableId, Response $response, QueueEvent $queueForEvents, Database $dbForProject)
{
$site = $dbForProject->getDocument('sites', $siteId);
@@ -77,6 +80,8 @@ class Delete extends Base
'live' => false,
]));
$queueForEvents->setParam('variableId', $variable->getId());
$response->noContent();
}
}
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Modules\Sites\Http\Variables;
use Appwrite\Event\Event as QueueEvent;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Compute\Base;
use Appwrite\SDK\AuthType;
@@ -35,6 +36,7 @@ class Update extends Base
->desc('Update variable')
->groups(['api', 'sites'])
->label('scope', 'sites.write')
->label('event', 'variables.[variableId].update')
->label('audits.event', 'variable.update')
->label('audits.resource', 'site/{request.siteId}')
->label('resourceType', RESOURCE_TYPE_SITES)
@@ -55,10 +57,11 @@ class Update extends Base
))
->param('siteId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Site unique ID.', false, ['dbForProject'])
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('key', null, new Nullable(new Text(255, 0)), 'Variable key. Max length: 255 chars.', true)
->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true)
->inject('response')
->inject('queueForEvents')
->inject('dbForProject')
->callback($this->action(...));
}
@@ -66,10 +69,11 @@ class Update extends Base
public function action(
string $siteId,
string $variableId,
string $key,
?string $key,
?string $value,
?bool $secret,
Response $response,
QueueEvent $queueForEvents,
Database $dbForProject
) {
$site = $dbForProject->getDocument('sites', $siteId);
@@ -87,19 +91,27 @@ class Update extends Base
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
}
$variable
->setAttribute('key', $key)
->setAttribute('value', $value ?? $variable->getAttribute('value'))
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
->setAttribute('search', implode(' ', [$variableId, $site->getId(), $key, 'site']));
if (\is_null($key) && \is_null($value) && \is_null($secret)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID);
}
$updates = new Document();
if (!\is_null($key)) {
$updates->setAttribute('key', $key);
$updates->setAttribute('search', implode(' ', [$variableId, $site->getId(), $key, 'site']));
}
if (!\is_null($value)) {
$updates->setAttribute('value', $value);
}
if (!\is_null($secret)) {
$updates->setAttribute('secret', $secret);
}
try {
$dbForProject->updateDocument('variables', $variable->getId(), new Document([
'key' => $variable->getAttribute('key'),
'value' => $variable->getAttribute('value'),
'secret' => $variable->getAttribute('secret'),
'search' => $variable->getAttribute('search'),
]));
$variable = $dbForProject->updateDocument('variables', $variable->getId(), $updates);
} catch (DuplicateException $th) {
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
}
@@ -108,6 +120,8 @@ class Update extends Base
'live' => false,
]));
$queueForEvents->setParam('variableId', $variable->getId());
$response->dynamic($variable, Response::MODEL_VARIABLE);
}
}

Some files were not shown because too many files have changed in this diff Show More