diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000000..80d880244c --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,47 @@ +name: Nightly Security Scan +on: + schedule: + - cron: '0 0 * * *' # 12am UTC daily runtime + workflow_dispatch: + +jobs: + scan-image: + name: Scan Docker Image + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Build the Docker image + run: docker build . -t appwrite_image:latest + - name: Run Trivy vulnerability scanner on image + uses: aquasecurity/trivy-action@0.20.0 + with: + image-ref: 'appwrite_image:latest' + format: 'sarif' + output: 'trivy-image-results.sarif' + ignore-unfixed: 'false' + severity: 'CRITICAL,HIGH' + - name: Upload Docker Image Scan Results + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: 'trivy-image-results.sarif' + + scan-code: + name: Scan Code + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Run Trivy vulnerability scanner on filesystem + uses: aquasecurity/trivy-action@0.20.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@v2 + with: + sarif_file: 'trivy-fs-results.sarif' diff --git a/.github/workflows/pr-scan.yml b/.github/workflows/pr-scan.yml index af510ccc3b..eded58985d 100644 --- a/.github/workflows/pr-scan.yml +++ b/.github/workflows/pr-scan.yml @@ -1,17 +1,22 @@ name: PR Security Scan -on: - pull_request: +on: + pull_request_target: types: [opened, synchronize, reopened] - workflow_dispatch: + jobs: scan: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write steps: - - name: Check out code + - name: Check out code uses: actions/checkout@v4 with: + ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 submodules: 'recursive' + - name: Build the Docker image uses: docker/build-push-action@v5 with: @@ -19,6 +24,7 @@ jobs: push: false load: true tags: pr_image:${{ github.sha }} + - name: Run Trivy vulnerability scanner on image uses: aquasecurity/trivy-action@0.20.0 with: @@ -26,6 +32,7 @@ jobs: format: 'json' output: 'trivy-image-results.json' severity: 'CRITICAL,HIGH' + - name: Run Trivy vulnerability scanner on source code uses: aquasecurity/trivy-action@0.20.0 with: @@ -34,10 +41,11 @@ jobs: format: 'json' output: 'trivy-fs-results.json' severity: 'CRITICAL,HIGH' - - name: Process and post Trivy scan results + + - name: Process Trivy scan results + id: process-results uses: actions/github-script@v7 with: - github-token: ${{secrets.GITHUB_TOKEN}} script: | const fs = require('fs'); let commentBody = '## Security Scan Results for PR\n\n'; @@ -79,9 +87,19 @@ jobs: commentBody += 'Please contact the core team for assistance.'; } - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: commentBody - }); + core.setOutput('comment-body', commentBody); + - name: Find Comment + uses: peter-evans/find-comment@v3 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: Security Scan Results for PR + + - name: Create or update comment + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + comment-id: ${{ steps.fc.outputs.comment-id }} + body: ${{ steps.process-results.outputs.comment-body }} + edit-mode: replace diff --git a/CHANGES.md b/CHANGES.md index e6649d795e..9b6172eeab 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,145 @@ +# Version 1.6.0 + +## What's Changed + +### Notable changes + +* Allow execution filter attributes in [#7607](https://github.com/appwrite/appwrite/pull/7607) +* Add dynamic API keys for function executions in [#7512](https://github.com/appwrite/appwrite/pull/7512) +* Add metrics for successful and failed builds in [#8210](https://github.com/appwrite/appwrite/pull/8210) +* Update logging config to use a DSN approach in [#8187](https://github.com/appwrite/appwrite/pull/8187) +* Add projects.createJWT endpoint for dynamic keys in [#8213](https://github.com/appwrite/appwrite/pull/8213) +* Add users.createJWT() endpoint for local function development in [#8207](https://github.com/appwrite/appwrite/pull/8207) +* Added cancel build endpoint in [#7605](https://github.com/appwrite/appwrite/pull/7605) +* Add CLI as a function deployment type in [#8215](https://github.com/appwrite/appwrite/pull/8215) +* Add vcs.getRepositoryContents() endpoint in [#8330](https://github.com/appwrite/appwrite/pull/8330) +* Add appwrite version in function variables in [#8336](https://github.com/appwrite/appwrite/pull/8336) +* Add support for scheduled executions in [#8243](https://github.com/appwrite/appwrite/pull/8243) +* Add endpoint to delete execution in [#8337](https://github.com/appwrite/appwrite/pull/8337) +* OPR v4 support in [#8323](https://github.com/appwrite/appwrite/pull/8323) +* Mock OTP and phone numbers in [#7565](https://github.com/appwrite/appwrite/pull/7565) +* Support scheduled executions in [#8355](https://github.com/appwrite/appwrite/pull/8355) +* Add alert for new sessions in [#8315](https://github.com/appwrite/appwrite/pull/8315) +* Update delete authenticator to remove OTP Validation in [#8367](https://github.com/appwrite/appwrite/pull/8367) +* Track project last activity in [#8366](https://github.com/appwrite/appwrite/pull/8366) +* Containerize the console in [#8406](https://github.com/appwrite/appwrite/pull/8406) +* Implement MBSeconds Metric on 1.5.X in [#8385](https://github.com/appwrite/appwrite/pull/8385) +* Support JWTs without session ID in [#8420](https://github.com/appwrite/appwrite/pull/8420) +* 1.6.x sdks in [#8359](https://github.com/appwrite/appwrite/pull/8359) +* Base migration for 1.6.x in [#8417](https://github.com/appwrite/appwrite/pull/8417) +* 1.6.x migrations and filters in [#8403](https://github.com/appwrite/appwrite/pull/8403) +* Add APPWRITE_REGION in function variables in [#8394](https://github.com/appwrite/appwrite/pull/8394) +* Support dynamic keys for domain executions in [#8428](https://github.com/appwrite/appwrite/pull/8428) +* Bump DBIP to latest version in [#8467](https://github.com/appwrite/appwrite/pull/8467) +* Automatically restart function on crash in [#8473](https://github.com/appwrite/appwrite/pull/8473) +* Don't send session alerts for otp and magic-url logins in [#8459](https://github.com/appwrite/appwrite/pull/8459) +* Mark 4XX executions as successful in [#8493](https://github.com/appwrite/appwrite/pull/8493) +* Add dynamic keys in builds in [#8492](https://github.com/appwrite/appwrite/pull/8492) +* Allow deployment queries on type and size in [#8515](https://github.com/appwrite/appwrite/pull/8515) +* Add OTP email template in [#8501](https://github.com/appwrite/appwrite/pull/8501) +* Update console links in [#8523](https://github.com/appwrite/appwrite/pull/8523) +* Add multipart support in [#8477](https://github.com/appwrite/appwrite/pull/8477) +* Separate deployment sizes in [#8556](https://github.com/appwrite/appwrite/pull/8556) +* Add go runtime in [#8572](https://github.com/appwrite/appwrite/pull/8572) +* Add react native platform in [#8562](https://github.com/appwrite/appwrite/pull/8562) +* Merge deployments and build storage metrics together in API in [#8443](https://github.com/appwrite/appwrite/pull/8443) +* Support string attribute resizing in [#8597](https://github.com/appwrite/appwrite/pull/8597) +* Support renaming attributes in [#8544](https://github.com/appwrite/appwrite/pull/8544) +* Add VCS vars to deployments & executions in [#8631](https://github.com/appwrite/appwrite/pull/8631) +* Function storage metrics in [#8668](https://github.com/appwrite/appwrite/pull/8668) +* External messaging usage count in [#8672](https://github.com/appwrite/appwrite/pull/8672) + +### Fixes + +* Fix execution duration in [#8357](https://github.com/appwrite/appwrite/pull/8357) +* Fix file size calculations in [#8432](https://github.com/appwrite/appwrite/pull/8432) +* Fix disabled function logging in [#8398](https://github.com/appwrite/appwrite/pull/8398) +* Fix function redeployments in [#8434](https://github.com/appwrite/appwrite/pull/8434) +* Add value to variables template in [#8483](https://github.com/appwrite/appwrite/pull/8483) +* Fix build size limits in [#8396](https://github.com/appwrite/appwrite/pull/8396) +* Fix deployment method name in [#8490](https://github.com/appwrite/appwrite/pull/8490) +* Fix function disconnecting from git in [#8500](https://github.com/appwrite/appwrite/pull/8500) +* Increase buckets metadata in [#8452](https://github.com/appwrite/appwrite/pull/8452) +* Fix deploy from git with space in [#8517](https://github.com/appwrite/appwrite/pull/8517) +* Fix missing build logs in [#8484](https://github.com/appwrite/appwrite/pull/8484) +* Delete team memberships synchronously in [#8217](https://github.com/appwrite/appwrite/pull/8217) +* Fix Anyof validator in specs in [#8543](https://github.com/appwrite/appwrite/pull/8543) +* Fix missing function variables in [#8554](https://github.com/appwrite/appwrite/pull/8554) +* Fix deadlock in [#8609](https://github.com/appwrite/appwrite/pull/8609) +* Fix domain execution stats in [#8608](https://github.com/appwrite/appwrite/pull/8608) +* Update console redirect to include query params in [#8619](https://github.com/appwrite/appwrite/pull/8619) +* Update abuse-key for mfa challenge endpoints in [#8649](https://github.com/appwrite/appwrite/pull/8649) +* Fix cross-project scheduler stability in [#8641](https://github.com/appwrite/appwrite/pull/8641) +* Fix vcs deployment size in [#8640](https://github.com/appwrite/appwrite/pull/8640) +* Fix logging behaviour for Functions in [#8627](https://github.com/appwrite/appwrite/pull/8627) +* Add retention env vars to deletes worker in [#8662](https://github.com/appwrite/appwrite/pull/8662) +* Fix scheduled executions data in [#8639](https://github.com/appwrite/appwrite/pull/8639) + +### Miscellaneous + +* Sync 1.6.x with main in [#8163](https://github.com/appwrite/appwrite/pull/8163) +* Remove build ID from rebuild deployment endpoint in [#8214](https://github.com/appwrite/appwrite/pull/8214) +* 1.6.x specs in [#8304](https://github.com/appwrite/appwrite/pull/8304) +* Sync with main in [#8295](https://github.com/appwrite/appwrite/pull/8295) +* Fix 1.6.x failing tests in [#8333](https://github.com/appwrite/appwrite/pull/8333) +* Ensure CI/CD works in [#8350](https://github.com/appwrite/appwrite/pull/8350) +* Update specs in [#8356](https://github.com/appwrite/appwrite/pull/8356) +* Sync main to 1.6.x in [#8430](https://github.com/appwrite/appwrite/pull/8430) +* Add scheduledAt in execution response model in [#8425](https://github.com/appwrite/appwrite/pull/8425) +* Move functions marketplace to appwrite in [#8427](https://github.com/appwrite/appwrite/pull/8427) +* Refactor deployment check in function tests in [#8444](https://github.com/appwrite/appwrite/pull/8444) +* Add ci/cd benchmark in [#8414](https://github.com/appwrite/appwrite/pull/8414) +* Upgrade SDK version in [#8465](https://github.com/appwrite/appwrite/pull/8465) +* Improve session alert in [#8399](https://github.com/appwrite/appwrite/pull/8399) +* Address review comments in [#8422](https://github.com/appwrite/appwrite/pull/8422) +* Add scopes to function template in [#8496](https://github.com/appwrite/appwrite/pull/8496) +* Update benchmark comment in [#8507](https://github.com/appwrite/appwrite/pull/8507) +* Add key to runtime model in [#8503](https://github.com/appwrite/appwrite/pull/8503) +* Upgrade logger in [#8497](https://github.com/appwrite/appwrite/pull/8497) +* Change default email addresses in [#8466](https://github.com/appwrite/appwrite/pull/8466) +* Improve scheduled executions in [#8412](https://github.com/appwrite/appwrite/pull/8412) +* Sync 1.5.x into main in [#8509](https://github.com/appwrite/appwrite/pull/8509) +* Sync 1.6 with main in [#8529](https://github.com/appwrite/appwrite/pull/8529) +* Fix templates CORS in [#8528](https://github.com/appwrite/appwrite/pull/8528) +* Update size to specification for variable runtimes in [#8537](https://github.com/appwrite/appwrite/pull/8537) +* Add boundary to multipart header in [#8539](https://github.com/appwrite/appwrite/pull/8539) +* Support manual templates in [#8527](https://github.com/appwrite/appwrite/pull/8527) +* Reorder runtimes in [#8540](https://github.com/appwrite/appwrite/pull/8540) +* Fix 1.6 bugs in [#8358](https://github.com/appwrite/appwrite/pull/8358) +* Add seconds precision to scheduledAt in [#8546](https://github.com/appwrite/appwrite/pull/8546) +* Update docker base image in [#8485](https://github.com/appwrite/appwrite/pull/8485) +* Update create execution return type in [#8542](https://github.com/appwrite/appwrite/pull/8542) +* Default fallback to for templateBranch in [#8547](https://github.com/appwrite/appwrite/pull/8547) +* Fix env vars functions test in [#8555](https://github.com/appwrite/appwrite/pull/8555) +* Fix session alerts in [#8550](https://github.com/appwrite/appwrite/pull/8550) +* Add runtime controls in [#8384](https://github.com/appwrite/appwrite/pull/8384) +* Revert request type to json in create execution in [#8563](https://github.com/appwrite/appwrite/pull/8563) +* Sync 1.6.x Filters and Migrations with latest in [#8553](https://github.com/appwrite/appwrite/pull/8553) +* Update sdks in [#8551](https://github.com/appwrite/appwrite/pull/8551) +* Update Docs in [#8567](https://github.com/appwrite/appwrite/pull/8567) +* Headers validator benchmark in [#8561](https://github.com/appwrite/appwrite/pull/8561) +* Fix go version in [#8571](https://github.com/appwrite/appwrite/pull/8571) +* Update dependencies in [#8574](https://github.com/appwrite/appwrite/pull/8574) +* Upgrade console in [#8575](https://github.com/appwrite/appwrite/pull/8575) +* 1.6.x logging test in [#8580](https://github.com/appwrite/appwrite/pull/8580) +* Bump console sdk in [#8581](https://github.com/appwrite/appwrite/pull/8581) +* Update sdks in [#8582](https://github.com/appwrite/appwrite/pull/8582) +* Add changelogs for dart and flutter in [#8587](https://github.com/appwrite/appwrite/pull/8587) +* Add payload validator in [#8594](https://github.com/appwrite/appwrite/pull/8594) +* Update geodb in [#8615](https://github.com/appwrite/appwrite/pull/8615) +* Update createdeployment methodtype to upload in [#8616](https://github.com/appwrite/appwrite/pull/8616) +* Remove tenant in document filter in [#8624](https://github.com/appwrite/appwrite/pull/8624) +* Improve mail datetime format in [#8628](https://github.com/appwrite/appwrite/pull/8628) +* Fix router function execution logging in [#8625](https://github.com/appwrite/appwrite/pull/8625) +* Add Functions templates async test in [#8622](https://github.com/appwrite/appwrite/pull/8622) +* Update console in [#8629](https://github.com/appwrite/appwrite/pull/8629) +* 1.6.1 in [#8630](https://github.com/appwrite/appwrite/pull/8630) +* Update version in [#8646](https://github.com/appwrite/appwrite/pull/8646) +* Phone auth metric rename in [#8648](https://github.com/appwrite/appwrite/pull/8648) +* Pretty print specs in [#8643](https://github.com/appwrite/appwrite/pull/8643) +* Fix messaging metrics in [#8674](https://github.com/appwrite/appwrite/pull/8674) +* Bump console to 5.0.6 in [#8585](https://github.com/appwrite/appwrite/pull/8585) + # Version 1.5.10 ## What's Changed diff --git a/Dockerfile b/Dockerfile index 4dc996fe89..22b37982d0 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM composer:2.0 as composer +FROM composer:2.0 AS composer ARG TESTING=false ENV TESTING=$TESTING @@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.9.2 as final +FROM appwrite/base:0.9.3 AS final LABEL maintainer="team@appwrite.io" diff --git a/README-CN.md b/README-CN.md index 5e0cbab4c4..a5eac1be03 100644 --- a/README-CN.md +++ b/README-CN.md @@ -67,7 +67,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.5.10 + appwrite/appwrite:1.6.0 ``` ### Windows @@ -79,7 +79,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.5.10 + appwrite/appwrite:1.6.0 ``` #### PowerShell @@ -89,7 +89,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.5.10 + appwrite/appwrite:1.6.0 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 8305d78ef0..a9856a7310 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> Our Appwrite Init event has concluded. You can check out all the new and upcoming features [on our Init website](https://appwrite.io/init) 🚀 +> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) 🚀

@@ -75,7 +75,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.5.10 + appwrite/appwrite:1.6.0 ``` ### Windows @@ -87,7 +87,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.5.10 + appwrite/appwrite:1.6.0 ``` #### PowerShell @@ -97,7 +97,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.5.10 + appwrite/appwrite:1.6.0 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. @@ -134,6 +134,12 @@ Choose from one of the providers below:
Akamai Compute + + + AWS Logo +
AWS Marketplace
+ + diff --git a/app/cli.php b/app/cli.php index 9b85804349..0d5a92b23c 100644 --- a/app/cli.php +++ b/app/cli.php @@ -1,12 +1,12 @@ getAll(supported: false)); + +// require controllers after overwriting runtimes +require_once __DIR__ . '/controllers/general.php'; + Authorization::disable(); CLI::setResource('register', fn () => $register); diff --git a/app/config/function-templates.php b/app/config/function-templates.php index 4b58cc44be..db26ff2c19 100644 --- a/app/config/function-templates.php +++ b/app/config/function-templates.php @@ -82,7 +82,7 @@ return [ 'providerOwner' => 'appwrite', 'providerVersion' => '0.2.*', 'variables' => [], - 'scopes' => ["users.read"] + 'scopes' => ['users.read'] ], [ 'icon' => 'icon-upstash', @@ -125,7 +125,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-redis', @@ -167,7 +168,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-neo4j', @@ -217,7 +219,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-mongodb', @@ -253,7 +256,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-neon', @@ -320,7 +324,8 @@ return [ 'required' => true, 'type' => 'text' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-open-ai', @@ -380,7 +385,8 @@ return [ 'required' => false, 'type' => 'number' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-discord', @@ -442,7 +448,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-perspective-api', @@ -476,7 +483,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-pangea', @@ -523,7 +531,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-document', @@ -543,7 +552,8 @@ return [ 'providerRepositoryId' => 'templates', 'providerOwner' => 'appwrite', 'providerVersion' => '0.2.*', - 'variables' => [] + 'variables' => [], + 'scopes' => [] ], [ 'icon' => 'icon-github', @@ -586,7 +596,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-bookmark', @@ -637,7 +648,7 @@ return [ 'type' => 'url' ] ], - 'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"] + 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write'] ], [ 'icon' => 'icon-algolia', @@ -718,7 +729,7 @@ return [ 'type' => 'password' ], ], - 'scopes' => ["databases.read", "collections.read", "documents.read"] + 'scopes' => ['databases.read', 'collections.read', 'documents.read'] ], [ 'icon' => 'icon-meilisearch', @@ -811,7 +822,7 @@ return [ 'type' => 'text' ], ], - 'scopes' => ["databases.read", "collections.read", "documents.read"] + 'scopes' => ['databases.read', 'collections.read', 'documents.read'] ], [ 'icon' => 'icon-vonage', @@ -896,7 +907,8 @@ return [ 'required' => true, 'type' => 'phone' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-bell', @@ -951,7 +963,8 @@ return [ 'required' => true, 'type' => 'url' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-mail', @@ -1033,7 +1046,8 @@ return [ 'required' => false, 'type' => 'text' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-stripe', @@ -1074,7 +1088,7 @@ return [ 'type' => 'password' ] ], - 'scopes' => ["users.read", "sessions.write", "users.write"] + 'scopes' => ['users.read', 'sessions.write', 'users.write'] ], [ 'icon' => 'icon-stripe', @@ -1131,7 +1145,7 @@ return [ 'type' => 'text' ] ], - 'scopes' => ["databases.read", "databases.write", "collections.write", "attributes.write", "documents.read", "documents.write"] + 'scopes' => ['databases.read', 'databases.write', 'collections.write', 'attributes.write', 'documents.read', 'documents.write'] ], [ 'icon' => 'icon-chat', @@ -1164,7 +1178,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-translate', @@ -1197,7 +1212,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-eye', @@ -1255,7 +1271,7 @@ return [ 'type' => 'password' ] ], - 'scopes' => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"] + 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read'] ], [ 'icon' => 'icon-eye', @@ -1313,7 +1329,7 @@ return [ 'type' => 'password' ] ], - "scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"] + 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read'] ], [ 'icon' => 'icon-text', @@ -1371,7 +1387,7 @@ return [ 'type' => 'password' ] ], - "scopes" => ["databases.read", "databases.write", "collections.read", "collections.write", "attributes.write", "documents.read", "documents.write", "buckets.read", "buckets.write", "files.read"] + 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.write', 'documents.read', 'documents.write', 'buckets.read', 'buckets.write', 'files.read'] ], [ 'icon' => 'icon-chat', @@ -1429,7 +1445,7 @@ return [ 'type' => 'password' ] ], - "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"] + 'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write'] ], [ 'icon' => 'icon-chip', @@ -1463,7 +1479,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-chip', @@ -1505,7 +1522,7 @@ return [ 'type' => 'text' ] ], - "scopes" => ["buckets.write", "files.read", "files.write"] + 'scopes' => ['buckets.write', 'files.read', 'files.write'] ], [ 'icon' => 'icon-chip', @@ -1579,7 +1596,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-document-search', @@ -1642,7 +1660,7 @@ return [ 'type' => 'text' ] ], - "scopes" => ["databases.read", "collections.read", "documents.read"] + 'scopes' => ['databases.read', 'collections.read', 'documents.read'] ], [ 'icon' => 'icon-chip', @@ -1705,7 +1723,7 @@ return [ 'type' => 'text' ] ], - "scopes" => ["databases.read", "collections.read", "documents.read"] + 'scopes' => ['databases.read', 'collections.read', 'documents.read'] ], [ 'icon' => 'icon-chat', @@ -1760,7 +1778,7 @@ return [ 'type' => 'text' ] ], - "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"] + 'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write'] ], [ 'icon' => 'icon-chip', @@ -1801,7 +1819,7 @@ return [ 'type' => 'text' ] ], - "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"] + 'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write'] ], [ 'icon' => 'icon-chip', @@ -1841,7 +1859,8 @@ return [ 'required' => false, 'type' => 'number' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-music-note', @@ -1883,7 +1902,7 @@ return [ 'type' => 'password' ] ], - "scopes" => ["buckets.read", "buckets.write", "files.read", "files.write"] + 'scopes' => ['buckets.read', 'buckets.write', 'files.read', 'files.write'] ], [ 'icon' => 'icon-chip', @@ -1917,7 +1936,8 @@ return [ 'required' => true, 'type' => 'password' ] - ] + ], + 'scopes' => [] ], [ 'icon' => 'icon-currency-dollar', @@ -1972,7 +1992,7 @@ return [ 'type' => 'text' ] ], - "scopes" => ["users.read", "users.write"] + 'scopes' => ['users.read', 'users.write'] ], [ 'icon' => 'icon-currency-dollar', @@ -2043,6 +2063,6 @@ return [ 'type' => 'text' ] ], - "scopes" => ["users.read", "users.write"] + 'scopes' => ['users.read', 'users.write'] ] ]; diff --git a/app/config/specs/open-api3-1.6.x-console.json b/app/config/specs/open-api3-1.6.x-console.json index 48c37c9393..07749889d8 100644 --- a/app/config/specs/open-api3-1.6.x-console.json +++ b/app/config/specs/open-api3-1.6.x-console.json @@ -37384,6 +37384,18 @@ "x-example": 0, "format": "int32" }, + "buildsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of builds storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, + "deploymentsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of deployments storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "bucketsTotal": { "type": "integer", "description": "Total aggregated number of buckets.", @@ -37482,6 +37494,8 @@ "usersTotal", "filesStorageTotal", "functionsStorageTotal", + "buildsStorageTotal", + "deploymentsStorageTotal", "bucketsTotal", "executionsMbSecondsTotal", "buildsMbSecondsTotal", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 48c37c9393..07749889d8 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -37384,6 +37384,18 @@ "x-example": 0, "format": "int32" }, + "buildsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of builds storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, + "deploymentsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of deployments storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "bucketsTotal": { "type": "integer", "description": "Total aggregated number of buckets.", @@ -37482,6 +37494,8 @@ "usersTotal", "filesStorageTotal", "functionsStorageTotal", + "buildsStorageTotal", + "deploymentsStorageTotal", "bucketsTotal", "executionsMbSecondsTotal", "buildsMbSecondsTotal", diff --git a/app/config/specs/swagger2-1.6.x-console.json b/app/config/specs/swagger2-1.6.x-console.json index 4b2bdc0c4d..51935a5e01 100644 --- a/app/config/specs/swagger2-1.6.x-console.json +++ b/app/config/specs/swagger2-1.6.x-console.json @@ -37939,6 +37939,18 @@ "x-example": 0, "format": "int32" }, + "buildsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of builds storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, + "deploymentsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of deployments storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "bucketsTotal": { "type": "integer", "description": "Total aggregated number of buckets.", @@ -38046,6 +38058,8 @@ "usersTotal", "filesStorageTotal", "functionsStorageTotal", + "buildsStorageTotal", + "deploymentsStorageTotal", "bucketsTotal", "executionsMbSecondsTotal", "buildsMbSecondsTotal", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 4b2bdc0c4d..51935a5e01 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -37939,6 +37939,18 @@ "x-example": 0, "format": "int32" }, + "buildsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of builds storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, + "deploymentsStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of deployments storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "bucketsTotal": { "type": "integer", "description": "Total aggregated number of buckets.", @@ -38046,6 +38058,8 @@ "usersTotal", "filesStorageTotal", "functionsStorageTotal", + "buildsStorageTotal", + "deploymentsStorageTotal", "bucketsTotal", "executionsMbSecondsTotal", "buildsMbSecondsTotal", diff --git a/app/config/variables.php b/app/config/variables.php index 70d0bcee1c..113fbae335 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -193,7 +193,7 @@ return [ 'introduction' => '1.5.1', 'default' => '', 'required' => true, - 'question' => '', + 'question' => 'Enter an email that will be used when registering for SSL certificates', 'filter' => '' ], [ diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 6a5e12f4fd..b08e4ee0c7 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1923,7 +1923,7 @@ App::post('/v1/functions/:functionId/executions') 'path' => $path, 'method' => $method, 'body' => $body, - 'jwt' => $jwt, + 'userId' => $user->getId() ]; $schedule = $dbForConsole->createDocument('schedules', new Document([ diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index a4444b1716..d885e980df 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -273,6 +273,8 @@ App::get('/v1/project/usage') 'bucketsTotal' => $total[METRIC_BUCKETS], 'filesStorageTotal' => $total[METRIC_FILES_STORAGE], 'functionsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE] + $total[METRIC_BUILDS_STORAGE], + 'buildsStorageTotal' => $total[METRIC_BUILDS_STORAGE], + 'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE], 'executionsBreakdown' => $executionsBreakdown, 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 792cb15138..8f6f45d531 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -603,10 +603,11 @@ App::shutdown() /** * Trigger functions. */ - $queueForFunctions - ->from($queueForEvents) - ->trigger(); - + if (!$queueForEvents->isPaused()) { + $queueForFunctions + ->from($queueForEvents) + ->trigger(); + } /** * Trigger webhooks. */ diff --git a/app/init.php b/app/init.php index b6c73f9919..68ebba459c 100644 --- a/app/init.php +++ b/app/init.php @@ -227,8 +227,18 @@ const API_KEY_DYNAMIC = 'dynamic'; // Usage metrics const METRIC_TEAMS = 'teams'; const METRIC_USERS = 'users'; + const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone'; const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}'; +const METRIC_MESSAGES = 'messages'; +const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent'; +const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed'; +const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}'; +const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent'; +const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed'; +const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}'; +const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent'; +const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed'; const METRIC_SESSIONS = 'sessions'; const METRIC_DATABASES = 'databases'; const METRIC_COLLECTIONS = 'collections'; diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 991bb09d7d..8f83fed544 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -166,7 +166,7 @@ $image = $this->getParam('image', ''); appwrite-console: <<: *x-logging container_name: appwrite-console - image: /console:5.0.11 + image: /console:5.0.12 restart: unless-stopped networks: - appwrite @@ -336,6 +336,9 @@ $image = $this->getParam('image', ''); - _APP_LOGGING_CONFIG - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_EXECUTION appwrite-worker-databases: image: /: @@ -676,6 +679,7 @@ $image = $this->getParam('image', ''); entrypoint: worker-usage-dump <<: *x-logging container_name: appwrite-worker-usage-dump + restart: unless-stopped networks: - appwrite depends_on: @@ -789,7 +793,7 @@ $image = $this->getParam('image', ''); <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.6.7 + image: openruntimes/executor:0.6.11 networks: - appwrite - runtimes diff --git a/composer.lock b/composer.lock index d717482314..ed6ef1a32f 100644 --- a/composer.lock +++ b/composer.lock @@ -3564,16 +3564,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.2.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", + "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", "shasum": "" }, "require": { @@ -3616,9 +3616,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.2.0" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-09-15T16:40:33+00:00" }, { "name": "phar-io/manifest", diff --git a/docker-compose.yml b/docker-compose.yml index 301ba3564c..6ecb0ecff8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -196,7 +196,7 @@ services: appwrite-console: <<: *x-logging container_name: appwrite-console - image: appwrite/console:5.0.11 + image: appwrite/console:5.0.12 restart: unless-stopped networks: - appwrite @@ -874,7 +874,7 @@ services: hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.6.7 + image: openruntimes/executor:0.6.11 restart: unless-stopped networks: - appwrite diff --git a/public/images/integrations/aws-logo.svg b/public/images/integrations/aws-logo.svg new file mode 100644 index 0000000000..3ab41cde07 --- /dev/null +++ b/public/images/integrations/aws-logo.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 3595be9216..468b30e7a9 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -55,6 +55,7 @@ class Event protected array $context = []; protected ?Document $project = null; protected ?Document $user = null; + protected ?string $userId = null; protected bool $paused = false; /** @@ -153,6 +154,18 @@ class Event return $this; } + /** + * Set user ID for this event. + * + * @return self + */ + public function setUserId(string $userId): self + { + $this->userId = $userId; + + return $this; + } + /** * Get user responsible for triggering this event. * @@ -163,6 +176,14 @@ class Event return $this->user; } + /** + * Get user responsible for triggering this event. + */ + public function getUserId(): ?string + { + return $this->userId; + } + /** * Set payload for this event. * @@ -312,6 +333,7 @@ class Event 'sourceRegion' => $this->getSourceRegion(), 'project' => $this->project, 'user' => $this->user, + 'userId' => $this->userId, 'payload' => $this->payload, 'context' => $this->context, 'events' => Event::generateEvents($this->getEvent(), $this->getParams()) diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index a099f7152c..0bd6cd8192 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -223,6 +223,7 @@ class Func extends Event 'sourceRegion' => $this->getSourceRegion(), 'project' => $this->project, 'user' => $this->user, + 'userId' => $this->userId, 'function' => $this->function, 'functionId' => $this->functionId, 'execution' => $this->execution, diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index f8d1477494..cee1b2d263 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -88,7 +88,8 @@ abstract class Migration '1.5.7' => 'V20', '1.5.8' => 'V20', '1.5.9' => 'V20', - '1.5.10' => 'V20', + '1.5.10' => 'V20', + '1.5.11' => 'V20', '1.6.0' => 'V21', ]; diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 29ca74ee8e..f687cda858 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -164,7 +164,7 @@ abstract class ScheduleBase extends Action $total = $total + $sum; foreach ($results as $document) { - $localDocument = $schedules[$document['resourceId']] ?? null; + $localDocument = $this->schedules[$document->getInternalId()] ?? null; // Check if resource has been updated since last sync $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 2822aea130..1dd09f362c 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -45,23 +45,27 @@ class ScheduleExecutions extends ScheduleBase continue; } + $data = $dbForConsole->getDocument( + 'schedules', + $schedule['$id'], + )->getAttribute('data', []); + $delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp(); - - \go(function () use ($queueForFunctions, $schedule, $delay) { + \go(function () use ($queueForFunctions, $schedule, $delay, $data) { Co::sleep($delay); - $queueForFunctions - ->setType('schedule') + $queueForFunctions->setType('schedule') // Set functionId instead of function as we don't have $dbForProject // TODO: Refactor to use function instead of functionId ->setFunctionId($schedule['resource']['functionId']) ->setExecution($schedule['resource']) - ->setMethod($schedule['data']['method'] ?? 'POST') - ->setPath($schedule['data']['path'] ?? '/') - ->setHeaders($schedule['data']['headers'] ?? []) - ->setBody($schedule['data']['body'] ?? '') + ->setMethod($data['method'] ?? 'POST') + ->setPath($data['path'] ?? '/') + ->setHeaders($data['headers'] ?? []) + ->setBody($data['body'] ?? '') ->setProject($schedule['project']) + ->setUserId($data['userId'] ?? '') ->trigger(); }); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index e03a8ac069..5934855f1b 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -77,12 +77,6 @@ class Functions extends Action throw new Exception('Missing payload'); } - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new Exception('Missing payload'); - } - $type = $payload['type'] ?? ''; $events = $payload['events'] ?? []; $data = $payload['body'] ?? ''; @@ -91,9 +85,23 @@ class Functions extends Action $function = new Document($payload['function'] ?? []); $functionId = $payload['functionId'] ?? ''; $user = new Document($payload['user'] ?? []); + $userId = $payload['userId'] ?? ''; $method = $payload['method'] ?? 'POST'; $headers = $payload['headers'] ?? []; $path = $payload['path'] ?? '/'; + $jwt = $payload['jwt'] ?? ''; + + if ($user->isEmpty() && !empty($userId)) { + $user = $dbForProject->getDocument('users', $userId); + } + + if (empty($jwt) && !$user->isEmpty()) { + $jwtExpiry = $function->getAttribute('timeout', 900); + $jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0); + $jwt = $jwtObj->encode([ + 'userId' => $user->getId(), + ]); + } if ($project->getId() === 'console') { return; @@ -166,7 +174,6 @@ class Functions extends Action */ switch ($type) { case 'http': - $jwt = $payload['jwt'] ?? ''; $execution = new Document($payload['execution'] ?? []); $user = new Document($payload['user'] ?? []); $this->execute( @@ -205,9 +212,9 @@ class Functions extends Action path: $path, method: $method, headers: $headers, - data: null, - user: null, - jwt: null, + data: $data, + user: $user, + jwt: $jwt, event: null, eventData: null, executionId: $execution->getId() ?? null diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 271bbfedf1..b0f05522fa 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -101,7 +101,7 @@ class Messaging extends Action case MESSAGE_SEND_TYPE_EXTERNAL: $message = $dbForProject->getDocument('messages', $payload['messageId']); - $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project); + $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $queueForUsage); break; default: throw new \Exception('Unknown message type: ' . $type); @@ -113,6 +113,7 @@ class Messaging extends Action Document $message, Device $deviceForFiles, Document $project, + Usage $queueForUsage ): void { $topicIds = $message->getAttribute('topics', []); $targetIds = $message->getAttribute('targets', []); @@ -218,8 +219,8 @@ class Messaging extends Action /** * @var array $results */ - $results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project) { - return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project) { + $results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $queueForUsage) { + return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $queueForUsage) { if (\array_key_exists($providerId, $providers)) { $provider = $providers[$providerId]; } else { @@ -246,8 +247,8 @@ class Messaging extends Action $adapter->getMaxMessagesPerRequest() ); - return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $project) { - return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $project) { + return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $queueForUsage) { + return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $queueForUsage) { $deliveredTotal = 0; $deliveryErrors = []; $messageData = clone $message; @@ -286,6 +287,20 @@ class Messaging extends Action } catch (\Throwable $e) { $deliveryErrors[] = 'Failed sending to targets with error: ' . $e->getMessage(); } finally { + $errorTotal = count($deliveryErrors); + $queueForUsage + ->setProject($project) + ->addMetric(METRIC_MESSAGES, ($deliveredTotal + $errorTotal)) + ->addMetric(METRIC_MESSAGES_SENT, $deliveredTotal) + ->addMetric(METRIC_MESSAGES_FAILED, $errorTotal) + ->addMetric(str_replace('{type}', $provider->getAttribute('type'), METRIC_MESSAGES_TYPE), ($deliveredTotal + $errorTotal)) + ->addMetric(str_replace('{type}', $provider->getAttribute('type'), METRIC_MESSAGES_TYPE_SENT), $deliveredTotal) + ->addMetric(str_replace('{type}', $provider->getAttribute('type'), METRIC_MESSAGES_TYPE_FAILED), $errorTotal) + ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER), ($deliveredTotal + $errorTotal)) + ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER_SENT), $deliveredTotal) + ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER_FAILED), $errorTotal) + ->trigger(); + return [ 'deliveredTotal' => $deliveredTotal, 'deliveryErrors' => $deliveryErrors, @@ -318,6 +333,7 @@ class Messaging extends Action $message->setAttribute('status', MessageStatus::SENT); } + $message->removeAttribute('to'); foreach ($providers as $provider) { diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 8448951886..2703691238 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -46,6 +46,18 @@ class UsageProject extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('buildsStorageTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated sum of builds storage size (in bytes).', + 'default' => 0, + 'example' => 0, + ]) + ->addRule('deploymentsStorageTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated sum of deployments storage size (in bytes).', + 'default' => 0, + 'example' => 0, + ]) ->addRule('bucketsTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of buckets.', diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 32e9eb6454..d3623acffc 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -143,7 +143,7 @@ class UsageTest extends Scope ); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(18, count($response['body'])); + $this->assertEquals(20, count($response['body'])); $this->validateDates($response['body']['network']); $this->validateDates($response['body']['requests']); $this->validateDates($response['body']['users']); @@ -324,7 +324,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(18, count($response['body'])); + $this->assertEquals(20, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); $this->validateDates($response['body']['requests']); @@ -545,7 +545,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(18, count($response['body'])); + $this->assertEquals(20, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals(1, count($response['body']['network'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 3c0993be85..92b7c33034 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -164,7 +164,8 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals($executions['body']['executions'][1]['status'], 'completed'); $this->assertEquals($executions['body']['executions'][1]['responseStatusCode'], 200); $this->assertEquals($executions['body']['executions'][1]['responseBody'], ''); - $this->assertEquals($executions['body']['executions'][1]['logs'], ''); + $this->assertNotEmpty($executions['body']['executions'][1]['logs'], ''); + $this->assertNotEmpty($executions['body']['executions'][1]['errors'], ''); $this->assertGreaterThan(0, $executions['body']['executions'][1]['duration']); // Cleanup : Delete function @@ -228,17 +229,22 @@ class FunctionsCustomClientTest extends Scope ], $this->getHeaders()), [ 'async' => true, 'scheduledAt' => $futureTime->format(\DateTime::ATOM), - 'path' => '/custom', - 'method' => 'GET' + 'path' => '/custom-path', + 'method' => 'PATCH', + 'body' => 'custom-body', + 'headers' => [ + 'x-custom-header' => 'custom-value' + ] ]); $this->assertEquals(202, $execution['headers']['status-code']); $this->assertEquals('scheduled', $execution['body']['status']); + $this->assertEquals('PATCH', $execution['body']['requestMethod']); + $this->assertEquals('/custom-path', $execution['body']['requestPath']); + $this->assertCount(0, $execution['body']['requestHeaders']); $executionId = $execution['body']['$id']; - sleep(60 + 60 + 15); // up to 1 minute round up, 1 minute schedule postpone, 15s cold start safety - $start = \microtime(true); while (true) { $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [ @@ -251,7 +257,8 @@ class FunctionsCustomClientTest extends Scope break; } - if (\microtime(true) - $start > 10) { + $timeout = 60 + 60 + 15; // up to 1 minute round up, 1 minute schedule postpone, 15s cold start safety + if (\microtime(true) - $start > $timeout) { $this->fail('Scheduled execution did not complete with status ' . $execution['body']['status'] . ': ' . \json_encode($execution)); } @@ -261,8 +268,14 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(200, $execution['headers']['status-code']); $this->assertEquals(200, $execution['body']['responseStatusCode']); $this->assertEquals('completed', $execution['body']['status']); - $this->assertEquals('/custom', $execution['body']['requestPath']); - $this->assertEquals('GET', $execution['body']['requestMethod']); + $this->assertEquals('/custom-path', $execution['body']['requestPath']); + $this->assertEquals('PATCH', $execution['body']['requestMethod']); + $this->assertStringContainsString('body-is-custom-body', $execution['body']['logs']); + $this->assertStringContainsString('custom-header-is-custom-value', $execution['body']['logs']); + $this->assertStringContainsString('method-is-patch', $execution['body']['logs']); + $this->assertStringContainsString('path-is-/custom-path', $execution['body']['logs']); + $this->assertStringContainsString('user-is-' . $this->getUser()['$id'], $execution['body']['logs']); + $this->assertStringContainsString('jwt-is-valid', $execution['body']['logs']); $this->assertGreaterThan(0, $execution['body']['duration']); /* Test for FAILURE */ diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index f3524c03bd..2958e6cb5f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1179,8 +1179,8 @@ class FunctionsCustomServerTest extends Scope $this->assertStringContainsString('8.0', $execution['body']['responseBody']); $this->assertStringContainsString('Global Variable Value', $execution['body']['responseBody']); // $this->assertStringContainsString('êä', $execution['body']['responseBody']); // tests unknown utf-8 chars - $this->assertEquals('', $execution['body']['errors']); - $this->assertEquals('', $execution['body']['logs']); + $this->assertNotEmpty($execution['body']['errors']); + $this->assertNotEmpty($execution['body']['logs']); $this->assertLessThan(10, $execution['body']['duration']); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ diff --git a/tests/resources/functions/php/index.php b/tests/resources/functions/php/index.php index 76c58e87b5..27a9418b3c 100644 --- a/tests/resources/functions/php/index.php +++ b/tests/resources/functions/php/index.php @@ -1,6 +1,20 @@ log('body-is-' . ($context->req->body ?? '')); + $context->log('custom-header-is-' . ($context->req->headers['x-custom-header'] ?? '')); + $context->log('method-is-' . \strtolower($context->req->method ?? '')); + $context->log('path-is-' . ($context->req->path ?? '')); + $context->log('user-is-' . $context->req->headers['x-appwrite-user-id'] ?? ''); + + if (empty($context->req->headers['x-appwrite-user-jwt'] ?? '')) { + $context->log('jwt-is-invalid'); + } else { + $context->log('jwt-is-valid'); + } + + $context->error('error-log-works'); + $statusCode = $context->req->query['code'] ?? '200'; return $context->res->json([